USB/Thunderbolt patches for 5.18-rc1

Here is the big set of USB and Thunderbolt changes for 5.18-rc1.
 
 Nothing major in here, just lots of little improvements and cleanups and
 new device support.  Highlights are:
 	- list iterator fixups for when we walk past the end of the list
 	  (a common problem that was cut/pasted in almost all USB gadget
 	  drivers)
 	- xen USB driver "hardening" for malicious hosts
 	- xhci driver updates and fixes for more hardware types
 	- xhci debug cable fixes to make it actually work again
 	- usb gadget audio driver improvements
 	- usb gadget storage fixes to work with OS-X
 	- lots of other small usb gadget fixes and updates
 	- USB DWC3 driver improvements for more hardware types
 	- Lots of other small USB driver improvements
 	- DTS updates for some USB platforms
 
 Note, the DTS updates will have a merge conflict in your tree.  The
 fixup should be simple, but if not, I can provide a merged tree if
 needed.
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYj7qpQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylRsQCcCryYifAvE2gHaNfI4B01JRWk7gQAoLNiUgvl
 l+srEAXgIVueDhmDxy5P
 =/Ppv
 -----END PGP SIGNATURE-----

Merge tag 'usb-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB/Thunderbolt updates from Greg KH:
 "Here is the big set of USB and Thunderbolt changes for 5.18-rc1.

  Nothing major in here, just lots of little improvements and cleanups
  and new device support. Highlights are:

   - list iterator fixups for when we walk past the end of the list (a
     common problem that was cut/pasted in almost all USB gadget
     drivers)

   - xen USB driver "hardening" for malicious hosts

   - xhci driver updates and fixes for more hardware types

   - xhci debug cable fixes to make it actually work again

   - usb gadget audio driver improvements

   - usb gadget storage fixes to work with OS-X

   - lots of other small usb gadget fixes and updates

   - USB DWC3 driver improvements for more hardware types

   - Lots of other small USB driver improvements

   - DTS updates for some USB platforms

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (172 commits)
  usb: gadget: fsl_qe_udc: Add missing semicolon in qe_ep_dequeue()
  dt-bindings: usb: mtk-xhci: add compatible for mt8186
  usb: dwc3: Issue core soft reset before enabling run/stop
  usb: gadget: Makefile: remove ccflags-y
  USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c
  usb: gadget: eliminate anonymous module_init & module_exit
  usb: usbip: eliminate anonymous module_init & module_exit
  xen/usb: harden xen_hcd against malicious backends
  usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue
  usb: dwc3: gadget: move cmd_endtransfer to extra function
  usb: dwc3: gadget: ep_queue simplify isoc start condition
  xen/usb: don't use arbitrary_virt_to_machine()
  usb: isp1760: remove redundant max_packet() macro
  usb: oxu210hp-hcd: remove redundant call to max_packet() macro
  usb: common: usb-conn-gpio: Make VBUS supply completely optional
  USB: storage: ums-realtek: fix error code in rts51x_read_mem()
  usb: early: xhci-dbc: Fix xdbc number parsing
  usb: early: xhci-dbc: Remove duplicate keep parsing
  x86/tsc: Be consistent about use_tsc_delay()
  usb: gadget: udc: s3c2410: remove usage of list iterator past the loop body
  ...
This commit is contained in:
Linus Torvalds 2022-03-26 13:08:25 -07:00
commit 710f5d627a
143 changed files with 4886 additions and 1248 deletions

View File

@ -6,7 +6,7 @@ Description:
===================== =======================================
c_chmask capture channel mask
c_srate capture sampling rate
c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
@ -17,7 +17,7 @@ Description:
c_volume_res capture volume control resolution
(in 1/256 dB)
p_chmask playback channel mask
p_srate playback sampling rate
p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
@ -29,4 +29,5 @@ Description:
(in 1/256 dB)
req_number the number of pre-allocated requests
for both capture and playback
function_name name of the interface
===================== =======================================

View File

@ -6,8 +6,9 @@ Description:
===================== =======================================
c_chmask capture channel mask
c_srate capture sampling rate
c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_hs_bint capture bInterval for HS/SS (1-4: fixed, 0: auto)
c_sync capture synchronization type
(async/adaptive)
c_mute_present capture mute control enable
@ -20,8 +21,9 @@ Description:
(in 1/256 dB)
fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
p_srate playback sampling rate
p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_hs_bint playback bInterval for HS/SS (1-4: fixed, 0: auto)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
p_volume_min playback volume control min value
@ -32,4 +34,5 @@ Description:
(in 1/256 dB)
req_number the number of pre-allocated requests
for both capture and playback
function_name name of the interface
===================== =======================================

View File

@ -0,0 +1,9 @@
What: /sys/bus/platform/drivers/eud/.../enable
Date: February 2022
Contact: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
Description:
The Enable/Disable sysfs interface for Embedded
USB Debugger(EUD). This enables and disables the
EUD based on a 1 or a 0 value. By enabling EUD,
the user is able to activate the mini-usb hub of
EUD for debug and trace capabilities.

View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/soc/qcom/qcom,eud.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Qualcomm Embedded USB Debugger
maintainers:
- Souradeep Chowdhury <quic_schowdhu@quicinc.com>
description:
This binding is used to describe the Qualcomm Embedded USB Debugger, which is
mini USB-hub implemented on chip to support USB-based debug capabilities.
properties:
compatible:
items:
- enum:
- qcom,sc7280-eud
- const: qcom,eud
reg:
items:
- description: EUD Base Register Region
- description: EUD Mode Manager Register
interrupts:
description: EUD interrupt
maxItems: 1
ports:
$ref: /schemas/graph.yaml#/properties/ports
description:
These ports is to be attached to the endpoint of the DWC3 controller node
and type C connector node. The controller has the "usb-role-switch"
property.
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: This port is to be attached to the DWC3 controller.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: This port is to be attached to the type C connector.
required:
- compatible
- reg
- ports
additionalProperties: false
examples:
- |
eud@88e0000 {
compatible = "qcom,sc7280-eud","qcom,eud";
reg = <0x88e0000 0x2000>,
<0x88e2000 0x1000>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
eud_ep: endpoint {
remote-endpoint = <&usb2_role_switch>;
};
};
port@1 {
reg = <1>;
eud_con: endpoint {
remote-endpoint = <&con_eud>;
};
};
};
};

View File

@ -1,115 +0,0 @@
Samsung Exynos SoC USB controller
The USB devices interface with USB controllers on Exynos SOCs.
The device node has following properties.
EHCI
Required properties:
- compatible: should be "samsung,exynos4210-ehci" for USB 2.0
EHCI controller in host mode.
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
- clocks: from common clock binding: handle to usb clock.
- clock-names: from common clock binding: Shall be "usbhost".
- phys: from the *Generic PHY* bindings; array specifying phy(s) used
by the root port.
- phy-names: from the *Generic PHY* bindings; array of the names for
each phy for the root ports, must be a subset of the following:
"host", "hsic0", "hsic1".
Optional properties:
- samsung,vbus-gpio: if present, specifies the GPIO that
needs to be pulled up for the bus to be powered.
Example:
usb@12110000 {
compatible = "samsung,exynos4210-ehci";
reg = <0x12110000 0x100>;
interrupts = <0 71 0>;
samsung,vbus-gpio = <&gpx2 6 1 3 3>;
clocks = <&clock 285>;
clock-names = "usbhost";
phys = <&usb2phy 1>;
phy-names = "host";
};
OHCI
Required properties:
- compatible: should be "samsung,exynos4210-ohci" for USB 2.0
OHCI companion controller in host mode.
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
- clocks: from common clock binding: handle to usb clock.
- clock-names: from common clock binding: Shall be "usbhost".
- phys: from the *Generic PHY* bindings; array specifying phy(s) used
by the root port.
- phy-names: from the *Generic PHY* bindings; array of the names for
each phy for the root ports, must be a subset of the following:
"host", "hsic0", "hsic1".
Example:
usb@12120000 {
compatible = "samsung,exynos4210-ohci";
reg = <0x12120000 0x100>;
interrupts = <0 71 0>;
clocks = <&clock 285>;
clock-names = "usbhost";
phys = <&usb2phy 1>;
phy-names = "host";
};
DWC3
Required properties:
- compatible: should be one of the following -
"samsung,exynos5250-dwusb3": for USB 3.0 DWC3 controller on
Exynos5250/5420.
"samsung,exynos5433-dwusb3": for USB 3.0 DWC3 controller on
Exynos5433.
"samsung,exynos7-dwusb3": for USB 3.0 DWC3 controller on Exynos7.
- #address-cells, #size-cells : should be '1' if the device has sub-nodes
with 'reg' property.
- ranges: allows valid 1:1 translation between child's address space and
parent's address space
- clocks: Clock IDs array as required by the controller.
- clock-names: Names of clocks corresponding to IDs in the clock property.
Following clock names shall be provided for different
compatibles:
- samsung,exynos5250-dwusb3: "usbdrd30",
- samsung,exynos5433-dwusb3: "aclk", "susp_clk", "pipe_pclk",
"phyclk",
- samsung,exynos7-dwusb3: "usbdrd30", "usbdrd30_susp_clk",
"usbdrd30_axius_clk"
- vdd10-supply: 1.0V powr supply
- vdd33-supply: 3.0V/3.3V power supply
Sub-nodes:
The dwc3 core should be added as subnode to Exynos dwc3 glue.
- dwc3 :
The binding details of dwc3 can be found in:
Documentation/devicetree/bindings/usb/snps,dwc3.yaml
Example:
usb@12000000 {
compatible = "samsung,exynos5250-dwusb3";
clocks = <&clock 286>;
clock-names = "usbdrd30";
#address-cells = <1>;
#size-cells = <1>;
ranges;
vdd10-supply = <&ldo11_reg>;
vdd33-supply = <&ldo9_reg>;
dwc3 {
compatible = "synopsys,dwc3";
reg = <0x12000000 0x10000>;
interrupts = <0 72 0>;
usb-phy = <&usb2_phy &usb3_phy>;
};
};

View File

@ -15,9 +15,9 @@ properties:
const: fsl,imx8mp-dwc3
reg:
maxItems: 1
description: Address and length of the register set for the wrapper of
dwc3 core on the SOC.
items:
- description: Address and length of the register set for HSIO Block Control
- description: Address and length of the register set for the wrapper of dwc3 core on the SOC.
"#address-cells":
enum: [ 1, 2 ]
@ -49,6 +49,28 @@ properties:
- const: hsio
- const: suspend
fsl,permanently-attached:
type: boolean
description:
Indicates if the device atached to a downstream port is
permanently attached.
fsl,disable-port-power-control:
type: boolean
description:
Indicates whether the host controller implementation includes port
power control. Defines Bit 3 in capability register (HCCPARAMS).
fsl,over-current-active-low:
type: boolean
description:
Over current signal polarity is active low.
fsl,power-active-low:
type: boolean
description:
Power pad (PWR) polarity is active low.
# Required child node:
patternProperties:
@ -74,7 +96,8 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb3_0: usb@32f10100 {
compatible = "fsl,imx8mp-dwc3";
reg = <0x32f10100 0x8>;
reg = <0x32f10100 0x8>,
<0x381f0000 0x20>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "hsio", "suspend";

View File

@ -30,6 +30,7 @@ properties:
- mediatek,mt7629-xhci
- mediatek,mt8173-xhci
- mediatek,mt8183-xhci
- mediatek,mt8186-xhci
- mediatek,mt8192-xhci
- mediatek,mt8195-xhci
- const: mediatek,mtk-xhci
@ -146,7 +147,11 @@ properties:
2 - used by mt2712 etc, revision 2 following IPM rule;
101 - used by mt8183, specific 1.01;
102 - used by mt8192, specific 1.02;
enum: [1, 2, 101, 102]
103 - used by mt8195, IP0, specific 1.03;
104 - used by mt8195, IP1, specific 1.04;
105 - used by mt8195, IP2, specific 1.05;
106 - used by mt8195, IP3, specific 1.06;
enum: [1, 2, 101, 102, 103, 104, 105, 106]
mediatek,u3p-dis-msk:
$ref: /schemas/types.yaml#/definitions/uint32

View File

@ -0,0 +1,59 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/microchip,mpfs-musb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip MPFS USB Controller Device Tree Bindings
allOf:
- $ref: usb-drd.yaml#
maintainers:
- Conor Dooley <conor.dooley@microchip.com>
properties:
compatible:
enum:
- microchip,mpfs-musb
dr_mode: true
reg:
maxItems: 1
interrupts:
minItems: 2
maxItems: 2
interrupt-names:
items:
- const: dma
- const: mc
clocks:
maxItems: 1
required:
- compatible
- reg
- interrupts
- interrupt-names
- clocks
additionalProperties: false
examples:
- |
#include "dt-bindings/clock/microchip,mpfs-clock.h"
usb@20201000 {
compatible = "microchip,mpfs-musb";
reg = <0x20201000 0x1000>;
clocks = <&clkcfg CLK_USB>;
interrupt-parent = <&plic>;
interrupts = <86>, <87>;
interrupt-names = "dma", "mc";
dr_mode = "host";
};
...

View File

@ -16,6 +16,7 @@ properties:
- qcom,ipq4019-dwc3
- qcom,ipq6018-dwc3
- qcom,ipq8064-dwc3
- qcom,msm8953-dwc3
- qcom,msm8996-dwc3
- qcom,msm8998-dwc3
- qcom,sc7180-dwc3

View File

@ -0,0 +1,85 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: "http://devicetree.org/schemas/usb/richtek,rt1719.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Richtek RT1719 sink-only Type-C PD controller bindings
maintainers:
- ChiYuan Huang <cy_huang@richtek.com>
description: |
The RT1719 is a sink-only USB Type-C contoller that complies with the latest
USB Type-C and PD standards. It does the USB Type-C detection including attach
and orientation. It integrates the physical layer of the USB BMC power
delivery protocol to allow up to 100W of power. The BMC PD block enables full
support for alternative interfaces of the Type-C specification.
properties:
compatible:
enum:
- richtek,rt1719
reg:
maxItems: 1
interrupts:
maxItems: 1
wakeup-source:
description: enable IRQ remote wakeup, see power/wakeup-source.txt
type: boolean
connector:
type: object
$ref: ../connector/usb-connector.yaml#
description:
Properties for usb c connector.
additionalProperties: false
required:
- compatible
- reg
- connector
- interrupts
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
rt1719@43 {
compatible = "richtek,rt1719";
reg = <0x43>;
interrupts-extended = <&gpio26 2 IRQ_TYPE_LEVEL_LOW>;
wakeup-source;
connector {
compatible = "usb-c-connector";
label = "USB-C";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
endpoint {
remote-endpoint = <&usb_hs>;
};
};
port@1 {
reg = <1>;
endpoint {
remote-endpoint = <&usb_ss>;
};
};
};
};
};
};
...

View File

@ -30,6 +30,7 @@ select:
enum:
- rockchip,rk3328-dwc3
- rockchip,rk3399-dwc3
- rockchip,rk3568-dwc3
required:
- compatible
@ -39,6 +40,7 @@ properties:
- enum:
- rockchip,rk3328-dwc3
- rockchip,rk3399-dwc3
- rockchip,rk3568-dwc3
- const: snps,dwc3
reg:

View File

@ -0,0 +1,129 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/samsung,exynos-dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC USB 3.0 DWC3 Controller
maintainers:
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
properties:
compatible:
enum:
- samsung,exynos5250-dwusb3
- samsung,exynos5433-dwusb3
- samsung,exynos7-dwusb3
'#address-cells':
const: 1
clocks:
minItems: 1
maxItems: 4
clock-names:
minItems: 1
maxItems: 4
ranges: true
'#size-cells':
const: 1
vdd10-supply:
description: 1.0V power supply
vdd33-supply:
description: 3.0V/3.3V power supply
patternProperties:
"^usb@[0-9a-f]+$":
$ref: snps,dwc3.yaml#
description: Required child node
required:
- compatible
- '#address-cells'
- clocks
- clock-names
- ranges
- '#size-cells'
- vdd10-supply
- vdd33-supply
allOf:
- if:
properties:
compatible:
contains:
const: samsung,exynos5250-dwusb3
then:
properties:
clocks:
minItems: 1
maxItems: 1
clock-names:
items:
- const: usbdrd30
- if:
properties:
compatible:
contains:
const: samsung,exynos54333-dwusb3
then:
properties:
clocks:
minItems: 4
maxItems: 4
clock-names:
items:
- const: aclk
- const: susp_clk
- const: pipe_pclk
- const: phyclk
- if:
properties:
compatible:
contains:
const: samsung,exynos7-dwusb3
then:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
items:
- const: usbdrd30
- const: usbdrd30_susp_clk
- const: usbdrd30_axius_clk
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/exynos5420.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb {
compatible = "samsung,exynos5250-dwusb3";
#address-cells = <1>;
#size-cells = <1>;
ranges;
clocks = <&clock CLK_USBD300>;
clock-names = "usbdrd30";
vdd33-supply = <&ldo9_reg>;
vdd10-supply = <&ldo11_reg>;
usb@12000000 {
compatible = "snps,dwc3";
reg = <0x12000000 0x10000>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usbdrd_phy0 0>, <&usbdrd_phy0 1>;
phy-names = "usb2-phy", "usb3-phy";
snps,dis_u3_susphy_quirk;
};
};

View File

@ -0,0 +1,117 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/samsung,exynos-usb2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC USB 2.0 EHCI/OHCI Controller
maintainers:
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
properties:
compatible:
enum:
- samsung,exynos4210-ehci
- samsung,exynos4210-ohci
'#address-cells':
const: 1
clocks:
maxItems: 1
clock-names:
items:
- const: usbhost
interrupts:
maxItems: 1
phys:
minItems: 1
maxItems: 3
phy-names:
items:
enum: [host, hsic0, hsic1]
minItems: 1
maxItems: 3
reg:
maxItems: 1
samsung,vbus-gpio:
description:
Only for controller in EHCI mode, if present, specifies the GPIO that
needs to be pulled up for the bus to be powered.
'#size-cells':
const: 0
patternProperties:
"^.*@[0-9a-f]{1,2}$":
description: The hard wired USB devices
type: object
$ref: /usb/usb-device.yaml
required:
- compatible
- clocks
- clock-names
- interrupts
- phys
- phy-names
allOf:
- if:
properties:
compatible:
contains:
const: samsung,exynos4210-ohci
then:
properties:
samsung,vbus-gpio: false
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/exynos5420.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb@12110000 {
compatible = "samsung,exynos4210-ehci";
reg = <0x12110000 0x100>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clock CLK_USBH20>;
clock-names = "usbhost";
phys = <&usb2_phy 0>;
phy-names = "host";
#address-cells = <1>;
#size-cells = <0>;
hub@1 {
compatible = "usb0424,9514";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
usbether@1 {
compatible = "usb0424,ec00";
reg = <1>;
local-mac-address = [00 00 00 00 00 00];
};
};
};
usb@12120000 {
compatible = "samsung,exynos4210-ohci";
reg = <0x12120000 0x100>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clock CLK_USBH20>;
clock-names = "usbhost";
phys = <&usb2_phy 0>;
phy-names = "host";
};

View File

@ -263,8 +263,11 @@ properties:
Value for REFCLKPER field of GUCTL register for reference clock period in
nanoseconds, when the hardware set default does not match the actual
clock.
minimum: 1
maximum: 0x3ff
This binding is deprecated. Instead, provide an appropriate reference clock.
minimum: 8
maximum: 62
deprecated: true
snps,rx-thr-num-pkt-prd:
description:
@ -332,6 +335,12 @@ properties:
items:
enum: [1, 4, 8, 16, 32, 64, 128, 256]
port:
$ref: /schemas/graph.yaml#/properties/port
description:
This port is used with the 'usb-role-switch' property to connect the
dwc3 to type C connector.
unevaluatedProperties: false
required:

View File

@ -33,7 +33,7 @@ patternProperties:
"^.*@[0-9a-f]{1,2}$":
description: The hard wired USB devices
type: object
$ref: /usb/usb-device.yaml
$ref: /schemas/usb/usb-device.yaml
additionalProperties: true

View File

@ -0,0 +1,75 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/willsemi,wusb3801.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WUSB3801 Type-C port controller DT bindings
description:
The Will Semiconductor WUSB3801 is a USB Type-C port controller which
supports role and plug orientation detection using the CC pins. It is
compatible with the USB Type-C Cable and Connector Specification v1.2.
maintainers:
- Samuel Holland <samuel@sholland.org>
properties:
compatible:
enum:
- willsemi,wusb3801
reg:
maxItems: 1
interrupts:
maxItems: 1
connector:
type: object
$ref: ../connector/usb-connector.yaml#
description:
The managed USB Type-C connector. Since WUSB3801 does not support
Power Delivery, the node should have the "pd-disable" property.
properties:
compatible:
const: usb-c-connector
required:
- pd-disable
required:
- compatible
- reg
- interrupts
- connector
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
tcpc@60 {
compatible = "willsemi,wusb3801";
reg = <0x60>;
interrupt-parent = <&gpio0>;
interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
connector {
compatible = "usb-c-connector";
label = "USB-C";
vbus-supply = <&otg_switch>;
power-role = "dual";
try-power-role = "sink";
data-role = "dual";
typec-power-opmode = "default";
pd-disable;
};
};
};

View File

@ -1371,6 +1371,8 @@ patternProperties:
description: Wi2Wi, Inc.
"^wiligear,.*":
description: Wiligear, Ltd.
"^willsemi,.*":
description: Will Semiconductor Ltd.
"^winbond,.*":
description: Winbond Electronics corp.
"^wingtech,.*":

View File

@ -726,7 +726,7 @@ The uac2 function provides these attributes in its function directory:
================ ====================================================
c_chmask capture channel mask
c_srate capture sampling rate
c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_sync capture synchronization type (async/adaptive)
c_mute_present capture mute control enable
@ -734,17 +734,20 @@ The uac2 function provides these attributes in its function directory:
c_volume_min capture volume control min value (in 1/256 dB)
c_volume_max capture volume control max value (in 1/256 dB)
c_volume_res capture volume control resolution (in 1/256 dB)
c_hs_bint capture bInterval for HS/SS (1-4: fixed, 0: auto)
fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask
p_srate playback sampling rate
p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
p_volume_min playback volume control min value (in 1/256 dB)
p_volume_max playback volume control max value (in 1/256 dB)
p_volume_res playback volume control resolution (in 1/256 dB)
p_hs_bint playback bInterval for HS/SS (1-4: fixed, 0: auto)
req_number the number of pre-allocated request for both capture
and playback
function_name name of the interface
================ ====================================================
The attributes have sane default values.
@ -916,7 +919,7 @@ The uac1 function provides these attributes in its function directory:
================ ====================================================
c_chmask capture channel mask
c_srate capture sampling rate
c_srate list of capture sampling rates (comma-separated)
c_ssize capture sample size (bytes)
c_mute_present capture mute control enable
c_volume_present capture volume control enable
@ -924,7 +927,7 @@ The uac1 function provides these attributes in its function directory:
c_volume_max capture volume control max value (in 1/256 dB)
c_volume_res capture volume control resolution (in 1/256 dB)
p_chmask playback channel mask
p_srate playback sampling rate
p_srate list of playback sampling rates (comma-separated)
p_ssize playback sample size (bytes)
p_mute_present playback mute control enable
p_volume_present playback volume control enable
@ -933,6 +936,7 @@ The uac1 function provides these attributes in its function directory:
p_volume_res playback volume control resolution (in 1/256 dB)
req_number the number of pre-allocated requests for both capture
and playback
function_name name of the interface
================ ====================================================
The attributes have sane default values.

View File

@ -15917,6 +15917,14 @@ F: sound/soc/codecs/wcd-clsh-v2.*
F: sound/soc/codecs/wsa881x.c
F: sound/soc/qcom/
QCOM EMBEDDED USB DEBUGGER (EUD)
M: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-driver-eud
F: Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml
F: drivers/usb/misc/qcom_eud.c
QCOM IPA DRIVER
M: Alex Elder <elder@kernel.org>
L: netdev@vger.kernel.org

View File

@ -921,7 +921,8 @@
usb3_0: usb@32f10100 {
compatible = "fsl,imx8mp-dwc3";
reg = <0x32f10100 0x8>;
reg = <0x32f10100 0x8>,
<0x381f0000 0x20>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "hsio", "suspend";
@ -963,7 +964,8 @@
usb3_1: usb@32f10108 {
compatible = "fsl,imx8mp-dwc3";
reg = <0x32f10108 0x8>;
reg = <0x32f10108 0x8>,
<0x382f0000 0x20>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "hsio", "suspend";

View File

@ -752,12 +752,13 @@
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
phys = <&qusb_phy_0>, <&usb0_ssphy>;
phy-names = "usb2-phy", "usb3-phy";
clocks = <&xo>;
clock-names = "ref";
tx-fifo-resize;
snps,is-utmi-l1-suspend;
snps,hird-threshold = /bits/ 8 <0x0>;
snps,dis_u2_susphy_quirk;
snps,dis_u3_susphy_quirk;
snps,ref-clock-period-ns = <0x29>;
dr_mode = "host";
};
};

View File

@ -223,11 +223,11 @@
clocks = <&zynqmp_clk UART1_REF>, <&zynqmp_clk LPD_LSBUS>;
};
&usb0 {
&dwc3_0 {
clocks = <&zynqmp_clk USB0_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>;
};
&usb1 {
&dwc3_1 {
clocks = <&zynqmp_clk USB1_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>;
};

View File

@ -809,7 +809,6 @@
status = "disabled";
compatible = "xlnx,zynqmp-dwc3";
reg = <0x0 0xff9d0000 0x0 0x100>;
clock-names = "bus_clk", "ref_clk";
power-domains = <&zynqmp_firmware PD_USB_0>;
resets = <&zynqmp_reset ZYNQMP_RESET_USB0_CORERESET>,
<&zynqmp_reset ZYNQMP_RESET_USB0_HIBERRESET>,
@ -823,6 +822,7 @@
interrupt-parent = <&gic>;
interrupt-names = "dwc_usb3", "otg";
interrupts = <0 65 4>, <0 69 4>;
clock-names = "bus_early", "ref";
iommus = <&smmu 0x860>;
snps,quirk-frame-length-adjustment = <0x20>;
/* dma-coherent; */
@ -835,7 +835,6 @@
status = "disabled";
compatible = "xlnx,zynqmp-dwc3";
reg = <0x0 0xff9e0000 0x0 0x100>;
clock-names = "bus_clk", "ref_clk";
power-domains = <&zynqmp_firmware PD_USB_1>;
resets = <&zynqmp_reset ZYNQMP_RESET_USB1_CORERESET>,
<&zynqmp_reset ZYNQMP_RESET_USB1_HIBERRESET>,
@ -849,6 +848,7 @@
interrupt-parent = <&gic>;
interrupt-names = "dwc_usb3", "otg";
interrupts = <0 70 4>, <0 74 4>;
clock-names = "bus_early", "ref";
iommus = <&smmu 0x861>;
snps,quirk-frame-length-adjustment = <0x20>;
/* dma-coherent; */

View File

@ -387,7 +387,7 @@ static int __init setup_early_printk(char *buf)
#endif
#ifdef CONFIG_EARLY_PRINTK_USB_XDBC
if (!strncmp(buf, "xdbc", 4))
early_xdbc_parse_parameter(buf + 4);
early_xdbc_parse_parameter(buf + 4, keep);
#endif
buf++;

View File

@ -1487,6 +1487,9 @@ static unsigned long __init get_loops_per_jiffy(void)
static void __init tsc_enable_sched_clock(void)
{
loops_per_jiffy = get_loops_per_jiffy();
use_tsc_delay();
/* Sanitize TSC ADJUST before cyc2ns gets initialized */
tsc_store_and_check_tsc_adjust(true);
cyc2ns_init_boot_cpu();
@ -1502,8 +1505,6 @@ void __init tsc_early_init(void)
return;
if (!determine_cpu_tsc_frequencies(true))
return;
loops_per_jiffy = get_loops_per_jiffy();
tsc_enable_sched_clock();
}
@ -1537,7 +1538,6 @@ void __init tsc_init(void)
enable_sched_clock_irqtime();
lpj_fine = get_loops_per_jiffy();
use_tsc_delay();
check_system_tsc_reliable();

View File

@ -14,15 +14,15 @@
static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
void **return_value)
{
struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
struct fwnode_reference_args args;
struct fwnode_handle *fwnode;
struct tb_nhi *nhi = data;
struct acpi_device *adev;
struct pci_dev *pdev;
struct device *dev;
int ret;
if (acpi_bus_get_device(handle, &adev))
if (!adev)
return AE_OK;
fwnode = acpi_fwnode_handle(adev);

View File

@ -17,7 +17,7 @@
*/
static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
{
return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
}
/*
@ -25,7 +25,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
*/
static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
{
return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
}
enum tb_eeprom_transfer {
@ -46,18 +46,18 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
if (res)
return res;
if (enable) {
ctl.access_high = 1;
ctl.bit_banging_enable = 1;
res = tb_eeprom_ctl_write(sw, &ctl);
if (res)
return res;
ctl.access_low = 0;
ctl.fl_cs = 0;
return tb_eeprom_ctl_write(sw, &ctl);
} else {
ctl.access_low = 1;
ctl.fl_cs = 1;
res = tb_eeprom_ctl_write(sw, &ctl);
if (res)
return res;
ctl.access_high = 0;
ctl.bit_banging_enable = 0;
return tb_eeprom_ctl_write(sw, &ctl);
}
}
@ -65,8 +65,8 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
/*
* tb_eeprom_transfer - transfer one bit
*
* If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in.
* If TB_EEPROM_OUT is passed, then ctl->data_out will be written.
* If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->fl_do.
* If TB_EEPROM_OUT is passed, then ctl->fl_di will be written.
*/
static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
enum tb_eeprom_transfer direction)
@ -77,7 +77,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
if (res)
return res;
}
ctl->clock = 1;
ctl->fl_sk = 1;
res = tb_eeprom_ctl_write(sw, ctl);
if (res)
return res;
@ -86,7 +86,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
if (res)
return res;
}
ctl->clock = 0;
ctl->fl_sk = 0;
return tb_eeprom_ctl_write(sw, ctl);
}
@ -101,7 +101,7 @@ static int tb_eeprom_out(struct tb_switch *sw, u8 val)
if (res)
return res;
for (i = 0; i < 8; i++) {
ctl.data_out = val & 0x80;
ctl.fl_di = val & 0x80;
res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_OUT);
if (res)
return res;
@ -126,7 +126,7 @@ static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_IN);
if (res)
return res;
*val |= ctl.data_in;
*val |= ctl.fl_do;
}
return 0;
}
@ -553,9 +553,9 @@ static int tb_drom_parse(struct tb_switch *sw)
crc = tb_crc8((u8 *) &header->uid, 8);
if (crc != header->uid_crc8) {
tb_sw_warn(sw,
"DROM UID CRC8 mismatch (expected: %#x, got: %#x), aborting\n",
"DROM UID CRC8 mismatch (expected: %#x, got: %#x)\n",
header->uid_crc8, crc);
return -EINVAL;
return -EILSEQ;
}
if (!sw->uid)
sw->uid = header->uid;
@ -654,6 +654,7 @@ int tb_drom_read(struct tb_switch *sw)
sw->drom = kzalloc(size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
read:
res = tb_drom_read_n(sw, 0, sw->drom, size);
if (res)
goto err;
@ -662,7 +663,11 @@ parse:
header = (void *) sw->drom;
if (header->data_len + TB_DROM_DATA_START != size) {
tb_sw_warn(sw, "drom size mismatch, aborting\n");
tb_sw_warn(sw, "drom size mismatch\n");
if (retries--) {
msleep(100);
goto read;
}
goto err;
}
@ -683,11 +688,9 @@ parse:
/* If the DROM parsing fails, wait a moment and retry once */
if (res == -EILSEQ && retries--) {
tb_sw_warn(sw, "parsing DROM failed, retrying\n");
tb_sw_warn(sw, "parsing DROM failed\n");
msleep(100);
res = tb_drom_read_n(sw, 0, sw->drom, size);
if (!res)
goto parse;
goto read;
}
if (!res)

View File

@ -217,6 +217,116 @@ bool tb_lc_is_clx_supported(struct tb_port *port)
return !!(val & TB_LC_LINK_ATTR_CPS);
}
/**
* tb_lc_is_usb_plugged() - Is there USB device connected to port
* @port: Device router lane 0 adapter
*
* Returns true if the @port has USB type-C device connected.
*/
bool tb_lc_is_usb_plugged(struct tb_port *port)
{
struct tb_switch *sw = port->sw;
int cap, ret;
u32 val;
if (sw->generation != 3)
return false;
cap = find_port_lc_cap(port);
if (cap < 0)
return false;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_CS_42, 1);
if (ret)
return false;
return !!(val & TB_LC_CS_42_USB_PLUGGED);
}
/**
* tb_lc_is_xhci_connected() - Is the internal xHCI connected
* @port: Device router lane 0 adapter
*
* Returns true if the internal xHCI has been connected to @port.
*/
bool tb_lc_is_xhci_connected(struct tb_port *port)
{
struct tb_switch *sw = port->sw;
int cap, ret;
u32 val;
if (sw->generation != 3)
return false;
cap = find_port_lc_cap(port);
if (cap < 0)
return false;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
if (ret)
return false;
return !!(val & TB_LC_LINK_REQ_XHCI_CONNECT);
}
static int __tb_lc_xhci_connect(struct tb_port *port, bool connect)
{
struct tb_switch *sw = port->sw;
int cap, ret;
u32 val;
if (sw->generation != 3)
return -EINVAL;
cap = find_port_lc_cap(port);
if (cap < 0)
return cap;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
if (ret)
return ret;
if (connect)
val |= TB_LC_LINK_REQ_XHCI_CONNECT;
else
val &= ~TB_LC_LINK_REQ_XHCI_CONNECT;
return tb_sw_write(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
}
/**
* tb_lc_xhci_connect() - Connect internal xHCI
* @port: Device router lane 0 adapter
*
* Tells LC to connect the internal xHCI to @port. Returns %0 on success
* and negative errno in case of failure. Can be called for Thunderbolt 3
* routers only.
*/
int tb_lc_xhci_connect(struct tb_port *port)
{
int ret;
ret = __tb_lc_xhci_connect(port, true);
if (ret)
return ret;
tb_port_dbg(port, "xHCI connected\n");
return 0;
}
/**
* tb_lc_xhci_disconnect() - Disconnect internal xHCI
* @port: Device router lane 0 adapter
*
* Tells LC to disconnect the internal xHCI from @port. Can be called
* for Thunderbolt 3 routers only.
*/
void tb_lc_xhci_disconnect(struct tb_port *port)
{
__tb_lc_xhci_connect(port, false);
tb_port_dbg(port, "xHCI disconnected\n");
}
static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
unsigned int flags)
{

View File

@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/delay.h>
@ -1229,8 +1230,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
spin_lock_init(&nhi->lock);
res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (res)
res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (res) {
dev_err(&pdev->dev, "failed to set DMA mask\n");
return res;

View File

@ -1528,7 +1528,13 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active)
case PCI_DEVICE_ID_INTEL_PORT_RIDGE:
break;
default:
data |= 4;
/*
* Skip Alpine Ridge, it needs to have vendor
* specific USB hotplug event enabled for the
* internal xHCI to work.
*/
if (!tb_switch_is_alpine_ridge(sw))
data |= TB_PLUG_EVENTS_USB_DISABLE;
}
} else {
data = data | 0x7c;
@ -2778,10 +2784,8 @@ int tb_switch_add(struct tb_switch *sw)
/* read drom */
ret = tb_drom_read(sw);
if (ret) {
dev_err(&sw->dev, "reading DROM failed\n");
return ret;
}
if (ret)
dev_warn(&sw->dev, "reading DROM failed: %d\n", ret);
tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);
tb_check_quirks(sw);
@ -2974,6 +2978,10 @@ int tb_switch_resume(struct tb_switch *sw)
return err;
}
/* We don't have any way to confirm this was the same device */
if (!sw->uid)
return -ENODEV;
if (tb_switch_is_usb4(sw))
err = usb4_switch_read_uid(sw, &uid);
else
@ -3689,3 +3697,66 @@ int tb_switch_pcie_l1_enable(struct tb_switch *sw)
/* Write to Upstream PCIe bridge #0 aka Up0 */
return tb_switch_pcie_bridge_write(sw, 0, 0x143, 0x0c5806b1);
}
/**
* tb_switch_xhci_connect() - Connect internal xHCI
* @sw: Router whose xHCI to connect
*
* Can be called to any router. For Alpine Ridge and Titan Ridge
* performs special flows that bring the xHCI functional for any device
* connected to the type-C port. Call only after PCIe tunnel has been
* established. The function only does the connect if not done already
* so can be called several times for the same router.
*/
int tb_switch_xhci_connect(struct tb_switch *sw)
{
bool usb_port1, usb_port3, xhci_port1, xhci_port3;
struct tb_port *port1, *port3;
int ret;
port1 = &sw->ports[1];
port3 = &sw->ports[3];
if (tb_switch_is_alpine_ridge(sw)) {
usb_port1 = tb_lc_is_usb_plugged(port1);
usb_port3 = tb_lc_is_usb_plugged(port3);
xhci_port1 = tb_lc_is_xhci_connected(port1);
xhci_port3 = tb_lc_is_xhci_connected(port3);
/* Figure out correct USB port to connect */
if (usb_port1 && !xhci_port1) {
ret = tb_lc_xhci_connect(port1);
if (ret)
return ret;
}
if (usb_port3 && !xhci_port3)
return tb_lc_xhci_connect(port3);
} else if (tb_switch_is_titan_ridge(sw)) {
ret = tb_lc_xhci_connect(port1);
if (ret)
return ret;
return tb_lc_xhci_connect(port3);
}
return 0;
}
/**
* tb_switch_xhci_disconnect() - Disconnect internal xHCI
* @sw: Router whose xHCI to disconnect
*
* The opposite of tb_switch_xhci_connect(). Disconnects xHCI on both
* ports.
*/
void tb_switch_xhci_disconnect(struct tb_switch *sw)
{
if (sw->generation == 3) {
struct tb_port *port1 = &sw->ports[1];
struct tb_port *port3 = &sw->ports[3];
tb_lc_xhci_disconnect(port1);
tb_port_dbg(port1, "disconnected xHCI\n");
tb_lc_xhci_disconnect(port3);
tb_port_dbg(port3, "disconnected xHCI\n");
}
}

View File

@ -1054,6 +1054,8 @@ static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
if (WARN_ON(!tunnel))
return -ENODEV;
tb_switch_xhci_disconnect(sw);
tb_tunnel_deactivate(tunnel);
list_del(&tunnel->list);
tb_tunnel_free(tunnel);
@ -1099,6 +1101,9 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
if (tb_switch_pcie_l1_enable(sw))
tb_sw_warn(sw, "failed to enable PCIe L1 for Titan Ridge\n");
if (tb_switch_xhci_connect(sw))
tb_sw_warn(sw, "failed to connect xHCI\n");
list_add_tail(&tunnel->list, &tcm->tunnel_list);
return 0;
}
@ -1256,12 +1261,18 @@ static void tb_handle_hotplug(struct work_struct *work)
tb_port_unconfigure_xdomain(port);
} else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
tb_dp_resource_unavailable(tb, port);
} else if (!port->port) {
tb_sw_dbg(sw, "xHCI disconnect request\n");
tb_switch_xhci_disconnect(sw);
} else {
tb_port_dbg(port,
"got unplug event for disconnected port, ignoring\n");
}
} else if (port->remote) {
tb_port_dbg(port, "got plug event for connected port, ignoring\n");
} else if (!port->port && sw->authorized) {
tb_sw_dbg(sw, "xHCI connect request\n");
tb_switch_xhci_connect(sw);
} else {
if (tb_port_is_null(port)) {
tb_port_dbg(port, "hotplug: scanning\n");

View File

@ -855,6 +855,7 @@ static inline bool tb_switch_is_alpine_ridge(const struct tb_switch *sw)
if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) {
switch (sw->config.device_id) {
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE:
@ -987,6 +988,9 @@ int tb_switch_mask_clx_objections(struct tb_switch *sw);
int tb_switch_pcie_l1_enable(struct tb_switch *sw);
int tb_switch_xhci_connect(struct tb_switch *sw);
void tb_switch_xhci_disconnect(struct tb_switch *sw);
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
int tb_port_add_nfc_credits(struct tb_port *port, int credits);
int tb_port_clear_counter(struct tb_port *port, int counter);
@ -1081,6 +1085,10 @@ int tb_lc_configure_xdomain(struct tb_port *port);
void tb_lc_unconfigure_xdomain(struct tb_port *port);
int tb_lc_start_lane_initialization(struct tb_port *port);
bool tb_lc_is_clx_supported(struct tb_port *port);
bool tb_lc_is_usb_plugged(struct tb_port *port);
bool tb_lc_is_xhci_connected(struct tb_port *port);
int tb_lc_xhci_connect(struct tb_port *port);
void tb_lc_xhci_disconnect(struct tb_port *port);
int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
int tb_lc_set_sleep(struct tb_switch *sw);
bool tb_lc_lane_bonding_possible(struct tb_switch *sw);

View File

@ -133,11 +133,11 @@ struct tb_cap_phy {
} __packed;
struct tb_eeprom_ctl {
bool clock:1; /* send pulse to transfer one bit */
bool access_low:1; /* set to 0 before access */
bool data_out:1; /* to eeprom */
bool data_in:1; /* from eeprom */
bool access_high:1; /* set to 1 before access */
bool fl_sk:1; /* send pulse to transfer one bit */
bool fl_cs:1; /* set to 0 before access */
bool fl_di:1; /* to eeprom */
bool fl_do:1; /* from eeprom */
bool bit_banging_enable:1; /* set to 1 before access */
bool not_present:1; /* should be 0 */
bool unknown1:1;
bool present:1; /* should be 1 */
@ -146,14 +146,14 @@ struct tb_eeprom_ctl {
struct tb_cap_plug_events {
struct tb_cap_extended_short cap_header;
u32 __unknown1:2;
u32 plug_events:5;
u32 __unknown2:25;
u32 __unknown3;
u32 __unknown4;
u32 __unknown1:2; /* VSC_CS_1 */
u32 plug_events:5; /* VSC_CS_1 */
u32 __unknown2:25; /* VSC_CS_1 */
u32 vsc_cs_2;
u32 vsc_cs_3;
struct tb_eeprom_ctl eeprom_ctl;
u32 __unknown5[7];
u32 drom_offset; /* 32 bit register, but eeprom addresses are 16 bit */
u32 __unknown5[7]; /* VSC_CS_5 -> VSC_CS_11 */
u32 drom_offset; /* VSC_CS_12: 32 bit register, but eeprom addresses are 16 bit */
} __packed;
/* device headers */
@ -389,6 +389,7 @@ struct tb_regs_port_header {
#define DP_COMMON_CAP_1_LANE 0x0
#define DP_COMMON_CAP_2_LANES 0x1
#define DP_COMMON_CAP_4_LANES 0x2
#define DP_COMMON_CAP_LTTPR_NS BIT(27)
#define DP_COMMON_CAP_DPRX_DONE BIT(31)
/* PCIe adapter registers */
@ -462,6 +463,12 @@ struct tb_regs_hop {
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2 BIT(3)
/* Plug Events registers */
#define TB_PLUG_EVENTS_USB_DISABLE BIT(2)
#define TB_PLUG_EVENTS_CS_1_LANE_DISABLE BIT(3)
#define TB_PLUG_EVENTS_CS_1_DPOUT_DISABLE BIT(4)
#define TB_PLUG_EVENTS_CS_1_LOW_DPIN_DISABLE BIT(5)
#define TB_PLUG_EVENTS_CS_1_HIGH_DPIN_DISABLE BIT(6)
#define TB_PLUG_EVENTS_PCIE_WR_DATA 0x1b
#define TB_PLUG_EVENTS_PCIE_CMD 0x1c
#define TB_PLUG_EVENTS_PCIE_CMD_DW_OFFSET_MASK GENMASK(9, 0)
@ -501,6 +508,9 @@ struct tb_regs_hop {
#define TB_LC_POWER 0x740
/* Link controller registers */
#define TB_LC_CS_42 0x2a
#define TB_LC_CS_42_USB_PLUGGED BIT(31)
#define TB_LC_PORT_ATTR 0x8d
#define TB_LC_PORT_ATTR_BE BIT(12)
@ -521,4 +531,7 @@ struct tb_regs_hop {
#define TB_LC_LINK_ATTR 0x97
#define TB_LC_LINK_ATTR_CPS BIT(18)
#define TB_LC_LINK_REQ 0xad
#define TB_LC_LINK_REQ_XHCI_CONNECT BIT(31)
#endif

View File

@ -580,6 +580,16 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
out_dp_cap = tb_dp_cap_set_lanes(out_dp_cap, new_lanes);
}
/*
* Titan Ridge does not disable AUX timers when it gets
* SET_CONFIG with SET_LTTPR_MODE set. This causes problems with
* DP tunneling.
*/
if (tb_route(out->sw) && tb_switch_is_titan_ridge(out->sw)) {
out_dp_cap |= DP_COMMON_CAP_LTTPR_NS;
tb_port_dbg(out, "disabling LTTPR\n");
}
return tb_port_write(in, &out_dp_cap, TB_CFG_PORT,
in->cap_adap + DP_REMOTE_CAP, 1);
}

View File

@ -182,208 +182,211 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
int ep_id = TRB_TO_EP_INDEX(field3) - 1;
int type = TRB_FIELD_TO_TYPE(field3);
unsigned int ep_num;
int ret = 0;
int ret;
u32 temp;
ep_num = DIV_ROUND_UP(ep_id, 2);
switch (type) {
case TRB_LINK:
ret += snprintf(str, size,
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
field1, field0, GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_TC ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
field1, field0, GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_TC ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_TRANSFER:
case TRB_COMPLETION:
case TRB_PORT_STATUS:
case TRB_HC_EVENT:
ret += snprintf(str, size,
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
" len %ld slot %ld flags %c:%c",
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
cdnsp_trb_type_string(type), field1, field0,
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
" len %ld slot %ld flags %c:%c",
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
cdnsp_trb_type_string(type), field1, field0,
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_MFINDEX_WRAP:
ret += snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SETUP:
ret += snprintf(str, size,
"type '%s' bRequestType %02x bRequest %02x "
"wValue %02x%02x wIndex %02x%02x wLength %d "
"length %ld TD size %ld intr %ld Setup ID %ld "
"flags %c:%c:%c",
cdnsp_trb_type_string(type),
field0 & 0xff,
(field0 & 0xff00) >> 8,
(field0 & 0xff000000) >> 24,
(field0 & 0xff0000) >> 16,
(field1 & 0xff00) >> 8,
field1 & 0xff,
(field1 & 0xff000000) >> 16 |
(field1 & 0xff0000) >> 16,
TRB_LEN(field2), GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
TRB_SETUPID_TO_TYPE(field3),
field3 & TRB_IDT ? 'D' : 'd',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"type '%s' bRequestType %02x bRequest %02x "
"wValue %02x%02x wIndex %02x%02x wLength %d "
"length %ld TD size %ld intr %ld Setup ID %ld "
"flags %c:%c:%c",
cdnsp_trb_type_string(type),
field0 & 0xff,
(field0 & 0xff00) >> 8,
(field0 & 0xff000000) >> 24,
(field0 & 0xff0000) >> 16,
(field1 & 0xff00) >> 8,
field1 & 0xff,
(field1 & 0xff000000) >> 16 |
(field1 & 0xff0000) >> 16,
TRB_LEN(field2), GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
TRB_SETUPID_TO_TYPE(field3),
field3 & TRB_IDT ? 'D' : 'd',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DATA:
ret += snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld TD size %ld "
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_IDT ? 'D' : 'i',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld TD size %ld "
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_IDT ? 'D' : 'i',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STATUS:
ret += snprintf(str, size,
"Buffer %08x%08x length %ld TD size %ld intr"
"%ld type '%s' flags %c:%c:%c:%c",
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"Buffer %08x%08x length %ld TD size %ld intr"
"%ld type '%s' flags %c:%c:%c:%c",
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_NORMAL:
case TRB_ISOC:
case TRB_EVENT_DATA:
case TRB_TR_NOOP:
ret += snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld "
"TD size %ld intr %ld "
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_BEI ? 'B' : 'b',
field3 & TRB_IDT ? 'T' : 't',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c',
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
ret = snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld "
"TD size %ld intr %ld "
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_BEI ? 'B' : 'b',
field3 & TRB_IDT ? 'T' : 't',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c',
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
break;
case TRB_CMD_NOOP:
case TRB_ENABLE_SLOT:
ret += snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DISABLE_SLOT:
ret += snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ADDR_DEV:
ret += snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_BSR ? 'B' : 'b',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_BSR ? 'B' : 'b',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_CONFIG_EP:
ret += snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_DC ? 'D' : 'd',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_DC ? 'D' : 'd',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_EVAL_CONTEXT:
ret += snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_EP:
case TRB_HALT_ENDPOINT:
case TRB_FLUSH_ENDPOINT:
ret += snprintf(str, size,
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STOP_RING:
ret += snprintf(str, size,
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SET_DEQ:
ret += snprintf(str, size,
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_STREAM_ID(field2),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_STREAM_ID(field2),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_DEV:
ret += snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ENDPOINT_NRDY:
temp = TRB_TO_HOST_STREAM(field2);
temp = TRB_TO_HOST_STREAM(field2);
ret += snprintf(str, size,
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), temp,
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
temp == STREAM_REJECTED ? "(REJECTED)" : "",
TRB_TO_DEV_STREAM(field0),
field3 & TRB_STAT ? 'S' : 's',
field3 & TRB_CYCLE ? 'C' : 'c');
ret = snprintf(str, size,
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), temp,
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
temp == STREAM_REJECTED ? "(REJECTED)" : "",
TRB_TO_DEV_STREAM(field0),
field3 & TRB_STAT ? 'S' : 's',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
default:
ret += snprintf(str, size,
"type '%s' -> raw %08x %08x %08x %08x",
cdnsp_trb_type_string(type),
field0, field1, field2, field3);
ret = snprintf(str, size,
"type '%s' -> raw %08x %08x %08x %08x",
cdnsp_trb_type_string(type),
field0, field1, field2, field3);
}
if (ret >= size)
pr_info("CDNSP: buffer overflowed.\n");
return str;
}

View File

@ -1243,12 +1243,9 @@ static int cdnsp_run(struct cdnsp_device *pdev,
enum usb_device_speed speed)
{
u32 fs_speed = 0;
u64 temp_64;
u32 temp;
int ret;
temp_64 = cdnsp_read_64(&pdev->ir_set->erst_dequeue);
temp_64 &= ~ERST_PTR_MASK;
temp = readl(&pdev->ir_set->irq_control);
temp &= ~IMOD_INTERVAL_MASK;
temp |= ((IMOD_DEFAULT_INTERVAL / 250) & IMOD_INTERVAL_MASK);

View File

@ -2152,7 +2152,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
{
/*
* host doesn't care B_SESSION_VALID event
* so clear and disbale BSV irq
* so clear and disable BSV irq
*/
if (ci->is_otg)
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);

View File

@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk/clk-conf.h>
@ -232,9 +233,64 @@ err:
return 0;
}
static int ulpi_regs_read(struct seq_file *seq, void *data)
{
struct ulpi *ulpi = seq->private;
#define ulpi_print(name, reg) do { \
int ret = ulpi_read(ulpi, reg); \
if (ret < 0) \
return ret; \
seq_printf(seq, name " %.02x\n", ret); \
} while (0)
ulpi_print("Vendor ID Low ", ULPI_VENDOR_ID_LOW);
ulpi_print("Vendor ID High ", ULPI_VENDOR_ID_HIGH);
ulpi_print("Product ID Low ", ULPI_PRODUCT_ID_LOW);
ulpi_print("Product ID High ", ULPI_PRODUCT_ID_HIGH);
ulpi_print("Function Control ", ULPI_FUNC_CTRL);
ulpi_print("Interface Control ", ULPI_IFC_CTRL);
ulpi_print("OTG Control ", ULPI_OTG_CTRL);
ulpi_print("USB Interrupt Enable Rising ", ULPI_USB_INT_EN_RISE);
ulpi_print("USB Interrupt Enable Falling", ULPI_USB_INT_EN_FALL);
ulpi_print("USB Interrupt Status ", ULPI_USB_INT_STS);
ulpi_print("USB Interrupt Latch ", ULPI_USB_INT_LATCH);
ulpi_print("Debug ", ULPI_DEBUG);
ulpi_print("Scratch Register ", ULPI_SCRATCH);
ulpi_print("Carkit Control ", ULPI_CARKIT_CTRL);
ulpi_print("Carkit Interrupt Delay ", ULPI_CARKIT_INT_DELAY);
ulpi_print("Carkit Interrupt Enable ", ULPI_CARKIT_INT_EN);
ulpi_print("Carkit Interrupt Status ", ULPI_CARKIT_INT_STS);
ulpi_print("Carkit Interrupt Latch ", ULPI_CARKIT_INT_LATCH);
ulpi_print("Carkit Pulse Control ", ULPI_CARKIT_PLS_CTRL);
ulpi_print("Transmit Positive Width ", ULPI_TX_POS_WIDTH);
ulpi_print("Transmit Negative Width ", ULPI_TX_NEG_WIDTH);
ulpi_print("Receive Polarity Recovery ", ULPI_POLARITY_RECOVERY);
return 0;
}
static int ulpi_regs_open(struct inode *inode, struct file *f)
{
struct ulpi *ulpi = inode->i_private;
return single_open(f, ulpi_regs_read, ulpi);
}
static const struct file_operations ulpi_regs_ops = {
.owner = THIS_MODULE,
.open = ulpi_regs_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek
};
#define ULPI_ROOT debugfs_lookup(KBUILD_MODNAME, NULL)
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
{
int ret;
struct dentry *root;
ulpi->dev.parent = dev; /* needed early for ops */
ulpi->dev.bus = &ulpi_bus;
@ -259,6 +315,9 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
return ret;
}
root = debugfs_create_dir(dev_name(dev), ULPI_ROOT);
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_ops);
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
ulpi->id.vendor, ulpi->id.product);
@ -304,6 +363,8 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
*/
void ulpi_unregister_interface(struct ulpi *ulpi)
{
debugfs_remove_recursive(debugfs_lookup(dev_name(&ulpi->dev),
ULPI_ROOT));
device_unregister(&ulpi->dev);
}
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
@ -312,13 +373,21 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
static int __init ulpi_init(void)
{
return bus_register(&ulpi_bus);
int ret;
struct dentry *root;
root = debugfs_create_dir(KBUILD_MODNAME, NULL);
ret = bus_register(&ulpi_bus);
if (ret)
debugfs_remove(root);
return ret;
}
subsys_initcall(ulpi_init);
static void __exit ulpi_exit(void)
{
bus_unregister(&ulpi_bus);
debugfs_remove_recursive(ULPI_ROOT);
}
module_exit(ulpi_exit);

View File

@ -175,7 +175,6 @@ static int usb_conn_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usb_conn_info *info;
bool need_vbus = true;
int ret = 0;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
@ -205,22 +204,9 @@ static int usb_conn_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
/*
* If the USB connector is a child of a USB port and that port already provides the VBUS
* supply, there's no need for the USB connector to provide it again.
*/
if (dev->parent && dev->parent->of_node) {
if (of_find_property(dev->parent->of_node, "vbus-supply", NULL))
need_vbus = false;
}
if (!need_vbus) {
info->vbus = devm_regulator_get_optional(dev, "vbus");
if (PTR_ERR(info->vbus) == -ENODEV)
info->vbus = NULL;
} else {
info->vbus = devm_regulator_get(dev, "vbus");
}
info->vbus = devm_regulator_get_optional(dev, "vbus");
if (PTR_ERR(info->vbus) == -ENODEV)
info->vbus = NULL;
if (IS_ERR(info->vbus)) {
ret = PTR_ERR(info->vbus);

View File

@ -139,30 +139,42 @@ MODULE_PARM_DESC(usbfs_memory_mb,
/* Hard limit, necessary to avoid arithmetic overflow */
#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
static atomic64_t usbfs_memory_usage; /* Total memory currently allocated */
static DEFINE_SPINLOCK(usbfs_memory_usage_lock);
static u64 usbfs_memory_usage; /* Total memory currently allocated */
/* Check whether it's okay to allocate more memory for a transfer */
static int usbfs_increase_memory_usage(u64 amount)
{
u64 lim;
u64 lim, total_mem;
unsigned long flags;
int ret;
lim = READ_ONCE(usbfs_memory_mb);
lim <<= 20;
atomic64_add(amount, &usbfs_memory_usage);
ret = 0;
spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
total_mem = usbfs_memory_usage + amount;
if (lim > 0 && total_mem > lim)
ret = -ENOMEM;
else
usbfs_memory_usage = total_mem;
spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
atomic64_sub(amount, &usbfs_memory_usage);
return -ENOMEM;
}
return 0;
return ret;
}
/* Memory for a transfer is being deallocated */
static void usbfs_decrease_memory_usage(u64 amount)
{
atomic64_sub(amount, &usbfs_memory_usage);
unsigned long flags;
spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
if (amount > usbfs_memory_usage)
usbfs_memory_usage = 0;
else
usbfs_memory_usage -= amount;
spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
}
static int connected(struct usb_dev_state *ps)

View File

@ -248,7 +248,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
hcd->rsrc_len, driver->description))
break;
}
if (region == PCI_ROM_RESOURCE) {
if (region == PCI_STD_NUM_BARS) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
goto put_hcd;

View File

@ -2983,8 +2983,12 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
status);
}
/* Check for disconnect or reset */
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
/*
* Check for disconnect or reset, and bail out after several
* reset attempts to avoid warm reset loop.
*/
if (status == 0 || status == -ENOTCONN || status == -ENODEV ||
(status == -EBUSY && i == PORT_RESET_TRIES - 1)) {
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_RESET);
@ -5005,6 +5009,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
retval = usb_get_bos_descriptor(udev);
if (!retval) {
udev->lpm_capable = usb_device_supports_lpm(udev);
udev->lpm_disable_count = 1;
usb_set_lpm_parameters(udev);
}
}
@ -5928,16 +5933,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
*/
usb_disable_usb2_hardware_lpm(udev);
/* Disable LPM while we reset the device and reinstall the alt settings.
* Device-initiated LPM, and system exit latency settings are cleared
* when the device is reset, so we have to set them up again.
*/
ret = usb_unlocked_disable_lpm(udev);
if (ret) {
dev_err(&udev->dev, "%s Failed to disable LPM\n", __func__);
goto re_enumerate_no_bos;
}
bos = udev->bos;
udev->bos = NULL;
@ -6042,8 +6037,6 @@ done:
re_enumerate:
usb_release_bos_descriptor(udev);
udev->bos = bos;
re_enumerate_no_bos:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}

View File

@ -166,7 +166,7 @@ usb_acpi_get_companion_for_port(struct usb_port *port_dev)
if (!parent_handle)
return NULL;
acpi_bus_get_device(parent_handle, &adev);
adev = acpi_fetch_acpi_dev(parent_handle);
port1 = port_dev->portnum;
}

View File

@ -688,6 +688,10 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev);
* Drivers for USB interfaces should normally record such references in
* their probe() methods, when they bind to an interface, and release
* them by calling usb_put_dev(), in their disconnect() methods.
* However, if a driver does not access the usb_device structure after
* its disconnect() method returns then refcounting is not necessary,
* because the USB core guarantees that a usb_device will not be
* deallocated until after all of its interface drivers have been unbound.
*
* Return: A pointer to the device with the incremented reference counter.
*/
@ -722,6 +726,10 @@ EXPORT_SYMBOL_GPL(usb_put_dev);
* Drivers for USB interfaces should normally record such references in
* their probe() methods, when they bind to an interface, and release
* them by calling usb_put_intf(), in their disconnect() methods.
* However, if a driver does not access the usb_interface structure after
* its disconnect() method returns then refcounting is not necessary,
* because the USB core guarantees that a usb_interface will not be
* deallocated until after its driver has been unbound.
*
* Return: A pointer to the interface with the incremented reference counter.
*/

View File

@ -82,6 +82,14 @@ static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
p->phy_utmi_width = 8;
}
static void dwc2_set_socfpga_agilex_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
p->no_clock_gating = true;
}
static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@ -239,6 +247,8 @@ const struct of_device_id dwc2_of_match_table[] = {
.data = dwc2_set_stm32mp15_fsotg_params },
{ .compatible = "st,stm32mp15-hsotg",
.data = dwc2_set_stm32mp15_hsotg_params },
{ .compatible = "intel,socfpga-agilex-hsotg",
.data = dwc2_set_socfpga_agilex_params },
{},
};
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);

View File

@ -115,8 +115,6 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
dwc->current_dr_role = mode;
}
static int dwc3_core_soft_reset(struct dwc3 *dwc);
static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
@ -261,7 +259,7 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
*/
static int dwc3_core_soft_reset(struct dwc3 *dwc)
int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
int retries = 1000;
@ -347,17 +345,64 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
*/
static void dwc3_ref_clk_period(struct dwc3 *dwc)
{
unsigned long period;
unsigned long fladj;
unsigned long decr;
unsigned long rate;
u32 reg;
if (dwc->ref_clk_per == 0)
if (dwc->ref_clk) {
rate = clk_get_rate(dwc->ref_clk);
if (!rate)
return;
period = NSEC_PER_SEC / rate;
} else if (dwc->ref_clk_per) {
period = dwc->ref_clk_per;
rate = NSEC_PER_SEC / period;
} else {
return;
}
reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per);
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
}
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
/*
* The calculation below is
*
* 125000 * (NSEC_PER_SEC / (rate * period) - 1)
*
* but rearranged for fixed-point arithmetic. The division must be
* 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and
* neither does rate * period).
*
* Note that rate * period ~= NSEC_PER_SECOND, minus the number of
* nanoseconds of error caused by the truncation which happened during
* the division when calculating rate or period (whichever one was
* derived from the other). We first calculate the relative error, then
* scale it to units of 8 ppm.
*/
fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period);
fladj -= 125000;
/*
* The documented 240MHz constant is scaled by 2 to get PLS1 as well.
*/
decr = 480000000 / rate;
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
& ~DWC3_GFLADJ_240MHZDECR
& ~DWC3_GFLADJ_240MHZDECR_PLS1;
reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
}
/**
* dwc3_free_one_event_buffer - Frees one event buffer
@ -745,6 +790,38 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
return 0;
}
static int dwc3_clk_enable(struct dwc3 *dwc)
{
int ret;
ret = clk_prepare_enable(dwc->bus_clk);
if (ret)
return ret;
ret = clk_prepare_enable(dwc->ref_clk);
if (ret)
goto disable_bus_clk;
ret = clk_prepare_enable(dwc->susp_clk);
if (ret)
goto disable_ref_clk;
return 0;
disable_ref_clk:
clk_disable_unprepare(dwc->ref_clk);
disable_bus_clk:
clk_disable_unprepare(dwc->bus_clk);
return ret;
}
static void dwc3_clk_disable(struct dwc3 *dwc)
{
clk_disable_unprepare(dwc->susp_clk);
clk_disable_unprepare(dwc->ref_clk);
clk_disable_unprepare(dwc->bus_clk);
}
static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
@ -758,7 +835,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
dwc3_clk_disable(dwc);
reset_control_assert(dwc->reset);
}
@ -1088,6 +1165,11 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->parkmode_disable_ss_quirk)
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
(dwc->maximum_speed == USB_SPEED_HIGH ||
dwc->maximum_speed == USB_SPEED_FULL))
reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
@ -1605,25 +1687,31 @@ static int dwc3_probe(struct platform_device *pdev)
return PTR_ERR(dwc->reset);
if (dev->of_node) {
ret = devm_clk_bulk_get_all(dev, &dwc->clks);
if (ret == -EPROBE_DEFER)
return ret;
/*
* Clocks are optional, but new DT platforms should support all
* clocks as required by the DT-binding.
*/
if (ret < 0)
dwc->num_clks = 0;
else
dwc->num_clks = ret;
dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
if (IS_ERR(dwc->bus_clk))
return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
"could not get bus clock\n");
dwc->ref_clk = devm_clk_get_optional(dev, "ref");
if (IS_ERR(dwc->ref_clk))
return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
"could not get ref clock\n");
dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
if (IS_ERR(dwc->susp_clk))
return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
"could not get suspend clock\n");
}
ret = reset_control_deassert(dwc->reset);
if (ret)
return ret;
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
ret = dwc3_clk_enable(dwc);
if (ret)
goto assert_reset;
@ -1711,7 +1799,7 @@ err1:
pm_runtime_disable(&pdev->dev);
disable_clks:
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
dwc3_clk_disable(dwc);
assert_reset:
reset_control_assert(dwc->reset);
@ -1755,7 +1843,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
if (ret)
return ret;
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
ret = dwc3_clk_enable(dwc);
if (ret)
goto assert_reset;
@ -1766,7 +1854,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
return 0;
disable_clks:
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
dwc3_clk_disable(dwc);
assert_reset:
reset_control_assert(dwc->reset);

View File

@ -259,6 +259,7 @@
/* Global User Control 1 Register */
#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31)
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
@ -388,6 +389,9 @@
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
/* Global User Control Register*/
#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
@ -733,6 +737,7 @@ struct dwc3_ep {
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
#define DWC3_EP_DELAY_STOP BIT(13)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@ -978,8 +983,9 @@ struct dwc3_scratchpad_array {
* @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
* @clks: array of clocks
* @num_clks: number of clocks
* @bus_clk: clock for accessing the registers
* @ref_clk: reference clock
* @susp_clk: clock used when the SS phy is in low power (S3) state
* @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
@ -1134,8 +1140,9 @@ struct dwc3 {
struct usb_gadget *gadget;
struct usb_gadget_driver *gadget_driver;
struct clk_bulk_data *clks;
int num_clks;
struct clk *bus_clk;
struct clk *ref_clk;
struct clk *susp_clk;
struct reset_control *reset;
@ -1525,6 +1532,8 @@ bool dwc3_has_imod(struct dwc3 *dwc);
int dwc3_event_buffers_setup(struct dwc3 *dwc);
void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
int dwc3_core_soft_reset(struct dwc3 *dwc);
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);

View File

@ -9,6 +9,7 @@
#include <linux/extcon.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/property.h>
@ -559,6 +560,18 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
if (IS_ERR(dwc->role_sw))
return PTR_ERR(dwc->role_sw);
if (dwc->dev->of_node) {
/* populate connector entry */
int ret = devm_of_platform_populate(dwc->dev);
if (ret) {
usb_role_switch_unregister(dwc->role_sw);
dwc->role_sw = NULL;
dev_err(dwc->dev, "DWC3 platform devices creation failed: %i\n", ret);
return ret;
}
}
dwc3_set_mode(dwc, mode);
return 0;
}

View File

@ -36,9 +36,21 @@
#define USB_WAKEUP_EN_MASK GENMASK(5, 0)
/* USB glue registers */
#define USB_CTRL0 0x00
#define USB_CTRL1 0x04
#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */
#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */
#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */
#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */
#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */
struct dwc3_imx8mp {
struct device *dev;
struct platform_device *dwc3;
void __iomem *hsio_blk_base;
void __iomem *glue_base;
struct clk *hsio_clk;
struct clk *suspend_clk;
@ -47,6 +59,42 @@ struct dwc3_imx8mp {
bool wakeup_pending;
};
static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
{
struct device *dev = dwc3_imx->dev;
u32 value;
if (!dwc3_imx->glue_base)
return;
value = readl(dwc3_imx->glue_base + USB_CTRL0);
if (device_property_read_bool(dev, "fsl,permanently-attached"))
value |= (USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
else
value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
if (device_property_read_bool(dev, "fsl,disable-port-power-control"))
value &= ~(USB_CTRL0_PORTPWR_EN);
else
value |= USB_CTRL0_PORTPWR_EN;
writel(value, dwc3_imx->glue_base + USB_CTRL0);
value = readl(dwc3_imx->glue_base + USB_CTRL1);
if (device_property_read_bool(dev, "fsl,over-current-active-low"))
value |= USB_CTRL1_OC_POLARITY;
else
value &= ~USB_CTRL1_OC_POLARITY;
if (device_property_read_bool(dev, "fsl,power-active-low"))
value |= USB_CTRL1_PWR_POLARITY;
else
value &= ~USB_CTRL1_PWR_POLARITY;
writel(value, dwc3_imx->glue_base + USB_CTRL1);
}
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
{
struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
@ -55,7 +103,7 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
if (!dwc3)
return;
val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL);
val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci)
val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN |
@ -64,16 +112,16 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
USB_WAKEUP_VBUS_SRC_SESS_VAL;
writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL);
writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
}
static void dwc3_imx8mp_wakeup_disable(struct dwc3_imx8mp *dwc3_imx)
{
u32 val;
val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL);
val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK);
writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL);
writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
}
static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
@ -100,6 +148,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *dwc3_np, *node = dev->of_node;
struct dwc3_imx8mp *dwc3_imx;
struct resource *res;
int err, irq;
if (!node) {
@ -115,9 +164,18 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
dwc3_imx->dev = dev;
dwc3_imx->glue_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dwc3_imx->glue_base))
return PTR_ERR(dwc3_imx->glue_base);
dwc3_imx->hsio_blk_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dwc3_imx->hsio_blk_base))
return PTR_ERR(dwc3_imx->hsio_blk_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_warn(dev, "Base address for glue layer missing. Continuing without, some features are missing though.");
} else {
dwc3_imx->glue_base = devm_ioremap_resource(dev, res);
if (IS_ERR(dwc3_imx->glue_base))
return PTR_ERR(dwc3_imx->glue_base);
}
dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio");
if (IS_ERR(dwc3_imx->hsio_clk)) {
@ -152,6 +210,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
}
dwc3_imx->irq = irq;
imx8mp_configure_glue(dwc3_imx);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
err = pm_runtime_get_sync(dev);
@ -252,6 +312,9 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
dwc3_imx8mp_wakeup_disable(dwc3_imx);
dwc3_imx->pm_suspended = false;
/* Upon power loss any previous configuration is lost, restore it */
imx8mp_configure_glue(dwc3_imx);
if (dwc3_imx->wakeup_pending) {
dwc3_imx->wakeup_pending = false;
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) {

View File

@ -188,7 +188,7 @@ static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
* reset to recover usage of the port.
*/
static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
static const struct dwc3_meson_g12a_drvdata gxl_drvdata = {
.otg_switch_supported = true,
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
@ -202,7 +202,7 @@ static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
.usb_post_init = dwc3_meson_gxl_usb_post_init,
};
static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
static const struct dwc3_meson_g12a_drvdata gxm_drvdata = {
.otg_switch_supported = true,
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
@ -216,7 +216,7 @@ static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
.usb_post_init = dwc3_meson_gxl_usb_post_init,
};
static struct dwc3_meson_g12a_drvdata axg_drvdata = {
static const struct dwc3_meson_g12a_drvdata axg_drvdata = {
.otg_switch_supported = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_gxl_clocks),
@ -229,7 +229,7 @@ static struct dwc3_meson_g12a_drvdata axg_drvdata = {
.usb_post_init = dwc3_meson_gxl_usb_post_init,
};
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
static const struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.otg_switch_supported = true,
.clks = meson_g12a_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
@ -241,7 +241,7 @@ static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.usb_init = dwc3_meson_g12a_usb_init,
};
static struct dwc3_meson_g12a_drvdata a1_drvdata = {
static const struct dwc3_meson_g12a_drvdata a1_drvdata = {
.otg_switch_supported = false,
.clks = meson_a1_clocks,
.num_clks = ARRAY_SIZE(meson_a1_clocks),

View File

@ -242,7 +242,7 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
break;
case OMAP_DWC3_ID_FLOAT:
if (omap->vbus_reg)
if (omap->vbus_reg && regulator_is_enabled(omap->vbus_reg))
regulator_disable(omap->vbus_reg);
val = dwc3_omap_read_utmi_ctrl(omap);
val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG;

View File

@ -40,6 +40,7 @@
#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee
#define PCI_DEVICE_ID_INTEL_TGPH 0x43ee
#define PCI_DEVICE_ID_INTEL_JSP 0x4dee
#define PCI_DEVICE_ID_INTEL_ADL 0x465e
#define PCI_DEVICE_ID_INTEL_ADLP 0x51ee
#define PCI_DEVICE_ID_INTEL_ADLM 0x54ee
#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1
@ -120,6 +121,13 @@ static const struct property_entry dwc3_pci_intel_properties[] = {
{}
};
static const struct property_entry dwc3_pci_intel_phy_charger_detect_properties[] = {
PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
PROPERTY_ENTRY_BOOL("linux,phy_charger_detect"),
{}
};
static const struct property_entry dwc3_pci_intel_byt_properties[] = {
PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
@ -169,6 +177,10 @@ static const struct software_node dwc3_pci_intel_swnode = {
.properties = dwc3_pci_intel_properties,
};
static const struct software_node dwc3_pci_intel_phy_charger_detect_swnode = {
.properties = dwc3_pci_intel_phy_charger_detect_properties,
};
static const struct software_node dwc3_pci_intel_byt_swnode = {
.properties = dwc3_pci_intel_byt_properties,
};
@ -185,7 +197,8 @@ static const struct software_node dwc3_pci_amd_mr_swnode = {
.properties = dwc3_pci_mr_properties,
};
static int dwc3_pci_quirks(struct dwc3_pci *dwc)
static int dwc3_pci_quirks(struct dwc3_pci *dwc,
const struct software_node *swnode)
{
struct pci_dev *pdev = dwc->pci;
@ -239,10 +252,30 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
gpiod_put(gpio);
usleep_range(10000, 11000);
}
/*
* Make the pdev name predictable (only 1 DWC3 on BYT)
* and patch the phy dev-name into the lookup table so
* that the phy-driver can get the GPIOs.
*/
dwc->dwc3->id = PLATFORM_DEVID_NONE;
platform_bytcr_gpios.dev_id = "dwc3.ulpi";
/*
* Some Android tablets with a Crystal Cove PMIC
* (INT33FD), rely on the TUSB1211 phy for charger
* detection. These can be identified by them _not_
* using the standard ACPI battery and ac drivers.
*/
if (acpi_dev_present("INT33FD", "1", 2) &&
acpi_quirk_skip_acpi_ac_and_battery()) {
dev_info(&pdev->dev, "Using TUSB1211 phy for charger detection\n");
swnode = &dwc3_pci_intel_phy_charger_detect_swnode;
}
}
}
return 0;
return device_add_software_node(&dwc->dwc3->dev, swnode);
}
#ifdef CONFIG_PM
@ -307,11 +340,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
dwc->dwc3->dev.parent = dev;
ACPI_COMPANION_SET(&dwc->dwc3->dev, ACPI_COMPANION(dev));
ret = device_add_software_node(&dwc->dwc3->dev, (void *)id->driver_data);
if (ret < 0)
goto err;
ret = dwc3_pci_quirks(dwc);
ret = dwc3_pci_quirks(dwc, (void *)id->driver_data);
if (ret)
goto err;
@ -412,6 +441,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_JSP),
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL),
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLP),
(kernel_ulong_t) &dwc3_pci_intel_swnode, },

View File

@ -271,6 +271,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
int ret;
int i;
complete(&dwc->ep0_in_setup);
@ -279,6 +280,19 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
DWC3_TRBCTL_CONTROL_SETUP, false);
ret = dwc3_ep0_start_trans(dep);
WARN_ON(ret < 0);
for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
struct dwc3_ep *dwc3_ep;
dwc3_ep = dwc->eps[i];
if (!dwc3_ep)
continue;
if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
continue;
dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
dwc3_stop_active_transfer(dwc3_ep, true, true);
}
}
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)

View File

@ -654,9 +654,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt);
/**
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
* @dwc: pointer to the DWC3 context
@ -1673,6 +1670,40 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
return DWC3_DSTS_SOFFN(reg);
}
/**
* __dwc3_stop_active_transfer - stop the current active transfer
* @dep: isoc endpoint
* @force: set forcerm bit in the command
* @interrupt: command complete interrupt after End Transfer command
*
* When setting force, the ForceRM bit will be set. In that case
* the controller won't update the TRB progress on command
* completion. It also won't clear the HWO bit in the TRB.
* The command will also not complete immediately in that case.
*/
static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
{
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
if (!interrupt)
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
else if (!ret)
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
return ret;
}
/**
* dwc3_gadget_start_isoc_quirk - workaround invalid frame number
* @dep: isoc endpoint
@ -1830,7 +1861,13 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
}
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
int future_interval = i + 1;
/* Give the controller at least 500us to schedule transfers */
if (desc->bInterval < 3)
future_interval += 3 - desc->bInterval;
dep->frame_number = DWC3_ALIGN_FRAME(dep, future_interval);
ret = __dwc3_gadget_kick_transfer(dep);
if (ret != -EAGAIN)
@ -1842,21 +1879,8 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
* status, issue END_TRANSFER command and retry on the next XferNotReady
* event.
*/
if (ret == -EAGAIN) {
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
cmd = DWC3_DEPCMD_ENDTRANSFER |
DWC3_DEPCMD_CMDIOC |
DWC3_DEPCMD_PARAM(dep->resource_index);
dep->resource_index = 0;
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (!ret)
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
}
if (ret == -EAGAIN)
ret = __dwc3_stop_active_transfer(dep, false, true);
return ret;
}
@ -1899,6 +1923,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
*/
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
(dep->flags & DWC3_EP_WEDGE) ||
(dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_STALL)) {
dep->flags |= DWC3_EP_DELAY_START;
return 0;
@ -1913,13 +1938,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* errors which will force us issue EndTransfer command.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
if (!(dep->flags & DWC3_EP_PENDING_REQUEST) &&
!(dep->flags & DWC3_EP_TRANSFER_STARTED))
return 0;
if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
if ((dep->flags & DWC3_EP_PENDING_REQUEST))
return __dwc3_gadget_start_isoc(dep);
return 0;
}
}
@ -2033,6 +2056,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
if (r == req) {
struct dwc3_request *t;
/*
* If a Setup packet is received but yet to DMA out, the controller will
* not process the End Transfer command of any endpoint. Polling of its
* DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
* timeout. Delay issuing the End Transfer command until the Setup TRB is
* prepared.
*/
if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
dep->flags |= DWC3_EP_DELAY_STOP;
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true);
@ -2116,7 +2149,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
(dep->flags & DWC3_EP_DELAY_STOP)) {
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
return 0;
}
@ -2544,6 +2578,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
dwc->ev_buf->length;
}
} else {
/*
* In the Synopsys DWC_usb31 1.90a programming guide section
* 4.1.9, it specifies that for a reconnect after a
* device-initiated disconnect requires a core soft reset
* (DCTL.CSftRst) before enabling the run/stop bit.
*/
spin_unlock_irqrestore(&dwc->lock, flags);
dwc3_core_soft_reset(dwc);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_event_buffers_setup(dwc);
__dwc3_gadget_start(dwc);
}
@ -3596,14 +3641,11 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
}
}
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
(dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return;
@ -3634,19 +3676,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
* This mode is NOT available on the DWC_usb31 IP.
*/
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
if (!interrupt)
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
else
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
__dwc3_stop_active_transfer(dep, force, interrupt);
}
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)

View File

@ -116,6 +116,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW

View File

@ -599,23 +599,26 @@ static int __init xdbc_early_setup(void)
return 0;
}
int __init early_xdbc_parse_parameter(char *s)
int __init early_xdbc_parse_parameter(char *s, int keep_early)
{
unsigned long dbgp_num = 0;
u32 bus, dev, func, offset;
char *e;
int ret;
if (!early_pci_allowed())
return -EPERM;
if (strstr(s, "keep"))
early_console_keep = true;
early_console_keep = keep_early;
if (xdbc.xdbc_reg)
return 0;
if (*s && kstrtoul(s, 0, &dbgp_num))
dbgp_num = 0;
if (*s) {
dbgp_num = simple_strtoul(s, &e, 10);
if (s == e)
dbgp_num = 0;
}
pr_notice("dbgp_num: %lu\n", dbgp_num);

View File

@ -4,7 +4,6 @@
#
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o

View File

@ -863,24 +863,25 @@ static int set_config(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl, unsigned number)
{
struct usb_gadget *gadget = cdev->gadget;
struct usb_configuration *c = NULL;
struct usb_configuration *c = NULL, *iter;
int result = -EINVAL;
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
int tmp;
if (number) {
list_for_each_entry(c, &cdev->configs, list) {
if (c->bConfigurationValue == number) {
/*
* We disable the FDs of the previous
* configuration only if the new configuration
* is a valid one
*/
if (cdev->config)
reset_config(cdev);
result = 0;
break;
}
list_for_each_entry(iter, &cdev->configs, list) {
if (iter->bConfigurationValue != number)
continue;
/*
* We disable the FDs of the previous
* configuration only if the new configuration
* is a valid one
*/
if (cdev->config)
reset_config(cdev);
c = iter;
result = 0;
break;
}
if (result < 0)
goto done;
@ -1690,6 +1691,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
struct usb_function *iter;
u8 endp;
if (w_length > USB_COMP_EP0_BUFSIZ) {
@ -2046,12 +2048,12 @@ unknown:
if (!cdev->config)
break;
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
list_for_each_entry(f, &cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
list_for_each_entry(iter, &cdev->config->functions, list) {
if (test_bit(endp, iter->endpoints)) {
f = iter;
break;
}
}
if (&f->list == &cdev->config->functions)
f = NULL;
break;
}
try_fun_setup:

View File

@ -418,7 +418,7 @@ static int config_usb_cfg_link(
struct usb_function_instance *fi =
to_usb_function_instance(usb_func_ci);
struct usb_function_instance *a_fi;
struct usb_function_instance *a_fi = NULL, *iter;
struct usb_function *f;
int ret;
@ -428,11 +428,13 @@ static int config_usb_cfg_link(
* from another gadget or a random directory.
* Also a function instance can only be linked once.
*/
list_for_each_entry(a_fi, &gi->available_func, cfs_list) {
if (a_fi == fi)
break;
list_for_each_entry(iter, &gi->available_func, cfs_list) {
if (iter != fi)
continue;
a_fi = iter;
break;
}
if (a_fi != fi) {
if (!a_fi) {
ret = -EINVAL;
goto out;
}
@ -882,15 +884,17 @@ static int os_desc_link(struct config_item *os_desc_ci,
struct gadget_info *gi = os_desc_item_to_gadget_info(os_desc_ci);
struct usb_composite_dev *cdev = &gi->cdev;
struct config_usb_cfg *c_target = to_config_usb_cfg(usb_cfg_ci);
struct usb_configuration *c;
struct usb_configuration *c = NULL, *iter;
int ret;
mutex_lock(&gi->lock);
list_for_each_entry(c, &cdev->configs, list) {
if (c == &c_target->c)
break;
list_for_each_entry(iter, &cdev->configs, list) {
if (iter != &c_target->c)
continue;
c = iter;
break;
}
if (c != &c_target->c) {
if (!c) {
ret = -EINVAL;
goto out;
}

View File

@ -919,12 +919,12 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
data_len, ret);
data_len -= ret;
buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL);
buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf->length = data_len;
buf->data = buf->storage;
memcpy(buf->storage, data + ret, data_len);
memcpy(buf->storage, data + ret, flex_array_size(buf, storage, data_len));
/*
* At this point read_buffer is NULL or READ_BUFFER_DROP (if

View File

@ -1189,6 +1189,8 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
int msf = common->cmnd[1] & 0x02;
int start_track = common->cmnd[6];
u8 *buf = (u8 *)bh->buf;
u8 format;
int i, len;
if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
start_track > 1) {
@ -1196,18 +1198,62 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
return -EINVAL;
}
memset(buf, 0, 20);
buf[1] = (20-2); /* TOC data length */
buf[2] = 1; /* First track number */
buf[3] = 1; /* Last track number */
buf[5] = 0x16; /* Data track, copying allowed */
buf[6] = 0x01; /* Only track is number 1 */
store_cdrom_address(&buf[8], msf, 0);
format = common->cmnd[2] & 0xf;
/*
* Check if CDB is old style SFF-8020i
* i.e. format is in 2 MSBs of byte 9
* Mac OS-X host sends us this.
*/
if (format == 0)
format = (common->cmnd[9] >> 6) & 0x3;
buf[13] = 0x16; /* Lead-out track is data */
buf[14] = 0xAA; /* Lead-out track number */
store_cdrom_address(&buf[16], msf, curlun->num_sectors);
return 20;
switch (format) {
case 0:
/* Formatted TOC */
len = 4 + 2*8; /* 4 byte header + 2 descriptors */
memset(buf, 0, len);
buf[1] = len - 2; /* TOC Length excludes length field */
buf[2] = 1; /* First track number */
buf[3] = 1; /* Last track number */
buf[5] = 0x16; /* Data track, copying allowed */
buf[6] = 0x01; /* Only track is number 1 */
store_cdrom_address(&buf[8], msf, 0);
buf[13] = 0x16; /* Lead-out track is data */
buf[14] = 0xAA; /* Lead-out track number */
store_cdrom_address(&buf[16], msf, curlun->num_sectors);
return len;
case 2:
/* Raw TOC */
len = 4 + 3*11; /* 4 byte header + 3 descriptors */
memset(buf, 0, len); /* Header + A0, A1 & A2 descriptors */
buf[1] = len - 2; /* TOC Length excludes length field */
buf[2] = 1; /* First complete session */
buf[3] = 1; /* Last complete session */
buf += 4;
/* fill in A0, A1 and A2 points */
for (i = 0; i < 3; i++) {
buf[0] = 1; /* Session number */
buf[1] = 0x16; /* Data track, copying allowed */
/* 2 - Track number 0 -> TOC */
buf[3] = 0xA0 + i; /* A0, A1, A2 point */
/* 4, 5, 6 - Min, sec, frame is zero */
buf[8] = 1; /* Pmin: last track number */
buf += 11; /* go to next track descriptor */
}
buf -= 11; /* go back to A2 descriptor */
/* For A2, 7, 8, 9, 10 - zero, Pmin, Psec, Pframe of Lead out */
store_cdrom_address(&buf[7], msf, curlun->num_sectors);
return len;
default:
/* Multi-session, PMA, ATIP, CD-TEXT not supported/required */
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
}
static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
@ -1945,7 +1991,7 @@ static int do_scsi_command(struct fsg_common *common)
common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]);
reply = check_command(common, 10, DATA_DIR_TO_HOST,
(7<<6) | (1<<1), 1,
(0xf<<6) | (3<<1), 1,
"READ TOC");
if (reply == 0)
reply = do_read_toc(common, bh);

View File

@ -668,10 +668,8 @@ static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
{
struct f_phonet *fp;
struct f_phonet_opts *opts;
int size;
size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
fp = kzalloc(size, GFP_KERNEL);
fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL);
if (!fp)
return ERR_PTR(-ENOMEM);

View File

@ -345,6 +345,10 @@ static void gser_free(struct usb_function *f)
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_gser *gser = func_to_gser(f);
/* Ensure port is disconnected before unbinding */
gserial_disconnect(&gser->port);
usb_free_all_descriptors(f);
}

View File

@ -3,6 +3,7 @@
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
*
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
* Copyright (C) 2021 Julian Scheel <julian@jusst.de>
*
* This driver doesn't expect any real Audio codec to be present
* on the device - the audio streams are simply sinked to and
@ -42,6 +43,9 @@ struct f_uac1 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
int ctl_id; /* EP id */
int c_srate; /* current capture srate */
int p_srate; /* current playback prate */
};
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@ -188,16 +192,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
};
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
#define uac_format_type_i_discrete_descriptor \
uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
.bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
.bSamFreqType = 1,
.bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@ -221,14 +227,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.wLockDelay = cpu_to_le16(1),
};
static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
.bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
.bSamFreqType = 1,
.bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@ -303,7 +309,7 @@ enum {
};
static struct usb_string strings_uac1[] = {
[STR_AC_IF].s = "AC Interface",
/* [STR_AC_IF].s = DYNAMIC, */
[STR_USB_OUT_IT].s = "Playback Input terminal",
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
[STR_IO_OUT_OT].s = "Playback Output terminal",
@ -333,6 +339,30 @@ static struct usb_gadget_strings *uac1_strings[] = {
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
{
struct usb_function *fn = ep->driver_data;
struct usb_composite_dev *cdev = fn->config->cdev;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac1 *uac1 = func_to_uac1(fn);
u8 *buf = (u8 *)req->buf;
u32 val = 0;
if (req->actual != 3) {
WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
return;
}
val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
if (uac1->ctl_id == (USB_DIR_IN | 2)) {
uac1->p_srate = val;
u_audio_set_playback_srate(agdev, uac1->p_srate);
} else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
uac1->c_srate = val;
u_audio_set_capture_srate(agdev, uac1->c_srate);
}
}
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
{
struct g_audio *audio = req->context;
@ -707,18 +737,27 @@ static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = f->config->cdev->req;
struct f_uac1 *uac1 = func_to_uac1(f);
int value = -EOPNOTSUPP;
u16 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
u8 cs = w_value >> 8;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
case UAC_SET_CUR:
case UAC_SET_CUR: {
if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
cdev->gadget->ep0->driver_data = f;
uac1->ctl_id = ep;
req->complete = uac_cs_attr_sample_rate;
}
value = len;
break;
}
case UAC_SET_MIN:
break;
@ -743,16 +782,33 @@ static int audio_get_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = f->config->cdev->req;
struct f_uac1 *uac1 = func_to_uac1(f);
u8 *buf = (u8 *)req->buf;
int value = -EOPNOTSUPP;
u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
u8 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
u8 cs = w_value >> 8;
u32 val = 0;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
case UAC_GET_CUR:
case UAC_GET_CUR: {
if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
if (ep == (USB_DIR_IN | 2))
val = uac1->p_srate;
else if (ep == (USB_DIR_OUT | 1))
val = uac1->c_srate;
buf[2] = (val >> 16) & 0xff;
buf[1] = (val >> 8) & 0xff;
buf[0] = val & 0xff;
}
value = len;
break;
}
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
@ -905,6 +961,14 @@ static void f_audio_disable(struct usb_function *f)
usb_ep_disable(uac1->int_ep);
}
static void
f_audio_suspend(struct usb_function *f)
{
struct f_uac1 *uac1 = func_to_uac1(f);
u_audio_suspend(&uac1->g_audio);
}
/*-------------------------------------------------------------------------*/
static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
{
@ -1074,10 +1138,10 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
} else if (!opts->p_srate) {
} else if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
} else if (!opts->c_srate) {
} else if (!opts->c_srates[0]) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
@ -1118,10 +1182,9 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uac1_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
u8 *sam_freq;
int rate;
int ba_iface_id;
int status;
int idx, i;
status = f_audio_validate_opts(audio, dev);
if (status)
@ -1129,6 +1192,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
strings_uac1[STR_AC_IF].s = audio_opts->function_name;
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
@ -1213,12 +1278,25 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
/* Set sample rates */
rate = audio_opts->c_srate;
sam_freq = as_out_type_i_desc.tSamFreq[0];
memcpy(sam_freq, &rate, 3);
rate = audio_opts->p_srate;
sam_freq = as_in_type_i_desc.tSamFreq[0];
memcpy(sam_freq, &rate, 3);
for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
if (audio_opts->c_srates[i] == 0)
break;
memcpy(as_out_type_i_desc.tSamFreq[idx++],
&audio_opts->c_srates[i], 3);
}
as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
as_out_type_i_desc.bSamFreqType = idx;
for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
if (audio_opts->p_srates[i] == 0)
break;
memcpy(as_in_type_i_desc.tSamFreq[idx++],
&audio_opts->p_srates[i], 3);
}
as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
as_in_type_i_desc.bSamFreqType = idx;
uac1->p_srate = audio_opts->p_srates[0];
uac1->c_srate = audio_opts->c_srates[0];
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
@ -1297,7 +1375,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
audio->params.c_srate = audio_opts->c_srate;
memcpy(audio->params.c_srates, audio_opts->c_srates,
sizeof(audio->params.c_srates));
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
audio->params.p_fu.id = USB_IN_FU_ID;
@ -1309,7 +1388,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
}
audio->params.p_chmask = audio_opts->p_chmask;
audio->params.p_srate = audio_opts->p_srate;
memcpy(audio->params.p_srates, audio_opts->p_srates,
sizeof(audio->params.p_srates));
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
audio->params.c_fu.id = USB_OUT_FU_ID;
@ -1414,11 +1494,106 @@ end: \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
#define UAC1_RATE_ATTRIBUTE(name) \
static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
char *page) \
{ \
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
int result = 0; \
int i; \
\
mutex_lock(&opts->lock); \
page[0] = '\0'; \
for (i = 0; i < UAC_MAX_RATES; i++) { \
if (opts->name##s[i] == 0) \
break; \
result += sprintf(page + strlen(page), "%u,", \
opts->name##s[i]); \
} \
if (strlen(page) > 0) \
page[strlen(page) - 1] = '\n'; \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
char *split_page = NULL; \
int ret = -EINVAL; \
char *token; \
u32 num; \
int i; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
i = 0; \
memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
\
opts->name##s[i++] = num; \
ret = len; \
}; \
\
end: \
kfree(split_page); \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
#define UAC1_ATTRIBUTE_STRING(name) \
static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
char *page) \
{ \
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
int result; \
\
mutex_lock(&opts->lock); \
result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
int ret = 0; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
ret = snprintf(opts->name, min(sizeof(opts->name), len), \
"%s", page); \
\
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
UAC1_ATTRIBUTE(u32, c_chmask);
UAC1_ATTRIBUTE(u32, c_srate);
UAC1_RATE_ATTRIBUTE(c_srate);
UAC1_ATTRIBUTE(u32, c_ssize);
UAC1_ATTRIBUTE(u32, p_chmask);
UAC1_ATTRIBUTE(u32, p_srate);
UAC1_RATE_ATTRIBUTE(p_srate);
UAC1_ATTRIBUTE(u32, p_ssize);
UAC1_ATTRIBUTE(u32, req_number);
@ -1433,6 +1608,7 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
UAC1_ATTRIBUTE_STRING(function_name);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
@ -1455,6 +1631,8 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_volume_max,
&f_uac1_opts_attr_c_volume_res,
&f_uac1_opts_attr_function_name,
NULL,
};
@ -1487,10 +1665,10 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
&f_uac1_func_type);
opts->c_chmask = UAC1_DEF_CCHMASK;
opts->c_srate = UAC1_DEF_CSRATE;
opts->c_srates[0] = UAC1_DEF_CSRATE;
opts->c_ssize = UAC1_DEF_CSSIZE;
opts->p_chmask = UAC1_DEF_PCHMASK;
opts->p_srate = UAC1_DEF_PSRATE;
opts->p_srates[0] = UAC1_DEF_PSRATE;
opts->p_ssize = UAC1_DEF_PSSIZE;
opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
@ -1506,6 +1684,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->c_volume_res = UAC1_DEF_RES_DB;
opts->req_number = UAC1_DEF_REQ_NUM;
snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
return &opts->func_inst;
}
@ -1562,6 +1743,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
uac1->g_audio.func.get_alt = f_audio_get_alt;
uac1->g_audio.func.setup = f_audio_setup;
uac1->g_audio.func.disable = f_audio_disable;
uac1->g_audio.func.suspend = f_audio_suspend;
uac1->g_audio.func.free_func = f_audio_free;
return &uac1->g_audio.func;

View File

@ -70,6 +70,8 @@ struct f_uac2 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
/* transient state, only valid during handling of a single control request */
int clock_id;
};
static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
@ -104,14 +106,11 @@ enum {
STR_AS_IN_ALT1,
};
static char clksrc_in[8];
static char clksrc_out[8];
static struct usb_string strings_fn[] = {
[STR_ASSOC].s = "Source/Sink",
/* [STR_ASSOC].s = DYNAMIC, */
[STR_IF_CTRL].s = "Topology Control",
[STR_CLKSRC_IN].s = clksrc_in,
[STR_CLKSRC_OUT].s = clksrc_out,
[STR_CLKSRC_IN].s = "Input Clock",
[STR_CLKSRC_OUT].s = "Output Clock",
[STR_USB_IT].s = "USBH Out",
[STR_IO_IT].s = "USBD Out",
[STR_USB_OT].s = "USBH In",
@ -125,6 +124,16 @@ static struct usb_string strings_fn[] = {
{ },
};
static const char *const speed_names[] = {
[USB_SPEED_UNKNOWN] = "UNKNOWN",
[USB_SPEED_LOW] = "LS",
[USB_SPEED_FULL] = "FS",
[USB_SPEED_HIGH] = "HS",
[USB_SPEED_WIRELESS] = "W",
[USB_SPEED_SUPER] = "SS",
[USB_SPEED_SUPER_PLUS] = "SS+",
};
static struct usb_gadget_strings str_fn = {
.language = 0x0409, /* en-us */
.strings = strings_fn,
@ -166,7 +175,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
.bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@ -178,7 +187,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
.bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@ -344,7 +353,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
/* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
.bInterval = 4,
/* .bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor ss_epout_desc = {
@ -354,7 +363,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = {
.bEndpointAddress = USB_DIR_OUT,
/* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
.bInterval = 4,
/* .bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = {
@ -478,7 +487,7 @@ static struct usb_endpoint_descriptor hs_epin_desc = {
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
/* .wMaxPacketSize = DYNAMIC */
.bInterval = 4,
/* .bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor ss_epin_desc = {
@ -488,7 +497,7 @@ static struct usb_endpoint_descriptor ss_epin_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
/* .wMaxPacketSize = DYNAMIC */
.bInterval = 4,
/* .bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = {
@ -634,44 +643,50 @@ struct cntrl_cur_lay3 {
__le32 dCUR;
};
struct cntrl_range_lay3 {
__le16 wNumSubRanges;
struct cntrl_subrange_lay3 {
__le32 dMIN;
__le32 dMAX;
__le32 dRES;
} __packed;
static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
struct usb_endpoint_descriptor *ep_desc,
enum usb_device_speed speed, bool is_playback)
#define ranges_lay3_size(c) (sizeof(c.wNumSubRanges) \
+ le16_to_cpu(c.wNumSubRanges) \
* sizeof(struct cntrl_subrange_lay3))
#define DECLARE_UAC2_CNTRL_RANGES_LAY3(k, n) \
struct cntrl_ranges_lay3_##k { \
__le16 wNumSubRanges; \
struct cntrl_subrange_lay3 r[n]; \
} __packed
DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES);
static int get_max_srate(const int *srates)
{
int i, max_srate = 0;
for (i = 0; i < UAC_MAX_RATES; i++) {
if (srates[i] == 0)
break;
if (srates[i] > max_srate)
max_srate = srates[i];
}
return max_srate;
}
static int get_max_bw_for_bint(const struct f_uac2_opts *uac2_opts,
u8 bint, unsigned int factor, bool is_playback)
{
int chmask, srate, ssize;
u16 max_size_bw, max_size_ep;
unsigned int factor;
switch (speed) {
case USB_SPEED_FULL:
max_size_ep = 1023;
factor = 1000;
break;
case USB_SPEED_HIGH:
case USB_SPEED_SUPER:
max_size_ep = 1024;
factor = 8000;
break;
default:
return -EINVAL;
}
u16 max_size_bw;
if (is_playback) {
chmask = uac2_opts->p_chmask;
srate = uac2_opts->p_srate;
srate = get_max_srate(uac2_opts->p_srates);
ssize = uac2_opts->p_ssize;
} else {
chmask = uac2_opts->c_chmask;
srate = uac2_opts->c_srate;
srate = get_max_srate(uac2_opts->c_srates);
ssize = uac2_opts->c_ssize;
}
@ -681,14 +696,76 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
srate = srate * (1000 + uac2_opts->fb_max) / 1000;
// updated srate is always bigger, therefore DIV_ROUND_UP always yields +1
max_size_bw = num_channels(chmask) * ssize *
(DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))));
(DIV_ROUND_UP(srate, factor / (1 << (bint - 1))));
} else {
// adding 1 frame provision for Win10
max_size_bw = num_channels(chmask) * ssize *
(DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))) + 1);
(DIV_ROUND_UP(srate, factor / (1 << (bint - 1))) + 1);
}
ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
max_size_ep));
return max_size_bw;
}
static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_opts *uac2_opts,
struct usb_endpoint_descriptor *ep_desc,
enum usb_device_speed speed, bool is_playback)
{
u16 max_size_bw, max_size_ep;
u8 bint, opts_bint;
char *dir;
switch (speed) {
case USB_SPEED_FULL:
max_size_ep = 1023;
// fixed
bint = ep_desc->bInterval;
max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 1000, is_playback);
break;
case USB_SPEED_HIGH:
case USB_SPEED_SUPER:
max_size_ep = 1024;
if (is_playback)
opts_bint = uac2_opts->p_hs_bint;
else
opts_bint = uac2_opts->c_hs_bint;
if (opts_bint > 0) {
/* fixed bint */
bint = opts_bint;
max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback);
} else {
/* checking bInterval from 4 to 1 whether the required bandwidth fits */
for (bint = 4; bint > 0; --bint) {
max_size_bw = get_max_bw_for_bint(
uac2_opts, bint, 8000, is_playback);
if (max_size_bw <= max_size_ep)
break;
}
}
break;
default:
return -EINVAL;
}
if (is_playback)
dir = "Playback";
else
dir = "Capture";
if (max_size_bw <= max_size_ep)
dev_dbg(dev,
"%s %s: Would use wMaxPacketSize %d and bInterval %d\n",
speed_names[speed], dir, max_size_bw, bint);
else {
dev_warn(dev,
"%s %s: Req. wMaxPacketSize %d at bInterval %d > max ISOC %d, may drop data!\n",
speed_names[speed], dir, max_size_bw, bint, max_size_ep);
max_size_bw = max_size_ep;
}
ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw);
ep_desc->bInterval = bint;
return 0;
}
@ -896,50 +973,45 @@ static void setup_descriptor(struct f_uac2_opts *opts)
static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
{
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
const char *msg = NULL;
if (!opts->p_chmask && !opts->c_chmask) {
dev_err(dev, "Error: no playback and capture channels\n");
return -EINVAL;
} else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) {
dev_err(dev, "Error: unsupported playback channels mask\n");
return -EINVAL;
} else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) {
dev_err(dev, "Error: unsupported capture channels mask\n");
return -EINVAL;
} else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) {
dev_err(dev, "Error: incorrect playback sample size\n");
return -EINVAL;
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
} else if (!opts->p_srate) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
} else if (!opts->c_srate) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
if (!opts->p_chmask && !opts->c_chmask)
msg = "no playback and capture channels";
else if (opts->p_chmask & ~UAC2_CHANNEL_MASK)
msg = "unsupported playback channels mask";
else if (opts->c_chmask & ~UAC2_CHANNEL_MASK)
msg = "unsupported capture channels mask";
else if ((opts->p_ssize < 1) || (opts->p_ssize > 4))
msg = "incorrect playback sample size";
else if ((opts->c_ssize < 1) || (opts->c_ssize > 4))
msg = "incorrect capture sample size";
else if (!opts->p_srates[0])
msg = "incorrect playback sampling rate";
else if (!opts->c_srates[0])
msg = "incorrect capture sampling rate";
if (opts->p_volume_max <= opts->p_volume_min) {
dev_err(dev, "Error: incorrect playback volume max/min\n");
return -EINVAL;
} else if (opts->c_volume_max <= opts->c_volume_min) {
dev_err(dev, "Error: incorrect capture volume max/min\n");
return -EINVAL;
} else if (opts->p_volume_res <= 0) {
dev_err(dev, "Error: negative/zero playback volume resolution\n");
return -EINVAL;
} else if (opts->c_volume_res <= 0) {
dev_err(dev, "Error: negative/zero capture volume resolution\n");
return -EINVAL;
}
else if (opts->p_volume_max <= opts->p_volume_min)
msg = "incorrect playback volume max/min";
else if (opts->c_volume_max <= opts->c_volume_min)
msg = "incorrect capture volume max/min";
else if (opts->p_volume_res <= 0)
msg = "negative/zero playback volume resolution";
else if (opts->c_volume_res <= 0)
msg = "negative/zero capture volume resolution";
if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
dev_err(dev, "Error: incorrect playback volume resolution\n");
return -EINVAL;
} else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
dev_err(dev, "Error: incorrect capture volume resolution\n");
return -EINVAL;
else if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res)
msg = "incorrect playback volume resolution";
else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res)
msg = "incorrect capture volume resolution";
else if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4))
msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)";
else if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4))
msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)";
if (msg) {
dev_err(dev, "Error: %s\n", msg);
return -EINVAL;
}
return 0;
@ -961,6 +1033,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
if (ret)
return ret;
strings_fn[STR_ASSOC].s = uac2_opts->function_name;
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
return PTR_ERR(us);
@ -1037,9 +1111,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
*bma = cpu_to_le32(control);
}
snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
@ -1103,44 +1174,49 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
std_ac_if_desc.bNumEndpoints = 1;
}
hs_epin_desc.bInterval = uac2_opts->p_hs_bint;
ss_epin_desc.bInterval = uac2_opts->p_hs_bint;
hs_epout_desc.bInterval = uac2_opts->c_hs_bint;
ss_epout_desc.bInterval = uac2_opts->c_hs_bint;
/* Calculate wMaxPacketSize according to audio bandwidth */
ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
true);
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epin_desc,
USB_SPEED_FULL, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
false);
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epout_desc,
USB_SPEED_FULL, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
true);
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epin_desc,
USB_SPEED_HIGH, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
false);
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epout_desc,
USB_SPEED_HIGH, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER,
true);
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epin_desc,
USB_SPEED_SUPER, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER,
false);
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epout_desc,
USB_SPEED_SUPER, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
@ -1209,7 +1285,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
agdev->params.p_srate = uac2_opts->p_srate;
memcpy(agdev->params.p_srates, uac2_opts->p_srates,
sizeof(agdev->params.p_srates));
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
agdev->params.p_fu.id = USB_IN_FU_ID;
@ -1220,7 +1297,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
}
agdev->params.c_chmask = uac2_opts->c_chmask;
agdev->params.c_srate = uac2_opts->c_srate;
memcpy(agdev->params.c_srates, uac2_opts->c_srates,
sizeof(agdev->params.c_srates));
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
agdev->params.c_fu.id = USB_OUT_FU_ID;
@ -1411,6 +1489,14 @@ afunc_disable(struct usb_function *fn)
usb_ep_disable(uac2->int_ep);
}
static void
afunc_suspend(struct usb_function *fn)
{
struct f_uac2 *uac2 = func_to_uac2(fn);
u_audio_suspend(&uac2->g_audio);
}
static int
in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
@ -1423,10 +1509,10 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
int p_srate, c_srate;
u32 p_srate, c_srate;
p_srate = opts->p_srate;
c_srate = opts->c_srate;
u_audio_get_playback_srate(agdev, &p_srate);
u_audio_get_capture_srate(agdev, &c_srate);
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
@ -1500,28 +1586,39 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
int p_srate, c_srate;
p_srate = opts->p_srate;
c_srate = opts->c_srate;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
struct cntrl_range_lay3 r;
struct cntrl_ranges_lay3_srates rs;
int i;
int wNumSubRanges = 0;
int srate;
int *srates;
if (entity_id == USB_IN_CLK_ID)
r.dMIN = cpu_to_le32(p_srate);
srates = opts->p_srates;
else if (entity_id == USB_OUT_CLK_ID)
r.dMIN = cpu_to_le32(c_srate);
srates = opts->c_srates;
else
return -EOPNOTSUPP;
for (i = 0; i < UAC_MAX_RATES; i++) {
srate = srates[i];
if (srate == 0)
break;
r.dMAX = r.dMIN;
r.dRES = 0;
r.wNumSubRanges = cpu_to_le16(1);
value = min_t(unsigned int, w_length, sizeof(r));
memcpy(req->buf, &r, value);
rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate);
rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate);
rs.r[wNumSubRanges].dRES = 0;
wNumSubRanges++;
dev_dbg(&agdev->gadget->dev,
"%s(): clk %d: rate ID %d: %d\n",
__func__, entity_id, wNumSubRanges, srate);
}
rs.wNumSubRanges = cpu_to_le16(wNumSubRanges);
value = min_t(unsigned int, w_length, ranges_lay3_size(rs));
dev_dbg(&agdev->gadget->dev, "%s(): sending %d rates, size %d\n",
__func__, rs.wNumSubRanges, value);
memcpy(req->buf, &rs, value);
} else {
dev_err(&agdev->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
@ -1580,6 +1677,25 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return -EOPNOTSUPP;
}
static void uac2_cs_control_sam_freq(struct usb_ep *ep, struct usb_request *req)
{
struct usb_function *fn = ep->driver_data;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac2 *uac2 = func_to_uac2(fn);
u32 val;
if (req->actual != 4)
return;
val = le32_to_cpu(*((__le32 *)req->buf));
dev_dbg(&agdev->gadget->dev, "%s val: %d.\n", __func__, val);
if (uac2->clock_id == USB_IN_CLK_ID) {
u_audio_set_playback_srate(agdev, val);
} else if (uac2->clock_id == USB_OUT_CLK_ID) {
u_audio_set_capture_srate(agdev, val);
}
}
static void
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
{
@ -1631,6 +1747,7 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
static int
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_composite_dev *cdev = fn->config->cdev;
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
@ -1640,10 +1757,17 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
u8 clock_id = w_index >> 8;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
dev_dbg(&agdev->gadget->dev,
"control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", clock_id);
cdev->gadget->ep0->driver_data = fn;
uac2->clock_id = clock_id;
req->complete = uac2_cs_control_sam_freq;
return w_length;
}
} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
memcpy(&uac2->setup_cr, cr, sizeof(*cr));
@ -1731,10 +1855,12 @@ static struct configfs_item_operations f_uac2_item_ops = {
.release = f_uac2_attr_release,
};
#define uac2_kstrtou8 kstrtou8
#define uac2_kstrtou32 kstrtou32
#define uac2_kstrtos16 kstrtos16
#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res))
static const char *u8_fmt = "%u\n";
static const char *u32_fmt = "%u\n";
static const char *s16_fmt = "%hd\n";
static const char *bool_fmt = "%u\n";
@ -1836,13 +1962,110 @@ end: \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
#define UAC2_RATE_ATTRIBUTE(name) \
static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
char *page) \
{ \
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
int result = 0; \
int i; \
\
mutex_lock(&opts->lock); \
page[0] = '\0'; \
for (i = 0; i < UAC_MAX_RATES; i++) { \
if (opts->name##s[i] == 0) \
break; \
result += sprintf(page + strlen(page), "%u,", \
opts->name##s[i]); \
} \
if (strlen(page) > 0) \
page[strlen(page) - 1] = '\n'; \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
char *split_page = NULL; \
int ret = -EINVAL; \
char *token; \
u32 num; \
int i; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
i = 0; \
memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
split_page = kstrdup(page, GFP_KERNEL); \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
goto end; \
\
opts->name##s[i++] = num; \
ret = len; \
}; \
\
end: \
kfree(split_page); \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
#define UAC2_ATTRIBUTE_STRING(name) \
static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
char *page) \
{ \
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
int result; \
\
mutex_lock(&opts->lock); \
result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
int ret = 0; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
ret = snprintf(opts->name, min(sizeof(opts->name), len), \
"%s", page); \
\
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
UAC2_ATTRIBUTE(u32, p_chmask);
UAC2_ATTRIBUTE(u32, p_srate);
UAC2_RATE_ATTRIBUTE(p_srate);
UAC2_ATTRIBUTE(u32, p_ssize);
UAC2_ATTRIBUTE(u8, p_hs_bint);
UAC2_ATTRIBUTE(u32, c_chmask);
UAC2_ATTRIBUTE(u32, c_srate);
UAC2_RATE_ATTRIBUTE(c_srate);
UAC2_ATTRIBUTE_SYNC(c_sync);
UAC2_ATTRIBUTE(u32, c_ssize);
UAC2_ATTRIBUTE(u8, c_hs_bint);
UAC2_ATTRIBUTE(u32, req_number);
UAC2_ATTRIBUTE(bool, p_mute_present);
@ -1857,14 +2080,17 @@ UAC2_ATTRIBUTE(s16, c_volume_min);
UAC2_ATTRIBUTE(s16, c_volume_max);
UAC2_ATTRIBUTE(s16, c_volume_res);
UAC2_ATTRIBUTE(u32, fb_max);
UAC2_ATTRIBUTE_STRING(function_name);
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
&f_uac2_opts_attr_p_srate,
&f_uac2_opts_attr_p_ssize,
&f_uac2_opts_attr_p_hs_bint,
&f_uac2_opts_attr_c_chmask,
&f_uac2_opts_attr_c_srate,
&f_uac2_opts_attr_c_ssize,
&f_uac2_opts_attr_c_hs_bint,
&f_uac2_opts_attr_c_sync,
&f_uac2_opts_attr_req_number,
&f_uac2_opts_attr_fb_max,
@ -1881,6 +2107,8 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_volume_max,
&f_uac2_opts_attr_c_volume_res,
&f_uac2_opts_attr_function_name,
NULL,
};
@ -1913,11 +2141,13 @@ static struct usb_function_instance *afunc_alloc_inst(void)
&f_uac2_func_type);
opts->p_chmask = UAC2_DEF_PCHMASK;
opts->p_srate = UAC2_DEF_PSRATE;
opts->p_srates[0] = UAC2_DEF_PSRATE;
opts->p_ssize = UAC2_DEF_PSSIZE;
opts->p_hs_bint = UAC2_DEF_PHSBINT;
opts->c_chmask = UAC2_DEF_CCHMASK;
opts->c_srate = UAC2_DEF_CSRATE;
opts->c_srates[0] = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
opts->c_hs_bint = UAC2_DEF_CHSBINT;
opts->c_sync = UAC2_DEF_CSYNC;
opts->p_mute_present = UAC2_DEF_MUTE_PRESENT;
@ -1934,6 +2164,9 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->req_number = UAC2_DEF_REQ_NUM;
opts->fb_max = FBACK_FAST_MAX;
snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
return &opts->func_inst;
}
@ -1985,6 +2218,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
uac2->g_audio.func.set_alt = afunc_set_alt;
uac2->g_audio.func.get_alt = afunc_get_alt;
uac2->g_audio.func.disable = afunc_disable;
uac2->g_audio.func.suspend = afunc_suspend;
uac2->g_audio.func.setup = afunc_setup;
uac2->g_audio.func.free_func = afunc_free;

View File

@ -32,6 +32,7 @@ enum {
UAC_P_PITCH_CTRL,
UAC_MUTE_CTRL,
UAC_VOLUME_CTRL,
UAC_RATE_CTRL,
};
/* Runtime data params for one stream */
@ -62,6 +63,10 @@ struct uac_rtd_params {
s16 volume;
int mute;
struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
int srate; /* selected samplerate */
int active; /* playback/capture running */
spinlock_t lock; /* lock for control transfers */
};
@ -150,8 +155,6 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
struct snd_pcm_runtime *runtime;
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
struct g_audio *audio_dev = uac->audio_dev;
struct uac_params *params = &audio_dev->params;
unsigned int frames, p_pktsize;
unsigned long long pitched_rate_mil, p_pktsize_residue_mil,
residue_frames_mil, div_result;
@ -196,15 +199,14 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
*/
unsigned long long p_interval_mil = uac->p_interval * 1000000ULL;
pitched_rate_mil = (unsigned long long)
params->p_srate * prm->pitch;
pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch;
div_result = pitched_rate_mil;
do_div(div_result, uac->p_interval);
do_div(div_result, 1000000);
frames = (unsigned int) div_result;
pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n",
params->p_srate, prm->pitch, p_interval_mil, frames);
prm->srate, prm->pitch, p_interval_mil, frames);
p_pktsize = min_t(unsigned int,
uac->p_framesize * frames,
@ -281,7 +283,6 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
struct g_audio *audio_dev = uac->audio_dev;
struct uac_params *params = &audio_dev->params;
int status = req->status;
/* i/f shutting down */
@ -303,7 +304,7 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
__func__, status, req->actual, req->length);
u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep,
params->c_srate, prm->pitch,
prm->srate, prm->pitch,
req->buf);
if (usb_ep_queue(ep, req, GFP_ATOMIC))
@ -387,16 +388,14 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct g_audio *audio_dev;
struct uac_params *params;
struct uac_rtd_params *prm;
int p_ssize, c_ssize;
int p_srate, c_srate;
int p_chmask, c_chmask;
audio_dev = uac->audio_dev;
params = &audio_dev->params;
p_ssize = params->p_ssize;
c_ssize = params->c_ssize;
p_srate = params->p_srate;
c_srate = params->c_srate;
p_chmask = params->p_chmask;
c_chmask = params->c_chmask;
uac->p_residue_mil = 0;
@ -404,19 +403,18 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
runtime->hw = uac_pcm_hardware;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min = p_srate;
runtime->hw.formats = uac_ssize_to_fmt(p_ssize);
runtime->hw.channels_min = num_channels(p_chmask);
runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
/ runtime->hw.periods_min;
prm = &uac->p_prm;
} else {
runtime->hw.rate_min = c_srate;
runtime->hw.formats = uac_ssize_to_fmt(c_ssize);
runtime->hw.channels_min = num_channels(c_chmask);
runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
/ runtime->hw.periods_min;
prm = &uac->c_prm;
}
runtime->hw.period_bytes_min = 2 * prm->max_psize
/ runtime->hw.periods_min;
runtime->hw.rate_min = prm->srate;
runtime->hw.rate_max = runtime->hw.rate_min;
runtime->hw.channels_max = runtime->hw.channels_min;
@ -493,6 +491,99 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
}
static void set_active(struct uac_rtd_params *prm, bool active)
{
// notifying through the Rate ctrl
struct snd_kcontrol *kctl = prm->snd_kctl_rate;
unsigned long flags;
spin_lock_irqsave(&prm->lock, flags);
if (prm->active != active) {
prm->active = active;
snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
&kctl->id);
}
spin_unlock_irqrestore(&prm->lock, flags);
}
int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
{
struct uac_params *params = &audio_dev->params;
struct snd_uac_chip *uac = audio_dev->uac;
struct uac_rtd_params *prm;
int i;
unsigned long flags;
dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
prm = &uac->c_prm;
for (i = 0; i < UAC_MAX_RATES; i++) {
if (params->c_srates[i] == srate) {
spin_lock_irqsave(&prm->lock, flags);
prm->srate = srate;
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
if (params->c_srates[i] == 0)
break;
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val)
{
struct snd_uac_chip *uac = audio_dev->uac;
struct uac_rtd_params *prm;
unsigned long flags;
prm = &uac->c_prm;
spin_lock_irqsave(&prm->lock, flags);
*val = prm->srate;
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(u_audio_get_capture_srate);
int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
{
struct uac_params *params = &audio_dev->params;
struct snd_uac_chip *uac = audio_dev->uac;
struct uac_rtd_params *prm;
int i;
unsigned long flags;
dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
prm = &uac->p_prm;
for (i = 0; i < UAC_MAX_RATES; i++) {
if (params->p_srates[i] == srate) {
spin_lock_irqsave(&prm->lock, flags);
prm->srate = srate;
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
if (params->p_srates[i] == 0)
break;
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val)
{
struct snd_uac_chip *uac = audio_dev->uac;
struct uac_rtd_params *prm;
unsigned long flags;
prm = &uac->p_prm;
spin_lock_irqsave(&prm->lock, flags);
*val = prm->srate;
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(u_audio_get_playback_srate);
int u_audio_start_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
@ -504,8 +595,9 @@ int u_audio_start_capture(struct g_audio *audio_dev)
struct uac_params *params = &audio_dev->params;
int req_len, i;
ep = audio_dev->out_ep;
prm = &uac->c_prm;
dev_dbg(dev, "start capture with rate %d\n", prm->srate);
ep = audio_dev->out_ep;
config_ep_by_speed(gadget, &audio_dev->func, ep);
req_len = ep->maxpacket;
@ -531,6 +623,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
set_active(&uac->c_prm, true);
ep_fback = audio_dev->in_ep_fback;
if (!ep_fback)
return 0;
@ -562,7 +656,7 @@ int u_audio_start_capture(struct g_audio *audio_dev)
*/
prm->pitch = 1000000;
u_audio_set_fback_frequency(audio_dev->gadget->speed, ep,
params->c_srate, prm->pitch,
prm->srate, prm->pitch,
req_fback->buf);
if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
@ -576,6 +670,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
set_active(&uac->c_prm, false);
if (audio_dev->in_ep_fback)
free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
free_ep(&uac->c_prm, audio_dev->out_ep);
@ -596,8 +691,9 @@ int u_audio_start_playback(struct g_audio *audio_dev)
int req_len, i;
unsigned int p_pktsize;
ep = audio_dev->in_ep;
prm = &uac->p_prm;
dev_dbg(dev, "start playback with rate %d\n", prm->srate);
ep = audio_dev->in_ep;
config_ep_by_speed(gadget, &audio_dev->func, ep);
ep_desc = ep->desc;
@ -618,7 +714,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
p_pktsize = min_t(unsigned int,
uac->p_framesize *
(params->p_srate / uac->p_interval),
(prm->srate / uac->p_interval),
ep->maxpacket);
req_len = p_pktsize;
@ -646,6 +742,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
set_active(&uac->p_prm, true);
return 0;
}
EXPORT_SYMBOL_GPL(u_audio_start_playback);
@ -654,10 +752,20 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
set_active(&uac->p_prm, false);
free_ep(&uac->p_prm, audio_dev->in_ep);
}
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
void u_audio_suspend(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
set_active(&uac->p_prm, false);
set_active(&uac->c_prm, false);
}
EXPORT_SYMBOL_GPL(u_audio_suspend);
int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
{
struct snd_uac_chip *uac = audio_dev->uac;
@ -943,6 +1051,68 @@ static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
static int get_max_srate(const int *srates)
{
int i, max_srate = 0;
for (i = 0; i < UAC_MAX_RATES; i++) {
if (srates[i] == 0)
break;
if (srates[i] > max_srate)
max_srate = srates[i];
}
return max_srate;
}
static int get_min_srate(const int *srates)
{
int i, min_srate = INT_MAX;
for (i = 0; i < UAC_MAX_RATES; i++) {
if (srates[i] == 0)
break;
if (srates[i] < min_srate)
min_srate = srates[i];
}
return min_srate;
}
static int u_audio_rate_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
const int *srates;
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
struct snd_uac_chip *uac = prm->uac;
struct g_audio *audio_dev = uac->audio_dev;
struct uac_params *params = &audio_dev->params;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
if (prm == &uac->c_prm)
srates = params->c_srates;
else
srates = params->p_srates;
uinfo->value.integer.min = get_min_srate(srates);
uinfo->value.integer.max = get_max_srate(srates);
return 0;
}
static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
unsigned long flags;
spin_lock_irqsave(&prm->lock, flags);
if (prm->active)
ucontrol->value.integer.value[0] = prm->srate;
else
/* not active: reporting zero rate */
ucontrol->value.integer.value[0] = 0;
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
static struct snd_kcontrol_new u_audio_controls[] = {
[UAC_FBACK_CTRL] {
@ -973,6 +1143,13 @@ static struct snd_kcontrol_new u_audio_controls[] = {
.get = u_audio_volume_get,
.put = u_audio_volume_put,
},
[UAC_RATE_CTRL] {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "", /* will be filled later */
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = u_audio_rate_info,
.get = u_audio_rate_get,
},
};
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
@ -1005,6 +1182,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
spin_lock_init(&prm->lock);
uac->c_prm.uac = uac;
prm->max_psize = g_audio->out_ep_maxpsize;
prm->srate = params->c_srates[0];
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
@ -1029,6 +1207,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
spin_lock_init(&prm->lock);
uac->p_prm.uac = uac;
prm->max_psize = g_audio->in_ep_maxpsize;
prm->srate = params->p_srates[0];
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
@ -1186,6 +1365,25 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
prm->volume_min = fu->volume_min;
prm->volume_res = fu->volume_res;
}
/* Add rate control */
snprintf(ctrl_name, sizeof(ctrl_name),
"%s Rate", direction);
u_audio_controls[UAC_RATE_CTRL].name = ctrl_name;
kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm);
if (!kctl) {
err = -ENOMEM;
goto snd_fail;
}
kctl->id.device = pcm->device;
kctl->id.subdevice = 0;
err = snd_ctl_add(card, kctl);
if (err < 0)
goto snd_fail;
prm->snd_kctl_rate = kctl;
}
strscpy(card->driver, card_name, sizeof(card->driver));

View File

@ -10,6 +10,7 @@
#define __U_AUDIO_H
#include <linux/usb/composite.h>
#include "uac_common.h"
/*
* Same maximum frequency deviation on the slower side as in
@ -40,16 +41,18 @@ struct uac_fu_params {
struct uac_params {
/* playback */
int p_chmask; /* channel mask */
int p_srate; /* rate in Hz */
int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
int p_ssize; /* sample size */
struct uac_fu_params p_fu; /* Feature Unit parameters */
/* capture */
int c_chmask; /* channel mask */
int c_srate; /* rate in Hz */
int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
int c_ssize; /* sample size */
struct uac_fu_params c_fu; /* Feature Unit parameters */
/* rates are dynamic, in uac_rtd_params */
int req_number; /* number of preallocated requests */
int fb_max; /* upper frequency drift feedback limit per-mil */
};
@ -117,9 +120,16 @@ void u_audio_stop_capture(struct g_audio *g_audio);
int u_audio_start_playback(struct g_audio *g_audio);
void u_audio_stop_playback(struct g_audio *g_audio);
int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val);
int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate);
int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val);
int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate);
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
void u_audio_suspend(struct g_audio *g_audio);
#endif /* __U_AUDIO_H */

View File

@ -9,6 +9,7 @@
#define __U_UAC1_H
#include <linux/usb/composite.h>
#include "uac_common.h"
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
#define UAC1_DEF_CCHMASK 0x3
@ -30,10 +31,10 @@
struct f_uac1_opts {
struct usb_function_instance func_inst;
int c_chmask;
int c_srate;
int c_srates[UAC_MAX_RATES];
int c_ssize;
int p_chmask;
int p_srate;
int p_srates[UAC_MAX_RATES];
int p_ssize;
bool p_mute_present;
@ -51,6 +52,8 @@ struct f_uac1_opts {
int req_number;
unsigned bound:1;
char function_name[32];
struct mutex lock;
int refcnt;
};

View File

@ -14,13 +14,16 @@
#define U_UAC2_H
#include <linux/usb/composite.h>
#include "uac_common.h"
#define UAC2_DEF_PCHMASK 0x3
#define UAC2_DEF_PSRATE 48000
#define UAC2_DEF_PSSIZE 2
#define UAC2_DEF_PHSBINT 0
#define UAC2_DEF_CCHMASK 0x3
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
#define UAC2_DEF_CHSBINT 0
#define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC
#define UAC2_DEF_MUTE_PRESENT 1
@ -35,12 +38,14 @@
struct f_uac2_opts {
struct usb_function_instance func_inst;
int p_chmask;
int p_srate;
int p_srates[UAC_MAX_RATES];
int p_ssize;
u8 p_hs_bint;
int c_chmask;
int c_srate;
int c_srates[UAC_MAX_RATES];
int c_ssize;
int c_sync;
u8 c_hs_bint;
bool p_mute_present;
bool p_volume_present;
@ -58,6 +63,8 @@ struct f_uac2_opts {
int fb_max;
bool bound;
char function_name[32];
struct mutex lock;
int refcnt;
};

View File

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
*/
#ifndef UAC_COMMON_H
#define UAC_COMMON_H
#define UAC_MAX_RATES 10 /* maximum number of rates configurable by f_uac1/2 */
#endif

View File

@ -22,91 +22,108 @@ USB_GADGET_COMPOSITE_OPTIONS();
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC2_DEF_PCHMASK;
module_param(p_chmask, uint, S_IRUGO);
module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
static int p_srate = UAC2_DEF_PSRATE;
module_param(p_srate, uint, S_IRUGO);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
static int p_srates[UAC_MAX_RATES] = {UAC2_DEF_PSRATE};
static int p_srates_cnt = 1;
module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
module_param(p_ssize, uint, S_IRUGO);
module_param(p_ssize, uint, 0444);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Playback bInterval for HS/SS (1-4: fixed, 0: auto) */
static u8 p_hs_bint = UAC2_DEF_PHSBINT;
module_param(p_hs_bint, byte, 0444);
MODULE_PARM_DESC(p_hs_bint,
"Playback bInterval for HS/SS (1-4: fixed, 0: auto)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC2_DEF_CCHMASK;
module_param(c_chmask, uint, S_IRUGO);
module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
static int c_srate = UAC2_DEF_CSRATE;
module_param(c_srate, uint, S_IRUGO);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
static int c_srates[UAC_MAX_RATES] = {UAC2_DEF_CSRATE};
static int c_srates_cnt = 1;
module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC2_DEF_CSSIZE;
module_param(c_ssize, uint, S_IRUGO);
module_param(c_ssize, uint, 0444);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
/* capture bInterval for HS/SS (1-4: fixed, 0: auto) */
static u8 c_hs_bint = UAC2_DEF_CHSBINT;
module_param(c_hs_bint, byte, 0444);
MODULE_PARM_DESC(c_hs_bint,
"Capture bInterval for HS/SS (1-4: fixed, 0: auto)");
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
#include "u_uac1.h"
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC1_DEF_PCHMASK;
module_param(p_chmask, uint, S_IRUGO);
module_param(p_chmask, uint, 0444);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
static int p_srate = UAC1_DEF_PSRATE;
module_param(p_srate, uint, S_IRUGO);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
static int p_srates[UAC_MAX_RATES] = {UAC1_DEF_PSRATE};
static int p_srates_cnt = 1;
module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
/* Playback Default 16bits/sample */
static int p_ssize = UAC1_DEF_PSSIZE;
module_param(p_ssize, uint, S_IRUGO);
module_param(p_ssize, uint, 0444);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC1_DEF_CCHMASK;
module_param(c_chmask, uint, S_IRUGO);
module_param(c_chmask, uint, 0444);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 48 KHz */
static int c_srate = UAC1_DEF_CSRATE;
module_param(c_srate, uint, S_IRUGO);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
static int c_srates[UAC_MAX_RATES] = {UAC1_DEF_CSRATE};
static int c_srates_cnt = 1;
module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
/* Capture Default 16bits/sample */
static int c_ssize = UAC1_DEF_CSSIZE;
module_param(c_ssize, uint, S_IRUGO);
module_param(c_ssize, uint, 0444);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
#else /* CONFIG_GADGET_UAC1_LEGACY */
#include "u_uac1_legacy.h"
static char *fn_play = FILE_PCM_PLAYBACK;
module_param(fn_play, charp, S_IRUGO);
module_param(fn_play, charp, 0444);
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
static char *fn_cap = FILE_PCM_CAPTURE;
module_param(fn_cap, charp, S_IRUGO);
module_param(fn_cap, charp, 0444);
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
static char *fn_cntl = FILE_CONTROL;
module_param(fn_cntl, charp, S_IRUGO);
module_param(fn_cntl, charp, 0444);
MODULE_PARM_DESC(fn_cntl, "Control device file name");
static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
module_param(req_buf_size, int, S_IRUGO);
module_param(req_buf_size, int, 0444);
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
static int req_count = UAC1_REQ_COUNT;
module_param(req_count, int, S_IRUGO);
module_param(req_count, int, 0444);
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
module_param(audio_buf_size, int, S_IRUGO);
module_param(audio_buf_size, int, 0444);
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
#endif /* CONFIG_GADGET_UAC1_LEGACY */
#endif
@ -237,9 +254,11 @@ static int audio_bind(struct usb_composite_dev *cdev)
{
#ifndef CONFIG_GADGET_UAC1
struct f_uac2_opts *uac2_opts;
int i;
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
struct f_uac1_opts *uac1_opts;
int i;
#else
struct f_uac1_legacy_opts *uac1_opts;
#endif
@ -263,20 +282,34 @@ static int audio_bind(struct usb_composite_dev *cdev)
#ifndef CONFIG_GADGET_UAC1
uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
uac2_opts->p_chmask = p_chmask;
uac2_opts->p_srate = p_srate;
for (i = 0; i < p_srates_cnt; ++i)
uac2_opts->p_srates[i] = p_srates[i];
uac2_opts->p_ssize = p_ssize;
uac2_opts->p_hs_bint = p_hs_bint;
uac2_opts->c_chmask = c_chmask;
uac2_opts->c_srate = c_srate;
for (i = 0; i < c_srates_cnt; ++i)
uac2_opts->c_srates[i] = c_srates[i];
uac2_opts->c_ssize = c_ssize;
uac2_opts->c_hs_bint = c_hs_bint;
uac2_opts->req_number = UAC2_DEF_REQ_NUM;
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
uac1_opts->p_chmask = p_chmask;
uac1_opts->p_srate = p_srate;
for (i = 0; i < p_srates_cnt; ++i)
uac1_opts->p_srates[i] = p_srates[i];
uac1_opts->p_ssize = p_ssize;
uac1_opts->c_chmask = c_chmask;
uac1_opts->c_srate = c_srate;
for (i = 0; i < c_srates_cnt; ++i)
uac1_opts->c_srates[i] = c_srates[i];
uac1_opts->c_ssize = c_ssize;
uac1_opts->req_number = UAC1_DEF_REQ_NUM;
#else /* CONFIG_GADGET_UAC1_LEGACY */

View File

@ -134,7 +134,7 @@ static int hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
struct hidg_func_node *n, *m;
struct hidg_func_node *n = NULL, *m, *iter_n;
struct f_hid_opts *hid_opts;
int status, funcs = 0;
@ -144,18 +144,19 @@ static int hid_bind(struct usb_composite_dev *cdev)
if (!funcs)
return -ENODEV;
list_for_each_entry(n, &hidg_func_list, node) {
n->fi = usb_get_function_instance("hid");
if (IS_ERR(n->fi)) {
status = PTR_ERR(n->fi);
list_for_each_entry(iter_n, &hidg_func_list, node) {
iter_n->fi = usb_get_function_instance("hid");
if (IS_ERR(iter_n->fi)) {
status = PTR_ERR(iter_n->fi);
n = iter_n;
goto put;
}
hid_opts = container_of(n->fi, struct f_hid_opts, func_inst);
hid_opts->subclass = n->func->subclass;
hid_opts->protocol = n->func->protocol;
hid_opts->report_length = n->func->report_length;
hid_opts->report_desc_length = n->func->report_desc_length;
hid_opts->report_desc = n->func->report_desc;
hid_opts = container_of(iter_n->fi, struct f_hid_opts, func_inst);
hid_opts->subclass = iter_n->func->subclass;
hid_opts->protocol = iter_n->func->protocol;
hid_opts->report_length = iter_n->func->report_length;
hid_opts->report_desc_length = iter_n->func->report_desc_length;
hid_opts->report_desc = iter_n->func->report_desc;
}

View File

@ -2101,7 +2101,7 @@ MODULE_ALIAS_FS("gadgetfs");
/*----------------------------------------------------------------------*/
static int __init init (void)
static int __init gadgetfs_init (void)
{
int status;
@ -2111,12 +2111,12 @@ static int __init init (void)
shortname, driver_desc);
return status;
}
module_init (init);
module_init (gadgetfs_init);
static void __exit cleanup (void)
static void __exit gadgetfs_cleanup (void)
{
pr_debug ("unregister %s\n", shortname);
unregister_filesystem (&gadgetfs_type);
}
module_exit (cleanup);
module_exit (gadgetfs_cleanup);

View File

@ -758,6 +758,7 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
unsigned long flags;
struct usb_endpoint_descriptor *desc;
struct raw_ep *ep;
bool ep_props_matched = false;
desc = memdup_user((void __user *)value, sizeof(*desc));
if (IS_ERR(desc))
@ -787,13 +788,14 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
for (i = 0; i < dev->eps_num; i++) {
ep = &dev->eps[i];
if (ep->state != STATE_EP_DISABLED)
continue;
if (ep->addr != usb_endpoint_num(desc) &&
ep->addr != USB_RAW_EP_ADDR_ANY)
continue;
if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
continue;
ep_props_matched = true;
if (ep->state != STATE_EP_DISABLED)
continue;
ep->ep->desc = desc;
ret = usb_ep_enable(ep->ep);
if (ret < 0) {
@ -815,8 +817,13 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n");
ret = -EBUSY;
if (!ep_props_matched) {
dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
ret = -EINVAL;
} else {
dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
ret = -EBUSY;
}
out_free:
kfree(desc);
@ -1157,7 +1164,7 @@ static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
struct usb_raw_eps_info *info;
struct raw_ep *ep;
info = kmalloc(sizeof(*info), GFP_KERNEL);
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
ret = -ENOMEM;
goto out;
@ -1177,7 +1184,6 @@ static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
goto out_free;
}
memset(info, 0, sizeof(*info));
for (i = 0; i < dev->eps_num; i++) {
ep = &dev->eps[i];
strscpy(&info->eps[i].name[0], ep->ep->name,

View File

@ -273,7 +273,7 @@ static struct usb_composite_driver gserial_driver = {
static int switch_gserial_enable(bool do_enable)
{
if (!serial_config_driver.label)
/* init() was not called, yet */
/* gserial_init() was not called, yet */
return 0;
if (do_enable)
@ -283,7 +283,7 @@ static int switch_gserial_enable(bool do_enable)
return 0;
}
static int __init init(void)
static int __init gserial_init(void)
{
/* We *could* export two configs; that'd be much cleaner...
* but neither of these product IDs was defined that way.
@ -314,11 +314,11 @@ static int __init init(void)
return usb_composite_probe(&gserial_driver);
}
module_init(init);
module_init(gserial_init);
static void __exit cleanup(void)
static void __exit gserial_cleanup(void)
{
if (enable)
usb_composite_unregister(&gserial_driver);
}
module_exit(cleanup);
module_exit(gserial_cleanup);

View File

@ -466,19 +466,21 @@ static int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
{
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub *vhub = ep->vhub;
struct ast_vhub_req *req;
struct ast_vhub_req *req = NULL, *iter;
unsigned long flags;
int rc = -EINVAL;
spin_lock_irqsave(&vhub->lock, flags);
/* Make sure it's actually queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == u_req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != u_req)
continue;
req = iter;
break;
}
if (&req->req == u_req) {
if (req) {
EPVDBG(ep, "dequeue req @%p active=%d\n",
req, req->active);
if (req->active)

View File

@ -704,7 +704,7 @@ done:
static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct at91_ep *ep;
struct at91_request *req;
struct at91_request *req = NULL, *iter;
unsigned long flags;
struct at91_udc *udc;
@ -717,11 +717,13 @@ static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&udc->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
spin_unlock_irqrestore(&udc->lock, flags);
return -EINVAL;
}

View File

@ -860,7 +860,8 @@ static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct usba_ep *ep = to_usba_ep(_ep);
struct usba_udc *udc = ep->udc;
struct usba_request *req;
struct usba_request *req = NULL;
struct usba_request *iter;
unsigned long flags;
u32 status;
@ -869,12 +870,14 @@ static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&udc->lock, flags);
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
spin_unlock_irqrestore(&udc->lock, flags);
return -EINVAL;
}

View File

@ -1757,6 +1757,7 @@ static int bdc_gadget_ep_dequeue(struct usb_ep *_ep,
struct usb_request *_req)
{
struct bdc_req *req;
struct bdc_req *iter;
unsigned long flags;
struct bdc_ep *ep;
struct bdc *bdc;
@ -1771,12 +1772,16 @@ static int bdc_gadget_ep_dequeue(struct usb_ep *_ep,
dev_dbg(bdc->dev, "%s ep:%s req:%p\n", __func__, ep->name, req);
bdc_dbg_bd_list(bdc, ep);
spin_lock_irqsave(&bdc->lock, flags);
req = NULL;
/* make sure it's still queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->usb_req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->usb_req != _req)
continue;
req = iter;
break;
}
if (&req->usb_req != _req) {
if (!req) {
spin_unlock_irqrestore(&bdc->lock, flags);
dev_err(bdc->dev, "usb_req !=req n");
return -EINVAL;

View File

@ -1525,7 +1525,7 @@ err1:
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
struct usb_udc *udc = NULL, *iter;
int ret = -ENODEV;
if (!driver || !driver->bind || !driver->setup)
@ -1533,10 +1533,12 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
mutex_lock(&udc_lock);
if (driver->udc_name) {
list_for_each_entry(udc, &udc_list, list) {
ret = strcmp(driver->udc_name, dev_name(&udc->dev));
if (!ret)
break;
list_for_each_entry(iter, &udc_list, list) {
ret = strcmp(driver->udc_name, dev_name(&iter->dev));
if (ret)
continue;
udc = iter;
break;
}
if (ret)
ret = -ENODEV;
@ -1545,10 +1547,12 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
else
goto found;
} else {
list_for_each_entry(udc, &udc_list, list) {
list_for_each_entry(iter, &udc_list, list) {
/* For now we take the first one */
if (!udc->driver)
goto found;
if (iter->driver)
continue;
udc = iter;
goto found;
}
}

View File

@ -751,7 +751,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
struct dummy *dum;
int retval = -EINVAL;
unsigned long flags;
struct dummy_request *req = NULL;
struct dummy_request *req = NULL, *iter;
if (!_ep || !_req)
return retval;
@ -763,13 +763,14 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
local_irq_save(flags);
spin_lock(&dum->lock);
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req) {
list_del_init(&req->queue);
_req->status = -ECONNRESET;
retval = 0;
break;
}
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
list_del_init(&iter->queue);
_req->status = -ECONNRESET;
req = iter;
retval = 0;
break;
}
spin_unlock(&dum->lock);
@ -2765,7 +2766,7 @@ static struct platform_driver dummy_hcd_driver = {
static struct platform_device *the_udc_pdev[MAX_NUM_UDC];
static struct platform_device *the_hcd_pdev[MAX_NUM_UDC];
static int __init init(void)
static int __init dummy_hcd_init(void)
{
int retval = -ENOMEM;
int i;
@ -2887,9 +2888,9 @@ err_alloc_udc:
platform_device_put(the_hcd_pdev[i]);
return retval;
}
module_init(init);
module_init(dummy_hcd_init);
static void __exit cleanup(void)
static void __exit dummy_hcd_cleanup(void)
{
int i;
@ -2905,4 +2906,4 @@ static void __exit cleanup(void)
platform_driver_unregister(&dummy_udc_driver);
platform_driver_unregister(&dummy_hcd_driver);
}
module_exit(cleanup);
module_exit(dummy_hcd_cleanup);

View File

@ -1776,7 +1776,8 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
struct qe_req *req;
struct qe_req *req = NULL;
struct qe_req *iter;
unsigned long flags;
if (!_ep || !_req)
@ -1785,12 +1786,14 @@ static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&ep->udc->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
spin_unlock_irqrestore(&ep->udc->lock, flags);
return -EINVAL;
}

View File

@ -918,7 +918,8 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
struct fsl_req *req;
struct fsl_req *req = NULL;
struct fsl_req *iter;
unsigned long flags;
int ep_num, stopped, ret = 0;
u32 epctrl;
@ -940,11 +941,13 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
ret = -EINVAL;
goto out;
}

View File

@ -809,7 +809,7 @@ static void nuke(struct goku_ep *ep, int status)
/* dequeue JUST ONE request */
static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct goku_request *req;
struct goku_request *req = NULL, *iter;
struct goku_ep *ep;
struct goku_udc *dev;
unsigned long flags;
@ -833,11 +833,13 @@ static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&dev->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
spin_unlock_irqrestore (&dev->lock, flags);
return -EINVAL;
}

View File

@ -1690,7 +1690,7 @@ static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req,
/* Dequeue JUST ONE request */
static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct gr_request *req;
struct gr_request *req = NULL, *iter;
struct gr_ep *ep;
struct gr_udc *dev;
int ret = 0;
@ -1710,11 +1710,13 @@ static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&dev->lock, flags);
/* Make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
ret = -EINVAL;
goto out;
}

View File

@ -1830,7 +1830,7 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct lpc32xx_ep *ep;
struct lpc32xx_request *req;
struct lpc32xx_request *req = NULL, *iter;
unsigned long flags;
ep = container_of(_ep, struct lpc32xx_ep, ep);
@ -1840,11 +1840,13 @@ static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&ep->udc->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
spin_unlock_irqrestore(&ep->udc->lock, flags);
return -EINVAL;
}

View File

@ -1044,22 +1044,26 @@ static int max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct max3420_req *t, *req = to_max3420_req(_req);
struct max3420_req *t = NULL;
struct max3420_req *req = to_max3420_req(_req);
struct max3420_req *iter;
struct max3420_ep *ep = to_max3420_ep(_ep);
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
/* Pluck the descriptor from queue */
list_for_each_entry(t, &ep->queue, queue)
if (t == req) {
list_del_init(&req->queue);
break;
}
list_for_each_entry(iter, &ep->queue, queue) {
if (iter != req)
continue;
list_del_init(&req->queue);
t = iter;
break;
}
spin_unlock_irqrestore(&ep->lock, flags);
if (t == req)
if (t)
max3420_req_done(req, -ECONNRESET);
return 0;

View File

@ -844,7 +844,7 @@ mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct mv_u3d_ep *ep;
struct mv_u3d_req *req;
struct mv_u3d_req *req = NULL, *iter;
struct mv_u3d *u3d;
struct mv_u3d_ep_context *ep_context;
struct mv_u3d_req *next_req;
@ -861,11 +861,13 @@ static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&ep->u3d->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
ret = -EINVAL;
goto out;
}

View File

@ -771,7 +771,7 @@ static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
struct mv_req *req;
struct mv_req *req = NULL, *iter;
struct mv_udc *udc = ep->udc;
unsigned long flags;
int stopped, ret = 0;
@ -793,11 +793,13 @@ static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
ret = -EINVAL;
goto out;
}

View File

@ -926,7 +926,7 @@ static int
net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct net2272_ep *ep;
struct net2272_request *req;
struct net2272_request *req = NULL, *iter;
unsigned long flags;
int stopped;
@ -939,11 +939,13 @@ net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
ep->stopped = 1;
/* make sure it's still queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
ep->stopped = stopped;
spin_unlock_irqrestore(&ep->dev->lock, flags);
return -EINVAL;
@ -954,7 +956,6 @@ net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
dev_dbg(ep->dev->dev, "unlink (%s) pio\n", _ep->name);
net2272_done(ep, req, -ECONNRESET);
}
req = NULL;
ep->stopped = stopped;
spin_unlock_irqrestore(&ep->dev->lock, flags);

View File

@ -1240,7 +1240,8 @@ static void nuke(struct net2280_ep *ep)
static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct net2280_ep *ep;
struct net2280_request *req;
struct net2280_request *req = NULL;
struct net2280_request *iter;
unsigned long flags;
u32 dmactl;
int stopped;
@ -1266,11 +1267,13 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
}
/* make sure it's still queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
ep->stopped = stopped;
spin_unlock_irqrestore(&ep->dev->lock, flags);
ep_dbg(ep->dev, "%s: Request mismatch\n", __func__);

View File

@ -1003,7 +1003,7 @@ irq_wait:
static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct omap_ep *ep = container_of(_ep, struct omap_ep, ep);
struct omap_req *req;
struct omap_req *req = NULL, *iter;
unsigned long flags;
if (!_ep || !_req)
@ -1012,11 +1012,13 @@ static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&ep->udc->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
spin_unlock_irqrestore(&ep->udc->lock, flags);
return -EINVAL;
}

View File

@ -966,7 +966,8 @@ static void nuke(struct pxa25x_ep *ep, int status)
static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct pxa25x_ep *ep;
struct pxa25x_request *req;
struct pxa25x_request *req = NULL;
struct pxa25x_request *iter;
unsigned long flags;
ep = container_of(_ep, struct pxa25x_ep, ep);
@ -976,11 +977,13 @@ static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
local_irq_save(flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
break;
}
if (&req->req != _req) {
if (!req) {
local_irq_restore(flags);
return -EINVAL;
}

View File

@ -1159,7 +1159,7 @@ static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct pxa_ep *ep;
struct udc_usb_ep *udc_usb_ep;
struct pxa27x_request *req;
struct pxa27x_request *req = NULL, *iter;
unsigned long flags;
int rc = -EINVAL;
@ -1173,11 +1173,12 @@ static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&ep->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req) {
rc = 0;
break;
}
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
req = iter;
rc = 0;
break;
}
spin_unlock_irqrestore(&ep->lock, flags);

View File

@ -2730,7 +2730,7 @@ static const struct soc_device_attribute renesas_usb3_quirks_match[] = {
.soc_id = "r8a7795", .revision = "ES1.*",
.data = &renesas_usb3_priv_r8a7795_es1,
},
{ /* sentinel */ },
{ /* sentinel */ }
};
static const unsigned int renesas_usb3_cable[] = {

View File

@ -877,7 +877,7 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c_hsudc_ep *hsep = our_ep(_ep);
struct s3c_hsudc *hsudc = hsep->dev;
struct s3c_hsudc_req *hsreq;
struct s3c_hsudc_req *hsreq = NULL, *iter;
unsigned long flags;
hsep = our_ep(_ep);
@ -886,11 +886,13 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
spin_lock_irqsave(&hsudc->lock, flags);
list_for_each_entry(hsreq, &hsep->queue, queue) {
if (&hsreq->req == _req)
break;
list_for_each_entry(iter, &hsep->queue, queue) {
if (&iter->req != _req)
continue;
hsreq = iter;
break;
}
if (&hsreq->req != _req) {
if (!hsreq) {
spin_unlock_irqrestore(&hsudc->lock, flags);
return -EINVAL;
}

View File

@ -1265,7 +1265,7 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
int retval = -EINVAL;
unsigned long flags;
struct s3c2410_request *req = NULL;
struct s3c2410_request *req = NULL, *iter;
dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
@ -1277,13 +1277,14 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
local_irq_save(flags);
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req) {
list_del_init(&req->queue);
_req->status = -ECONNRESET;
retval = 0;
break;
}
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
list_del_init(&iter->queue);
_req->status = -ECONNRESET;
req = iter;
retval = 0;
break;
}
if (retval == 0) {

View File

@ -80,7 +80,7 @@ static int stop_timer;
* This cannot be solved by letting the RX DMA disabled until a
* request gets queued because there may be other OUT packets
* in the FIFO (important for not blocking control traffic).
* The value of set_rde controls the correspondig timer.
* The value of set_rde controls the corresponding timer.
*
* set_rde -1 == not used, means it is alloed to be set to 0 or 1
* set_rde 0 == do not touch RDE, do no start the RDE timer

View File

@ -32,9 +32,6 @@
#include <linux/workqueue.h>
/* XUSB_DEV registers */
#define SPARAM 0x000
#define SPARAM_ERSTMAX_MASK GENMASK(20, 16)
#define SPARAM_ERSTMAX(x) (((x) << 16) & SPARAM_ERSTMAX_MASK)
#define DB 0x004
#define DB_TARGET_MASK GENMASK(15, 8)
#define DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK)
@ -275,8 +272,10 @@ BUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff)
BUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff)
BUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff)
BUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff)
BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 24, 0xff)
BUILD_EP_CONTEXT_RW(rsvd, rsvd[0], 24, 0x1)
BUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1)
BUILD_EP_CONTEXT_RW(splitxstate, rsvd[0], 26, 0x1)
BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 27, 0x1f)
BUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3)
BUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff)
BUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f)
@ -1413,18 +1412,20 @@ __tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
struct tegra_xudc_request *req)
{
struct tegra_xudc *xudc = ep->xudc;
struct tegra_xudc_request *r;
struct tegra_xudc_request *r = NULL, *iter;
struct tegra_xudc_trb *deq_trb;
bool busy, kick_queue = false;
int ret = 0;
/* Make sure the request is actually queued to this endpoint. */
list_for_each_entry(r, &ep->queue, list) {
if (r == req)
break;
list_for_each_entry(iter, &ep->queue, list) {
if (iter != req)
continue;
r = iter;
break;
}
if (r != req)
if (!r)
return -EINVAL;
/* Request hasn't been queued in the transfer ring yet. */
@ -1557,6 +1558,9 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
ep_reload(xudc, ep->index);
ep_ctx_write_state(ep->context, EP_STATE_RUNNING);
ep_ctx_write_rsvd(ep->context, 0);
ep_ctx_write_partial_td(ep->context, 0);
ep_ctx_write_splitxstate(ep->context, 0);
ep_ctx_write_seq_num(ep->context, 0);
ep_reload(xudc, ep->index);
@ -2812,7 +2816,10 @@ static void tegra_xudc_reset(struct tegra_xudc *xudc)
xudc->setup_seq_num = 0;
xudc->queued_setup_packet = false;
ep_ctx_write_seq_num(ep0->context, xudc->setup_seq_num);
ep_ctx_write_rsvd(ep0->context, 0);
ep_ctx_write_partial_td(ep0->context, 0);
ep_ctx_write_splitxstate(ep0->context, 0);
ep_ctx_write_seq_num(ep0->context, 0);
deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]);
@ -3295,11 +3302,6 @@ static void tegra_xudc_init_event_ring(struct tegra_xudc *xudc)
unsigned int i;
u32 val;
val = xudc_readl(xudc, SPARAM);
val &= ~(SPARAM_ERSTMAX_MASK);
val |= SPARAM_ERSTMAX(XUDC_NR_EVENT_RINGS);
xudc_writel(xudc, val, SPARAM);
for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE *
sizeof(*xudc->event_ring[i]));

View File

@ -1136,17 +1136,20 @@ static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct xusb_ep *ep = to_xusb_ep(_ep);
struct xusb_req *req = to_xusb_req(_req);
struct xusb_req *req = NULL;
struct xusb_req *iter;
struct xusb_udc *udc = ep->udc;
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
/* Make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->usb_req == _req)
break;
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->usb_req != _req)
continue;
req = iter;
break;
}
if (&req->usb_req != _req) {
if (!req) {
spin_unlock_irqrestore(&udc->lock, flags);
return -EINVAL;
}

Some files were not shown because too many files have changed in this diff Show More