mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
USB patches for 3.9-rc1
Here's the big USB merge for 3.9-rc1 Nothing major, lots of gadget fixes, and of course, xhci stuff. All of this has been in linux-next for a while, with the exception of the last 3 patches, which were reverts of patches in the tree that caused problems, they went in yesterday. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlEmZ/kACgkQMUfUDdst+ylEhwCgyM0JEOgLuW7M8D+oNcitZn51 g7oAniD0IkLG8RCB8plLj+82AvthalCo =bHSs -----END PGP SIGNATURE----- Merge tag 'usb-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB patches from Greg Kroah-Hartman: "Here's the big USB merge for 3.9-rc1 Nothing major, lots of gadget fixes, and of course, xhci stuff. All of this has been in linux-next for a while, with the exception of the last 3 patches, which were reverts of patches in the tree that caused problems, they went in yesterday." * tag 'usb-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (190 commits) Revert "USB: EHCI: make ehci-vt8500 a separate driver" Revert "USB: EHCI: make ehci-orion a separate driver" Revert "USB: update host controller Kconfig entries" USB: update host controller Kconfig entries USB: EHCI: make ehci-orion a separate driver USB: EHCI: make ehci-vt8500 a separate driver USB: usb-storage: unusual_devs update for Super TOP SATA bridge USB: ehci-omap: Fix autoloading of module USB: ehci-omap: Don't free gpios that we didn't request USB: option: add Huawei "ACM" devices using protocol = vendor USB: serial: fix null-pointer dereferences on disconnect USB: option: add Yota / Megafon M100-1 4g modem drivers/usb: add missing GENERIC_HARDIRQS dependencies USB: storage: properly handle the endian issues of idProduct testusb: remove all mentions of 'usbfs' usb: gadget: imx_udc: make it depend on BROKEN usb: omap_control_usb: fix compile warning ARM: OMAP: USB: Add phy binding information ARM: OMAP2: MUSB: Specify omap4 has mailbox ARM: OMAP: devices: create device for usb part of control module ...
This commit is contained in:
commit
74e1a2a393
@ -227,3 +227,12 @@ Contact: Lan Tianyu <tianyu.lan@intel.com>
|
||||
Description:
|
||||
The /sys/bus/usb/devices/.../(hub interface)/portX
|
||||
is usb port device's sysfs directory.
|
||||
|
||||
What: /sys/bus/usb/devices/.../(hub interface)/portX/connect_type
|
||||
Date: January 2013
|
||||
Contact: Lan Tianyu <tianyu.lan@intel.com>
|
||||
Description:
|
||||
Some platforms provide usb port connect types through ACPI.
|
||||
This attribute is to expose these information to user space.
|
||||
The file will read "hotplug", "wired" and "not used" if the
|
||||
information is available, and "unknown" otherwise.
|
||||
|
22
Documentation/devicetree/bindings/usb/dwc3.txt
Normal file
22
Documentation/devicetree/bindings/usb/dwc3.txt
Normal file
@ -0,0 +1,22 @@
|
||||
synopsys DWC3 CORE
|
||||
|
||||
DWC3- USB3 CONTROLLER
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "synopsys,dwc3"
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts: Interrupts used by the dwc3 controller.
|
||||
- usb-phy : array of phandle for the PHY device
|
||||
|
||||
Optional properties:
|
||||
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
|
||||
This is usually a subnode to DWC3 glue to which it is connected.
|
||||
|
||||
dwc3@4a030000 {
|
||||
compatible = "synopsys,dwc3";
|
||||
reg = <0x4a030000 0xcfff>;
|
||||
interrupts = <0 92 4>
|
||||
usb-phy = <&usb2_phy>, <&usb3,phy>;
|
||||
tx-fifo-resize;
|
||||
};
|
@ -1,8 +1,11 @@
|
||||
OMAP GLUE
|
||||
OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
|
||||
|
||||
OMAP MUSB GLUE
|
||||
- compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
|
||||
- ti,hwmods : must be "usb_otg_hs"
|
||||
- ti,has-mailbox : to specify that omap uses an external mailbox
|
||||
(in control module) to communicate with the musb core during device connect
|
||||
and disconnect.
|
||||
- multipoint : Should be "1" indicating the musb controller supports
|
||||
multipoint. This is a MUSB configuration-specific setting.
|
||||
- num_eps : Specifies the number of endpoints. This is also a
|
||||
@ -16,13 +19,19 @@ OMAP MUSB GLUE
|
||||
- power : Should be "50". This signifies the controller can supply upto
|
||||
100mA when operating in host mode.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module this glue uses to write to
|
||||
mailbox
|
||||
|
||||
SOC specific device node entry
|
||||
usb_otg_hs: usb_otg_hs@4a0ab000 {
|
||||
compatible = "ti,omap4-musb";
|
||||
ti,hwmods = "usb_otg_hs";
|
||||
ti,has-mailbox;
|
||||
multipoint = <1>;
|
||||
num_eps = <16>;
|
||||
ram_bits = <12>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
};
|
||||
|
||||
Board specific device node entry
|
||||
@ -31,3 +40,26 @@ Board specific device node entry
|
||||
mode = <3>;
|
||||
power = <50>;
|
||||
};
|
||||
|
||||
OMAP CONTROL USB
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-control-usb"
|
||||
- reg : Address and length of the register set for the device. It contains
|
||||
the address of "control_dev_conf" and "otghs_control" or "phy_power_usb"
|
||||
depending upon omap4 or omap5.
|
||||
- reg-names: The names of the register addresses corresponding to the registers
|
||||
filled in "reg".
|
||||
- ti,type: This is used to differentiate whether the control module has
|
||||
usb mailbox or usb3 phy power. omap4 has usb mailbox in control module to
|
||||
notify events to the musb core and omap5 has usb3 phy power register to
|
||||
power on usb3 phy. Should be "1" if it has mailbox and "2" if it has usb3
|
||||
phy power.
|
||||
|
||||
omap_control_usb: omap-control-usb@4a002300 {
|
||||
compatible = "ti,omap-control-usb";
|
||||
reg = <0x4a002300 0x4>,
|
||||
<0x4a00233c 0x4>;
|
||||
reg-names = "control_dev_conf", "otghs_control";
|
||||
ti,type = <1>;
|
||||
};
|
||||
|
55
Documentation/devicetree/bindings/usb/samsung-usbphy.txt
Normal file
55
Documentation/devicetree/bindings/usb/samsung-usbphy.txt
Normal file
@ -0,0 +1,55 @@
|
||||
* Samsung's usb phy transceiver
|
||||
|
||||
The Samsung's phy transceiver is used for controlling usb phy for
|
||||
s3c-hsotg as well as ehci-s5p and ohci-exynos usb controllers
|
||||
across Samsung SOCs.
|
||||
TODO: Adding the PHY binding with controller(s) according to the under
|
||||
developement generic PHY driver.
|
||||
|
||||
Required properties:
|
||||
|
||||
Exynos4210:
|
||||
- compatible : should be "samsung,exynos4210-usbphy"
|
||||
- reg : base physical address of the phy registers and length of memory mapped
|
||||
region.
|
||||
|
||||
Exynos5250:
|
||||
- compatible : should be "samsung,exynos5250-usbphy"
|
||||
- reg : base physical address of the phy registers and length of memory mapped
|
||||
region.
|
||||
|
||||
Optional properties:
|
||||
- #address-cells: should be '1' when usbphy node has a child node with 'reg'
|
||||
property.
|
||||
- #size-cells: should be '1' when usbphy node has a child node with 'reg'
|
||||
property.
|
||||
- ranges: allows valid translation between child's address space and parent's
|
||||
address space.
|
||||
|
||||
- The child node 'usbphy-sys' to the node 'usbphy' is for the system controller
|
||||
interface for usb-phy. It should provide the following information required by
|
||||
usb-phy controller to control phy.
|
||||
- reg : base physical address of PHY_CONTROL registers.
|
||||
The size of this register is the total sum of size of all PHY_CONTROL
|
||||
registers that the SoC has. For example, the size will be
|
||||
'0x4' in case we have only one PHY_CONTROL register (e.g.
|
||||
OTHERS register in S3C64XX or USB_PHY_CONTROL register in S5PV210)
|
||||
and, '0x8' in case we have two PHY_CONTROL registers (e.g.
|
||||
USBDEVICE_PHY_CONTROL and USBHOST_PHY_CONTROL registers in exynos4x).
|
||||
and so on.
|
||||
|
||||
Example:
|
||||
- Exynos4210
|
||||
|
||||
usbphy@125B0000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "samsung,exynos4210-usbphy";
|
||||
reg = <0x125B0000 0x100>;
|
||||
ranges;
|
||||
|
||||
usbphy-sys {
|
||||
/* USB device and host PHY_CONTROL registers */
|
||||
reg = <0x10020704 0x8>;
|
||||
};
|
||||
};
|
@ -4,14 +4,39 @@ OMAP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb2"
|
||||
- reg : Address and length of the register set for the device. Also
|
||||
add the address of control module dev conf register until a driver for
|
||||
control module is added
|
||||
- reg : Address and length of the register set for the device.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
compatible = "ti,omap-usb2";
|
||||
reg = <0x4a0ad080 0x58>,
|
||||
<0x4a002300 0x4>;
|
||||
reg = <0x4a0ad080 0x58>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
};
|
||||
|
||||
OMAP USB3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb3"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- reg-names: The names of the register addresses corresponding to the registers
|
||||
filled in "reg".
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb3phy@4a084400 {
|
||||
compatible = "ti,omap-usb3";
|
||||
reg = <0x4a084400 0x80>,
|
||||
<0x4a084800 0x64>,
|
||||
<0x4a084c00 0x40>;
|
||||
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
};
|
||||
|
20
Documentation/devicetree/bindings/usb/usb3503.txt
Normal file
20
Documentation/devicetree/bindings/usb/usb3503.txt
Normal file
@ -0,0 +1,20 @@
|
||||
SMSC USB3503 High-Speed Hub Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "smsc,usb3503".
|
||||
- reg: Specifies the i2c slave address, it should be 0x08.
|
||||
- connect-gpios: Should specify GPIO for connect.
|
||||
- intn-gpios: Should specify GPIO for interrupt.
|
||||
- reset-gpios: Should specify GPIO for reset.
|
||||
- initial-mode: Should specify initial mode.
|
||||
(1 for HUB mode, 2 for STANDBY mode)
|
||||
|
||||
Examples:
|
||||
usb3503@08 {
|
||||
compatible = "smsc,usb3503";
|
||||
reg = <0x08>;
|
||||
connect-gpios = <&gpx3 0 1>;
|
||||
intn-gpios = <&gpx3 4 1>;
|
||||
reset-gpios = <&gpx3 5 1>;
|
||||
initial-mode = <1>;
|
||||
};
|
@ -7927,9 +7927,10 @@ F: drivers/net/wireless/ath/ar5523/
|
||||
USB ATTACHED SCSI
|
||||
M: Matthew Wilcox <willy@linux.intel.com>
|
||||
M: Sarah Sharp <sarah.a.sharp@linux.intel.com>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
L: linux-scsi@vger.kernel.org
|
||||
S: Supported
|
||||
S: Maintained
|
||||
F: drivers/usb/storage/uas.c
|
||||
|
||||
USB CDC ETHERNET DRIVER
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
@ -263,6 +264,7 @@ static void __init omap_2430sdp_init(void)
|
||||
omap_hsmmc_init(mmc);
|
||||
|
||||
omap_mux_init_signal("usb0hs_stp", OMAP_PULL_ENA | OMAP_PULL_UP);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
|
||||
board_smc91x_init();
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
@ -579,6 +580,7 @@ static void __init omap_3430sdp_init(void)
|
||||
omap_ads7846_init(1, gpio_pendown, 310, NULL);
|
||||
omap_serial_init();
|
||||
omap_sdrc_init(hyb18m512160af6_sdrc_params, NULL);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
board_smc91x_init();
|
||||
board_flash_init(sdp_flash_partitions, chip_sel_3430, 0);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/leds_pwm.h>
|
||||
#include <linux/platform_data/omap4-keypad.h>
|
||||
#include <linux/usb/musb.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/hardware/gic.h>
|
||||
#include <asm/mach-types.h>
|
||||
@ -696,6 +697,7 @@ static void __init omap_4430sdp_init(void)
|
||||
omap4_sdp4430_wifi_init();
|
||||
omap4_twl6030_hsmmc_init(mmc);
|
||||
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "omap-usb2.1.auto");
|
||||
usb_musb_init(&musb_board_data);
|
||||
|
||||
status = omap_ethernet_init();
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/regulator/fixed.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/tdo24m.h>
|
||||
@ -724,6 +725,7 @@ static void __init cm_t3x_common_init(void)
|
||||
cm_t35_init_display();
|
||||
omap_twl4030_audio_init("cm-t3x");
|
||||
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
cm_t35_init_usbh();
|
||||
cm_t35_init_camera();
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
@ -622,6 +623,7 @@ static void __init devkit8000_init(void)
|
||||
|
||||
omap_ads7846_init(2, OMAP3_DEVKIT_TS_GPIO, 0, NULL);
|
||||
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
usbhs_init(&usbhs_bdata);
|
||||
board_nand_init(devkit8000_nand_partitions,
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
@ -625,6 +626,7 @@ static void __init igep_init(void)
|
||||
omap_serial_init();
|
||||
omap_sdrc_init(m65kxxxxam_sdrc_params,
|
||||
m65kxxxxam_sdrc_params);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
|
||||
igep_flash_init();
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/smsc911x.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
@ -418,6 +419,7 @@ static void __init omap_ldp_init(void)
|
||||
omap_ads7846_init(1, 54, 310, NULL);
|
||||
omap_serial_init();
|
||||
omap_sdrc_init(NULL, NULL);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
board_nand_init(ldp_nand_partitions, ARRAY_SIZE(ldp_nand_partitions),
|
||||
ZOOM_NAND_CS, 0, nand_default_timings);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
@ -519,6 +520,7 @@ static void __init omap3_beagle_init(void)
|
||||
omap_sdrc_init(mt46h32m32lf6_sdrc_params,
|
||||
mt46h32m32lf6_sdrc_params);
|
||||
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
usbhs_init(&usbhs_bdata);
|
||||
board_nand_init(omap3beagle_nand_partitions,
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
@ -734,6 +735,7 @@ static void __init omap3_evm_init(void)
|
||||
omap_mux_init_gpio(135, OMAP_PIN_OUTPUT);
|
||||
usbhs_bdata.reset_gpio_port[1] = 135;
|
||||
}
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(&musb_board_data);
|
||||
usbhs_init(&usbhs_bdata);
|
||||
board_nand_init(omap3evm_nand_partitions,
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
@ -215,6 +216,7 @@ static void __init omap3logic_init(void)
|
||||
board_mmc_init();
|
||||
board_smsc911x_init();
|
||||
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
|
||||
/* Ensure SDRC pins are mux'd for self-refresh */
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
@ -601,6 +602,7 @@ static void __init omap3pandora_init(void)
|
||||
ARRAY_SIZE(omap3pandora_spi_board_info));
|
||||
omap_ads7846_init(1, OMAP3_PANDORA_TS_GPIO, 0, NULL);
|
||||
usbhs_init(&usbhs_bdata);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
gpmc_nand_init(&pandora_nand_data, NULL);
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/smsc911x.h>
|
||||
#include <linux/i2c/at24.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
@ -404,6 +405,7 @@ static void __init omap3_stalker_init(void)
|
||||
|
||||
omap_serial_init();
|
||||
omap_sdrc_init(mt46h32m32lf6_sdrc_params, NULL);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
usbhs_init(&usbhs_bdata);
|
||||
omap_ads7846_init(1, OMAP3_STALKER_TS_GPIO, 310, NULL);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
#include <linux/spi/spi.h>
|
||||
@ -365,6 +366,7 @@ static void __init omap3_touchbook_init(void)
|
||||
|
||||
/* Touchscreen and accelerometer */
|
||||
omap_ads7846_init(4, OMAP3_TS_GPIO, 310, &ads7846_pdata);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
usbhs_init(&usbhs_bdata);
|
||||
board_nand_init(omap3touchbook_nand_partitions,
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/regulator/fixed.h>
|
||||
#include <linux/ti_wilink_st.h>
|
||||
#include <linux/usb/musb.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/wl12xx.h>
|
||||
#include <linux/platform_data/omap-abe-twl6040.h>
|
||||
|
||||
@ -447,6 +448,7 @@ static void __init omap4_panda_init(void)
|
||||
omap_sdrc_init(NULL, NULL);
|
||||
omap4_twl6030_hsmmc_init(mmc);
|
||||
omap4_ehci_init();
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "omap-usb2.1.auto");
|
||||
usb_musb_init(&musb_board_data);
|
||||
omap4_panda_display_init();
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <linux/platform_data/mtd-nand-omap2.h>
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
@ -499,6 +500,7 @@ static void __init overo_init(void)
|
||||
mt46h32m32lf6_sdrc_params);
|
||||
board_nand_init(overo_nand_partitions,
|
||||
ARRAY_SIZE(overo_nand_partitions), NAND_CS, 0, NULL);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
usbhs_init(&usbhs_bdata);
|
||||
overo_spi_init();
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/platform_data/mtd-onenand-omap2.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach-types.h>
|
||||
@ -134,6 +135,7 @@ static void __init rm680_init(void)
|
||||
sdrc_params = nokia_get_sdram_timings();
|
||||
omap_sdrc_init(sdrc_params, sdrc_params);
|
||||
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
rm680_peripherals_init();
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/wl12xx.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/platform_data/gpio-omap.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
@ -298,6 +299,7 @@ void __init zoom_peripherals_init(void)
|
||||
omap_hsmmc_init(mmc);
|
||||
omap_i2c_init();
|
||||
platform_device_register(&omap_vwlan_device);
|
||||
usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
|
||||
usb_musb_init(NULL);
|
||||
enable_board_wakeup_source();
|
||||
omap_serial_init();
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/pinctrl/machine.h>
|
||||
#include <linux/platform_data/omap4-keypad.h>
|
||||
#include <linux/platform_data/omap_ocp2scp.h>
|
||||
#include <linux/usb/omap_control_usb.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/map.h>
|
||||
@ -254,6 +255,49 @@ static inline void omap_init_camera(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
|
||||
static struct omap_control_usb_platform_data omap4_control_usb_pdata = {
|
||||
.type = 1,
|
||||
};
|
||||
|
||||
struct resource omap4_control_usb_res[] = {
|
||||
{
|
||||
.name = "control_dev_conf",
|
||||
.start = 0x4a002300,
|
||||
.end = 0x4a002303,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.name = "otghs_control",
|
||||
.start = 0x4a00233c,
|
||||
.end = 0x4a00233f,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device omap4_control_usb = {
|
||||
.name = "omap-control-usb",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &omap4_control_usb_pdata,
|
||||
},
|
||||
.num_resources = 2,
|
||||
.resource = omap4_control_usb_res,
|
||||
};
|
||||
|
||||
static inline void __init omap_init_control_usb(void)
|
||||
{
|
||||
if (!cpu_is_omap44xx())
|
||||
return;
|
||||
|
||||
if (platform_device_register(&omap4_control_usb))
|
||||
pr_err("Error registering omap_control_usb device\n");
|
||||
}
|
||||
|
||||
#else
|
||||
static inline void omap_init_control_usb(void) { }
|
||||
#endif /* CONFIG_OMAP_CONTROL_USB */
|
||||
|
||||
int __init omap4_keyboard_init(struct omap4_keypad_platform_data
|
||||
*sdp4430_keypad_data, struct omap_board_data *bdata)
|
||||
{
|
||||
@ -721,6 +765,7 @@ static int __init omap2_init_devices(void)
|
||||
omap_init_mbox();
|
||||
/* If dtb is there, the devices will be created dynamically */
|
||||
if (!of_have_populated_dt()) {
|
||||
omap_init_control_usb();
|
||||
omap_init_dmic();
|
||||
omap_init_mcpdm();
|
||||
omap_init_mcspi();
|
||||
|
@ -2702,13 +2702,6 @@ static struct resource omap44xx_usb_phy_and_pll_addrs[] = {
|
||||
.end = 0x4a0ae000,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
/* XXX: Remove this once control module driver is in place */
|
||||
.name = "ctrl_dev",
|
||||
.start = 0x4a002300,
|
||||
.end = 0x4a002303,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -6156,12 +6149,6 @@ static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
|
||||
.pa_end = 0x4a0ab7ff,
|
||||
.flags = ADDR_TYPE_RT
|
||||
},
|
||||
{
|
||||
/* XXX: Remove this once control module driver is in place */
|
||||
.pa_start = 0x4a00233c,
|
||||
.pa_end = 0x4a00233f,
|
||||
.flags = ADDR_TYPE_RT
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -85,6 +85,9 @@ void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
|
||||
musb_plat.mode = board_data->mode;
|
||||
musb_plat.extvbus = board_data->extvbus;
|
||||
|
||||
if (cpu_is_omap44xx())
|
||||
musb_plat.has_mailbox = true;
|
||||
|
||||
if (soc_is_am35xx()) {
|
||||
oh_name = "am35x_otg_hs";
|
||||
name = "musb-am35x";
|
||||
|
@ -91,6 +91,7 @@ enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_flags);
|
||||
|
||||
/**
|
||||
* __dev_pm_qos_read_value - Get PM QoS constraint for a given device.
|
||||
|
@ -237,7 +237,7 @@ void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie)
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Transactions */
|
||||
|
||||
static inline u16 ll_recv_msg(struct c67x00_device *dev)
|
||||
static inline int ll_recv_msg(struct c67x00_device *dev)
|
||||
{
|
||||
u16 res;
|
||||
|
||||
|
@ -19,7 +19,7 @@ struct usbmisc_usb_device {
|
||||
struct device *dev; /* usb controller device */
|
||||
int index;
|
||||
|
||||
int disable_oc:1; /* over current detect disabled */
|
||||
unsigned int disable_oc:1; /* over current detect disabled */
|
||||
};
|
||||
|
||||
int usbmisc_set_ops(const struct usbmisc_ops *ops);
|
||||
|
@ -411,7 +411,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
base = devm_request_and_ioremap(dev, res);
|
||||
if (!res) {
|
||||
if (!base) {
|
||||
dev_err(dev, "can't request and ioremap resource\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
|
||||
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
|
||||
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
|
||||
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
|
||||
usbcore-y += port.o
|
||||
|
||||
usbcore-$(CONFIG_PCI) += hcd-pci.o
|
||||
usbcore-$(CONFIG_ACPI) += usb-acpi.o
|
||||
|
@ -316,17 +316,23 @@ static char *usb_dump_iad_descriptor(char *start, char *end,
|
||||
*/
|
||||
static char *usb_dump_config_descriptor(char *start, char *end,
|
||||
const struct usb_config_descriptor *desc,
|
||||
int active)
|
||||
int active, int speed)
|
||||
{
|
||||
int mul;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
if (speed == USB_SPEED_SUPER)
|
||||
mul = 8;
|
||||
else
|
||||
mul = 2;
|
||||
start += sprintf(start, format_config,
|
||||
/* mark active/actual/current cfg. */
|
||||
active ? '*' : ' ',
|
||||
desc->bNumInterfaces,
|
||||
desc->bConfigurationValue,
|
||||
desc->bmAttributes,
|
||||
desc->bMaxPower * 2);
|
||||
desc->bMaxPower * mul);
|
||||
return start;
|
||||
}
|
||||
|
||||
@ -342,7 +348,8 @@ static char *usb_dump_config(int speed, char *start, char *end,
|
||||
if (!config)
|
||||
/* getting these some in 2.3.7; none in 2.3.6 */
|
||||
return start + sprintf(start, "(null Cfg. desc.)\n");
|
||||
start = usb_dump_config_descriptor(start, end, &config->desc, active);
|
||||
start = usb_dump_config_descriptor(start, end, &config->desc, active,
|
||||
speed);
|
||||
for (i = 0; i < USB_MAXIADS; i++) {
|
||||
if (config->intf_assoc[i] == NULL)
|
||||
break;
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/signal.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/hcd.h> /* for usbcore internals */
|
||||
@ -1077,7 +1078,7 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
|
||||
if (!intf || !intf->dev.driver)
|
||||
ret = -ENODATA;
|
||||
else {
|
||||
strncpy(gd.driver, intf->dev.driver->name,
|
||||
strlcpy(gd.driver, intf->dev.driver->name,
|
||||
sizeof(gd.driver));
|
||||
ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ int usb_choose_configuration(struct usb_device *udev)
|
||||
*/
|
||||
|
||||
/* Rule out configs that draw too much bus current */
|
||||
if (c->desc.bMaxPower * 2 > udev->bus_mA) {
|
||||
if (usb_get_max_power(udev, c) > udev->bus_mA) {
|
||||
insufficient_power++;
|
||||
continue;
|
||||
}
|
||||
|
@ -620,6 +620,10 @@ nongeneric:
|
||||
status = hcd->driver->hub_control (hcd,
|
||||
typeReq, wValue, wIndex,
|
||||
tbuf, wLength);
|
||||
|
||||
if (typeReq == GetHubDescriptor)
|
||||
usb_hub_adjust_deviceremovable(hcd->self.root_hub,
|
||||
(struct usb_hub_descriptor *)tbuf);
|
||||
break;
|
||||
error:
|
||||
/* "protocol stall" on error */
|
||||
@ -2550,7 +2554,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
}
|
||||
|
||||
/* starting here, usbcore will pay attention to this root hub */
|
||||
rhdev->bus_mA = min(500u, hcd->power_budget);
|
||||
if ((retval = register_root_hub(hcd)) != 0)
|
||||
goto err_register_root_hub;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
122
drivers/usb/core/hub.h
Normal file
122
drivers/usb/core/hub.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* usb hub driver head file
|
||||
*
|
||||
* Copyright (C) 1999 Linus Torvalds
|
||||
* Copyright (C) 1999 Johannes Erdfelt
|
||||
* Copyright (C) 1999 Gregory P. Smith
|
||||
* Copyright (C) 2001 Brad Hards (bhards@bigpond.net.au)
|
||||
* Copyright (C) 2012 Intel Corp (tianyu.lan@intel.com)
|
||||
*
|
||||
* move struct usb_hub to this file.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch11.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "usb.h"
|
||||
|
||||
struct usb_hub {
|
||||
struct device *intfdev; /* the "interface" device */
|
||||
struct usb_device *hdev;
|
||||
struct kref kref;
|
||||
struct urb *urb; /* for interrupt polling pipe */
|
||||
|
||||
/* buffer for urb ... with extra space in case of babble */
|
||||
u8 (*buffer)[8];
|
||||
union {
|
||||
struct usb_hub_status hub;
|
||||
struct usb_port_status port;
|
||||
} *status; /* buffer for status reports */
|
||||
struct mutex status_mutex; /* for the status buffer */
|
||||
|
||||
int error; /* last reported error */
|
||||
int nerrors; /* track consecutive errors */
|
||||
|
||||
struct list_head event_list; /* hubs w/data or errs ready */
|
||||
unsigned long event_bits[1]; /* status change bitmask */
|
||||
unsigned long change_bits[1]; /* ports with logical connect
|
||||
status change */
|
||||
unsigned long busy_bits[1]; /* ports being reset or
|
||||
resumed */
|
||||
unsigned long removed_bits[1]; /* ports with a "removed"
|
||||
device present */
|
||||
unsigned long wakeup_bits[1]; /* ports that have signaled
|
||||
remote wakeup */
|
||||
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
||||
#error event_bits[] is too short!
|
||||
#endif
|
||||
|
||||
struct usb_hub_descriptor *descriptor; /* class descriptor */
|
||||
struct usb_tt tt; /* Transaction Translator */
|
||||
|
||||
unsigned mA_per_port; /* current for each child */
|
||||
|
||||
unsigned limited_power:1;
|
||||
unsigned quiescing:1;
|
||||
unsigned disconnected:1;
|
||||
|
||||
unsigned quirk_check_port_auto_suspend:1;
|
||||
|
||||
unsigned has_indicators:1;
|
||||
u8 indicator[USB_MAXCHILDREN];
|
||||
struct delayed_work leds;
|
||||
struct delayed_work init_work;
|
||||
struct usb_port **ports;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct usb port - kernel's representation of a usb port
|
||||
* @child: usb device attatched to the port
|
||||
* @dev: generic device interface
|
||||
* @port_owner: port's owner
|
||||
* @connect_type: port's connect type
|
||||
* @portnum: port index num based one
|
||||
* @power_is_on: port's power state
|
||||
* @did_runtime_put: port has done pm_runtime_put().
|
||||
*/
|
||||
struct usb_port {
|
||||
struct usb_device *child;
|
||||
struct device dev;
|
||||
struct dev_state *port_owner;
|
||||
enum usb_port_connect_type connect_type;
|
||||
u8 portnum;
|
||||
unsigned power_is_on:1;
|
||||
unsigned did_runtime_put:1;
|
||||
};
|
||||
|
||||
#define to_usb_port(_dev) \
|
||||
container_of(_dev, struct usb_port, dev)
|
||||
|
||||
extern int usb_hub_create_port_device(struct usb_hub *hub,
|
||||
int port1);
|
||||
extern void usb_hub_remove_port_device(struct usb_hub *hub,
|
||||
int port1);
|
||||
extern int usb_hub_set_port_power(struct usb_device *hdev,
|
||||
int port1, bool set);
|
||||
extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
|
||||
extern int hub_port_debounce(struct usb_hub *hub, int port1,
|
||||
bool must_be_connected);
|
||||
extern int usb_clear_port_feature(struct usb_device *hdev,
|
||||
int port1, int feature);
|
||||
|
||||
static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
|
||||
int port1)
|
||||
{
|
||||
return hub_port_debounce(hub, port1, true);
|
||||
}
|
||||
|
||||
static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
|
||||
int port1)
|
||||
{
|
||||
return hub_port_debounce(hub, port1, false);
|
||||
}
|
||||
|
@ -1751,7 +1751,7 @@ free_interfaces:
|
||||
}
|
||||
}
|
||||
|
||||
i = dev->bus_mA - cp->desc.bMaxPower * 2;
|
||||
i = dev->bus_mA - usb_get_max_power(dev, cp);
|
||||
if (i < 0)
|
||||
dev_warn(&dev->dev, "new config #%d exceeds power "
|
||||
"limit by %dmA\n",
|
||||
|
202
drivers/usb/core/port.c
Normal file
202
drivers/usb/core/port.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* usb port device code
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corp
|
||||
*
|
||||
* Author: Lan Tianyu <tianyu.lan@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#include "hub.h"
|
||||
|
||||
static const struct attribute_group *port_dev_group[];
|
||||
|
||||
static ssize_t show_port_connect_type(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
char *result;
|
||||
|
||||
switch (port_dev->connect_type) {
|
||||
case USB_PORT_CONNECT_TYPE_HOT_PLUG:
|
||||
result = "hotplug";
|
||||
break;
|
||||
case USB_PORT_CONNECT_TYPE_HARD_WIRED:
|
||||
result = "hardwired";
|
||||
break;
|
||||
case USB_PORT_NOT_USED:
|
||||
result = "not used";
|
||||
break;
|
||||
default:
|
||||
result = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", result);
|
||||
}
|
||||
static DEVICE_ATTR(connect_type, S_IRUGO, show_port_connect_type,
|
||||
NULL);
|
||||
|
||||
static struct attribute *port_dev_attrs[] = {
|
||||
&dev_attr_connect_type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group port_dev_attr_grp = {
|
||||
.attrs = port_dev_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *port_dev_group[] = {
|
||||
&port_dev_attr_grp,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void usb_port_device_release(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
|
||||
dev_pm_qos_hide_flags(dev);
|
||||
kfree(port_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
static int usb_port_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
struct usb_device *hdev = to_usb_device(dev->parent->parent);
|
||||
struct usb_interface *intf = to_usb_interface(dev->parent);
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
int port1 = port_dev->portnum;
|
||||
int retval;
|
||||
|
||||
if (!hub)
|
||||
return -EINVAL;
|
||||
|
||||
usb_autopm_get_interface(intf);
|
||||
set_bit(port1, hub->busy_bits);
|
||||
|
||||
retval = usb_hub_set_port_power(hdev, port1, true);
|
||||
if (port_dev->child && !retval) {
|
||||
/*
|
||||
* Wait for usb hub port to be reconnected in order to make
|
||||
* the resume procedure successful.
|
||||
*/
|
||||
retval = hub_port_debounce_be_connected(hub, port1);
|
||||
if (retval < 0) {
|
||||
dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
|
||||
retval);
|
||||
goto out;
|
||||
}
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
|
||||
|
||||
/* Set return value to 0 if debounce successful */
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
clear_bit(port1, hub->busy_bits);
|
||||
usb_autopm_put_interface(intf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int usb_port_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
struct usb_device *hdev = to_usb_device(dev->parent->parent);
|
||||
struct usb_interface *intf = to_usb_interface(dev->parent);
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
int port1 = port_dev->portnum;
|
||||
int retval;
|
||||
|
||||
if (!hub)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
|
||||
== PM_QOS_FLAGS_ALL)
|
||||
return -EAGAIN;
|
||||
|
||||
usb_autopm_get_interface(intf);
|
||||
set_bit(port1, hub->busy_bits);
|
||||
retval = usb_hub_set_port_power(hdev, port1, false);
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
|
||||
clear_bit(port1, hub->busy_bits);
|
||||
usb_autopm_put_interface(intf);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops usb_port_pm_ops = {
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
.runtime_suspend = usb_port_runtime_suspend,
|
||||
.runtime_resume = usb_port_runtime_resume,
|
||||
.runtime_idle = pm_generic_runtime_idle,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct device_type usb_port_device_type = {
|
||||
.name = "usb_port",
|
||||
.release = usb_port_device_release,
|
||||
.pm = &usb_port_pm_ops,
|
||||
};
|
||||
|
||||
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
|
||||
{
|
||||
struct usb_port *port_dev = NULL;
|
||||
int retval;
|
||||
|
||||
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
|
||||
if (!port_dev) {
|
||||
retval = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hub->ports[port1 - 1] = port_dev;
|
||||
port_dev->portnum = port1;
|
||||
port_dev->power_is_on = true;
|
||||
port_dev->dev.parent = hub->intfdev;
|
||||
port_dev->dev.groups = port_dev_group;
|
||||
port_dev->dev.type = &usb_port_device_type;
|
||||
dev_set_name(&port_dev->dev, "port%d", port1);
|
||||
|
||||
retval = device_register(&port_dev->dev);
|
||||
if (retval)
|
||||
goto error_register;
|
||||
|
||||
pm_runtime_set_active(&port_dev->dev);
|
||||
|
||||
/* It would be dangerous if user space couldn't
|
||||
* prevent usb device from being powered off. So don't
|
||||
* enable port runtime pm if failed to expose port's pm qos.
|
||||
*/
|
||||
if (!dev_pm_qos_expose_flags(&port_dev->dev,
|
||||
PM_QOS_FLAG_NO_POWER_OFF))
|
||||
pm_runtime_enable(&port_dev->dev);
|
||||
|
||||
device_enable_async_suspend(&port_dev->dev);
|
||||
return 0;
|
||||
|
||||
error_register:
|
||||
put_device(&port_dev->dev);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usb_hub_remove_port_device(struct usb_hub *hub,
|
||||
int port1)
|
||||
{
|
||||
device_unregister(&hub->ports[port1 - 1]->dev);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "usb.h"
|
||||
|
||||
/* Active configuration fields */
|
||||
#define usb_actconfig_show(field, multiplier, format_string) \
|
||||
#define usb_actconfig_show(field, format_string) \
|
||||
static ssize_t show_##field(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
@ -28,18 +28,31 @@ static ssize_t show_##field(struct device *dev, \
|
||||
actconfig = udev->actconfig; \
|
||||
if (actconfig) \
|
||||
return sprintf(buf, format_string, \
|
||||
actconfig->desc.field * multiplier); \
|
||||
actconfig->desc.field); \
|
||||
else \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
#define usb_actconfig_attr(field, multiplier, format_string) \
|
||||
usb_actconfig_show(field, multiplier, format_string) \
|
||||
#define usb_actconfig_attr(field, format_string) \
|
||||
usb_actconfig_show(field, format_string) \
|
||||
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
|
||||
|
||||
usb_actconfig_attr(bNumInterfaces, 1, "%2d\n")
|
||||
usb_actconfig_attr(bmAttributes, 1, "%2x\n")
|
||||
usb_actconfig_attr(bMaxPower, 2, "%3dmA\n")
|
||||
usb_actconfig_attr(bNumInterfaces, "%2d\n")
|
||||
usb_actconfig_attr(bmAttributes, "%2x\n")
|
||||
|
||||
static ssize_t show_bMaxPower(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct usb_host_config *actconfig;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
actconfig = udev->actconfig;
|
||||
if (!actconfig)
|
||||
return 0;
|
||||
return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
|
||||
}
|
||||
static DEVICE_ATTR(bMaxPower, S_IRUGO, show_bMaxPower, NULL);
|
||||
|
||||
static ssize_t show_configuration_string(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -56,7 +69,7 @@ static ssize_t show_configuration_string(struct device *dev,
|
||||
static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
|
||||
|
||||
/* configuration value is always present, and r/w */
|
||||
usb_actconfig_show(bConfigurationValue, 1, "%u\n");
|
||||
usb_actconfig_show(bConfigurationValue, "%u\n");
|
||||
|
||||
static ssize_t
|
||||
set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <linux/pm.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
struct usb_hub_descriptor;
|
||||
struct dev_state;
|
||||
|
||||
/* Functions local to drivers/usb/core/ */
|
||||
@ -38,6 +39,15 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
||||
extern int usb_choose_configuration(struct usb_device *udev);
|
||||
|
||||
static inline unsigned usb_get_max_power(struct usb_device *udev,
|
||||
struct usb_host_config *c)
|
||||
{
|
||||
/* SuperSpeed power is in 8 mA units; others are in 2 mA units */
|
||||
unsigned mul = (udev->speed == USB_SPEED_SUPER ? 8 : 2);
|
||||
|
||||
return c->desc.bMaxPower * mul;
|
||||
}
|
||||
|
||||
extern void usb_kick_khubd(struct usb_device *dev);
|
||||
extern int usb_match_one_id_intf(struct usb_device *dev,
|
||||
struct usb_host_interface *intf,
|
||||
@ -173,6 +183,8 @@ extern enum usb_port_connect_type
|
||||
usb_get_hub_port_connect_type(struct usb_device *hdev, int port1);
|
||||
extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
|
||||
enum usb_port_connect_type type);
|
||||
extern void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
|
||||
struct usb_hub_descriptor *desc);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int usb_acpi_register(void);
|
||||
|
@ -1,6 +1,6 @@
|
||||
config USB_DWC3
|
||||
tristate "DesignWare USB3 DRD Core Support"
|
||||
depends on (USB && USB_GADGET)
|
||||
depends on (USB || USB_GADGET) && GENERIC_HARDIRQS
|
||||
select USB_OTG_UTILS
|
||||
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
|
||||
help
|
||||
@ -12,6 +12,35 @@ config USB_DWC3
|
||||
|
||||
if USB_DWC3
|
||||
|
||||
choice
|
||||
bool "DWC3 Mode Selection"
|
||||
default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET)
|
||||
default USB_DWC3_HOST if (USB && !USB_GADGET)
|
||||
default USB_DWC3_GADGET if (!USB && USB_GADGET)
|
||||
|
||||
config USB_DWC3_HOST
|
||||
bool "Host only mode"
|
||||
depends on USB
|
||||
help
|
||||
Select this when you want to use DWC3 in host mode only,
|
||||
thereby the gadget feature will be regressed.
|
||||
|
||||
config USB_DWC3_GADGET
|
||||
bool "Gadget only mode"
|
||||
depends on USB_GADGET
|
||||
help
|
||||
Select this when you want to use DWC3 in gadget mode only,
|
||||
thereby the host feature will be regressed.
|
||||
|
||||
config USB_DWC3_DUAL_ROLE
|
||||
bool "Dual Role mode"
|
||||
depends on (USB && USB_GADGET)
|
||||
help
|
||||
This is the default mode of working of DWC3 controller where
|
||||
both host and gadget features are enabled.
|
||||
|
||||
endchoice
|
||||
|
||||
config USB_DWC3_DEBUG
|
||||
bool "Enable Debugging Messages"
|
||||
help
|
||||
|
@ -4,8 +4,14 @@ ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3.o
|
||||
|
||||
dwc3-y := core.o
|
||||
|
||||
ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
|
||||
dwc3-y += host.o
|
||||
endif
|
||||
|
||||
ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
|
||||
dwc3-y += gadget.o ep0.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_DEBUG_FS),)
|
||||
dwc3-y += debugfs.o
|
||||
|
@ -420,18 +420,27 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
|
||||
dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1);
|
||||
} else {
|
||||
dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(dwc->usb2_phy)) {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
|
||||
if (IS_ERR_OR_NULL(dwc->usb3_phy)) {
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
|
||||
@ -450,8 +459,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
else
|
||||
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
|
||||
|
||||
if (of_get_property(node, "tx-fifo-resize", NULL))
|
||||
dwc->needs_fifo_resize = true;
|
||||
dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
@ -550,9 +558,9 @@ err0:
|
||||
static int dwc3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
@ -580,11 +588,22 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{
|
||||
.compatible = "synopsys,dwc3"
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver dwc3_driver = {
|
||||
.probe = dwc3_probe,
|
||||
.remove = dwc3_remove,
|
||||
.driver = {
|
||||
.name = "dwc3",
|
||||
.of_match_table = of_match_ptr(of_dwc3_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -55,7 +55,9 @@
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
|
||||
#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
|
||||
#define DWC3_EVENT_SIZE 4 /* bytes */
|
||||
#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
|
||||
#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
|
||||
#define DWC3_EVENT_TYPE_MASK 0xfe
|
||||
|
||||
#define DWC3_EVENT_TYPE_DEV 0
|
||||
@ -405,7 +407,6 @@ struct dwc3_event_buffer {
|
||||
* @number: endpoint number (1 - 15)
|
||||
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
|
||||
* @resource_index: Resource transfer index
|
||||
* @current_uf: Current uf received through last event parameter
|
||||
* @interval: the intervall on which the ISOC transfer is started
|
||||
* @name: a human readable name e.g. ep1out-bulk
|
||||
* @direction: true for TX, false for RX
|
||||
@ -439,7 +440,6 @@ struct dwc3_ep {
|
||||
u8 number;
|
||||
u8 type;
|
||||
u8 resource_index;
|
||||
u16 current_uf;
|
||||
u32 interval;
|
||||
|
||||
char name[20];
|
||||
@ -581,6 +581,7 @@ struct dwc3_request {
|
||||
struct usb_request request;
|
||||
struct list_head list;
|
||||
struct dwc3_ep *dep;
|
||||
u32 start_slot;
|
||||
|
||||
u8 epnum;
|
||||
struct dwc3_trb *trb;
|
||||
@ -721,6 +722,7 @@ struct dwc3 {
|
||||
|
||||
struct dwc3_hwparams hwparams;
|
||||
struct dentry *root;
|
||||
struct debugfs_regset32 *regset;
|
||||
|
||||
u8 test_mode;
|
||||
u8 test_mode_nr;
|
||||
@ -862,10 +864,24 @@ union dwc3_event {
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
|
||||
int dwc3_gadget_resize_tx_fifos(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);
|
||||
#else
|
||||
static inline int dwc3_host_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline void dwc3_host_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_gadget_init(struct dwc3 *dwc);
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_CORE_H */
|
||||
|
@ -59,7 +59,7 @@
|
||||
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
|
||||
}
|
||||
|
||||
static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
static struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(GSBUSCFG0),
|
||||
dump_register(GSBUSCFG1),
|
||||
dump_register(GTXTHRCFG),
|
||||
@ -376,27 +376,6 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(OSTS),
|
||||
};
|
||||
|
||||
static int dwc3_regdump_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
|
||||
seq_printf(s, "DesignWare USB3 Core Register Dump\n");
|
||||
debugfs_print_regs32(s, dwc3_regs, ARRAY_SIZE(dwc3_regs),
|
||||
dwc->regs, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_regdump_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dwc3_regdump_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_regdump_fops = {
|
||||
.open = dwc3_regdump_open,
|
||||
.read = seq_read,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int dwc3_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
@ -666,13 +645,23 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
|
||||
dwc->root = root;
|
||||
|
||||
file = debugfs_create_file("regdump", S_IRUGO, root, dwc,
|
||||
&dwc3_regdump_fops);
|
||||
dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
|
||||
if (!dwc->regset) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc->regset->regs = dwc3_regs;
|
||||
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
|
||||
dwc->regset->base = dwc->regs;
|
||||
|
||||
file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET)
|
||||
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_mode_fops);
|
||||
if (!file) {
|
||||
@ -693,6 +682,7 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -42,7 +42,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 0);
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -53,7 +53,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 1);
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
@ -95,13 +95,14 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
struct platform_device *dwc3;
|
||||
struct dwc3_exynos *exynos;
|
||||
struct clk *clk;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
exynos = kzalloc(sizeof(*exynos), GFP_KERNEL);
|
||||
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
|
||||
if (!exynos) {
|
||||
dev_err(&pdev->dev, "not enough memory\n");
|
||||
goto err0;
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -116,30 +117,30 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
|
||||
ret = dwc3_exynos_register_phys(exynos);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "couldn't register PHYs\n");
|
||||
goto err1;
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
|
||||
if (!dwc3) {
|
||||
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
|
||||
dev_err(dev, "couldn't allocate dwc3 device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(dev, "usbdrd30");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "couldn't get clock\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "usbdrd30");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "couldn't get clock\n");
|
||||
ret = -EINVAL;
|
||||
goto err3;
|
||||
}
|
||||
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
|
||||
|
||||
dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
|
||||
|
||||
dwc3->dev.parent = &pdev->dev;
|
||||
dwc3->dev.dma_mask = pdev->dev.dma_mask;
|
||||
dwc3->dev.dma_parms = pdev->dev.dma_parms;
|
||||
dwc3->dev.parent = dev;
|
||||
dwc3->dev.dma_mask = dev->dma_mask;
|
||||
dwc3->dev.dma_parms = dev->dma_parms;
|
||||
exynos->dwc3 = dwc3;
|
||||
exynos->dev = &pdev->dev;
|
||||
exynos->dev = dev;
|
||||
exynos->clk = clk;
|
||||
|
||||
clk_enable(exynos->clk);
|
||||
@ -147,26 +148,23 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
ret = platform_device_add_resources(dwc3, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err4;
|
||||
dev_err(dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register dwc3 device\n");
|
||||
goto err4;
|
||||
dev_err(dev, "failed to register dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
err2:
|
||||
clk_disable(clk);
|
||||
clk_put(clk);
|
||||
err3:
|
||||
platform_device_put(dwc3);
|
||||
err1:
|
||||
kfree(exynos);
|
||||
err0:
|
||||
platform_device_put(dwc3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -179,16 +177,13 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
|
||||
platform_device_unregister(exynos->usb3_phy);
|
||||
|
||||
clk_disable(exynos->clk);
|
||||
clk_put(exynos->clk);
|
||||
|
||||
kfree(exynos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id exynos_dwc3_match[] = {
|
||||
{ .compatible = "samsung,exynos-dwc3" },
|
||||
{ .compatible = "samsung,exynos5250-dwusb3" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
|
||||
|
@ -43,10 +43,13 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/dwc3-omap.h>
|
||||
#include <linux/usb/dwc3-omap.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/nop-usb-xceiv.h>
|
||||
@ -78,23 +81,6 @@
|
||||
|
||||
/* SYSCONFIG REGISTER */
|
||||
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
|
||||
#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4)
|
||||
|
||||
#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0
|
||||
#define USBOTGSS_STANDBYMODE_NO_STANDBY 1
|
||||
#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2
|
||||
#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3
|
||||
|
||||
#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4)
|
||||
|
||||
#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2)
|
||||
|
||||
#define USBOTGSS_IDLEMODE_FORCE_IDLE 0
|
||||
#define USBOTGSS_IDLEMODE_NO_IDLE 1
|
||||
#define USBOTGSS_IDLEMODE_SMART_IDLE 2
|
||||
#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3
|
||||
|
||||
#define USBOTGSS_IDLEMODE_MASK (0x03 << 2)
|
||||
|
||||
/* IRQ_EOI REGISTER */
|
||||
#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0)
|
||||
@ -133,7 +119,6 @@ struct dwc3_omap {
|
||||
/* device lock */
|
||||
spinlock_t lock;
|
||||
|
||||
struct platform_device *dwc3;
|
||||
struct platform_device *usb2_phy;
|
||||
struct platform_device *usb3_phy;
|
||||
struct device *dev;
|
||||
@ -147,6 +132,8 @@ struct dwc3_omap {
|
||||
u32 dma_status:1;
|
||||
};
|
||||
|
||||
struct dwc3_omap *_omap;
|
||||
|
||||
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
@ -157,6 +144,57 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
|
||||
{
|
||||
u32 val;
|
||||
struct dwc3_omap *omap = _omap;
|
||||
|
||||
switch (status) {
|
||||
case OMAP_DWC3_ID_GROUND:
|
||||
dev_dbg(omap->dev, "ID GND\n");
|
||||
|
||||
val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_SESSEND);
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_VBUS_VALID:
|
||||
dev_dbg(omap->dev, "VBUS Connect\n");
|
||||
|
||||
val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_ID_FLOAT:
|
||||
case OMAP_DWC3_VBUS_OFF:
|
||||
dev_dbg(omap->dev, "VBUS Disconnect\n");
|
||||
|
||||
val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
|
||||
| USBOTGSS_UTMI_OTG_STATUS_IDDIG;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(omap->dev, "ID float\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
|
||||
|
||||
static int dwc3_omap_register_phys(struct dwc3_omap *omap)
|
||||
{
|
||||
struct nop_usb_xceiv_platform_data pdata;
|
||||
@ -165,7 +203,7 @@ static int dwc3_omap_register_phys(struct dwc3_omap *omap)
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 0);
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -176,7 +214,7 @@ static int dwc3_omap_register_phys(struct dwc3_omap *omap)
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 1);
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
@ -262,12 +300,20 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dwc3_omap_remove_core(struct device *dev, void *c)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_omap_data *pdata = pdev->dev.platform_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
struct platform_device *dwc3;
|
||||
struct dwc3_omap *omap;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -314,30 +360,32 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
|
||||
if (!dwc3) {
|
||||
dev_err(dev, "couldn't allocate dwc3 device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
|
||||
if (!context) {
|
||||
dev_err(dev, "couldn't allocate dwc3 context memory\n");
|
||||
goto err2;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&omap->lock);
|
||||
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
|
||||
|
||||
dwc3->dev.parent = dev;
|
||||
dwc3->dev.dma_mask = dev->dma_mask;
|
||||
dwc3->dev.dma_parms = dev->dma_parms;
|
||||
omap->resource_size = resource_size(res);
|
||||
omap->context = context;
|
||||
omap->dev = dev;
|
||||
omap->irq = irq;
|
||||
omap->base = base;
|
||||
omap->dwc3 = dwc3;
|
||||
|
||||
/*
|
||||
* REVISIT if we ever have two instances of the wrapper, we will be
|
||||
* in big trouble
|
||||
*/
|
||||
_omap = omap;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "get_sync failed with err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
|
||||
@ -368,21 +416,12 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
|
||||
|
||||
/* Set No-Idle and No-Standby */
|
||||
reg &= ~(USBOTGSS_STANDBYMODE_MASK
|
||||
| USBOTGSS_IDLEMODE_MASK);
|
||||
|
||||
reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
|
||||
| USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
goto err2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable all IRQs */
|
||||
@ -401,33 +440,28 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
|
||||
|
||||
ret = platform_device_add_resources(dwc3, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (node) {
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err2;
|
||||
dev_err(&pdev->dev,
|
||||
"failed to add create dwc3 core\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
platform_device_put(dwc3);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_omap_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_omap *omap = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(omap->dwc3);
|
||||
platform_device_unregister(omap->usb2_phy);
|
||||
platform_device_unregister(omap->usb3_phy);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -241,21 +241,23 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int i;
|
||||
|
||||
if (req->queued) {
|
||||
if (req->request.num_mapped_sgs)
|
||||
dep->busy_slot += req->request.num_mapped_sgs;
|
||||
else
|
||||
i = 0;
|
||||
do {
|
||||
dep->busy_slot++;
|
||||
|
||||
/*
|
||||
* Skip LINK TRB. We can't use req->trb and check for
|
||||
* DWC3_TRBCTL_LINK_TRB because it points the TRB we just
|
||||
* completed (not the LINK TRB).
|
||||
* DWC3_TRBCTL_LINK_TRB because it points the TRB we
|
||||
* just completed (not the LINK TRB).
|
||||
*/
|
||||
if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
if (((dep->busy_slot & DWC3_TRB_MASK) ==
|
||||
DWC3_TRB_NUM- 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
dep->busy_slot++;
|
||||
} while(++i < req->request.num_mapped_sgs);
|
||||
req->queued = false;
|
||||
}
|
||||
list_del(&req->list);
|
||||
req->trb = NULL;
|
||||
@ -749,33 +751,32 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
|
||||
*/
|
||||
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
struct dwc3_request *req, dma_addr_t dma,
|
||||
unsigned length, unsigned last, unsigned chain)
|
||||
unsigned length, unsigned last, unsigned chain, unsigned node)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
unsigned int cur_slot;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
|
||||
dep->name, req, (unsigned long long) dma,
|
||||
length, last ? " last" : "",
|
||||
chain ? " chain" : "");
|
||||
|
||||
trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
||||
cur_slot = dep->free_slot;
|
||||
/* Skip the LINK-TRB on ISOC */
|
||||
if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
dep->free_slot++;
|
||||
|
||||
/* Skip the LINK-TRB on ISOC */
|
||||
if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
return;
|
||||
trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
||||
|
||||
if (!req->trb) {
|
||||
dwc3_gadget_move_request_queued(req);
|
||||
req->trb = trb;
|
||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
|
||||
req->start_slot = dep->free_slot & DWC3_TRB_MASK;
|
||||
}
|
||||
|
||||
dep->free_slot++;
|
||||
|
||||
trb->size = DWC3_TRB_SIZE_LENGTH(length);
|
||||
trb->bpl = lower_32_bits(dma);
|
||||
trb->bph = upper_32_bits(dma);
|
||||
@ -786,9 +787,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (!node)
|
||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
else
|
||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
|
||||
|
||||
if (!req->request.no_interrupt)
|
||||
if (!req->request.no_interrupt && !chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
break;
|
||||
|
||||
@ -807,14 +811,13 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
} else {
|
||||
if (chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CHN;
|
||||
|
||||
if (last)
|
||||
} else if (last) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_LST;
|
||||
}
|
||||
|
||||
if (chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CHN;
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
|
||||
|
||||
@ -885,6 +888,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
list_for_each_entry_safe(req, n, &dep->request_list, list) {
|
||||
unsigned length;
|
||||
dma_addr_t dma;
|
||||
last_one = false;
|
||||
|
||||
if (req->request.num_mapped_sgs > 0) {
|
||||
struct usb_request *request = &req->request;
|
||||
@ -900,6 +904,8 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
|
||||
if (i == (request->num_mapped_sgs - 1) ||
|
||||
sg_is_last(s)) {
|
||||
if (list_is_last(&req->list,
|
||||
&dep->request_list))
|
||||
last_one = true;
|
||||
chain = false;
|
||||
}
|
||||
@ -912,7 +918,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
chain = false;
|
||||
|
||||
dwc3_prepare_one_trb(dep, req, dma, length,
|
||||
last_one, chain);
|
||||
last_one, chain, i);
|
||||
|
||||
if (last_one)
|
||||
break;
|
||||
@ -930,7 +936,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
last_one = 1;
|
||||
|
||||
dwc3_prepare_one_trb(dep, req, dma, length,
|
||||
last_one, false);
|
||||
last_one, false, 0);
|
||||
|
||||
if (last_one)
|
||||
break;
|
||||
@ -977,13 +983,14 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
if (start_new) {
|
||||
params.param0 = upper_32_bits(req->trb_dma);
|
||||
params.param1 = lower_32_bits(req->trb_dma);
|
||||
|
||||
if (start_new)
|
||||
cmd = DWC3_DEPCMD_STARTTRANSFER;
|
||||
else
|
||||
} else {
|
||||
cmd = DWC3_DEPCMD_UPDATETRANSFER;
|
||||
}
|
||||
|
||||
cmd |= DWC3_DEPCMD_PARAM(cmd_param);
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
@ -1082,8 +1089,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
*
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If xfernotready is already elapsed and it is a case
|
||||
* of isoc transfer, then issue END TRANSFER, so that
|
||||
@ -1091,7 +1096,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
* notion of current microframe.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
if (list_empty(&dep->req_queued)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1099,6 +1107,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
if (ret && ret != -EBUSY)
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1115,16 +1124,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
if (ret && ret != -EBUSY)
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* 3. Missed ISOC Handling. We need to start isoc transfer on the saved
|
||||
* uframe number.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
(dep->flags & DWC3_EP_MISSED_ISOC)) {
|
||||
__dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
|
||||
dep->flags &= ~DWC3_EP_MISSED_ISOC;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1652,24 +1652,14 @@ static void dwc3_gadget_release(struct device *dev)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
struct dwc3_request *req, struct dwc3_trb *trb,
|
||||
const struct dwc3_event_depevt *event, int status)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct dwc3_trb *trb;
|
||||
unsigned int count;
|
||||
unsigned int s_pkt = 0;
|
||||
unsigned int trb_status;
|
||||
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
trb = req->trb;
|
||||
|
||||
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
|
||||
/*
|
||||
* We continue despite the error. There is not much we
|
||||
@ -1680,7 +1670,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
* fixes the root cause instead of looking away :)
|
||||
*/
|
||||
dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
|
||||
dep->name, req->trb);
|
||||
dep->name, trb);
|
||||
count = trb->size & DWC3_TRB_SIZE_MASK;
|
||||
|
||||
if (dep->direction) {
|
||||
@ -1689,14 +1679,29 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
|
||||
dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
|
||||
dep->name);
|
||||
dep->current_uf = event->parameters &
|
||||
~(dep->interval - 1);
|
||||
/*
|
||||
* If missed isoc occurred and there is
|
||||
* no request queued then issue END
|
||||
* TRANSFER, so that core generates
|
||||
* next xfernotready and we will issue
|
||||
* a fresh START TRANSFER.
|
||||
* If there are still queued request
|
||||
* then wait, do not issue either END
|
||||
* or UPDATE TRANSFER, just attach next
|
||||
* request in request_list during
|
||||
* giveback.If any future queued request
|
||||
* is successfully transferred then we
|
||||
* will issue UPDATE TRANSFER for all
|
||||
* request in the request_list.
|
||||
*/
|
||||
dep->flags |= DWC3_EP_MISSED_ISOC;
|
||||
} else {
|
||||
dev_err(dwc->dev, "incomplete IN transfer %s\n",
|
||||
dep->name);
|
||||
status = -ECONNRESET;
|
||||
}
|
||||
} else {
|
||||
dep->flags &= ~DWC3_EP_MISSED_ISOC;
|
||||
}
|
||||
} else {
|
||||
if (count && (event->status & DEPEVT_STATUS_SHORT))
|
||||
@ -1711,18 +1716,71 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
* gadget driver for further processing.
|
||||
*/
|
||||
req->request.actual += req->request.length - count;
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
if (s_pkt)
|
||||
break;
|
||||
return 1;
|
||||
if ((event->status & DEPEVT_STATUS_LST) &&
|
||||
(trb->ctrl & (DWC3_TRB_CTRL_LST |
|
||||
DWC3_TRB_CTRL_HWO)))
|
||||
break;
|
||||
return 1;
|
||||
if ((event->status & DEPEVT_STATUS_IOC) &&
|
||||
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event, int status)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct dwc3_trb *trb;
|
||||
unsigned int slot;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
return 1;
|
||||
}
|
||||
i = 0;
|
||||
do {
|
||||
slot = req->start_slot + i;
|
||||
if ((slot == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
slot++;
|
||||
slot %= DWC3_TRB_NUM;
|
||||
trb = &dep->trb_pool[slot];
|
||||
|
||||
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
||||
event, status);
|
||||
if (ret)
|
||||
break;
|
||||
}while (++i < req->request.num_mapped_sgs);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
list_empty(&dep->req_queued)) {
|
||||
if (list_empty(&dep->request_list)) {
|
||||
/*
|
||||
* If there is no entry in request list then do
|
||||
* not issue END TRANSFER now. Just set PENDING
|
||||
* flag, so that END TRANSFER is issued when an
|
||||
* entry is added into request list.
|
||||
*/
|
||||
dep->flags = DWC3_EP_PENDING_REQUEST;
|
||||
} else {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((event->status & DEPEVT_STATUS_IOC) &&
|
||||
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
||||
return 0;
|
||||
@ -2157,6 +2215,26 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable USB2 LPM Capability */
|
||||
|
||||
if ((dwc->revision > DWC3_REVISION_194A)
|
||||
&& (speed != DWC3_DCFG_SUPERSPEED)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
|
||||
|
||||
/*
|
||||
* TODO: This should be configurable. For now using
|
||||
* maximum allowed HIRD threshold value of 0b1100
|
||||
*/
|
||||
reg |= DWC3_DCTL_HIRD_THRES(12);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
}
|
||||
|
||||
/* Recent versions support automatic phy suspend and don't need this */
|
||||
if (dwc->revision < DWC3_REVISION_194A) {
|
||||
/* Suspend unneeded PHY */
|
||||
@ -2463,20 +2541,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
DWC3_DEVTEN_DISCONNEVTEN);
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
|
||||
/* Enable USB2 LPM and automatic phy suspend only on recent versions */
|
||||
/* automatic phy suspend only on recent versions */
|
||||
if (dwc->revision >= DWC3_REVISION_194A) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
|
||||
|
||||
/* TODO: This should be configurable */
|
||||
reg |= DWC3_DCTL_HIRD_THRES(28);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
dwc3_gadget_usb2_phy_suspend(dwc, false);
|
||||
dwc3_gadget_usb3_phy_suspend(dwc, false);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
struct platform_device *xhci;
|
||||
int ret;
|
||||
|
||||
xhci = platform_device_alloc("xhci-hcd", -1);
|
||||
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
||||
if (!xhci) {
|
||||
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
|
||||
ret = -ENOMEM;
|
||||
|
@ -281,6 +281,7 @@ config USB_S3C_HSOTG
|
||||
config USB_IMX
|
||||
tristate "Freescale i.MX1 USB Peripheral Controller"
|
||||
depends on ARCH_MXC
|
||||
depends on BROKEN
|
||||
help
|
||||
Freescale's i.MX1 includes an integrated full speed
|
||||
USB 1.1 device controller.
|
||||
@ -319,6 +320,7 @@ config USB_S3C_HSUDC
|
||||
|
||||
config USB_MV_UDC
|
||||
tristate "Marvell USB2.0 Device Controller"
|
||||
depends on GENERIC_HARDIRQS
|
||||
help
|
||||
Marvell Socs (including PXA and MMP series) include a high speed
|
||||
USB2.0 OTG controller, which can be configured as high speed or
|
||||
@ -440,7 +442,7 @@ config USB_GOKU
|
||||
|
||||
config USB_EG20T
|
||||
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
|
||||
depends on PCI
|
||||
depends on PCI && GENERIC_HARDIRQS
|
||||
help
|
||||
This is a USB device driver for EG20T PCH.
|
||||
EG20T PCH is the platform controller hub that is used in Intel's
|
||||
@ -500,6 +502,15 @@ config USB_LIBCOMPOSITE
|
||||
tristate
|
||||
depends on USB_GADGET
|
||||
|
||||
config USB_F_ACM
|
||||
tristate
|
||||
|
||||
config USB_F_SS_LB
|
||||
tristate
|
||||
|
||||
config USB_U_SERIAL
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
@ -524,6 +535,7 @@ choice
|
||||
config USB_ZERO
|
||||
tristate "Gadget Zero (DEVELOPMENT)"
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_SS_LB
|
||||
help
|
||||
Gadget Zero is a two-configuration device. It either sinks and
|
||||
sources bulk data; or it loops back a configurable number of
|
||||
@ -750,6 +762,8 @@ config USB_GADGET_TARGET
|
||||
|
||||
config USB_G_SERIAL
|
||||
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Serial Gadget talks to the Linux-USB generic serial driver.
|
||||
@ -803,6 +817,8 @@ config USB_CDC_COMPOSITE
|
||||
tristate "CDC Composite Device (Ethernet and ACM)"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
|
||||
@ -818,6 +834,7 @@ config USB_G_NOKIA
|
||||
tristate "Nokia composite gadget"
|
||||
depends on PHONET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
help
|
||||
The Nokia composite gadget provides support for acm, obex
|
||||
and phonet in only one composite gadget driver.
|
||||
@ -829,6 +846,8 @@ config USB_G_ACM_MS
|
||||
tristate "CDC Composite Device (ACM and mass storage)"
|
||||
depends on BLOCK
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a mass storage, and a CDC ACM (serial port) link.
|
||||
@ -841,6 +860,8 @@ config USB_G_MULTI
|
||||
depends on BLOCK && NET
|
||||
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
help
|
||||
The Multifunction Composite Gadget provides Ethernet (RNDIS
|
||||
and/or CDC Ethernet), mass storage and ACM serial link
|
||||
@ -916,6 +937,7 @@ config USB_G_DBGP_PRINTK
|
||||
|
||||
config USB_G_DBGP_SERIAL
|
||||
depends on USB_G_DBGP
|
||||
select USB_U_SERIAL
|
||||
bool "serial"
|
||||
help
|
||||
Userland can interact using /dev/ttyGSxxx.
|
||||
|
@ -6,7 +6,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
|
||||
obj-$(CONFIG_USB_GADGET) += udc-core.o
|
||||
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
||||
libcomposite-y := usbstring.o config.o epautoconf.o
|
||||
libcomposite-y += composite.o
|
||||
libcomposite-y += composite.o functions.o
|
||||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
@ -74,3 +74,9 @@ obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
|
||||
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
|
||||
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
|
||||
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
|
||||
|
||||
# USB Functions
|
||||
obj-$(CONFIG_USB_F_ACM) += f_acm.o
|
||||
f_ss_lb-y := f_loopback.o f_sourcesink.o
|
||||
obj-$(CONFIG_USB_F_SS_LB) += f_ss_lb.o
|
||||
obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
|
||||
|
@ -40,9 +40,6 @@
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
|
||||
#include "u_serial.c"
|
||||
#include "f_acm.c"
|
||||
#include "f_mass_storage.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -112,12 +109,15 @@ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
|
||||
static struct fsg_common fsg_common;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static unsigned char tty_line;
|
||||
static struct usb_function *f_acm;
|
||||
static struct usb_function_instance *f_acm_inst;
|
||||
/*
|
||||
* We _always_ have both ACM and mass storage functions.
|
||||
*/
|
||||
static int __init acm_ms_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
int status;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
@ -125,16 +125,35 @@ static int __init acm_ms_do_config(struct usb_configuration *c)
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
f_acm_inst = usb_get_function_instance("acm");
|
||||
if (IS_ERR(f_acm_inst))
|
||||
return PTR_ERR(f_acm_inst);
|
||||
|
||||
status = acm_bind_config(c, 0);
|
||||
opts = container_of(f_acm_inst, struct f_serial_opts, func_inst);
|
||||
opts->port_num = tty_line;
|
||||
|
||||
f_acm = usb_get_function(f_acm_inst);
|
||||
if (IS_ERR(f_acm)) {
|
||||
status = PTR_ERR(f_acm);
|
||||
goto err_func;
|
||||
}
|
||||
|
||||
status = usb_add_function(c, f_acm);
|
||||
if (status < 0)
|
||||
return status;
|
||||
goto err_conf;
|
||||
|
||||
status = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (status < 0)
|
||||
return status;
|
||||
goto err_fsg;
|
||||
|
||||
return 0;
|
||||
err_fsg:
|
||||
usb_remove_function(c, f_acm);
|
||||
err_conf:
|
||||
usb_put_function(f_acm);
|
||||
err_func:
|
||||
usb_put_function_instance(f_acm_inst);
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct usb_configuration acm_ms_config_driver = {
|
||||
@ -153,7 +172,7 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev)
|
||||
void *retp;
|
||||
|
||||
/* set up serial link layer */
|
||||
status = gserial_setup(cdev->gadget, 1);
|
||||
status = gserial_alloc_line(&tty_line);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
@ -189,14 +208,15 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev)
|
||||
fail1:
|
||||
fsg_common_put(&fsg_common);
|
||||
fail0:
|
||||
gserial_cleanup();
|
||||
gserial_free_line(tty_line);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gserial_cleanup();
|
||||
|
||||
usb_put_function(f_acm);
|
||||
usb_put_function_instance(f_acm_inst);
|
||||
gserial_free_line(tty_line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1400,15 +1400,16 @@ static int udc_wakeup(struct usb_gadget *gadget)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd5536_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int amd5536_stop(struct usb_gadget_driver *driver);
|
||||
static int amd5536_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int amd5536_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
/* gadget operations */
|
||||
static const struct usb_gadget_ops udc_ops = {
|
||||
.wakeup = udc_wakeup,
|
||||
.get_frame = udc_get_frame,
|
||||
.start = amd5536_start,
|
||||
.stop = amd5536_stop,
|
||||
.udc_start = amd5536_udc_start,
|
||||
.udc_stop = amd5536_udc_stop,
|
||||
};
|
||||
|
||||
/* Setups endpoint parameters, adds endpoints to linked list */
|
||||
@ -1913,41 +1914,22 @@ static int setup_ep0(struct udc *dev)
|
||||
}
|
||||
|
||||
/* Called by gadget driver to register itself */
|
||||
static int amd5536_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int amd5536_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct udc *dev = udc;
|
||||
int retval;
|
||||
struct udc *dev = to_amd5536_udc(g);
|
||||
u32 tmp;
|
||||
|
||||
if (!driver || !bind || !driver->setup
|
||||
|| driver->max_speed < USB_SPEED_HIGH)
|
||||
return -EINVAL;
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
if (dev->driver)
|
||||
return -EBUSY;
|
||||
|
||||
driver->driver.bus = NULL;
|
||||
dev->driver = driver;
|
||||
dev->gadget.dev.driver = &driver->driver;
|
||||
|
||||
retval = bind(&dev->gadget, driver);
|
||||
|
||||
/* Some gadget drivers use both ep0 directions.
|
||||
* NOTE: to gadget driver, ep0 is just one endpoint...
|
||||
*/
|
||||
dev->ep[UDC_EP0OUT_IX].ep.driver_data =
|
||||
dev->ep[UDC_EP0IN_IX].ep.driver_data;
|
||||
|
||||
if (retval) {
|
||||
DBG(dev, "binding to %s returning %d\n",
|
||||
driver->driver.name, retval);
|
||||
dev->driver = NULL;
|
||||
dev->gadget.dev.driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* get ready for ep0 traffic */
|
||||
setup_ep0(dev);
|
||||
|
||||
@ -1969,14 +1951,9 @@ __acquires(dev->lock)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||
spin_unlock(&dev->lock);
|
||||
driver->disconnect(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
|
||||
/* empty queues and init hardware */
|
||||
udc_basic_init(dev);
|
||||
|
||||
for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
|
||||
empty_req_queue(&dev->ep[tmp]);
|
||||
|
||||
@ -1984,23 +1961,18 @@ __acquires(dev->lock)
|
||||
}
|
||||
|
||||
/* Called by gadget driver to unregister itself */
|
||||
static int amd5536_stop(struct usb_gadget_driver *driver)
|
||||
static int amd5536_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct udc *dev = udc;
|
||||
struct udc *dev = to_amd5536_udc(g);
|
||||
unsigned long flags;
|
||||
u32 tmp;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
if (!driver || driver != dev->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
udc_mask_unused_interrupts(dev);
|
||||
shutdown(dev, driver);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
driver->unbind(&dev->gadget);
|
||||
dev->gadget.dev.driver = NULL;
|
||||
dev->driver = NULL;
|
||||
|
||||
@ -2009,9 +1981,6 @@ static int amd5536_stop(struct usb_gadget_driver *driver)
|
||||
tmp |= AMD_BIT(UDC_DEVCTL_SD);
|
||||
writel(tmp, &dev->regs->ctl);
|
||||
|
||||
|
||||
DBG(dev, "%s: unregistered\n", driver->driver.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -563,6 +563,8 @@ struct udc {
|
||||
u16 cur_alt;
|
||||
};
|
||||
|
||||
#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
|
||||
|
||||
/* setup request data */
|
||||
union udc_setup_data {
|
||||
u32 data[2];
|
||||
|
@ -1621,7 +1621,6 @@ static void at91_vbus_timer(unsigned long data)
|
||||
* bus such as i2c or spi which may sleep, so schedule some work
|
||||
* to read the vbus gpio
|
||||
*/
|
||||
if (!work_pending(&udc->vbus_timer_work))
|
||||
schedule_work(&udc->vbus_timer_work);
|
||||
}
|
||||
|
||||
@ -1739,7 +1738,7 @@ static int at91udc_probe(struct platform_device *pdev)
|
||||
|
||||
/* rm9200 needs manual D+ pullup; off by default */
|
||||
if (cpu_is_at91rm9200()) {
|
||||
if (gpio_is_valid(udc->board.pullup_pin)) {
|
||||
if (!gpio_is_valid(udc->board.pullup_pin)) {
|
||||
DBG("no D+ pullup?\n");
|
||||
retval = -ENODEV;
|
||||
goto fail0;
|
||||
|
@ -42,9 +42,6 @@ USB_GADGET_COMPOSITE_OPTIONS();
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
|
||||
#include "u_serial.c"
|
||||
#include "f_acm.c"
|
||||
#include "f_ecm.c"
|
||||
#include "u_ether.c"
|
||||
|
||||
@ -108,12 +105,16 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static struct usb_function *f_acm;
|
||||
static struct usb_function_instance *fi_serial;
|
||||
|
||||
static unsigned char tty_line;
|
||||
/*
|
||||
* We _always_ have both CDC ECM and CDC ACM functions.
|
||||
*/
|
||||
static int __init cdc_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
int status;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
@ -125,11 +126,26 @@ static int __init cdc_do_config(struct usb_configuration *c)
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
status = acm_bind_config(c, 0);
|
||||
if (status < 0)
|
||||
return status;
|
||||
fi_serial = usb_get_function_instance("acm");
|
||||
if (IS_ERR(fi_serial))
|
||||
return PTR_ERR(fi_serial);
|
||||
|
||||
opts = container_of(fi_serial, struct f_serial_opts, func_inst);
|
||||
opts->port_num = tty_line;
|
||||
|
||||
f_acm = usb_get_function(fi_serial);
|
||||
if (IS_ERR(f_acm))
|
||||
goto err_func_acm;
|
||||
|
||||
status = usb_add_function(c, f_acm);
|
||||
if (status)
|
||||
goto err_conf;
|
||||
return 0;
|
||||
err_conf:
|
||||
usb_put_function(f_acm);
|
||||
err_func_acm:
|
||||
usb_put_function_instance(fi_serial);
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct usb_configuration cdc_config_driver = {
|
||||
@ -158,7 +174,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
return status;
|
||||
|
||||
/* set up serial link layer */
|
||||
status = gserial_setup(cdev->gadget, 1);
|
||||
status = gserial_alloc_line(&tty_line);
|
||||
if (status < 0)
|
||||
goto fail0;
|
||||
|
||||
@ -184,7 +200,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
|
||||
fail1:
|
||||
gserial_cleanup();
|
||||
gserial_free_line(tty_line);
|
||||
fail0:
|
||||
gether_cleanup();
|
||||
return status;
|
||||
@ -192,7 +208,9 @@ fail0:
|
||||
|
||||
static int __exit cdc_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gserial_cleanup();
|
||||
usb_put_function(f_acm);
|
||||
usb_put_function_instance(fi_serial);
|
||||
gserial_free_line(tty_line);
|
||||
gether_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,6 +28,12 @@
|
||||
* with the relevant device-wide data.
|
||||
*/
|
||||
|
||||
static struct usb_gadget_strings **get_containers_gs(
|
||||
struct usb_gadget_string_container *uc)
|
||||
{
|
||||
return (struct usb_gadget_strings **)uc->stash;
|
||||
}
|
||||
|
||||
/**
|
||||
* next_ep_desc() - advance to the next EP descriptor
|
||||
* @t: currect pointer within descriptor array
|
||||
@ -215,6 +221,18 @@ done:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_function);
|
||||
|
||||
void usb_remove_function(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
if (f->disable)
|
||||
f->disable(f);
|
||||
|
||||
bitmap_zero(f->endpoints, 32);
|
||||
list_del(&f->list);
|
||||
if (f->unbind)
|
||||
f->unbind(c, f);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_remove_function);
|
||||
|
||||
/**
|
||||
* usb_function_deactivate - prevent function and gadget enumeration
|
||||
* @function: the function that isn't yet ready to respond
|
||||
@ -320,6 +338,25 @@ int usb_interface_id(struct usb_configuration *config,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_interface_id);
|
||||
|
||||
static u8 encode_bMaxPower(enum usb_device_speed speed,
|
||||
struct usb_configuration *c)
|
||||
{
|
||||
unsigned val;
|
||||
|
||||
if (c->MaxPower)
|
||||
val = c->MaxPower;
|
||||
else
|
||||
val = CONFIG_USB_GADGET_VBUS_DRAW;
|
||||
if (!val)
|
||||
return 0;
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
return DIV_ROUND_UP(val, 8);
|
||||
default:
|
||||
return DIV_ROUND_UP(val, 2);
|
||||
};
|
||||
}
|
||||
|
||||
static int config_buf(struct usb_configuration *config,
|
||||
enum usb_device_speed speed, void *buf, u8 type)
|
||||
{
|
||||
@ -339,7 +376,7 @@ static int config_buf(struct usb_configuration *config,
|
||||
c->bConfigurationValue = config->bConfigurationValue;
|
||||
c->iConfiguration = config->iConfiguration;
|
||||
c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
|
||||
c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2);
|
||||
c->bMaxPower = encode_bMaxPower(speed, config);
|
||||
|
||||
/* There may be e.g. OTG descriptors */
|
||||
if (config->descriptors) {
|
||||
@ -656,7 +693,7 @@ static int set_config(struct usb_composite_dev *cdev,
|
||||
}
|
||||
|
||||
/* when we return, be sure our power usage is valid */
|
||||
power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
|
||||
power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
|
||||
done:
|
||||
usb_gadget_vbus_draw(gadget, power);
|
||||
if (result >= 0 && cdev->delayed_status)
|
||||
@ -664,6 +701,31 @@ done:
|
||||
return result;
|
||||
}
|
||||
|
||||
int usb_add_config_only(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *config)
|
||||
{
|
||||
struct usb_configuration *c;
|
||||
|
||||
if (!config->bConfigurationValue)
|
||||
return -EINVAL;
|
||||
|
||||
/* Prevent duplicate configuration identifiers */
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
if (c->bConfigurationValue == config->bConfigurationValue)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
config->cdev = cdev;
|
||||
list_add_tail(&config->list, &cdev->configs);
|
||||
|
||||
INIT_LIST_HEAD(&config->functions);
|
||||
config->next_interface_id = 0;
|
||||
memset(config->interface, 0, sizeof(config->interface));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_config_only);
|
||||
|
||||
/**
|
||||
* usb_add_config() - add a configuration to a device.
|
||||
* @cdev: wraps the USB gadget
|
||||
@ -684,30 +746,18 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
||||
int (*bind)(struct usb_configuration *))
|
||||
{
|
||||
int status = -EINVAL;
|
||||
struct usb_configuration *c;
|
||||
|
||||
if (!bind)
|
||||
goto done;
|
||||
|
||||
DBG(cdev, "adding config #%u '%s'/%p\n",
|
||||
config->bConfigurationValue,
|
||||
config->label, config);
|
||||
|
||||
if (!config->bConfigurationValue || !bind)
|
||||
status = usb_add_config_only(cdev, config);
|
||||
if (status)
|
||||
goto done;
|
||||
|
||||
/* Prevent duplicate configuration identifiers */
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
if (c->bConfigurationValue == config->bConfigurationValue) {
|
||||
status = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
config->cdev = cdev;
|
||||
list_add_tail(&config->list, &cdev->configs);
|
||||
|
||||
INIT_LIST_HEAD(&config->functions);
|
||||
config->next_interface_id = 0;
|
||||
memset(config->interface, 0, sizeof(config->interface));
|
||||
|
||||
status = bind(config);
|
||||
if (status < 0) {
|
||||
while (!list_empty(&config->functions)) {
|
||||
@ -860,6 +910,7 @@ static int get_string(struct usb_composite_dev *cdev,
|
||||
void *buf, u16 language, int id)
|
||||
{
|
||||
struct usb_composite_driver *composite = cdev->driver;
|
||||
struct usb_gadget_string_container *uc;
|
||||
struct usb_configuration *c;
|
||||
struct usb_function *f;
|
||||
int len;
|
||||
@ -892,6 +943,12 @@ static int get_string(struct usb_composite_dev *cdev,
|
||||
collect_langs(sp, s->wData);
|
||||
}
|
||||
}
|
||||
list_for_each_entry(uc, &cdev->gstrings, list) {
|
||||
struct usb_gadget_strings **sp;
|
||||
|
||||
sp = get_containers_gs(uc);
|
||||
collect_langs(sp, s->wData);
|
||||
}
|
||||
|
||||
for (len = 0; len <= 126 && s->wData[len]; len++)
|
||||
continue;
|
||||
@ -902,6 +959,15 @@ static int get_string(struct usb_composite_dev *cdev,
|
||||
return s->bLength;
|
||||
}
|
||||
|
||||
list_for_each_entry(uc, &cdev->gstrings, list) {
|
||||
struct usb_gadget_strings **sp;
|
||||
|
||||
sp = get_containers_gs(uc);
|
||||
len = lookup_string(sp, buf, language, id);
|
||||
if (len > 0)
|
||||
return len;
|
||||
}
|
||||
|
||||
/* String IDs are device-scoped, so we look up each string
|
||||
* table we're told about. These lookups are infrequent;
|
||||
* simpler-is-better here.
|
||||
@ -987,6 +1053,119 @@ int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_string_ids_tab);
|
||||
|
||||
static struct usb_gadget_string_container *copy_gadget_strings(
|
||||
struct usb_gadget_strings **sp, unsigned n_gstrings,
|
||||
unsigned n_strings)
|
||||
{
|
||||
struct usb_gadget_string_container *uc;
|
||||
struct usb_gadget_strings **gs_array;
|
||||
struct usb_gadget_strings *gs;
|
||||
struct usb_string *s;
|
||||
unsigned mem;
|
||||
unsigned n_gs;
|
||||
unsigned n_s;
|
||||
void *stash;
|
||||
|
||||
mem = sizeof(*uc);
|
||||
mem += sizeof(void *) * (n_gstrings + 1);
|
||||
mem += sizeof(struct usb_gadget_strings) * n_gstrings;
|
||||
mem += sizeof(struct usb_string) * (n_strings + 1) * (n_gstrings);
|
||||
uc = kmalloc(mem, GFP_KERNEL);
|
||||
if (!uc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
gs_array = get_containers_gs(uc);
|
||||
stash = uc->stash;
|
||||
stash += sizeof(void *) * (n_gstrings + 1);
|
||||
for (n_gs = 0; n_gs < n_gstrings; n_gs++) {
|
||||
struct usb_string *org_s;
|
||||
|
||||
gs_array[n_gs] = stash;
|
||||
gs = gs_array[n_gs];
|
||||
stash += sizeof(struct usb_gadget_strings);
|
||||
gs->language = sp[n_gs]->language;
|
||||
gs->strings = stash;
|
||||
org_s = sp[n_gs]->strings;
|
||||
|
||||
for (n_s = 0; n_s < n_strings; n_s++) {
|
||||
s = stash;
|
||||
stash += sizeof(struct usb_string);
|
||||
if (org_s->s)
|
||||
s->s = org_s->s;
|
||||
else
|
||||
s->s = "";
|
||||
org_s++;
|
||||
}
|
||||
s = stash;
|
||||
s->s = NULL;
|
||||
stash += sizeof(struct usb_string);
|
||||
|
||||
}
|
||||
gs_array[n_gs] = NULL;
|
||||
return uc;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gstrings_attach() - attach gadget strings to a cdev and assign ids
|
||||
* @cdev: the device whose string descriptor IDs are being allocated
|
||||
* and attached.
|
||||
* @sp: an array of usb_gadget_strings to attach.
|
||||
* @n_strings: number of entries in each usb_strings array (sp[]->strings)
|
||||
*
|
||||
* This function will create a deep copy of usb_gadget_strings and usb_string
|
||||
* and attach it to the cdev. The actual string (usb_string.s) will not be
|
||||
* copied but only a referenced will be made. The struct usb_gadget_strings
|
||||
* array may contain multiple languges and should be NULL terminated.
|
||||
* The ->language pointer of each struct usb_gadget_strings has to contain the
|
||||
* same amount of entries.
|
||||
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
|
||||
* usb_string entry of es-ES containts the translation of the first usb_string
|
||||
* entry of en-US. Therefore both entries become the same id assign.
|
||||
*/
|
||||
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
|
||||
struct usb_gadget_strings **sp, unsigned n_strings)
|
||||
{
|
||||
struct usb_gadget_string_container *uc;
|
||||
struct usb_gadget_strings **n_gs;
|
||||
unsigned n_gstrings = 0;
|
||||
unsigned i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; sp[i]; i++)
|
||||
n_gstrings++;
|
||||
|
||||
if (!n_gstrings)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
uc = copy_gadget_strings(sp, n_gstrings, n_strings);
|
||||
if (IS_ERR(uc))
|
||||
return ERR_PTR(PTR_ERR(uc));
|
||||
|
||||
n_gs = get_containers_gs(uc);
|
||||
ret = usb_string_ids_tab(cdev, n_gs[0]->strings);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (i = 1; i < n_gstrings; i++) {
|
||||
struct usb_string *m_s;
|
||||
struct usb_string *s;
|
||||
unsigned n;
|
||||
|
||||
m_s = n_gs[0]->strings;
|
||||
s = n_gs[i]->strings;
|
||||
for (n = 0; n < n_strings; n++) {
|
||||
s->id = m_s->id;
|
||||
s++;
|
||||
m_s++;
|
||||
}
|
||||
}
|
||||
list_add_tail(&uc->list, &cdev->gstrings);
|
||||
return n_gs[0]->strings;
|
||||
err:
|
||||
kfree(uc);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gstrings_attach);
|
||||
|
||||
/**
|
||||
* usb_string_ids_n() - allocate unused string IDs in batch
|
||||
* @c: the device whose string descriptor IDs are being allocated
|
||||
@ -1033,7 +1212,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
* housekeeping for the gadget function we're implementing. Most of
|
||||
* the work is in config and function specific setup.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
@ -1300,7 +1479,7 @@ done:
|
||||
return value;
|
||||
}
|
||||
|
||||
static void composite_disconnect(struct usb_gadget *gadget)
|
||||
void composite_disconnect(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
unsigned long flags;
|
||||
@ -1330,8 +1509,7 @@ static ssize_t composite_show_suspended(struct device *dev,
|
||||
|
||||
static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL);
|
||||
|
||||
static void
|
||||
composite_unbind(struct usb_gadget *gadget)
|
||||
static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
|
||||
@ -1348,19 +1526,21 @@ composite_unbind(struct usb_gadget *gadget)
|
||||
struct usb_configuration, list);
|
||||
remove_config(cdev, c);
|
||||
}
|
||||
if (cdev->driver->unbind)
|
||||
if (cdev->driver->unbind && unbind_driver)
|
||||
cdev->driver->unbind(cdev);
|
||||
|
||||
if (cdev->req) {
|
||||
kfree(cdev->req->buf);
|
||||
usb_ep_free_request(gadget->ep0, cdev->req);
|
||||
}
|
||||
device_remove_file(&gadget->dev, &dev_attr_suspended);
|
||||
composite_dev_cleanup(cdev);
|
||||
|
||||
kfree(cdev->def_manufacturer);
|
||||
kfree(cdev);
|
||||
set_gadget_data(gadget, NULL);
|
||||
}
|
||||
|
||||
static void composite_unbind(struct usb_gadget *gadget)
|
||||
{
|
||||
__composite_unbind(gadget, true);
|
||||
}
|
||||
|
||||
static void update_unchanged_dev_desc(struct usb_device_descriptor *new,
|
||||
const struct usb_device_descriptor *old)
|
||||
{
|
||||
@ -1399,34 +1579,25 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new,
|
||||
new->iProduct = iProduct;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv)
|
||||
int composite_dev_prepare(struct usb_composite_driver *composite,
|
||||
struct usb_composite_dev *cdev)
|
||||
{
|
||||
return container_of(gdrv, struct usb_composite_driver, gadget_driver);
|
||||
}
|
||||
|
||||
static int composite_bind(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *gdriver)
|
||||
{
|
||||
struct usb_composite_dev *cdev;
|
||||
struct usb_composite_driver *composite = to_cdriver(gdriver);
|
||||
int status = -ENOMEM;
|
||||
|
||||
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
|
||||
if (!cdev)
|
||||
return status;
|
||||
|
||||
spin_lock_init(&cdev->lock);
|
||||
cdev->gadget = gadget;
|
||||
set_gadget_data(gadget, cdev);
|
||||
INIT_LIST_HEAD(&cdev->configs);
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
/* preallocate control response and buffer */
|
||||
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
|
||||
if (!cdev->req)
|
||||
goto fail;
|
||||
return -ENOMEM;
|
||||
|
||||
cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
|
||||
if (!cdev->req->buf)
|
||||
goto fail;
|
||||
|
||||
ret = device_create_file(&gadget->dev, &dev_attr_suspended);
|
||||
if (ret)
|
||||
goto fail_dev;
|
||||
|
||||
cdev->req->complete = composite_setup_complete;
|
||||
gadget->ep0->driver_data = cdev;
|
||||
|
||||
@ -1444,7 +1615,51 @@ static int composite_bind(struct usb_gadget *gadget,
|
||||
* we force endpoints to start unassigned; few controller
|
||||
* drivers will zero ep->driver_data.
|
||||
*/
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
usb_ep_autoconfig_reset(gadget);
|
||||
return 0;
|
||||
fail_dev:
|
||||
kfree(cdev->req->buf);
|
||||
fail:
|
||||
usb_ep_free_request(gadget->ep0, cdev->req);
|
||||
cdev->req = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void composite_dev_cleanup(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget_string_container *uc, *tmp;
|
||||
|
||||
list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) {
|
||||
list_del(&uc->list);
|
||||
kfree(uc);
|
||||
}
|
||||
if (cdev->req) {
|
||||
kfree(cdev->req->buf);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
|
||||
}
|
||||
device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
|
||||
}
|
||||
|
||||
static int composite_bind(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *gdriver)
|
||||
{
|
||||
struct usb_composite_dev *cdev;
|
||||
struct usb_composite_driver *composite = to_cdriver(gdriver);
|
||||
int status = -ENOMEM;
|
||||
|
||||
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
|
||||
if (!cdev)
|
||||
return status;
|
||||
|
||||
spin_lock_init(&cdev->lock);
|
||||
cdev->gadget = gadget;
|
||||
set_gadget_data(gadget, cdev);
|
||||
INIT_LIST_HEAD(&cdev->configs);
|
||||
INIT_LIST_HEAD(&cdev->gstrings);
|
||||
|
||||
status = composite_dev_prepare(composite, cdev);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
/* composite gadget needs to assign strings for whole device (like
|
||||
* serial number), register function drivers, potentially update
|
||||
@ -1460,16 +1675,11 @@ static int composite_bind(struct usb_gadget *gadget,
|
||||
if (composite->needs_serial && !cdev->desc.iSerialNumber)
|
||||
WARNING(cdev, "userspace failed to provide iSerialNumber\n");
|
||||
|
||||
/* finish up */
|
||||
status = device_create_file(&gadget->dev, &dev_attr_suspended);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
INFO(cdev, "%s ready\n", composite->name);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
composite_unbind(gadget);
|
||||
__composite_unbind(gadget, false);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1518,10 +1728,10 @@ composite_resume(struct usb_gadget *gadget)
|
||||
f->resume(f);
|
||||
}
|
||||
|
||||
maxpower = cdev->config->bMaxPower;
|
||||
maxpower = cdev->config->MaxPower;
|
||||
|
||||
usb_gadget_vbus_draw(gadget, maxpower ?
|
||||
(2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
|
||||
maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
|
||||
}
|
||||
|
||||
cdev->suspended = 0;
|
||||
|
@ -13,9 +13,7 @@
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
#include "u_serial.c"
|
||||
#endif
|
||||
#include "u_serial.h"
|
||||
|
||||
#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
|
||||
#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
|
||||
@ -233,6 +231,10 @@ static void dbgp_unbind(struct usb_gadget *gadget)
|
||||
gadget->ep0->driver_data = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
static unsigned char tty_line;
|
||||
#endif
|
||||
|
||||
static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
|
||||
{
|
||||
int stp;
|
||||
@ -270,7 +272,7 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
|
||||
dbgp.serial->in->desc = &i_desc;
|
||||
dbgp.serial->out->desc = &o_desc;
|
||||
|
||||
if (gserial_setup(gadget, 1) < 0) {
|
||||
if (gserial_alloc_line(&tty_line)) {
|
||||
stp = 3;
|
||||
goto fail_3;
|
||||
}
|
||||
@ -379,7 +381,7 @@ static int dbgp_setup(struct usb_gadget *gadget,
|
||||
#ifdef CONFIG_USB_G_DBGP_PRINTK
|
||||
err = dbgp_enable_ep();
|
||||
#else
|
||||
err = gserial_connect(dbgp.serial, 0);
|
||||
err = gserial_connect(dbgp.serial, tty_line);
|
||||
#endif
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
@ -422,7 +424,7 @@ static void __exit dbgp_exit(void)
|
||||
{
|
||||
usb_gadget_unregister_driver(&dbgp_driver);
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
gserial_cleanup();
|
||||
gserial_free_line(tty_line);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,9 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
#include "gadget_chips.h"
|
||||
@ -283,7 +285,6 @@ static struct usb_string acm_string_defs[] = {
|
||||
[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
|
||||
[ACM_DATA_IDX].s = "CDC ACM Data",
|
||||
[ACM_IAD_IDX ].s = "CDC Serial",
|
||||
{ /* ZEROES END LIST */ },
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings acm_string_table = {
|
||||
@ -605,9 +606,23 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
/* REVISIT might want instance-specific strings to help
|
||||
* distinguish instances ...
|
||||
*/
|
||||
|
||||
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||
us = usb_gstrings_attach(cdev, acm_strings,
|
||||
ARRAY_SIZE(acm_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
|
||||
acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
|
||||
acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
@ -700,24 +715,42 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct f_acm *acm_alloc_basic_func(void)
|
||||
{
|
||||
struct f_acm *acm;
|
||||
|
||||
acm = kzalloc(sizeof(*acm), GFP_KERNEL);
|
||||
if (!acm)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&acm->lock);
|
||||
|
||||
acm->port.connect = acm_connect;
|
||||
acm->port.disconnect = acm_disconnect;
|
||||
acm->port.send_break = acm_send_break;
|
||||
|
||||
acm->port.func.name = "acm";
|
||||
/* descriptors are per-instance copies */
|
||||
acm->port.func.bind = acm_bind;
|
||||
acm->port.func.set_alt = acm_set_alt;
|
||||
acm->port.func.setup = acm_setup;
|
||||
acm->port.func.disable = acm_disable;
|
||||
|
||||
return acm;
|
||||
}
|
||||
|
||||
#ifdef USB_FACM_INCLUDED
|
||||
static void
|
||||
acm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
acm_old_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
acm_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
if (acm->notify_req)
|
||||
gs_free_req(acm->notify, acm->notify_req);
|
||||
kfree(acm);
|
||||
}
|
||||
|
||||
/* Some controllers can't support CDC ACM ... */
|
||||
static inline bool can_support_cdc(struct usb_configuration *c)
|
||||
{
|
||||
/* everything else is *probably* fine ... */
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* acm_bind_config - add a CDC ACM function to a configuration
|
||||
* @c: the configuration to support the CDC ACM instance
|
||||
@ -726,58 +759,80 @@ static inline bool can_support_cdc(struct usb_configuration *c)
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gserial_setup() with enough ports to
|
||||
* handle all the ones it binds. Caller is also responsible
|
||||
* for calling @gserial_cleanup() before module unload.
|
||||
*/
|
||||
int acm_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
{
|
||||
struct f_acm *acm;
|
||||
int status;
|
||||
|
||||
if (!can_support_cdc(c))
|
||||
return -EINVAL;
|
||||
|
||||
/* REVISIT might want instance-specific strings to help
|
||||
* distinguish instances ...
|
||||
*/
|
||||
|
||||
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||
if (acm_string_defs[0].id == 0) {
|
||||
status = usb_string_ids_tab(c->cdev, acm_string_defs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
acm_control_interface_desc.iInterface =
|
||||
acm_string_defs[ACM_CTRL_IDX].id;
|
||||
acm_data_interface_desc.iInterface =
|
||||
acm_string_defs[ACM_DATA_IDX].id;
|
||||
acm_iad_descriptor.iFunction = acm_string_defs[ACM_IAD_IDX].id;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
acm = kzalloc(sizeof *acm, GFP_KERNEL);
|
||||
acm = acm_alloc_basic_func();
|
||||
if (!acm)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&acm->lock);
|
||||
|
||||
acm->port_num = port_num;
|
||||
|
||||
acm->port.connect = acm_connect;
|
||||
acm->port.disconnect = acm_disconnect;
|
||||
acm->port.send_break = acm_send_break;
|
||||
|
||||
acm->port.func.name = "acm";
|
||||
acm->port.func.strings = acm_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
acm->port.func.bind = acm_bind;
|
||||
acm->port.func.unbind = acm_unbind;
|
||||
acm->port.func.set_alt = acm_set_alt;
|
||||
acm->port.func.setup = acm_setup;
|
||||
acm->port.func.disable = acm_disable;
|
||||
acm->port.func.unbind = acm_old_unbind;
|
||||
|
||||
status = usb_add_function(c, &acm->port.func);
|
||||
if (status)
|
||||
kfree(acm);
|
||||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
acm_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
if (acm->notify_req)
|
||||
gs_free_req(acm->notify, acm->notify_req);
|
||||
}
|
||||
|
||||
static void acm_free_func(struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
kfree(acm);
|
||||
}
|
||||
|
||||
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
struct f_acm *acm;
|
||||
|
||||
acm = acm_alloc_basic_func();
|
||||
if (!acm)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
acm->port_num = opts->port_num;
|
||||
acm->port.func.unbind = acm_unbind;
|
||||
acm->port.func.free_func = acm_free_func;
|
||||
|
||||
return &acm->port.func;
|
||||
}
|
||||
|
||||
static void acm_free_instance(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *acm_alloc_instance(void)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
opts->func_inst.free_func_inst = acm_free_instance;
|
||||
return &opts->func_inst;
|
||||
}
|
||||
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
@ -1103,8 +1103,8 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
|
||||
return 0;
|
||||
|
||||
for (;;) {
|
||||
char *end, *eq, *comma;
|
||||
unsigned long value;
|
||||
char *eq, *comma;
|
||||
|
||||
/* Option limit */
|
||||
comma = strchr(opts, ',');
|
||||
@ -1120,8 +1120,7 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
|
||||
*eq = 0;
|
||||
|
||||
/* Parse value */
|
||||
value = simple_strtoul(eq + 1, &end, 0);
|
||||
if (unlikely(*end != ',' && *end != 0)) {
|
||||
if (kstrtoul(eq + 1, 0, &value)) {
|
||||
pr_err("%s: invalid value: %s\n", opts, eq + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -15,10 +15,11 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "g_zero.h"
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/*
|
||||
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
|
||||
@ -44,9 +45,8 @@ static inline struct f_loopback *func_to_loop(struct usb_function *f)
|
||||
return container_of(f, struct f_loopback, function);
|
||||
}
|
||||
|
||||
static unsigned qlen = 32;
|
||||
module_param(qlen, uint, 0);
|
||||
MODULE_PARM_DESC(qlenn, "depth of loopback queue");
|
||||
static unsigned qlen;
|
||||
static unsigned buflen;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -171,8 +171,7 @@ static struct usb_gadget_strings *loopback_strings[] = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init
|
||||
loopback_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_loopback *loop = func_to_loop(f);
|
||||
@ -185,6 +184,12 @@ loopback_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
return id;
|
||||
loopback_intf.bInterfaceNumber = id;
|
||||
|
||||
id = usb_string_id(cdev);
|
||||
if (id < 0)
|
||||
return id;
|
||||
strings_loopback[0].id = id;
|
||||
loopback_intf.iInterface = id;
|
||||
|
||||
/* allocate endpoints */
|
||||
|
||||
loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
|
||||
@ -223,8 +228,7 @@ autoconf_fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
loopback_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
static void lb_free_func(struct usb_function *f)
|
||||
{
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(func_to_loop(f));
|
||||
@ -366,63 +370,64 @@ static void loopback_disable(struct usb_function *f)
|
||||
disable_loopback(loop);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init loopback_bind_config(struct usb_configuration *c)
|
||||
static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_loopback *loop;
|
||||
int status;
|
||||
struct f_lb_opts *lb_opts;
|
||||
|
||||
loop = kzalloc(sizeof *loop, GFP_KERNEL);
|
||||
if (!loop)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
lb_opts = container_of(fi, struct f_lb_opts, func_inst);
|
||||
buflen = lb_opts->bulk_buflen;
|
||||
qlen = lb_opts->qlen;
|
||||
if (!qlen)
|
||||
qlen = 32;
|
||||
|
||||
loop->function.name = "loopback";
|
||||
loop->function.bind = loopback_bind;
|
||||
loop->function.unbind = loopback_unbind;
|
||||
loop->function.set_alt = loopback_set_alt;
|
||||
loop->function.disable = loopback_disable;
|
||||
loop->function.strings = loopback_strings;
|
||||
|
||||
status = usb_add_function(c, &loop->function);
|
||||
if (status)
|
||||
kfree(loop);
|
||||
return status;
|
||||
loop->function.free_func = lb_free_func;
|
||||
|
||||
return &loop->function;
|
||||
}
|
||||
|
||||
static struct usb_configuration loopback_driver = {
|
||||
.label = "loopback",
|
||||
.strings = loopback_strings,
|
||||
.bConfigurationValue = 2,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
};
|
||||
|
||||
/**
|
||||
* loopback_add - add a loopback testing configuration to a device
|
||||
* @cdev: the device to support the loopback configuration
|
||||
*/
|
||||
int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
|
||||
static void lb_free_instance(struct usb_function_instance *fi)
|
||||
{
|
||||
int id;
|
||||
struct f_lb_opts *lb_opts;
|
||||
|
||||
/* allocate string ID(s) */
|
||||
id = usb_string_id(cdev);
|
||||
if (id < 0)
|
||||
return id;
|
||||
strings_loopback[0].id = id;
|
||||
|
||||
loopback_intf.iInterface = id;
|
||||
loopback_driver.iConfiguration = id;
|
||||
|
||||
/* support autoresume for remote wakeup testing */
|
||||
if (autoresume)
|
||||
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
|
||||
/* support OTG systems */
|
||||
if (gadget_is_otg(cdev->gadget)) {
|
||||
loopback_driver.descriptors = otg_desc;
|
||||
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
lb_opts = container_of(fi, struct f_lb_opts, func_inst);
|
||||
kfree(lb_opts);
|
||||
}
|
||||
|
||||
return usb_add_config(cdev, &loopback_driver, loopback_bind_config);
|
||||
static struct usb_function_instance *loopback_alloc_instance(void)
|
||||
{
|
||||
struct f_lb_opts *lb_opts;
|
||||
|
||||
lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
|
||||
if (!lb_opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
lb_opts->func_inst.free_func_inst = lb_free_instance;
|
||||
return &lb_opts->func_inst;
|
||||
}
|
||||
DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
|
||||
|
||||
int __init lb_modinit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb_function_register(&Loopbackusb_func);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
void __exit lb_modexit(void)
|
||||
{
|
||||
usb_function_unregister(&Loopbackusb_func);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -246,20 +246,6 @@ struct fsg_operations {
|
||||
* set).
|
||||
*/
|
||||
int (*thread_exits)(struct fsg_common *common);
|
||||
|
||||
/*
|
||||
* Called prior to ejection. Negative return means error,
|
||||
* zero means to continue with ejection, positive means not to
|
||||
* eject.
|
||||
*/
|
||||
int (*pre_eject)(struct fsg_common *common,
|
||||
struct fsg_lun *lun, int num);
|
||||
/*
|
||||
* Called after ejection. Negative return means error, zero
|
||||
* or positive is just a success.
|
||||
*/
|
||||
int (*post_eject)(struct fsg_common *common,
|
||||
struct fsg_lun *lun, int num);
|
||||
};
|
||||
|
||||
/* Data shared by all the FSG instances. */
|
||||
@ -1374,26 +1360,13 @@ static int do_start_stop(struct fsg_common *common)
|
||||
if (!loej)
|
||||
return 0;
|
||||
|
||||
/* Simulate an unload/eject */
|
||||
if (common->ops && common->ops->pre_eject) {
|
||||
int r = common->ops->pre_eject(common, curlun,
|
||||
curlun - common->luns);
|
||||
if (unlikely(r < 0))
|
||||
return r;
|
||||
else if (r)
|
||||
return 0;
|
||||
}
|
||||
|
||||
up_read(&common->filesem);
|
||||
down_write(&common->filesem);
|
||||
fsg_lun_close(curlun);
|
||||
up_write(&common->filesem);
|
||||
down_read(&common->filesem);
|
||||
|
||||
return common->ops && common->ops->post_eject
|
||||
? min(0, common->ops->post_eject(common, curlun,
|
||||
curlun - common->luns))
|
||||
: 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_prevent_allow(struct fsg_common *common)
|
||||
@ -1718,7 +1691,7 @@ static int check_command(struct fsg_common *common, int cmnd_size,
|
||||
int needs_medium, const char *name)
|
||||
{
|
||||
int i;
|
||||
int lun = common->cmnd[1] >> 5;
|
||||
unsigned int lun = common->cmnd[1] >> 5;
|
||||
static const char dirletter[4] = {'u', 'o', 'i', 'n'};
|
||||
char hdlen[20];
|
||||
struct fsg_lun *curlun;
|
||||
@ -1784,7 +1757,7 @@ static int check_command(struct fsg_common *common, int cmnd_size,
|
||||
|
||||
/* Check that the LUN values are consistent */
|
||||
if (common->lun != lun)
|
||||
DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n",
|
||||
DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n",
|
||||
common->lun, lun);
|
||||
|
||||
/* Check the LUN */
|
||||
@ -1804,7 +1777,7 @@ static int check_command(struct fsg_common *common, int cmnd_size,
|
||||
*/
|
||||
if (common->cmnd[0] != INQUIRY &&
|
||||
common->cmnd[0] != REQUEST_SENSE) {
|
||||
DBG(common, "unsupported LUN %d\n", common->lun);
|
||||
DBG(common, "unsupported LUN %u\n", common->lun);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -2196,7 +2169,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
if (common->data_size == 0)
|
||||
common->data_dir = DATA_DIR_NONE;
|
||||
common->lun = cbw->Lun;
|
||||
if (common->lun >= 0 && common->lun < common->nluns)
|
||||
if (common->lun < common->nluns)
|
||||
common->curlun = &common->luns[common->lun];
|
||||
else
|
||||
common->curlun = NULL;
|
||||
|
@ -56,8 +56,9 @@ struct f_ncm {
|
||||
u8 notify_state;
|
||||
bool is_open;
|
||||
|
||||
struct ndp_parser_opts *parser_opts;
|
||||
const struct ndp_parser_opts *parser_opts;
|
||||
bool is_crc;
|
||||
u32 ndp_sign;
|
||||
|
||||
/*
|
||||
* for notification, it is accessed from both
|
||||
@ -390,8 +391,8 @@ struct ndp_parser_opts {
|
||||
.next_fp_index = 2, \
|
||||
}
|
||||
|
||||
static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
|
||||
static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
|
||||
static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
|
||||
static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
|
||||
|
||||
static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
|
||||
{
|
||||
@ -732,8 +733,7 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
default:
|
||||
goto invalid;
|
||||
}
|
||||
ncm->parser_opts->ndp_sign &= ~NCM_NDP_HDR_CRC_MASK;
|
||||
ncm->parser_opts->ndp_sign |= ndp_hdr_crc;
|
||||
ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
@ -875,7 +875,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
|
||||
int ndp_align;
|
||||
int ndp_pad;
|
||||
unsigned max_size = ncm->port.fixed_in_len;
|
||||
struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
const struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
|
||||
|
||||
div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
|
||||
@ -921,7 +921,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
|
||||
tmp = (void *)tmp + ndp_pad;
|
||||
|
||||
/* NDP */
|
||||
put_unaligned_le32(opts->ndp_sign, tmp); /* dwSignature */
|
||||
put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */
|
||||
tmp += 2;
|
||||
/* wLength */
|
||||
put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++);
|
||||
@ -965,7 +965,7 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
struct sk_buff *skb2;
|
||||
int ret = -EINVAL;
|
||||
unsigned max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
|
||||
struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
const struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
|
||||
int dgram_counter;
|
||||
|
||||
@ -1002,7 +1002,7 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
|
||||
/* walk through NDP */
|
||||
tmp = ((void *)skb->data) + index;
|
||||
if (get_unaligned_le32(tmp) != opts->ndp_sign) {
|
||||
if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
|
||||
INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
|
||||
goto err;
|
||||
}
|
||||
|
@ -406,10 +406,6 @@ static inline bool can_support_obex(struct usb_configuration *c)
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gserial_setup() with enough ports to
|
||||
* handle all the ones it binds. Caller is also responsible
|
||||
* for calling @gserial_cleanup() before module unload.
|
||||
*/
|
||||
int __init obex_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
{
|
||||
|
@ -260,10 +260,6 @@ gser_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gserial_setup() with enough ports to
|
||||
* handle all the ones it binds. Caller is also responsible
|
||||
* for calling @gserial_cleanup() before module unload.
|
||||
*/
|
||||
int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
{
|
||||
|
@ -16,11 +16,12 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "g_zero.h"
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/*
|
||||
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
|
||||
* controller drivers.
|
||||
@ -62,24 +63,11 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
||||
}
|
||||
|
||||
static unsigned pattern;
|
||||
module_param(pattern, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none");
|
||||
|
||||
static unsigned isoc_interval = 4;
|
||||
module_param(isoc_interval, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_interval, "1 - 16");
|
||||
|
||||
static unsigned isoc_maxpacket = 1024;
|
||||
module_param(isoc_maxpacket, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
|
||||
|
||||
static unsigned isoc_interval;
|
||||
static unsigned isoc_maxpacket;
|
||||
static unsigned isoc_mult;
|
||||
module_param(isoc_mult, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)");
|
||||
|
||||
static unsigned isoc_maxburst;
|
||||
module_param(isoc_maxburst, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
|
||||
static unsigned buflen;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -313,7 +301,57 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len)
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
||||
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (req) {
|
||||
if (len)
|
||||
req->length = len;
|
||||
else
|
||||
req->length = buflen;
|
||||
req->buf = kmalloc(req->length, GFP_ATOMIC);
|
||||
if (!req->buf) {
|
||||
usb_ep_free_request(ep, req);
|
||||
req = NULL;
|
||||
}
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (ep->driver_data) {
|
||||
value = usb_ep_disable(ep);
|
||||
if (value < 0)
|
||||
DBG(cdev, "disable %s --> %d\n",
|
||||
ep->name, value);
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out)
|
||||
{
|
||||
disable_ep(cdev, in);
|
||||
disable_ep(cdev, out);
|
||||
if (iso_in)
|
||||
disable_ep(cdev, iso_in);
|
||||
if (iso_out)
|
||||
disable_ep(cdev, iso_out);
|
||||
}
|
||||
|
||||
static int
|
||||
sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
@ -450,7 +488,7 @@ no_iso:
|
||||
}
|
||||
|
||||
static void
|
||||
sourcesink_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
sourcesink_free_func(struct usb_function *f)
|
||||
{
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(func_to_ss(f));
|
||||
@ -531,8 +569,7 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
check_read_data(ss, req);
|
||||
if (pattern != 2)
|
||||
memset(req->buf, 0x55, req->length);
|
||||
} else
|
||||
reinit_write_data(ep, req);
|
||||
}
|
||||
break;
|
||||
|
||||
/* this endpoint is normally active while we're configured */
|
||||
@ -758,31 +795,10 @@ static void sourcesink_disable(struct usb_function *f)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init sourcesink_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_sourcesink *ss;
|
||||
int status;
|
||||
|
||||
ss = kzalloc(sizeof *ss, GFP_KERNEL);
|
||||
if (!ss)
|
||||
return -ENOMEM;
|
||||
|
||||
ss->function.name = "source/sink";
|
||||
ss->function.bind = sourcesink_bind;
|
||||
ss->function.unbind = sourcesink_unbind;
|
||||
ss->function.set_alt = sourcesink_set_alt;
|
||||
ss->function.get_alt = sourcesink_get_alt;
|
||||
ss->function.disable = sourcesink_disable;
|
||||
|
||||
status = usb_add_function(c, &ss->function);
|
||||
if (status)
|
||||
kfree(ss);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int sourcesink_setup(struct usb_configuration *c,
|
||||
static int sourcesink_setup(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_configuration *c = f->config;
|
||||
struct usb_request *req = c->cdev->req;
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||
@ -851,42 +867,76 @@ unknown:
|
||||
return value;
|
||||
}
|
||||
|
||||
static struct usb_configuration sourcesink_driver = {
|
||||
.label = "source/sink",
|
||||
.strings = sourcesink_strings,
|
||||
.setup = sourcesink_setup,
|
||||
.bConfigurationValue = 3,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
};
|
||||
|
||||
/**
|
||||
* sourcesink_add - add a source/sink testing configuration to a device
|
||||
* @cdev: the device to support the configuration
|
||||
*/
|
||||
int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume)
|
||||
static struct usb_function *source_sink_alloc_func(
|
||||
struct usb_function_instance *fi)
|
||||
{
|
||||
int id;
|
||||
struct f_sourcesink *ss;
|
||||
struct f_ss_opts *ss_opts;
|
||||
|
||||
/* allocate string ID(s) */
|
||||
id = usb_string_id(cdev);
|
||||
if (id < 0)
|
||||
return id;
|
||||
strings_sourcesink[0].id = id;
|
||||
ss = kzalloc(sizeof(*ss), GFP_KERNEL);
|
||||
if (!ss)
|
||||
return NULL;
|
||||
|
||||
source_sink_intf_alt0.iInterface = id;
|
||||
source_sink_intf_alt1.iInterface = id;
|
||||
sourcesink_driver.iConfiguration = id;
|
||||
ss_opts = container_of(fi, struct f_ss_opts, func_inst);
|
||||
pattern = ss_opts->pattern;
|
||||
isoc_interval = ss_opts->isoc_interval;
|
||||
isoc_maxpacket = ss_opts->isoc_maxpacket;
|
||||
isoc_mult = ss_opts->isoc_mult;
|
||||
isoc_maxburst = ss_opts->isoc_maxburst;
|
||||
buflen = ss_opts->bulk_buflen;
|
||||
|
||||
/* support autoresume for remote wakeup testing */
|
||||
if (autoresume)
|
||||
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
ss->function.name = "source/sink";
|
||||
ss->function.bind = sourcesink_bind;
|
||||
ss->function.set_alt = sourcesink_set_alt;
|
||||
ss->function.get_alt = sourcesink_get_alt;
|
||||
ss->function.disable = sourcesink_disable;
|
||||
ss->function.setup = sourcesink_setup;
|
||||
ss->function.strings = sourcesink_strings;
|
||||
|
||||
/* support OTG systems */
|
||||
if (gadget_is_otg(cdev->gadget)) {
|
||||
sourcesink_driver.descriptors = otg_desc;
|
||||
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
ss->function.free_func = sourcesink_free_func;
|
||||
|
||||
return &ss->function;
|
||||
}
|
||||
|
||||
return usb_add_config(cdev, &sourcesink_driver, sourcesink_bind_config);
|
||||
static void acm_free_instance(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_ss_opts *ss_opts;
|
||||
|
||||
ss_opts = container_of(fi, struct f_ss_opts, func_inst);
|
||||
kfree(ss_opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *source_sink_alloc_inst(void)
|
||||
{
|
||||
struct f_ss_opts *ss_opts;
|
||||
|
||||
ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
|
||||
if (!ss_opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ss_opts->func_inst.free_func_inst = acm_free_instance;
|
||||
return &ss_opts->func_inst;
|
||||
}
|
||||
DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
|
||||
source_sink_alloc_func);
|
||||
|
||||
static int __init sslb_modinit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb_function_register(&SourceSinkusb_func);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = lb_modinit();
|
||||
if (ret)
|
||||
usb_function_unregister(&SourceSinkusb_func);
|
||||
return ret;
|
||||
}
|
||||
static void __exit sslb_modexit(void)
|
||||
{
|
||||
usb_function_unregister(&SourceSinkusb_func);
|
||||
lb_modexit();
|
||||
}
|
||||
module_init(sslb_modinit);
|
||||
module_exit(sslb_modexit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -260,19 +260,14 @@ static int
|
||||
uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
|
||||
struct audio_dev *agdev = uac2_to_agdev(uac2);
|
||||
struct uac2_rtd_params *prm;
|
||||
unsigned long flags;
|
||||
struct usb_ep *ep;
|
||||
int err = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ep = agdev->in_ep;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prm = &uac2->p_prm;
|
||||
} else {
|
||||
ep = agdev->out_ep;
|
||||
else
|
||||
prm = &uac2->c_prm;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/video.h>
|
||||
@ -419,7 +420,7 @@ uvc_register_video(struct uvc_device *uvc)
|
||||
video->parent = &cdev->gadget->dev;
|
||||
video->fops = &uvc_v4l2_fops;
|
||||
video->release = video_device_release;
|
||||
strncpy(video->name, cdev->gadget->name, sizeof(video->name));
|
||||
strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
|
||||
|
||||
uvc->vdev = video;
|
||||
video_set_drvdata(video, uvc);
|
||||
|
@ -1894,7 +1894,7 @@ static int fsl_qe_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
/* defined in usb_gadget.h */
|
||||
static struct usb_gadget_ops qe_gadget_ops = {
|
||||
static const struct usb_gadget_ops qe_gadget_ops = {
|
||||
.get_frame = qe_get_frame,
|
||||
.udc_start = fsl_qe_start,
|
||||
.udc_stop = fsl_qe_stop,
|
||||
|
@ -1255,19 +1255,20 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int fsl_stop(struct usb_gadget_driver *driver);
|
||||
static int fsl_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int fsl_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
/* defined in gadget.h */
|
||||
static struct usb_gadget_ops fsl_gadget_ops = {
|
||||
static const struct usb_gadget_ops fsl_gadget_ops = {
|
||||
.get_frame = fsl_get_frame,
|
||||
.wakeup = fsl_wakeup,
|
||||
/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */
|
||||
.vbus_session = fsl_vbus_session,
|
||||
.vbus_draw = fsl_vbus_draw,
|
||||
.pullup = fsl_pullup,
|
||||
.start = fsl_start,
|
||||
.stop = fsl_stop,
|
||||
.udc_start = fsl_udc_start,
|
||||
.udc_stop = fsl_udc_stop,
|
||||
};
|
||||
|
||||
/* Set protocol stall on ep0, protocol stall will automatically be cleared
|
||||
@ -1951,22 +1952,12 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
|
||||
* Hook to gadget drivers
|
||||
* Called by initialization code of gadget drivers
|
||||
*----------------------------------------------------------------*/
|
||||
static int fsl_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int fsl_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
int retval = -ENODEV;
|
||||
int retval = 0;
|
||||
unsigned long flags = 0;
|
||||
|
||||
if (!udc_controller)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind || !driver->disconnect || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
if (udc_controller->driver)
|
||||
return -EBUSY;
|
||||
|
||||
/* lock is needed but whether should use this lock or another */
|
||||
spin_lock_irqsave(&udc_controller->lock, flags);
|
||||
|
||||
@ -1976,15 +1967,6 @@ static int fsl_start(struct usb_gadget_driver *driver,
|
||||
udc_controller->gadget.dev.driver = &driver->driver;
|
||||
spin_unlock_irqrestore(&udc_controller->lock, flags);
|
||||
|
||||
/* bind udc driver to gadget driver */
|
||||
retval = bind(&udc_controller->gadget, driver);
|
||||
if (retval) {
|
||||
VDBG("bind to %s --> %d", driver->driver.name, retval);
|
||||
udc_controller->gadget.dev.driver = NULL;
|
||||
udc_controller->driver = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
|
||||
/* Suspend the controller until OTG enable it */
|
||||
udc_controller->stopped = 1;
|
||||
@ -2010,28 +1992,17 @@ static int fsl_start(struct usb_gadget_driver *driver,
|
||||
udc_controller->ep0_state = WAIT_FOR_SETUP;
|
||||
udc_controller->ep0_dir = 0;
|
||||
}
|
||||
printk(KERN_INFO "%s: bind to driver %s\n",
|
||||
udc_controller->gadget.name, driver->driver.name);
|
||||
|
||||
out:
|
||||
if (retval)
|
||||
printk(KERN_WARNING "gadget driver register failed %d\n",
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Disconnect from gadget driver */
|
||||
static int fsl_stop(struct usb_gadget_driver *driver)
|
||||
static int fsl_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct fsl_ep *loop_ep;
|
||||
unsigned long flags;
|
||||
|
||||
if (!udc_controller)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver != udc_controller->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_ERR_OR_NULL(udc_controller->transceiver))
|
||||
otg_set_peripheral(udc_controller->transceiver->otg, NULL);
|
||||
|
||||
@ -2052,16 +2023,9 @@ static int fsl_stop(struct usb_gadget_driver *driver)
|
||||
nuke(loop_ep, -ESHUTDOWN);
|
||||
spin_unlock_irqrestore(&udc_controller->lock, flags);
|
||||
|
||||
/* report disconnect; the controller is already quiesced */
|
||||
driver->disconnect(&udc_controller->gadget);
|
||||
|
||||
/* unbind gadget and unhook driver. */
|
||||
driver->unbind(&udc_controller->gadget);
|
||||
udc_controller->gadget.dev.driver = NULL;
|
||||
udc_controller->driver = NULL;
|
||||
|
||||
printk(KERN_WARNING "unregistered gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
116
drivers/usb/gadget/functions.c
Normal file
116
drivers/usb/gadget/functions.c
Normal file
@ -0,0 +1,116 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
static LIST_HEAD(func_list);
|
||||
static DEFINE_MUTEX(func_lock);
|
||||
|
||||
static struct usb_function_instance *try_get_usb_function_instance(const char *name)
|
||||
{
|
||||
struct usb_function_driver *fd;
|
||||
struct usb_function_instance *fi;
|
||||
|
||||
fi = ERR_PTR(-ENOENT);
|
||||
mutex_lock(&func_lock);
|
||||
list_for_each_entry(fd, &func_list, list) {
|
||||
|
||||
if (strcmp(name, fd->name))
|
||||
continue;
|
||||
|
||||
if (!try_module_get(fd->mod)) {
|
||||
fi = ERR_PTR(-EBUSY);
|
||||
break;
|
||||
}
|
||||
fi = fd->alloc_inst();
|
||||
if (IS_ERR(fi))
|
||||
module_put(fd->mod);
|
||||
else
|
||||
fi->fd = fd;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&func_lock);
|
||||
return fi;
|
||||
}
|
||||
|
||||
struct usb_function_instance *usb_get_function_instance(const char *name)
|
||||
{
|
||||
struct usb_function_instance *fi;
|
||||
int ret;
|
||||
|
||||
fi = try_get_usb_function_instance(name);
|
||||
if (!IS_ERR(fi))
|
||||
return fi;
|
||||
ret = PTR_ERR(fi);
|
||||
if (ret != -ENOENT)
|
||||
return fi;
|
||||
ret = request_module("usbfunc:%s", name);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
return try_get_usb_function_instance(name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_function_instance);
|
||||
|
||||
struct usb_function *usb_get_function(struct usb_function_instance *fi)
|
||||
{
|
||||
struct usb_function *f;
|
||||
|
||||
f = fi->fd->alloc_func(fi);
|
||||
if (IS_ERR(f))
|
||||
return f;
|
||||
f->fi = fi;
|
||||
return f;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_function);
|
||||
|
||||
void usb_put_function_instance(struct usb_function_instance *fi)
|
||||
{
|
||||
struct module *mod;
|
||||
|
||||
if (!fi)
|
||||
return;
|
||||
|
||||
mod = fi->fd->mod;
|
||||
fi->free_func_inst(fi);
|
||||
module_put(mod);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_put_function_instance);
|
||||
|
||||
void usb_put_function(struct usb_function *f)
|
||||
{
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
f->free_func(f);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_put_function);
|
||||
|
||||
int usb_function_register(struct usb_function_driver *newf)
|
||||
{
|
||||
struct usb_function_driver *fd;
|
||||
int ret;
|
||||
|
||||
ret = -EEXIST;
|
||||
|
||||
mutex_lock(&func_lock);
|
||||
list_for_each_entry(fd, &func_list, list) {
|
||||
if (!strcmp(fd->name, newf->name))
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
list_add_tail(&newf->list, &func_list);
|
||||
out:
|
||||
mutex_unlock(&func_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_function_register);
|
||||
|
||||
void usb_function_unregister(struct usb_function_driver *fd)
|
||||
{
|
||||
mutex_lock(&func_lock);
|
||||
list_del(&fd->list);
|
||||
mutex_unlock(&func_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_function_unregister);
|
@ -1308,65 +1308,28 @@ static void init_controller(struct fusb300 *fusb300)
|
||||
iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1);
|
||||
}
|
||||
/*------------------------------------------------------------------------*/
|
||||
static struct fusb300 *the_controller;
|
||||
|
||||
static int fusb300_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int fusb300_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct fusb300 *fusb300 = the_controller;
|
||||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
if (!fusb300)
|
||||
return -ENODEV;
|
||||
|
||||
if (fusb300->driver)
|
||||
return -EBUSY;
|
||||
struct fusb300 *fusb300 = to_fusb300(g);
|
||||
|
||||
/* hook up the driver */
|
||||
driver->driver.bus = NULL;
|
||||
fusb300->driver = driver;
|
||||
fusb300->gadget.dev.driver = &driver->driver;
|
||||
|
||||
retval = device_add(&fusb300->gadget.dev);
|
||||
if (retval) {
|
||||
pr_err("device_add error (%d)\n", retval);
|
||||
goto error;
|
||||
}
|
||||
|
||||
retval = bind(&fusb300->gadget, driver);
|
||||
if (retval) {
|
||||
pr_err("bind to driver error (%d)\n", retval);
|
||||
device_del(&fusb300->gadget.dev);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
fusb300->driver = NULL;
|
||||
fusb300->gadget.dev.driver = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int fusb300_udc_stop(struct usb_gadget_driver *driver)
|
||||
static int fusb300_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct fusb300 *fusb300 = the_controller;
|
||||
|
||||
if (driver != fusb300->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
struct fusb300 *fusb300 = to_fusb300(g);
|
||||
|
||||
driver->unbind(&fusb300->gadget);
|
||||
fusb300->gadget.dev.driver = NULL;
|
||||
|
||||
init_controller(fusb300);
|
||||
device_del(&fusb300->gadget.dev);
|
||||
fusb300->driver = NULL;
|
||||
|
||||
return 0;
|
||||
@ -1378,10 +1341,10 @@ static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops fusb300_gadget_ops = {
|
||||
static const struct usb_gadget_ops fusb300_gadget_ops = {
|
||||
.pullup = fusb300_udc_pullup,
|
||||
.start = fusb300_udc_start,
|
||||
.stop = fusb300_udc_stop,
|
||||
.udc_start = fusb300_udc_start,
|
||||
.udc_stop = fusb300_udc_stop,
|
||||
};
|
||||
|
||||
static int __exit fusb300_remove(struct platform_device *pdev)
|
||||
@ -1505,8 +1468,6 @@ static int __init fusb300_probe(struct platform_device *pdev)
|
||||
fusb300->gadget.ep0 = &fusb300->ep[0]->ep;
|
||||
INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list);
|
||||
|
||||
the_controller = fusb300;
|
||||
|
||||
fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep,
|
||||
GFP_KERNEL);
|
||||
if (fusb300->ep0_req == NULL)
|
||||
@ -1517,9 +1478,19 @@ static int __init fusb300_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_add_udc;
|
||||
|
||||
ret = device_add(&fusb300->gadget.dev);
|
||||
if (ret) {
|
||||
pr_err("device_add error (%d)\n", ret);
|
||||
goto err_add_device;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_device:
|
||||
usb_del_gadget_udc(&fusb300->gadget);
|
||||
|
||||
err_add_udc:
|
||||
fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req);
|
||||
|
||||
|
@ -673,4 +673,6 @@ struct fusb300 {
|
||||
u8 reenum; /* if re-enumeration */
|
||||
};
|
||||
|
||||
#define to_fusb300(g) (container_of((g), struct fusb300, gadget))
|
||||
|
||||
#endif
|
||||
|
@ -6,11 +6,34 @@
|
||||
#ifndef __G_ZERO_H
|
||||
#define __G_ZERO_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
struct usb_zero_options {
|
||||
unsigned pattern;
|
||||
unsigned isoc_interval;
|
||||
unsigned isoc_maxpacket;
|
||||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned qlen;
|
||||
};
|
||||
|
||||
/* global state */
|
||||
extern unsigned buflen;
|
||||
extern const struct usb_descriptor_header *otg_desc[];
|
||||
struct f_ss_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
unsigned pattern;
|
||||
unsigned isoc_interval;
|
||||
unsigned isoc_maxpacket;
|
||||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
};
|
||||
|
||||
struct f_lb_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned qlen;
|
||||
};
|
||||
|
||||
void lb_modexit(void);
|
||||
int lb_modinit(void);
|
||||
|
||||
/* common utilities */
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len);
|
||||
@ -19,8 +42,4 @@ void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out);
|
||||
|
||||
/* configuration-specific linkup */
|
||||
int sourcesink_add(struct usb_composite_dev *cdev, bool autoresume);
|
||||
int loopback_add(struct usb_composite_dev *cdev, bool autoresume);
|
||||
|
||||
#endif /* __G_ZERO_H */
|
||||
|
@ -125,7 +125,7 @@ static struct usb_configuration midi_config = {
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_ONE,
|
||||
.bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
|
||||
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
|
||||
};
|
||||
|
||||
static int __init midi_bind_config(struct usb_configuration *c)
|
||||
|
@ -993,14 +993,15 @@ static int goku_get_frame(struct usb_gadget *_gadget)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int goku_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int goku_stop(struct usb_gadget_driver *driver);
|
||||
static int goku_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int goku_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
static const struct usb_gadget_ops goku_ops = {
|
||||
.get_frame = goku_get_frame,
|
||||
.start = goku_start,
|
||||
.stop = goku_stop,
|
||||
.udc_start = goku_udc_start,
|
||||
.udc_stop = goku_udc_stop,
|
||||
// no remote wakeup
|
||||
// not selfpowered
|
||||
};
|
||||
@ -1339,50 +1340,28 @@ static void udc_enable(struct goku_udc *dev)
|
||||
* - one function driver, initted second
|
||||
*/
|
||||
|
||||
static struct goku_udc *the_controller;
|
||||
|
||||
/* when a driver is successfully registered, it will receive
|
||||
* control requests including set_configuration(), which enables
|
||||
* non-control requests. then usb traffic follows until a
|
||||
* disconnect is reported. then a host may connect again, or
|
||||
* the driver might get unbound.
|
||||
*/
|
||||
static int goku_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int goku_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct goku_udc *dev = the_controller;
|
||||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->disconnect
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
if (dev->driver)
|
||||
return -EBUSY;
|
||||
struct goku_udc *dev = to_goku_udc(g);
|
||||
|
||||
/* hook up the driver */
|
||||
driver->driver.bus = NULL;
|
||||
dev->driver = driver;
|
||||
dev->gadget.dev.driver = &driver->driver;
|
||||
retval = bind(&dev->gadget, driver);
|
||||
if (retval) {
|
||||
DBG(dev, "bind to driver %s --> error %d\n",
|
||||
driver->driver.name, retval);
|
||||
dev->driver = NULL;
|
||||
dev->gadget.dev.driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* then enable host detection and ep0; and we're ready
|
||||
/*
|
||||
* then enable host detection and ep0; and we're ready
|
||||
* for set_configuration as well as eventual disconnect.
|
||||
*/
|
||||
udc_enable(dev);
|
||||
|
||||
DBG(dev, "registered gadget driver '%s'\n", driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1400,35 +1379,23 @@ stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver)
|
||||
udc_reset (dev);
|
||||
for (i = 0; i < 4; i++)
|
||||
nuke(&dev->ep [i], -ESHUTDOWN);
|
||||
if (driver) {
|
||||
spin_unlock(&dev->lock);
|
||||
driver->disconnect(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
|
||||
if (dev->driver)
|
||||
udc_enable(dev);
|
||||
}
|
||||
|
||||
static int goku_stop(struct usb_gadget_driver *driver)
|
||||
static int goku_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct goku_udc *dev = the_controller;
|
||||
struct goku_udc *dev = to_goku_udc(g);
|
||||
unsigned long flags;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
if (!driver || driver != dev->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
dev->driver = NULL;
|
||||
stop_activity(dev, driver);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
driver->unbind(&dev->gadget);
|
||||
dev->gadget.dev.driver = NULL;
|
||||
|
||||
DBG(dev, "unregistered driver '%s'\n", driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1754,7 +1721,6 @@ static void goku_remove(struct pci_dev *pdev)
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
dev->regs = NULL;
|
||||
the_controller = NULL;
|
||||
|
||||
INFO(dev, "unbind\n");
|
||||
}
|
||||
@ -1770,13 +1736,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
void __iomem *base = NULL;
|
||||
int retval;
|
||||
|
||||
/* if you want to support more than one controller in a system,
|
||||
* usb_gadget_driver_{register,unregister}() must change.
|
||||
*/
|
||||
if (the_controller) {
|
||||
pr_warning("ignoring %s\n", pci_name(pdev));
|
||||
return -EBUSY;
|
||||
}
|
||||
if (!pdev->irq) {
|
||||
printk(KERN_ERR "Check PCI %s IRQ setup!\n", pci_name(pdev));
|
||||
retval = -ENODEV;
|
||||
@ -1851,7 +1810,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev);
|
||||
#endif
|
||||
|
||||
the_controller = dev;
|
||||
retval = device_register(&dev->gadget.dev);
|
||||
if (retval) {
|
||||
put_device(&dev->gadget.dev);
|
||||
|
@ -261,6 +261,7 @@ struct goku_udc {
|
||||
/* statistics... */
|
||||
unsigned long irqs;
|
||||
};
|
||||
#define to_goku_udc(g) (container_of((g), struct goku_udc, gadget))
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -1463,42 +1463,16 @@ static struct usb_ep_ops m66592_ep_ops = {
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static struct m66592 *the_controller;
|
||||
|
||||
static int m66592_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int m66592_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct m66592 *m66592 = the_controller;
|
||||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->max_speed < USB_SPEED_HIGH
|
||||
|| !bind
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
if (!m66592)
|
||||
return -ENODEV;
|
||||
if (m66592->driver)
|
||||
return -EBUSY;
|
||||
struct m66592 *m66592 = to_m66592(g);
|
||||
|
||||
/* hook up the driver */
|
||||
driver->driver.bus = NULL;
|
||||
m66592->driver = driver;
|
||||
m66592->gadget.dev.driver = &driver->driver;
|
||||
|
||||
retval = device_add(&m66592->gadget.dev);
|
||||
if (retval) {
|
||||
pr_err("device_add error (%d)\n", retval);
|
||||
goto error;
|
||||
}
|
||||
|
||||
retval = bind(&m66592->gadget, driver);
|
||||
if (retval) {
|
||||
pr_err("bind to driver error (%d)\n", retval);
|
||||
device_del(&m66592->gadget.dev);
|
||||
goto error;
|
||||
}
|
||||
|
||||
m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
|
||||
if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) {
|
||||
m66592_start_xclock(m66592);
|
||||
@ -1510,26 +1484,12 @@ static int m66592_start(struct usb_gadget_driver *driver,
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
m66592->driver = NULL;
|
||||
m66592->gadget.dev.driver = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int m66592_stop(struct usb_gadget_driver *driver)
|
||||
static int m66592_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct m66592 *m66592 = the_controller;
|
||||
unsigned long flags;
|
||||
|
||||
if (driver != m66592->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&m66592->lock, flags);
|
||||
if (m66592->gadget.speed != USB_SPEED_UNKNOWN)
|
||||
m66592_usb_disconnect(m66592);
|
||||
spin_unlock_irqrestore(&m66592->lock, flags);
|
||||
struct m66592 *m66592 = to_m66592(g);
|
||||
|
||||
m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
|
||||
|
||||
@ -1539,8 +1499,8 @@ static int m66592_stop(struct usb_gadget_driver *driver)
|
||||
init_controller(m66592);
|
||||
disable_controller(m66592);
|
||||
|
||||
device_del(&m66592->gadget.dev);
|
||||
m66592->driver = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1566,10 +1526,10 @@ static int m66592_pullup(struct usb_gadget *gadget, int is_on)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops m66592_gadget_ops = {
|
||||
static const struct usb_gadget_ops m66592_gadget_ops = {
|
||||
.get_frame = m66592_get_frame,
|
||||
.start = m66592_start,
|
||||
.stop = m66592_stop,
|
||||
.udc_start = m66592_udc_start,
|
||||
.udc_stop = m66592_udc_stop,
|
||||
.pullup = m66592_pullup,
|
||||
};
|
||||
|
||||
@ -1578,6 +1538,7 @@ static int __exit m66592_remove(struct platform_device *pdev)
|
||||
struct m66592 *m66592 = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
usb_del_gadget_udc(&m66592->gadget);
|
||||
device_del(&m66592->gadget.dev);
|
||||
|
||||
del_timer_sync(&m66592->timer);
|
||||
iounmap(m66592->reg);
|
||||
@ -1706,8 +1667,6 @@ static int __init m66592_probe(struct platform_device *pdev)
|
||||
m66592->pipenum2ep[0] = &m66592->ep[0];
|
||||
m66592->epaddr2ep[0] = &m66592->ep[0];
|
||||
|
||||
the_controller = m66592;
|
||||
|
||||
m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
|
||||
if (m66592->ep0_req == NULL)
|
||||
goto clean_up3;
|
||||
@ -1715,6 +1674,12 @@ static int __init m66592_probe(struct platform_device *pdev)
|
||||
|
||||
init_controller(m66592);
|
||||
|
||||
ret = device_add(&m66592->gadget.dev);
|
||||
if (ret) {
|
||||
pr_err("device_add error (%d)\n", ret);
|
||||
goto err_device_add;
|
||||
}
|
||||
|
||||
ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget);
|
||||
if (ret)
|
||||
goto err_add_udc;
|
||||
@ -1723,6 +1688,9 @@ static int __init m66592_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_add_udc:
|
||||
device_del(&m66592->gadget.dev);
|
||||
|
||||
err_device_add:
|
||||
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
|
||||
|
||||
clean_up3:
|
||||
|
@ -492,6 +492,7 @@ struct m66592 {
|
||||
int isochronous;
|
||||
int num_dma;
|
||||
};
|
||||
#define to_m66592(g) (container_of((g), struct m66592, gadget))
|
||||
|
||||
#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
|
||||
#define m66592_to_gadget(m66592) (&m66592->gadget)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
#if defined USB_ETH_RNDIS
|
||||
# undef USB_ETH_RNDIS
|
||||
#endif
|
||||
@ -42,9 +43,6 @@ MODULE_LICENSE("GPL");
|
||||
*/
|
||||
#include "f_mass_storage.c"
|
||||
|
||||
#include "u_serial.c"
|
||||
#include "f_acm.c"
|
||||
|
||||
#include "f_ecm.c"
|
||||
#include "f_subset.c"
|
||||
#ifdef USB_ETH_RNDIS
|
||||
@ -137,10 +135,13 @@ static struct fsg_common fsg_common;
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
static unsigned char tty_line;
|
||||
static struct usb_function_instance *fi_acm;
|
||||
|
||||
/********** RNDIS **********/
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
static struct usb_function *f_acm_rndis;
|
||||
|
||||
static __init int rndis_do_config(struct usb_configuration *c)
|
||||
{
|
||||
@ -155,15 +156,25 @@ static __init int rndis_do_config(struct usb_configuration *c)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = acm_bind_config(c, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
f_acm_rndis = usb_get_function(fi_acm);
|
||||
if (IS_ERR(f_acm_rndis))
|
||||
goto err_func_acm;
|
||||
|
||||
ret = usb_add_function(c, f_acm_rndis);
|
||||
if (ret)
|
||||
goto err_conf;
|
||||
|
||||
ret = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_fsg;
|
||||
|
||||
return 0;
|
||||
err_fsg:
|
||||
usb_remove_function(c, f_acm_rndis);
|
||||
err_conf:
|
||||
usb_put_function(f_acm_rndis);
|
||||
err_func_acm:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rndis_config_register(struct usb_composite_dev *cdev)
|
||||
@ -192,6 +203,7 @@ static int rndis_config_register(struct usb_composite_dev *cdev)
|
||||
/********** CDC ECM **********/
|
||||
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
static struct usb_function *f_acm_multi;
|
||||
|
||||
static __init int cdc_do_config(struct usb_configuration *c)
|
||||
{
|
||||
@ -206,15 +218,26 @@ static __init int cdc_do_config(struct usb_configuration *c)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = acm_bind_config(c, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* implicit port_num is zero */
|
||||
f_acm_multi = usb_get_function(fi_acm);
|
||||
if (IS_ERR(f_acm_multi))
|
||||
goto err_func_acm;
|
||||
|
||||
ret = usb_add_function(c, f_acm_multi);
|
||||
if (ret)
|
||||
goto err_conf;
|
||||
|
||||
ret = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_fsg;
|
||||
|
||||
return 0;
|
||||
err_fsg:
|
||||
usb_remove_function(c, f_acm_multi);
|
||||
err_conf:
|
||||
usb_put_function(f_acm_multi);
|
||||
err_func_acm:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdc_config_register(struct usb_composite_dev *cdev)
|
||||
@ -243,10 +266,10 @@ static int cdc_config_register(struct usb_composite_dev *cdev)
|
||||
|
||||
/****************************** Gadget Bind ******************************/
|
||||
|
||||
|
||||
static int __ref multi_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct f_serial_opts *opts;
|
||||
int status;
|
||||
|
||||
if (!can_support_ecm(cdev->gadget)) {
|
||||
@ -261,10 +284,19 @@ static int __ref multi_bind(struct usb_composite_dev *cdev)
|
||||
return status;
|
||||
|
||||
/* set up serial link layer */
|
||||
status = gserial_setup(cdev->gadget, 1);
|
||||
status = gserial_alloc_line(&tty_line);
|
||||
if (status < 0)
|
||||
goto fail0;
|
||||
|
||||
fi_acm = usb_get_function_instance("acm");
|
||||
if (IS_ERR(fi_acm)) {
|
||||
status = PTR_ERR(fi_acm);
|
||||
goto fail0dot5;
|
||||
}
|
||||
|
||||
opts = container_of(fi_acm, struct f_serial_opts, func_inst);
|
||||
opts->port_num = tty_line;
|
||||
|
||||
/* set up mass storage function */
|
||||
{
|
||||
void *retp;
|
||||
@ -301,7 +333,9 @@ static int __ref multi_bind(struct usb_composite_dev *cdev)
|
||||
fail2:
|
||||
fsg_common_put(&fsg_common);
|
||||
fail1:
|
||||
gserial_cleanup();
|
||||
usb_put_function_instance(fi_acm);
|
||||
fail0dot5:
|
||||
gserial_free_line(tty_line);
|
||||
fail0:
|
||||
gether_cleanup();
|
||||
return status;
|
||||
@ -309,7 +343,14 @@ fail0:
|
||||
|
||||
static int __exit multi_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gserial_cleanup();
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
usb_put_function(f_acm_multi);
|
||||
#endif
|
||||
#ifdef USB_ETH_RNDIS
|
||||
usb_put_function(f_acm_rndis);
|
||||
#endif
|
||||
usb_put_function_instance(fi_acm);
|
||||
gserial_free_line(tty_line);
|
||||
gether_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
@ -61,9 +61,6 @@ static DECLARE_COMPLETION(release_done);
|
||||
static const char driver_name[] = "mv_udc";
|
||||
static const char driver_desc[] = DRIVER_DESC;
|
||||
|
||||
/* controller device global variable */
|
||||
static struct mv_udc *the_controller;
|
||||
|
||||
static void nuke(struct mv_ep *ep, int status);
|
||||
static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
|
||||
|
||||
@ -1268,9 +1265,8 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int mv_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int mv_udc_stop(struct usb_gadget_driver *driver);
|
||||
static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *);
|
||||
static int mv_udc_stop(struct usb_gadget *, struct usb_gadget_driver *);
|
||||
/* device controller usb_gadget_ops structure */
|
||||
static const struct usb_gadget_ops mv_ops = {
|
||||
|
||||
@ -1285,8 +1281,8 @@ static const struct usb_gadget_ops mv_ops = {
|
||||
|
||||
/* D+ pullup, software-controlled connect/disconnect to USB host */
|
||||
.pullup = mv_udc_pullup,
|
||||
.start = mv_udc_start,
|
||||
.stop = mv_udc_stop,
|
||||
.udc_start = mv_udc_start,
|
||||
.udc_stop = mv_udc_stop,
|
||||
};
|
||||
|
||||
static int eps_init(struct mv_udc *udc)
|
||||
@ -1373,15 +1369,14 @@ static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
|
||||
}
|
||||
}
|
||||
|
||||
static int mv_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int mv_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
struct mv_udc *udc;
|
||||
int retval = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
udc = container_of(gadget, struct mv_udc, gadget);
|
||||
|
||||
if (udc->driver)
|
||||
return -EBUSY;
|
||||
@ -1399,26 +1394,14 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
retval = bind(&udc->gadget, driver);
|
||||
if (retval) {
|
||||
dev_err(&udc->dev->dev, "bind to driver %s --> %d\n",
|
||||
driver->driver.name, retval);
|
||||
udc->driver = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(udc->transceiver)) {
|
||||
if (udc->transceiver) {
|
||||
retval = otg_set_peripheral(udc->transceiver->otg,
|
||||
&udc->gadget);
|
||||
if (retval) {
|
||||
dev_err(&udc->dev->dev,
|
||||
"unable to register peripheral to otg\n");
|
||||
if (driver->unbind) {
|
||||
driver->unbind(&udc->gadget);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
}
|
||||
udc->gadget.dev.driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
@ -1433,13 +1416,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_udc_stop(struct usb_gadget_driver *driver)
|
||||
static int mv_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
struct mv_udc *udc;
|
||||
unsigned long flags;
|
||||
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
udc = container_of(gadget, struct mv_udc, gadget);
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
@ -1454,7 +1437,6 @@ static int mv_udc_stop(struct usb_gadget_driver *driver)
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
/* unbind gadget driver */
|
||||
driver->unbind(&udc->gadget);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
|
||||
@ -1472,10 +1454,13 @@ static void mv_set_ptc(struct mv_udc *udc, u32 mode)
|
||||
|
||||
static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
struct mv_ep *mvep = container_of(ep, struct mv_ep, ep);
|
||||
struct mv_req *req = container_of(_req, struct mv_req, req);
|
||||
struct mv_udc *udc;
|
||||
unsigned long flags;
|
||||
|
||||
udc = mvep->udc;
|
||||
|
||||
dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode);
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
@ -2123,15 +2108,18 @@ static void mv_udc_vbus_work(struct work_struct *work)
|
||||
/* release device structure */
|
||||
static void gadget_release(struct device *_dev)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
struct mv_udc *udc;
|
||||
|
||||
udc = dev_get_drvdata(_dev);
|
||||
|
||||
complete(udc->done);
|
||||
}
|
||||
|
||||
static int mv_udc_remove(struct platform_device *dev)
|
||||
static int mv_udc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
int clk_i;
|
||||
struct mv_udc *udc;
|
||||
|
||||
udc = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
|
||||
@ -2140,57 +2128,27 @@ static int mv_udc_remove(struct platform_device *dev)
|
||||
destroy_workqueue(udc->qwork);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have transceiver inited,
|
||||
* then vbus irq will not be requested in udc driver.
|
||||
*/
|
||||
if (udc->pdata && udc->pdata->vbus
|
||||
&& udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver))
|
||||
free_irq(udc->pdata->vbus->irq, &dev->dev);
|
||||
|
||||
/* free memory allocated in probe */
|
||||
if (udc->dtd_pool)
|
||||
dma_pool_destroy(udc->dtd_pool);
|
||||
|
||||
if (udc->ep_dqh)
|
||||
dma_free_coherent(&dev->dev, udc->ep_dqh_size,
|
||||
dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
|
||||
udc->ep_dqh, udc->ep_dqh_dma);
|
||||
|
||||
kfree(udc->eps);
|
||||
|
||||
if (udc->irq)
|
||||
free_irq(udc->irq, &dev->dev);
|
||||
|
||||
mv_udc_disable(udc);
|
||||
|
||||
if (udc->cap_regs)
|
||||
iounmap(udc->cap_regs);
|
||||
|
||||
if (udc->phy_regs)
|
||||
iounmap(udc->phy_regs);
|
||||
|
||||
if (udc->status_req) {
|
||||
kfree(udc->status_req->req.buf);
|
||||
kfree(udc->status_req);
|
||||
}
|
||||
|
||||
for (clk_i = 0; clk_i <= udc->clknum; clk_i++)
|
||||
clk_put(udc->clk[clk_i]);
|
||||
|
||||
device_unregister(&udc->gadget.dev);
|
||||
|
||||
/* free dev, wait for the release() finished */
|
||||
wait_for_completion(udc->done);
|
||||
kfree(udc);
|
||||
|
||||
the_controller = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_udc_probe(struct platform_device *dev)
|
||||
static int mv_udc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_usb_platform_data *pdata = dev->dev.platform_data;
|
||||
struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct mv_udc *udc;
|
||||
int retval = 0;
|
||||
int clk_i = 0;
|
||||
@ -2198,71 +2156,73 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
size_t size;
|
||||
|
||||
if (pdata == NULL) {
|
||||
dev_err(&dev->dev, "missing platform_data\n");
|
||||
dev_err(&pdev->dev, "missing platform_data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum;
|
||||
udc = kzalloc(size, GFP_KERNEL);
|
||||
udc = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
|
||||
if (udc == NULL) {
|
||||
dev_err(&dev->dev, "failed to allocate memory for udc\n");
|
||||
dev_err(&pdev->dev, "failed to allocate memory for udc\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
the_controller = udc;
|
||||
udc->done = &release_done;
|
||||
udc->pdata = dev->dev.platform_data;
|
||||
udc->pdata = pdev->dev.platform_data;
|
||||
spin_lock_init(&udc->lock);
|
||||
|
||||
udc->dev = dev;
|
||||
udc->dev = pdev;
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
if (pdata->mode == MV_USB_MODE_OTG)
|
||||
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (pdata->mode == MV_USB_MODE_OTG) {
|
||||
udc->transceiver = devm_usb_get_phy(&pdev->dev,
|
||||
USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR_OR_NULL(udc->transceiver)) {
|
||||
udc->transceiver = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
udc->clknum = pdata->clknum;
|
||||
for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
|
||||
udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]);
|
||||
udc->clk[clk_i] = devm_clk_get(&pdev->dev,
|
||||
pdata->clkname[clk_i]);
|
||||
if (IS_ERR(udc->clk[clk_i])) {
|
||||
retval = PTR_ERR(udc->clk[clk_i]);
|
||||
goto err_put_clk;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs");
|
||||
if (r == NULL) {
|
||||
dev_err(&dev->dev, "no I/O memory resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_put_clk;
|
||||
dev_err(&pdev->dev, "no I/O memory resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
udc->cap_regs = (struct mv_cap_regs __iomem *)
|
||||
ioremap(r->start, resource_size(r));
|
||||
devm_ioremap(&pdev->dev, r->start, resource_size(r));
|
||||
if (udc->cap_regs == NULL) {
|
||||
dev_err(&dev->dev, "failed to map I/O memory\n");
|
||||
retval = -EBUSY;
|
||||
goto err_put_clk;
|
||||
dev_err(&pdev->dev, "failed to map I/O memory\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs");
|
||||
if (r == NULL) {
|
||||
dev_err(&dev->dev, "no phy I/O memory resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_iounmap_capreg;
|
||||
dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
udc->phy_regs = ioremap(r->start, resource_size(r));
|
||||
if (udc->phy_regs == NULL) {
|
||||
dev_err(&dev->dev, "failed to map phy I/O memory\n");
|
||||
retval = -EBUSY;
|
||||
goto err_iounmap_capreg;
|
||||
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* we will acces controller register, so enable the clk */
|
||||
retval = mv_udc_enable_internal(udc);
|
||||
if (retval)
|
||||
goto err_iounmap_phyreg;
|
||||
return retval;
|
||||
|
||||
udc->op_regs =
|
||||
(struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs
|
||||
@ -2279,11 +2239,11 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
|
||||
size = udc->max_eps * sizeof(struct mv_dqh) *2;
|
||||
size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1);
|
||||
udc->ep_dqh = dma_alloc_coherent(&dev->dev, size,
|
||||
udc->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
|
||||
&udc->ep_dqh_dma, GFP_KERNEL);
|
||||
|
||||
if (udc->ep_dqh == NULL) {
|
||||
dev_err(&dev->dev, "allocate dQH memory failed\n");
|
||||
dev_err(&pdev->dev, "allocate dQH memory failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_disable_clock;
|
||||
}
|
||||
@ -2291,7 +2251,7 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
|
||||
/* create dTD dma_pool resource */
|
||||
udc->dtd_pool = dma_pool_create("mv_dtd",
|
||||
&dev->dev,
|
||||
&pdev->dev,
|
||||
sizeof(struct mv_dtd),
|
||||
DTD_ALIGNMENT,
|
||||
DMA_BOUNDARY);
|
||||
@ -2302,19 +2262,20 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
}
|
||||
|
||||
size = udc->max_eps * sizeof(struct mv_ep) *2;
|
||||
udc->eps = kzalloc(size, GFP_KERNEL);
|
||||
udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
|
||||
if (udc->eps == NULL) {
|
||||
dev_err(&dev->dev, "allocate ep memory failed\n");
|
||||
dev_err(&pdev->dev, "allocate ep memory failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_destroy_dma;
|
||||
}
|
||||
|
||||
/* initialize ep0 status request structure */
|
||||
udc->status_req = kzalloc(sizeof(struct mv_req), GFP_KERNEL);
|
||||
udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req),
|
||||
GFP_KERNEL);
|
||||
if (!udc->status_req) {
|
||||
dev_err(&dev->dev, "allocate status_req memory failed\n");
|
||||
dev_err(&pdev->dev, "allocate status_req memory failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_free_eps;
|
||||
goto err_destroy_dma;
|
||||
}
|
||||
INIT_LIST_HEAD(&udc->status_req->queue);
|
||||
|
||||
@ -2329,17 +2290,17 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
|
||||
r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0);
|
||||
if (r == NULL) {
|
||||
dev_err(&dev->dev, "no IRQ resource defined\n");
|
||||
dev_err(&pdev->dev, "no IRQ resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_free_status_req;
|
||||
goto err_destroy_dma;
|
||||
}
|
||||
udc->irq = r->start;
|
||||
if (request_irq(udc->irq, mv_udc_irq,
|
||||
if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq,
|
||||
IRQF_SHARED, driver_name, udc)) {
|
||||
dev_err(&dev->dev, "Request irq %d for UDC failed\n",
|
||||
dev_err(&pdev->dev, "Request irq %d for UDC failed\n",
|
||||
udc->irq);
|
||||
retval = -ENODEV;
|
||||
goto err_free_status_req;
|
||||
goto err_destroy_dma;
|
||||
}
|
||||
|
||||
/* initialize gadget structure */
|
||||
@ -2351,26 +2312,27 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
|
||||
/* the "gadget" abstracts/virtualizes the controller */
|
||||
dev_set_name(&udc->gadget.dev, "gadget");
|
||||
udc->gadget.dev.parent = &dev->dev;
|
||||
udc->gadget.dev.dma_mask = dev->dev.dma_mask;
|
||||
udc->gadget.dev.parent = &pdev->dev;
|
||||
udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
||||
udc->gadget.dev.release = gadget_release;
|
||||
udc->gadget.name = driver_name; /* gadget name */
|
||||
|
||||
retval = device_register(&udc->gadget.dev);
|
||||
if (retval)
|
||||
goto err_free_irq;
|
||||
goto err_destroy_dma;
|
||||
|
||||
eps_init(udc);
|
||||
|
||||
/* VBUS detect: we can disable/enable clock on demand.*/
|
||||
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||
if (udc->transceiver)
|
||||
udc->clock_gating = 1;
|
||||
else if (pdata->vbus) {
|
||||
udc->clock_gating = 1;
|
||||
retval = request_threaded_irq(pdata->vbus->irq, NULL,
|
||||
retval = devm_request_threaded_irq(&pdev->dev,
|
||||
pdata->vbus->irq, NULL,
|
||||
mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc);
|
||||
if (retval) {
|
||||
dev_info(&dev->dev,
|
||||
dev_info(&pdev->dev,
|
||||
"Can not request irq for VBUS, "
|
||||
"disable clock gating\n");
|
||||
udc->clock_gating = 0;
|
||||
@ -2378,7 +2340,7 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
|
||||
udc->qwork = create_singlethread_workqueue("mv_udc_queue");
|
||||
if (!udc->qwork) {
|
||||
dev_err(&dev->dev, "cannot create workqueue\n");
|
||||
dev_err(&pdev->dev, "cannot create workqueue\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_unregister;
|
||||
}
|
||||
@ -2396,53 +2358,40 @@ static int mv_udc_probe(struct platform_device *dev)
|
||||
else
|
||||
udc->vbus_active = 1;
|
||||
|
||||
retval = usb_add_gadget_udc(&dev->dev, &udc->gadget);
|
||||
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
|
||||
if (retval)
|
||||
goto err_unregister;
|
||||
goto err_create_workqueue;
|
||||
|
||||
dev_info(&dev->dev, "successful probe UDC device %s clock gating.\n",
|
||||
platform_set_drvdata(pdev, udc);
|
||||
dev_info(&pdev->dev, "successful probe UDC device %s clock gating.\n",
|
||||
udc->clock_gating ? "with" : "without");
|
||||
|
||||
return 0;
|
||||
|
||||
err_create_workqueue:
|
||||
destroy_workqueue(udc->qwork);
|
||||
err_unregister:
|
||||
if (udc->pdata && udc->pdata->vbus
|
||||
&& udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver))
|
||||
free_irq(pdata->vbus->irq, &dev->dev);
|
||||
device_unregister(&udc->gadget.dev);
|
||||
err_free_irq:
|
||||
free_irq(udc->irq, &dev->dev);
|
||||
err_free_status_req:
|
||||
kfree(udc->status_req->req.buf);
|
||||
kfree(udc->status_req);
|
||||
err_free_eps:
|
||||
kfree(udc->eps);
|
||||
err_destroy_dma:
|
||||
dma_pool_destroy(udc->dtd_pool);
|
||||
err_free_dma:
|
||||
dma_free_coherent(&dev->dev, udc->ep_dqh_size,
|
||||
dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
|
||||
udc->ep_dqh, udc->ep_dqh_dma);
|
||||
err_disable_clock:
|
||||
mv_udc_disable_internal(udc);
|
||||
err_iounmap_phyreg:
|
||||
iounmap(udc->phy_regs);
|
||||
err_iounmap_capreg:
|
||||
iounmap(udc->cap_regs);
|
||||
err_put_clk:
|
||||
for (clk_i--; clk_i >= 0; clk_i--)
|
||||
clk_put(udc->clk[clk_i]);
|
||||
the_controller = NULL;
|
||||
kfree(udc);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mv_udc_suspend(struct device *_dev)
|
||||
static int mv_udc_suspend(struct device *dev)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
struct mv_udc *udc;
|
||||
|
||||
udc = dev_get_drvdata(dev);
|
||||
|
||||
/* if OTG is enabled, the following will be done in OTG driver*/
|
||||
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||
if (udc->transceiver)
|
||||
return 0;
|
||||
|
||||
if (udc->pdata->vbus && udc->pdata->vbus->poll)
|
||||
@ -2469,13 +2418,15 @@ static int mv_udc_suspend(struct device *_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_udc_resume(struct device *_dev)
|
||||
static int mv_udc_resume(struct device *dev)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
struct mv_udc *udc;
|
||||
int retval;
|
||||
|
||||
udc = dev_get_drvdata(dev);
|
||||
|
||||
/* if OTG is enabled, the following will be done in OTG driver*/
|
||||
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||
if (udc->transceiver)
|
||||
return 0;
|
||||
|
||||
if (!udc->clock_gating) {
|
||||
@ -2499,11 +2450,12 @@ static const struct dev_pm_ops mv_udc_pm_ops = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static void mv_udc_shutdown(struct platform_device *dev)
|
||||
static void mv_udc_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
struct mv_udc *udc;
|
||||
u32 mode;
|
||||
|
||||
udc = platform_get_drvdata(pdev);
|
||||
/* reset controller mode to IDLE */
|
||||
mv_udc_enable(udc);
|
||||
mode = readl(&udc->op_regs->usbmode);
|
||||
@ -2514,7 +2466,7 @@ static void mv_udc_shutdown(struct platform_device *dev)
|
||||
|
||||
static struct platform_driver udc_driver = {
|
||||
.probe = mv_udc_probe,
|
||||
.remove = __exit_p(mv_udc_remove),
|
||||
.remove = mv_udc_remove,
|
||||
.shutdown = mv_udc_shutdown,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -116,6 +116,10 @@ static bool enable_suspend = 0;
|
||||
/* "modprobe net2280 enable_suspend=1" etc */
|
||||
module_param (enable_suspend, bool, S_IRUGO);
|
||||
|
||||
/* force full-speed operation */
|
||||
static bool full_speed;
|
||||
module_param(full_speed, bool, 0444);
|
||||
MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
|
||||
|
||||
#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
|
||||
|
||||
@ -1899,6 +1903,10 @@ static int net2280_start(struct usb_gadget *_gadget,
|
||||
retval = device_create_file (&dev->pdev->dev, &dev_attr_queues);
|
||||
if (retval) goto err_func;
|
||||
|
||||
/* Enable force-full-speed testing mode, if desired */
|
||||
if (full_speed)
|
||||
writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag);
|
||||
|
||||
/* ... then enable host detection and ep0; and we're ready
|
||||
* for set_configuration as well as eventual disconnect.
|
||||
*/
|
||||
@ -1957,6 +1965,10 @@ static int net2280_stop(struct usb_gadget *_gadget,
|
||||
dev->driver = NULL;
|
||||
|
||||
net2280_led_active (dev, 0);
|
||||
|
||||
/* Disable full-speed test mode */
|
||||
writel(0, &dev->usb->xcvrdiag);
|
||||
|
||||
device_remove_file (&dev->pdev->dev, &dev_attr_function);
|
||||
device_remove_file (&dev->pdev->dev, &dev_attr_queues);
|
||||
|
||||
@ -2841,6 +2853,9 @@ static void net2280_shutdown (struct pci_dev *pdev)
|
||||
|
||||
/* disable the pullup so the host will think we're gone */
|
||||
writel (0, &dev->usb->usbctl);
|
||||
|
||||
/* Disable full-speed test mode */
|
||||
writel(0, &dev->usb->xcvrdiag);
|
||||
}
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "u_serial.c"
|
||||
#define USB_FACM_INCLUDED
|
||||
#include "f_acm.c"
|
||||
#include "f_ecm.c"
|
||||
#include "f_obex.c"
|
||||
@ -101,6 +101,15 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
enum {
|
||||
TTY_PORT_OBEX0,
|
||||
TTY_PORT_OBEX1,
|
||||
TTY_PORT_ACM,
|
||||
TTY_PORTS_MAX,
|
||||
};
|
||||
|
||||
static unsigned char tty_lines[TTY_PORTS_MAX];
|
||||
|
||||
static int __init nokia_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
int status = 0;
|
||||
@ -109,15 +118,15 @@ static int __init nokia_bind_config(struct usb_configuration *c)
|
||||
if (status)
|
||||
printk(KERN_DEBUG "could not bind phonet config\n");
|
||||
|
||||
status = obex_bind_config(c, 0);
|
||||
status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX0]);
|
||||
if (status)
|
||||
printk(KERN_DEBUG "could not bind obex config %d\n", 0);
|
||||
|
||||
status = obex_bind_config(c, 1);
|
||||
status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX1]);
|
||||
if (status)
|
||||
printk(KERN_DEBUG "could not bind obex config %d\n", 0);
|
||||
|
||||
status = acm_bind_config(c, 2);
|
||||
status = acm_bind_config(c, tty_lines[TTY_PORT_ACM]);
|
||||
if (status)
|
||||
printk(KERN_DEBUG "could not bind acm config\n");
|
||||
|
||||
@ -133,7 +142,7 @@ static struct usb_configuration nokia_config_500ma_driver = {
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_ONE,
|
||||
.bMaxPower = 250, /* 500mA */
|
||||
.MaxPower = 500,
|
||||
};
|
||||
|
||||
static struct usb_configuration nokia_config_100ma_driver = {
|
||||
@ -141,21 +150,24 @@ static struct usb_configuration nokia_config_100ma_driver = {
|
||||
.bConfigurationValue = 2,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
|
||||
.bMaxPower = 50, /* 100 mA */
|
||||
.MaxPower = 100,
|
||||
};
|
||||
|
||||
static int __init nokia_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status;
|
||||
int cur_line;
|
||||
|
||||
status = gphonet_setup(cdev->gadget);
|
||||
if (status < 0)
|
||||
goto err_phonet;
|
||||
|
||||
status = gserial_setup(cdev->gadget, 3);
|
||||
if (status < 0)
|
||||
goto err_serial;
|
||||
for (cur_line = 0; cur_line < TTY_PORTS_MAX; cur_line++) {
|
||||
status = gserial_alloc_line(&tty_lines[cur_line]);
|
||||
if (status)
|
||||
goto err_ether;
|
||||
}
|
||||
|
||||
status = gether_setup(cdev->gadget, hostaddr);
|
||||
if (status < 0)
|
||||
@ -192,8 +204,10 @@ static int __init nokia_bind(struct usb_composite_dev *cdev)
|
||||
err_usb:
|
||||
gether_cleanup();
|
||||
err_ether:
|
||||
gserial_cleanup();
|
||||
err_serial:
|
||||
cur_line--;
|
||||
while (cur_line >= 0)
|
||||
gserial_free_line(tty_lines[cur_line--]);
|
||||
|
||||
gphonet_cleanup();
|
||||
err_phonet:
|
||||
return status;
|
||||
@ -201,8 +215,13 @@ err_phonet:
|
||||
|
||||
static int __exit nokia_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
gphonet_cleanup();
|
||||
gserial_cleanup();
|
||||
|
||||
for (i = 0; i < TTY_PORTS_MAX; i++)
|
||||
gserial_free_line(tty_lines[i]);
|
||||
|
||||
gether_cleanup();
|
||||
|
||||
return 0;
|
||||
|
@ -1309,19 +1309,20 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int omap_udc_stop(struct usb_gadget_driver *driver);
|
||||
static int omap_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int omap_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
static struct usb_gadget_ops omap_gadget_ops = {
|
||||
static const struct usb_gadget_ops omap_gadget_ops = {
|
||||
.get_frame = omap_get_frame,
|
||||
.wakeup = omap_wakeup,
|
||||
.set_selfpowered = omap_set_selfpowered,
|
||||
.vbus_session = omap_vbus_session,
|
||||
.vbus_draw = omap_vbus_draw,
|
||||
.pullup = omap_pullup,
|
||||
.start = omap_udc_start,
|
||||
.stop = omap_udc_stop,
|
||||
.udc_start = omap_udc_start,
|
||||
.udc_stop = omap_udc_stop,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -2041,28 +2042,15 @@ static inline int machine_without_vbus_sense(void)
|
||||
|| cpu_is_omap7xx();
|
||||
}
|
||||
|
||||
static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int omap_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
int status = -ENODEV;
|
||||
struct omap_ep *ep;
|
||||
unsigned long flags;
|
||||
|
||||
/* basic sanity tests */
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
if (!driver
|
||||
/* FIXME if otg, check: driver->is_otg */
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (udc->driver) {
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* reset state */
|
||||
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
|
||||
ep->irqs = 0;
|
||||
@ -2084,15 +2072,6 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||
if (udc->dc_clk != NULL)
|
||||
omap_udc_enable_clock(1);
|
||||
|
||||
status = bind(&udc->gadget, driver);
|
||||
if (status) {
|
||||
DBG("bind to %s --> %d\n", driver->driver.name, status);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
goto done;
|
||||
}
|
||||
DBG("bound to driver %s\n", driver->driver.name);
|
||||
|
||||
omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
|
||||
|
||||
/* connect to bus through transceiver */
|
||||
@ -2124,19 +2103,16 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||
done:
|
||||
if (udc->dc_clk != NULL)
|
||||
omap_udc_enable_clock(0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int omap_udc_stop(struct usb_gadget_driver *driver)
|
||||
static int omap_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
unsigned long flags;
|
||||
int status = -ENODEV;
|
||||
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
if (!driver || driver != udc->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
if (udc->dc_clk != NULL)
|
||||
omap_udc_enable_clock(1);
|
||||
|
||||
@ -2152,13 +2128,12 @@ static int omap_udc_stop(struct usb_gadget_driver *driver)
|
||||
udc_quiesce(udc);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
driver->unbind(&udc->gadget);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
|
||||
if (udc->dc_clk != NULL)
|
||||
omap_udc_enable_clock(0);
|
||||
DBG("unregistered driver '%s'\n", driver->driver.name);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -375,6 +375,7 @@ struct pch_udc_dev {
|
||||
struct pch_udc_cfg_data cfg_data;
|
||||
struct pch_vbus_gpio_data vbus_gpio;
|
||||
};
|
||||
#define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget))
|
||||
|
||||
#define PCH_UDC_PCI_BAR 1
|
||||
#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
|
||||
@ -384,7 +385,6 @@ struct pch_udc_dev {
|
||||
|
||||
static const char ep0_string[] = "ep0in";
|
||||
static DEFINE_SPINLOCK(udc_stall_spinlock); /* stall spin lock */
|
||||
struct pch_udc_dev *pch_udc; /* pointer to device object */
|
||||
static bool speed_fs;
|
||||
module_param_named(speed_fs, speed_fs, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
|
||||
@ -1235,9 +1235,10 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int pch_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int pch_udc_stop(struct usb_gadget_driver *driver);
|
||||
static int pch_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pch_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static const struct usb_gadget_ops pch_udc_ops = {
|
||||
.get_frame = pch_udc_pcd_get_frame,
|
||||
.wakeup = pch_udc_pcd_wakeup,
|
||||
@ -1245,8 +1246,8 @@ static const struct usb_gadget_ops pch_udc_ops = {
|
||||
.pullup = pch_udc_pcd_pullup,
|
||||
.vbus_session = pch_udc_pcd_vbus_session,
|
||||
.vbus_draw = pch_udc_pcd_vbus_draw,
|
||||
.start = pch_udc_start,
|
||||
.stop = pch_udc_stop,
|
||||
.udc_start = pch_udc_start,
|
||||
.udc_stop = pch_udc_stop,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2981,40 +2982,15 @@ static int init_dma_pools(struct pch_udc_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int pch_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct pch_udc_dev *dev = pch_udc;
|
||||
int retval;
|
||||
struct pch_udc_dev *dev = to_pch_udc(g);
|
||||
|
||||
if (!driver || (driver->max_speed == USB_SPEED_UNKNOWN) || !bind ||
|
||||
!driver->setup || !driver->unbind || !driver->disconnect) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"%s: invalid driver parameter\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev->driver) {
|
||||
dev_err(&dev->pdev->dev, "%s: already bound\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
driver->driver.bus = NULL;
|
||||
dev->driver = driver;
|
||||
dev->gadget.dev.driver = &driver->driver;
|
||||
|
||||
/* Invoke the bind routine of the gadget driver */
|
||||
retval = bind(&dev->gadget, driver);
|
||||
|
||||
if (retval) {
|
||||
dev_err(&dev->pdev->dev, "%s: binding to %s returning %d\n",
|
||||
__func__, driver->driver.name, retval);
|
||||
dev->driver = NULL;
|
||||
dev->gadget.dev.driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
/* get ready for ep0 traffic */
|
||||
pch_udc_setup_ep0(dev);
|
||||
|
||||
@ -3026,30 +3002,21 @@ static int pch_udc_start(struct usb_gadget_driver *driver,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_udc_stop(struct usb_gadget_driver *driver)
|
||||
static int pch_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct pch_udc_dev *dev = pch_udc;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || (driver != dev->driver)) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"%s: invalid driver parameter\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
struct pch_udc_dev *dev = to_pch_udc(g);
|
||||
|
||||
pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
|
||||
|
||||
/* Assures that there are no pending requests with this driver */
|
||||
driver->disconnect(&dev->gadget);
|
||||
driver->unbind(&dev->gadget);
|
||||
dev->gadget.dev.driver = NULL;
|
||||
dev->driver = NULL;
|
||||
dev->connected = 0;
|
||||
|
||||
/* set SD */
|
||||
pch_udc_set_disconnect(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3164,11 +3131,6 @@ static int pch_udc_probe(struct pci_dev *pdev,
|
||||
int retval;
|
||||
struct pch_udc_dev *dev;
|
||||
|
||||
/* one udc only */
|
||||
if (pch_udc) {
|
||||
pr_err("%s: already probed\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
/* init */
|
||||
dev = kzalloc(sizeof *dev, GFP_KERNEL);
|
||||
if (!dev) {
|
||||
@ -3207,7 +3169,6 @@ static int pch_udc_probe(struct pci_dev *pdev,
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
}
|
||||
pch_udc = dev;
|
||||
/* initialize the hardware */
|
||||
if (pch_udc_pcd_init(dev)) {
|
||||
retval = -ENODEV;
|
||||
|
@ -996,9 +996,10 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int pxa25x_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int pxa25x_stop(struct usb_gadget_driver *driver);
|
||||
static int pxa25x_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pxa25x_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
static const struct usb_gadget_ops pxa25x_udc_ops = {
|
||||
.get_frame = pxa25x_udc_get_frame,
|
||||
@ -1006,8 +1007,8 @@ static const struct usb_gadget_ops pxa25x_udc_ops = {
|
||||
.vbus_session = pxa25x_udc_vbus_session,
|
||||
.pullup = pxa25x_udc_pullup,
|
||||
.vbus_draw = pxa25x_udc_vbus_draw,
|
||||
.start = pxa25x_start,
|
||||
.stop = pxa25x_stop,
|
||||
.udc_start = pxa25x_udc_start,
|
||||
.udc_stop = pxa25x_udc_stop,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -1254,23 +1255,12 @@ static void udc_enable (struct pxa25x_udc *dev)
|
||||
* disconnect is reported. then a host may connect again, or
|
||||
* the driver might get unbound.
|
||||
*/
|
||||
static int pxa25x_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int pxa25x_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct pxa25x_udc *dev = the_controller;
|
||||
struct pxa25x_udc *dev = to_pxa25x(g);
|
||||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->disconnect
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
if (dev->driver)
|
||||
return -EBUSY;
|
||||
|
||||
/* first hook up the driver ... */
|
||||
dev->driver = driver;
|
||||
dev->gadget.dev.driver = &driver->driver;
|
||||
@ -1278,35 +1268,21 @@ static int pxa25x_start(struct usb_gadget_driver *driver,
|
||||
|
||||
retval = device_add (&dev->gadget.dev);
|
||||
if (retval) {
|
||||
fail:
|
||||
dev->driver = NULL;
|
||||
dev->gadget.dev.driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
retval = bind(&dev->gadget, driver);
|
||||
if (retval) {
|
||||
DMSG("bind to driver %s --> error %d\n",
|
||||
driver->driver.name, retval);
|
||||
device_del (&dev->gadget.dev);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* ... then enable host detection and ep0; and we're ready
|
||||
* for set_configuration as well as eventual disconnect.
|
||||
*/
|
||||
DMSG("registered gadget driver '%s'\n", driver->driver.name);
|
||||
|
||||
/* connect to bus through transceiver */
|
||||
if (!IS_ERR_OR_NULL(dev->transceiver)) {
|
||||
retval = otg_set_peripheral(dev->transceiver->otg,
|
||||
&dev->gadget);
|
||||
if (retval) {
|
||||
DMSG("can't bind to transceiver\n");
|
||||
if (driver->unbind)
|
||||
driver->unbind(&dev->gadget);
|
||||
if (retval)
|
||||
goto bind_fail;
|
||||
}
|
||||
}
|
||||
|
||||
pullup(dev);
|
||||
dump_state(dev);
|
||||
@ -1334,22 +1310,14 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
|
||||
}
|
||||
del_timer_sync(&dev->timer);
|
||||
|
||||
/* report disconnect; the driver is already quiesced */
|
||||
if (driver)
|
||||
driver->disconnect(&dev->gadget);
|
||||
|
||||
/* re-init driver-visible data structures */
|
||||
udc_reinit(dev);
|
||||
}
|
||||
|
||||
static int pxa25x_stop(struct usb_gadget_driver *driver)
|
||||
static int pxa25x_udc_stop(struct usb_gadget*g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct pxa25x_udc *dev = the_controller;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
if (!driver || driver != dev->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
struct pxa25x_udc *dev = to_pxa25x(g);
|
||||
|
||||
local_irq_disable();
|
||||
dev->pullup = 0;
|
||||
@ -1360,14 +1328,12 @@ static int pxa25x_stop(struct usb_gadget_driver *driver)
|
||||
if (!IS_ERR_OR_NULL(dev->transceiver))
|
||||
(void) otg_set_peripheral(dev->transceiver->otg, NULL);
|
||||
|
||||
driver->unbind(&dev->gadget);
|
||||
dev->gadget.dev.driver = NULL;
|
||||
dev->driver = NULL;
|
||||
|
||||
device_del (&dev->gadget.dev);
|
||||
|
||||
DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
|
||||
dump_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -126,6 +126,7 @@ struct pxa25x_udc {
|
||||
struct dentry *debugfs_udc;
|
||||
#endif
|
||||
};
|
||||
#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget))
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -1671,9 +1671,10 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int pxa27x_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int pxa27x_udc_stop(struct usb_gadget_driver *driver);
|
||||
static int pxa27x_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int pxa27x_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
static const struct usb_gadget_ops pxa_udc_ops = {
|
||||
.get_frame = pxa_udc_get_frame,
|
||||
@ -1681,8 +1682,8 @@ static const struct usb_gadget_ops pxa_udc_ops = {
|
||||
.pullup = pxa_udc_pullup,
|
||||
.vbus_session = pxa_udc_vbus_session,
|
||||
.vbus_draw = pxa_udc_vbus_draw,
|
||||
.start = pxa27x_udc_start,
|
||||
.stop = pxa27x_udc_stop,
|
||||
.udc_start = pxa27x_udc_start,
|
||||
.udc_stop = pxa27x_udc_stop,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1802,20 +1803,12 @@ static void udc_enable(struct pxa_udc *udc)
|
||||
*
|
||||
* Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise
|
||||
*/
|
||||
static int pxa27x_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int pxa27x_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct pxa_udc *udc = the_controller;
|
||||
struct pxa_udc *udc = to_pxa(g);
|
||||
int retval;
|
||||
|
||||
if (!driver || driver->max_speed < USB_SPEED_FULL || !bind
|
||||
|| !driver->disconnect || !driver->setup)
|
||||
return -EINVAL;
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
if (udc->driver)
|
||||
return -EBUSY;
|
||||
|
||||
/* first hook up the driver ... */
|
||||
udc->driver = driver;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
@ -1824,23 +1817,14 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
|
||||
retval = device_add(&udc->gadget.dev);
|
||||
if (retval) {
|
||||
dev_err(udc->dev, "device_add error %d\n", retval);
|
||||
goto add_fail;
|
||||
goto fail;
|
||||
}
|
||||
retval = bind(&udc->gadget, driver);
|
||||
if (retval) {
|
||||
dev_err(udc->dev, "bind to driver %s --> error %d\n",
|
||||
driver->driver.name, retval);
|
||||
goto bind_fail;
|
||||
}
|
||||
dev_dbg(udc->dev, "registered gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
if (!IS_ERR_OR_NULL(udc->transceiver)) {
|
||||
retval = otg_set_peripheral(udc->transceiver->otg,
|
||||
&udc->gadget);
|
||||
if (retval) {
|
||||
dev_err(udc->dev, "can't bind to transceiver\n");
|
||||
goto transceiver_fail;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1848,12 +1832,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
|
||||
udc_enable(udc);
|
||||
return 0;
|
||||
|
||||
transceiver_fail:
|
||||
if (driver->unbind)
|
||||
driver->unbind(&udc->gadget);
|
||||
bind_fail:
|
||||
device_del(&udc->gadget.dev);
|
||||
add_fail:
|
||||
fail:
|
||||
udc->driver = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
return retval;
|
||||
@ -1878,9 +1857,6 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
|
||||
|
||||
for (i = 0; i < NR_USB_ENDPOINTS; i++)
|
||||
pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep);
|
||||
|
||||
if (driver)
|
||||
driver->disconnect(&udc->gadget);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1889,25 +1865,18 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
|
||||
*
|
||||
* Returns 0 if no error, -ENODEV, -EINVAL otherwise
|
||||
*/
|
||||
static int pxa27x_udc_stop(struct usb_gadget_driver *driver)
|
||||
static int pxa27x_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct pxa_udc *udc = the_controller;
|
||||
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
if (!driver || driver != udc->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
struct pxa_udc *udc = to_pxa(g);
|
||||
|
||||
stop_activity(udc, driver);
|
||||
udc_disable(udc);
|
||||
dplus_pullup(udc, 0);
|
||||
|
||||
driver->unbind(&udc->gadget);
|
||||
udc->driver = NULL;
|
||||
|
||||
device_del(&udc->gadget.dev);
|
||||
dev_info(udc->dev, "unregistered gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||
return otg_set_peripheral(udc->transceiver->otg, NULL);
|
||||
|
@ -473,6 +473,7 @@ struct pxa_udc {
|
||||
struct dentry *debugfs_eps;
|
||||
#endif
|
||||
};
|
||||
#define to_pxa(g) (container_of((g), struct pxa_udc, gadget))
|
||||
|
||||
static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget)
|
||||
{
|
||||
|
@ -1812,7 +1812,7 @@ static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops r8a66597_gadget_ops = {
|
||||
static const struct usb_gadget_ops r8a66597_gadget_ops = {
|
||||
.get_frame = r8a66597_get_frame,
|
||||
.udc_start = r8a66597_start,
|
||||
.udc_stop = r8a66597_stop,
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/platform_data/s3c-hsotg.h>
|
||||
|
||||
#include <mach/map.h>
|
||||
@ -133,7 +134,9 @@ struct s3c_hsotg_ep {
|
||||
* struct s3c_hsotg - driver state.
|
||||
* @dev: The parent device supplied to the probe function
|
||||
* @driver: USB gadget driver
|
||||
* @plat: The platform specific configuration data.
|
||||
* @phy: The otg phy transceiver structure for phy control.
|
||||
* @plat: The platform specific configuration data. This can be removed once
|
||||
* all SoCs support usb transceiver.
|
||||
* @regs: The memory area mapped for accessing registers.
|
||||
* @irq: The IRQ number we are using
|
||||
* @supplies: Definition of USB power supplies
|
||||
@ -153,6 +156,7 @@ struct s3c_hsotg_ep {
|
||||
struct s3c_hsotg {
|
||||
struct device *dev;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct usb_phy *phy;
|
||||
struct s3c_hsotg_plat *plat;
|
||||
|
||||
spinlock_t lock;
|
||||
@ -2854,7 +2858,10 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
|
||||
dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev);
|
||||
if (hsotg->plat->phy_init)
|
||||
|
||||
if (hsotg->phy)
|
||||
usb_phy_init(hsotg->phy);
|
||||
else if (hsotg->plat->phy_init)
|
||||
hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
|
||||
}
|
||||
|
||||
@ -2869,7 +2876,9 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
|
||||
if (hsotg->plat->phy_exit)
|
||||
if (hsotg->phy)
|
||||
usb_phy_shutdown(hsotg->phy);
|
||||
else if (hsotg->plat->phy_exit)
|
||||
hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
|
||||
}
|
||||
|
||||
@ -3055,7 +3064,7 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops s3c_hsotg_gadget_ops = {
|
||||
static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
|
||||
.get_frame = s3c_hsotg_gadget_getframe,
|
||||
.udc_start = s3c_hsotg_udc_start,
|
||||
.udc_stop = s3c_hsotg_udc_stop,
|
||||
@ -3492,6 +3501,7 @@ static void s3c_hsotg_release(struct device *dev)
|
||||
static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c_hsotg_plat *plat = pdev->dev.platform_data;
|
||||
struct usb_phy *phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct s3c_hsotg_ep *eps;
|
||||
struct s3c_hsotg *hsotg;
|
||||
@ -3500,20 +3510,27 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
plat = pdev->dev.platform_data;
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "no platform data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
|
||||
if (!hsotg) {
|
||||
dev_err(dev, "cannot get memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hsotg->dev = dev;
|
||||
phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR_OR_NULL(phy)) {
|
||||
/* Fallback for pdata */
|
||||
plat = pdev->dev.platform_data;
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "no platform data or transceiver defined\n");
|
||||
return -EPROBE_DEFER;
|
||||
} else {
|
||||
hsotg->plat = plat;
|
||||
}
|
||||
} else {
|
||||
hsotg->phy = phy;
|
||||
}
|
||||
|
||||
hsotg->dev = dev;
|
||||
|
||||
hsotg->clk = devm_clk_get(&pdev->dev, "otg");
|
||||
if (IS_ERR(hsotg->clk)) {
|
||||
@ -3571,7 +3588,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++)
|
||||
hsotg->supplies[i].supply = s3c_hsotg_supply_names[i];
|
||||
|
||||
ret = regulator_bulk_get(dev, ARRAY_SIZE(hsotg->supplies),
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||
@ -3661,8 +3678,6 @@ err_ep_mem:
|
||||
kfree(eps);
|
||||
err_supplies:
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
|
||||
@ -3687,7 +3702,6 @@ static int s3c_hsotg_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
|
||||
|
@ -435,7 +435,7 @@ static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
|
||||
struct s3c_hsudc_req *hsreq;
|
||||
u32 csr;
|
||||
|
||||
csr = readl((u32)hsudc->regs + S3C_ESR);
|
||||
csr = readl(hsudc->regs + S3C_ESR);
|
||||
if (csr & S3C_ESR_STALL) {
|
||||
writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
|
||||
return;
|
||||
@ -468,7 +468,7 @@ static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
|
||||
struct s3c_hsudc_req *hsreq;
|
||||
u32 csr;
|
||||
|
||||
csr = readl((u32)hsudc->regs + S3C_ESR);
|
||||
csr = readl(hsudc->regs + S3C_ESR);
|
||||
if (csr & S3C_ESR_STALL) {
|
||||
writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
|
||||
return;
|
||||
@ -901,12 +901,12 @@ static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
|
||||
if (list_empty(&hsep->queue) && !hsep->stopped) {
|
||||
offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
|
||||
if (ep_is_in(hsep)) {
|
||||
csr = readl((u32)hsudc->regs + offset);
|
||||
csr = readl(hsudc->regs + offset);
|
||||
if (!(csr & S3C_ESR_TX_SUCCESS) &&
|
||||
(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
|
||||
hsreq = NULL;
|
||||
} else {
|
||||
csr = readl((u32)hsudc->regs + offset);
|
||||
csr = readl(hsudc->regs + offset);
|
||||
if ((csr & S3C_ESR_RX_SUCCESS)
|
||||
&& (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
|
||||
hsreq = NULL;
|
||||
@ -1254,7 +1254,7 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
|
||||
static const struct usb_gadget_ops s3c_hsudc_gadget_ops = {
|
||||
.get_frame = s3c_hsudc_gadget_getframe,
|
||||
.udc_start = s3c_hsudc_start,
|
||||
.udc_stop = s3c_hsudc_stop,
|
||||
@ -1286,7 +1286,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
|
||||
hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
|
||||
|
||||
ret = regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
|
||||
hsudc->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||
@ -1366,7 +1366,6 @@ err_res:
|
||||
if (!IS_ERR_OR_NULL(hsudc->transceiver))
|
||||
usb_put_phy(hsudc->transceiver);
|
||||
|
||||
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
||||
err_supplies:
|
||||
return ret;
|
||||
}
|
||||
|
@ -1538,9 +1538,10 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int s3c2410_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int s3c2410_udc_stop(struct usb_gadget_driver *driver);
|
||||
static int s3c2410_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int s3c2410_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
static const struct usb_gadget_ops s3c2410_ops = {
|
||||
.get_frame = s3c2410_udc_get_frame,
|
||||
@ -1549,8 +1550,8 @@ static const struct usb_gadget_ops s3c2410_ops = {
|
||||
.pullup = s3c2410_udc_pullup,
|
||||
.vbus_session = s3c2410_udc_vbus_session,
|
||||
.vbus_draw = s3c2410_vbus_draw,
|
||||
.start = s3c2410_udc_start,
|
||||
.stop = s3c2410_udc_stop,
|
||||
.udc_start = s3c2410_udc_start,
|
||||
.udc_stop = s3c2410_udc_stop,
|
||||
};
|
||||
|
||||
static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd)
|
||||
@ -1664,33 +1665,14 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev)
|
||||
s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
|
||||
}
|
||||
|
||||
static int s3c2410_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
static int s3c2410_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct s3c2410_udc *udc = the_controller;
|
||||
struct s3c2410_udc *udc = to_s3c2410(g)
|
||||
int retval;
|
||||
|
||||
dprintk(DEBUG_NORMAL, "%s() '%s'\n", __func__, driver->driver.name);
|
||||
|
||||
/* Sanity checks */
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
|
||||
if (udc->driver)
|
||||
return -EBUSY;
|
||||
|
||||
if (!bind || !driver->setup || driver->max_speed < USB_SPEED_FULL) {
|
||||
dev_err(&udc->gadget.dev, "Invalid driver: bind %p setup %p speed %d\n",
|
||||
bind, driver->setup, driver->max_speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
#if defined(MODULE)
|
||||
if (!driver->unbind) {
|
||||
dev_err(&udc->gadget.dev, "Invalid driver: no unbind method\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Hook the driver */
|
||||
udc->driver = driver;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
@ -1702,15 +1684,6 @@ static int s3c2410_udc_start(struct usb_gadget_driver *driver,
|
||||
goto register_error;
|
||||
}
|
||||
|
||||
dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
retval = bind(&udc->gadget, driver);
|
||||
if (retval) {
|
||||
device_del(&udc->gadget.dev);
|
||||
goto register_error;
|
||||
}
|
||||
|
||||
/* Enable udc */
|
||||
s3c2410_udc_enable(udc);
|
||||
|
||||
@ -1722,24 +1695,10 @@ register_error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int s3c2410_udc_stop(struct usb_gadget_driver *driver)
|
||||
static int s3c2410_udc_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct s3c2410_udc *udc = the_controller;
|
||||
|
||||
if (!udc)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver != udc->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
dprintk(DEBUG_NORMAL, "usb_gadget_unregister_driver() '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
/* report disconnect */
|
||||
if (driver->disconnect)
|
||||
driver->disconnect(&udc->gadget);
|
||||
|
||||
driver->unbind(&udc->gadget);
|
||||
struct s3c2410_udc *udc = to_s3c2410(g);
|
||||
|
||||
device_del(&udc->gadget.dev);
|
||||
udc->driver = NULL;
|
||||
|
@ -95,5 +95,6 @@ struct s3c2410_udc {
|
||||
u8 vbus;
|
||||
struct dentry *regs_info;
|
||||
};
|
||||
#define to_s3c2410(g) (container_of((g), struct s3c2410_udc, gadget))
|
||||
|
||||
#endif
|
||||
|
@ -36,10 +36,8 @@
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "f_acm.c"
|
||||
#include "f_obex.c"
|
||||
#include "f_serial.c"
|
||||
#include "u_serial.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
@ -128,20 +126,25 @@ module_param(n_ports, uint, 0);
|
||||
MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static unsigned char tty_lines[MAX_U_SERIAL_PORTS];
|
||||
|
||||
static int __init serial_bind_config(struct usb_configuration *c)
|
||||
static int __init serial_bind_obex_config(struct usb_configuration *c)
|
||||
{
|
||||
unsigned i;
|
||||
int status = 0;
|
||||
|
||||
for (i = 0; i < n_ports && status == 0; i++) {
|
||||
if (use_acm)
|
||||
status = acm_bind_config(c, i);
|
||||
else if (use_obex)
|
||||
status = obex_bind_config(c, i);
|
||||
else
|
||||
status = gser_bind_config(c, i);
|
||||
for (i = 0; i < n_ports && status == 0; i++)
|
||||
status = obex_bind_config(c, tty_lines[i]);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __init serial_bind_gser_config(struct usb_configuration *c)
|
||||
{
|
||||
unsigned i;
|
||||
int status = 0;
|
||||
|
||||
for (i = 0; i < n_ports && status == 0; i++)
|
||||
status = gser_bind_config(c, tty_lines[i]);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -152,13 +155,70 @@ static struct usb_configuration serial_config_driver = {
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];
|
||||
static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
|
||||
|
||||
static int serial_register_ports(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *c, const char *f_name)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = usb_add_config_only(cdev, c);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < n_ports; i++) {
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
fi_serial[i] = usb_get_function_instance(f_name);
|
||||
if (IS_ERR(fi_serial[i])) {
|
||||
ret = PTR_ERR(fi_serial[i]);
|
||||
goto fail;
|
||||
}
|
||||
opts = container_of(fi_serial[i], struct f_serial_opts, func_inst);
|
||||
opts->port_num = tty_lines[i];
|
||||
|
||||
f_serial[i] = usb_get_function(fi_serial[i]);
|
||||
if (IS_ERR(f_serial[i])) {
|
||||
ret = PTR_ERR(f_serial[i]);
|
||||
goto err_get_func;
|
||||
}
|
||||
|
||||
ret = usb_add_function(c, f_serial[i]);
|
||||
if (ret)
|
||||
goto err_add_func;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_func:
|
||||
usb_put_function(f_serial[i]);
|
||||
err_get_func:
|
||||
usb_put_function_instance(fi_serial[i]);
|
||||
|
||||
fail:
|
||||
i--;
|
||||
while (i >= 0) {
|
||||
usb_remove_function(c, f_serial[i]);
|
||||
usb_put_function(f_serial[i]);
|
||||
usb_put_function_instance(fi_serial[i]);
|
||||
i--;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init gs_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int status;
|
||||
int cur_line;
|
||||
|
||||
status = gserial_setup(cdev->gadget, n_ports);
|
||||
if (status < 0)
|
||||
return status;
|
||||
for (cur_line = 0; cur_line < n_ports; cur_line++) {
|
||||
status = gserial_alloc_line(&tty_lines[cur_line]);
|
||||
if (status)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
@ -178,8 +238,16 @@ static int __init gs_bind(struct usb_composite_dev *cdev)
|
||||
}
|
||||
|
||||
/* register our configuration */
|
||||
if (use_acm) {
|
||||
status = serial_register_ports(cdev, &serial_config_driver,
|
||||
"acm");
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
} else if (use_obex)
|
||||
status = usb_add_config(cdev, &serial_config_driver,
|
||||
serial_bind_config);
|
||||
serial_bind_obex_config);
|
||||
else
|
||||
status = usb_add_config(cdev, &serial_config_driver,
|
||||
serial_bind_gser_config);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
@ -189,16 +257,31 @@ static int __init gs_bind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
gserial_cleanup();
|
||||
cur_line--;
|
||||
while (cur_line >= 0)
|
||||
gserial_free_line(tty_lines[cur_line--]);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int gs_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_ports; i++) {
|
||||
usb_put_function(f_serial[i]);
|
||||
usb_put_function_instance(fi_serial[i]);
|
||||
gserial_free_line(tty_lines[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __refdata struct usb_composite_driver gserial_driver = {
|
||||
.name = "g_serial",
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
.bind = gs_bind,
|
||||
.unbind = gs_unbind,
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
@ -234,6 +317,5 @@ module_init(init);
|
||||
static void __exit cleanup(void)
|
||||
{
|
||||
usb_composite_unregister(&gserial_driver);
|
||||
gserial_cleanup();
|
||||
}
|
||||
module_exit(cleanup);
|
||||
|
@ -93,18 +93,6 @@
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* CBI Interrupt data structure */
|
||||
struct interrupt_data {
|
||||
u8 bType;
|
||||
u8 bValue;
|
||||
};
|
||||
|
||||
#define CBI_INTERRUPT_DATA_LEN 2
|
||||
|
||||
/* CBI Accept Device-Specific Command request */
|
||||
#define USB_CBI_ADSC_REQUEST 0x00
|
||||
|
||||
|
||||
/* Length of a SCSI Command Data Block */
|
||||
#define MAX_COMMAND_SIZE 16
|
||||
|
||||
@ -385,41 +373,6 @@ static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
|
||||
/*.bMaxBurst = DYNAMIC, */
|
||||
};
|
||||
|
||||
static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = {
|
||||
.bLength = USB_DT_USB_EXT_CAP_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE_CAPABILITY,
|
||||
.bDevCapabilityType = USB_CAP_TYPE_EXT,
|
||||
|
||||
.bmAttributes = cpu_to_le32(USB_LPM_SUPPORT),
|
||||
};
|
||||
|
||||
static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = {
|
||||
.bLength = USB_DT_USB_SS_CAP_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE_CAPABILITY,
|
||||
.bDevCapabilityType = USB_SS_CAP_TYPE,
|
||||
|
||||
/* .bmAttributes = LTM is not supported yet */
|
||||
|
||||
.wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION
|
||||
| USB_FULL_SPEED_OPERATION
|
||||
| USB_HIGH_SPEED_OPERATION
|
||||
| USB_5GBPS_OPERATION),
|
||||
.bFunctionalitySupport = USB_LOW_SPEED_OPERATION,
|
||||
.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT,
|
||||
.bU2DevExitLat = cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT),
|
||||
};
|
||||
|
||||
static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = {
|
||||
.bLength = USB_DT_BOS_SIZE,
|
||||
.bDescriptorType = USB_DT_BOS,
|
||||
|
||||
.wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE
|
||||
+ USB_DT_USB_EXT_CAP_SIZE
|
||||
+ USB_DT_USB_SS_CAP_SIZE),
|
||||
|
||||
.bNumDeviceCaps = 2,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *fsg_ss_function[] = {
|
||||
(struct usb_descriptor_header *) &fsg_intf_desc,
|
||||
(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
|
||||
@ -429,20 +382,6 @@ static struct usb_descriptor_header *fsg_ss_function[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Maxpacket and other transfer characteristics vary by speed. */
|
||||
static __maybe_unused struct usb_endpoint_descriptor *
|
||||
fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
|
||||
struct usb_endpoint_descriptor *hs,
|
||||
struct usb_endpoint_descriptor *ss)
|
||||
{
|
||||
if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
|
||||
return ss;
|
||||
else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return hs;
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
|
||||
static struct usb_string fsg_strings[] = {
|
||||
{FSG_STRING_INTERFACE, fsg_string_interface},
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
||||
@ -35,11 +36,12 @@
|
||||
* "serial port" functionality through the USB gadget stack. Each such
|
||||
* port is exposed through a /dev/ttyGS* node.
|
||||
*
|
||||
* After initialization (gserial_setup), these TTY port devices stay
|
||||
* available until they are removed (gserial_cleanup). Each one may be
|
||||
* connected to a USB function (gserial_connect), or disconnected (with
|
||||
* gserial_disconnect) when the USB host issues a config change event.
|
||||
* Data can only flow when the port is connected to the host.
|
||||
* After this module has been loaded, the individual TTY port can be requested
|
||||
* (gserial_alloc_line()) and it will stay available until they are removed
|
||||
* (gserial_free_line()). Each one may be connected to a USB function
|
||||
* (gserial_connect), or disconnected (with gserial_disconnect) when the USB
|
||||
* host issues a config change event. Data can only flow when the port is
|
||||
* connected to the host.
|
||||
*
|
||||
* A given TTY port can be made available in multiple configurations.
|
||||
* For example, each one might expose a ttyGS0 node which provides a
|
||||
@ -119,13 +121,10 @@ struct gs_port {
|
||||
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
|
||||
};
|
||||
|
||||
/* increase N_PORTS if you need more */
|
||||
#define N_PORTS 4
|
||||
static struct portmaster {
|
||||
struct mutex lock; /* protect open/close */
|
||||
struct gs_port *port;
|
||||
} ports[N_PORTS];
|
||||
static unsigned n_ports;
|
||||
} ports[MAX_U_SERIAL_PORTS];
|
||||
|
||||
#define GS_CLOSE_TIMEOUT 15 /* seconds */
|
||||
|
||||
@ -309,6 +308,7 @@ gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
|
||||
|
||||
return req;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gs_alloc_req);
|
||||
|
||||
/*
|
||||
* gs_free_req
|
||||
@ -320,6 +320,7 @@ void gs_free_req(struct usb_ep *ep, struct usb_request *req)
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gs_free_req);
|
||||
|
||||
/*
|
||||
* gs_send_packet
|
||||
@ -1030,10 +1031,19 @@ static int
|
||||
gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
|
||||
{
|
||||
struct gs_port *port;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&ports[port_num].lock);
|
||||
if (ports[port_num].port) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
|
||||
if (port == NULL)
|
||||
return -ENOMEM;
|
||||
if (port == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tty_port_init(&port->port);
|
||||
spin_lock_init(&port->port_lock);
|
||||
@ -1049,109 +1059,9 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
|
||||
port->port_line_coding = *coding;
|
||||
|
||||
ports[port_num].port = port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gserial_setup - initialize TTY driver for one or more ports
|
||||
* @g: gadget to associate with these ports
|
||||
* @count: how many ports to support
|
||||
* Context: may sleep
|
||||
*
|
||||
* The TTY stack needs to know in advance how many devices it should
|
||||
* plan to manage. Use this call to set up the ports you will be
|
||||
* exporting through USB. Later, connect them to functions based
|
||||
* on what configuration is activated by the USB host; and disconnect
|
||||
* them as appropriate.
|
||||
*
|
||||
* An example would be a two-configuration device in which both
|
||||
* configurations expose port 0, but through different functions.
|
||||
* One configuration could even expose port 1 while the other
|
||||
* one doesn't.
|
||||
*
|
||||
* Returns negative errno or zero.
|
||||
*/
|
||||
int gserial_setup(struct usb_gadget *g, unsigned count)
|
||||
{
|
||||
unsigned i;
|
||||
struct usb_cdc_line_coding coding;
|
||||
int status;
|
||||
|
||||
if (count == 0 || count > N_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
gs_tty_driver = alloc_tty_driver(count);
|
||||
if (!gs_tty_driver)
|
||||
return -ENOMEM;
|
||||
|
||||
gs_tty_driver->driver_name = "g_serial";
|
||||
gs_tty_driver->name = PREFIX;
|
||||
/* uses dynamically assigned dev_t values */
|
||||
|
||||
gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
gs_tty_driver->init_termios = tty_std_termios;
|
||||
|
||||
/* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
|
||||
* MS-Windows. Otherwise, most of these flags shouldn't affect
|
||||
* anything unless we were to actually hook up to a serial line.
|
||||
*/
|
||||
gs_tty_driver->init_termios.c_cflag =
|
||||
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
gs_tty_driver->init_termios.c_ispeed = 9600;
|
||||
gs_tty_driver->init_termios.c_ospeed = 9600;
|
||||
|
||||
coding.dwDTERate = cpu_to_le32(9600);
|
||||
coding.bCharFormat = 8;
|
||||
coding.bParityType = USB_CDC_NO_PARITY;
|
||||
coding.bDataBits = USB_CDC_1_STOP_BITS;
|
||||
|
||||
tty_set_operations(gs_tty_driver, &gs_tty_ops);
|
||||
|
||||
/* make devices be openable */
|
||||
for (i = 0; i < count; i++) {
|
||||
mutex_init(&ports[i].lock);
|
||||
status = gs_port_alloc(i, &coding);
|
||||
if (status) {
|
||||
count = i;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
n_ports = count;
|
||||
|
||||
/* export the driver ... */
|
||||
status = tty_register_driver(gs_tty_driver);
|
||||
if (status) {
|
||||
pr_err("%s: cannot register, err %d\n",
|
||||
__func__, status);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
|
||||
for (i = 0; i < count; i++) {
|
||||
struct device *tty_dev;
|
||||
|
||||
tty_dev = tty_port_register_device(&ports[i].port->port,
|
||||
gs_tty_driver, i, &g->dev);
|
||||
if (IS_ERR(tty_dev))
|
||||
pr_warning("%s: no classdev for port %d, err %ld\n",
|
||||
__func__, i, PTR_ERR(tty_dev));
|
||||
}
|
||||
|
||||
pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
|
||||
count, (count == 1) ? "" : "s");
|
||||
|
||||
return status;
|
||||
fail:
|
||||
while (count--) {
|
||||
tty_port_destroy(&ports[count].port->port);
|
||||
kfree(ports[count].port);
|
||||
}
|
||||
put_tty_driver(gs_tty_driver);
|
||||
gs_tty_driver = NULL;
|
||||
return status;
|
||||
out:
|
||||
mutex_unlock(&ports[port_num].lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gs_closed(struct gs_port *port)
|
||||
@ -1164,55 +1074,77 @@ static int gs_closed(struct gs_port *port)
|
||||
return cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* gserial_cleanup - remove TTY-over-USB driver and devices
|
||||
* Context: may sleep
|
||||
*
|
||||
* This is called to free all resources allocated by @gserial_setup().
|
||||
* Accordingly, it may need to wait until some open /dev/ files have
|
||||
* closed.
|
||||
*
|
||||
* The caller must have issued @gserial_disconnect() for any ports
|
||||
* that had previously been connected, so that there is never any
|
||||
* I/O pending when it's called.
|
||||
*/
|
||||
void gserial_cleanup(void)
|
||||
static void gserial_free_port(struct gs_port *port)
|
||||
{
|
||||
unsigned i;
|
||||
struct gs_port *port;
|
||||
|
||||
if (!gs_tty_driver)
|
||||
return;
|
||||
|
||||
/* start sysfs and /dev/ttyGS* node removal */
|
||||
for (i = 0; i < n_ports; i++)
|
||||
tty_unregister_device(gs_tty_driver, i);
|
||||
|
||||
for (i = 0; i < n_ports; i++) {
|
||||
/* prevent new opens */
|
||||
mutex_lock(&ports[i].lock);
|
||||
port = ports[i].port;
|
||||
ports[i].port = NULL;
|
||||
mutex_unlock(&ports[i].lock);
|
||||
|
||||
tasklet_kill(&port->push);
|
||||
|
||||
/* wait for old opens to finish */
|
||||
wait_event(port->port.close_wait, gs_closed(port));
|
||||
|
||||
WARN_ON(port->port_usb != NULL);
|
||||
|
||||
tty_port_destroy(&port->port);
|
||||
kfree(port);
|
||||
}
|
||||
n_ports = 0;
|
||||
|
||||
tty_unregister_driver(gs_tty_driver);
|
||||
put_tty_driver(gs_tty_driver);
|
||||
gs_tty_driver = NULL;
|
||||
void gserial_free_line(unsigned char port_num)
|
||||
{
|
||||
struct gs_port *port;
|
||||
|
||||
pr_debug("%s: cleaned up ttyGS* support\n", __func__);
|
||||
mutex_lock(&ports[port_num].lock);
|
||||
if (WARN_ON(!ports[port_num].port)) {
|
||||
mutex_unlock(&ports[port_num].lock);
|
||||
return;
|
||||
}
|
||||
port = ports[port_num].port;
|
||||
ports[port_num].port = NULL;
|
||||
mutex_unlock(&ports[port_num].lock);
|
||||
|
||||
gserial_free_port(port);
|
||||
tty_unregister_device(gs_tty_driver, port_num);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_free_line);
|
||||
|
||||
int gserial_alloc_line(unsigned char *line_num)
|
||||
{
|
||||
struct usb_cdc_line_coding coding;
|
||||
struct device *tty_dev;
|
||||
int ret;
|
||||
int port_num;
|
||||
|
||||
coding.dwDTERate = cpu_to_le32(9600);
|
||||
coding.bCharFormat = 8;
|
||||
coding.bParityType = USB_CDC_NO_PARITY;
|
||||
coding.bDataBits = USB_CDC_1_STOP_BITS;
|
||||
|
||||
for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) {
|
||||
ret = gs_port_alloc(port_num, &coding);
|
||||
if (ret == -EBUSY)
|
||||
continue;
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
|
||||
|
||||
tty_dev = tty_port_register_device(&ports[port_num].port->port,
|
||||
gs_tty_driver, port_num, NULL);
|
||||
if (IS_ERR(tty_dev)) {
|
||||
struct gs_port *port;
|
||||
pr_err("%s: failed to register tty for port %d, err %ld\n",
|
||||
__func__, port_num, PTR_ERR(tty_dev));
|
||||
|
||||
ret = PTR_ERR(tty_dev);
|
||||
port = ports[port_num].port;
|
||||
ports[port_num].port = NULL;
|
||||
gserial_free_port(port);
|
||||
goto err;
|
||||
}
|
||||
*line_num = port_num;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_alloc_line);
|
||||
|
||||
/**
|
||||
* gserial_connect - notify TTY I/O glue that USB link is active
|
||||
@ -1229,8 +1161,8 @@ void gserial_cleanup(void)
|
||||
*
|
||||
* Caller needs to have set up the endpoints and USB function in @dev
|
||||
* before calling this, as well as the appropriate (speed-specific)
|
||||
* endpoint descriptors, and also have set up the TTY driver by calling
|
||||
* @gserial_setup().
|
||||
* endpoint descriptors, and also have allocate @port_num by calling
|
||||
* @gserial_alloc_line().
|
||||
*
|
||||
* Returns negative errno or zero.
|
||||
* On success, ep->driver_data will be overwritten.
|
||||
@ -1241,11 +1173,18 @@ int gserial_connect(struct gserial *gser, u8 port_num)
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
if (!gs_tty_driver || port_num >= n_ports)
|
||||
if (port_num >= MAX_U_SERIAL_PORTS)
|
||||
return -ENXIO;
|
||||
|
||||
/* we "know" gserial_cleanup() hasn't been called */
|
||||
port = ports[port_num].port;
|
||||
if (!port) {
|
||||
pr_err("serial line %d not allocated.\n", port_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (port->port_usb) {
|
||||
pr_err("serial line %d is in use.\n", port_num);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* activate the endpoints */
|
||||
status = usb_ep_enable(gser->in);
|
||||
@ -1292,7 +1231,7 @@ fail_out:
|
||||
gser->in->driver_data = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gserial_connect);
|
||||
/**
|
||||
* gserial_disconnect - notify TTY I/O glue that USB link is inactive
|
||||
* @gser: the function, on which gserial_connect() was called
|
||||
@ -1347,3 +1286,65 @@ void gserial_disconnect(struct gserial *gser)
|
||||
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_disconnect);
|
||||
|
||||
static int userial_init(void)
|
||||
{
|
||||
unsigned i;
|
||||
int status;
|
||||
|
||||
gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS);
|
||||
if (!gs_tty_driver)
|
||||
return -ENOMEM;
|
||||
|
||||
gs_tty_driver->driver_name = "g_serial";
|
||||
gs_tty_driver->name = PREFIX;
|
||||
/* uses dynamically assigned dev_t values */
|
||||
|
||||
gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
gs_tty_driver->init_termios = tty_std_termios;
|
||||
|
||||
/* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
|
||||
* MS-Windows. Otherwise, most of these flags shouldn't affect
|
||||
* anything unless we were to actually hook up to a serial line.
|
||||
*/
|
||||
gs_tty_driver->init_termios.c_cflag =
|
||||
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
gs_tty_driver->init_termios.c_ispeed = 9600;
|
||||
gs_tty_driver->init_termios.c_ospeed = 9600;
|
||||
|
||||
tty_set_operations(gs_tty_driver, &gs_tty_ops);
|
||||
for (i = 0; i < MAX_U_SERIAL_PORTS; i++)
|
||||
mutex_init(&ports[i].lock);
|
||||
|
||||
/* export the driver ... */
|
||||
status = tty_register_driver(gs_tty_driver);
|
||||
if (status) {
|
||||
pr_err("%s: cannot register, err %d\n",
|
||||
__func__, status);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
|
||||
MAX_U_SERIAL_PORTS,
|
||||
(MAX_U_SERIAL_PORTS == 1) ? "" : "s");
|
||||
|
||||
return status;
|
||||
fail:
|
||||
put_tty_driver(gs_tty_driver);
|
||||
gs_tty_driver = NULL;
|
||||
return status;
|
||||
}
|
||||
module_init(userial_init);
|
||||
|
||||
static void userial_cleanup(void)
|
||||
{
|
||||
tty_unregister_driver(gs_tty_driver);
|
||||
put_tty_driver(gs_tty_driver);
|
||||
gs_tty_driver = NULL;
|
||||
}
|
||||
module_exit(userial_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -15,6 +15,13 @@
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
|
||||
#define MAX_U_SERIAL_PORTS 4
|
||||
|
||||
struct f_serial_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
u8 port_num;
|
||||
};
|
||||
|
||||
/*
|
||||
* One non-multiplexed "serial" I/O port ... there can be several of these
|
||||
* on any given USB peripheral device, if it provides enough endpoints.
|
||||
@ -49,9 +56,9 @@ struct gserial {
|
||||
struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
|
||||
void gs_free_req(struct usb_ep *, struct usb_request *req);
|
||||
|
||||
/* port setup/teardown is handled by gadget driver */
|
||||
int gserial_setup(struct usb_gadget *g, unsigned n_ports);
|
||||
void gserial_cleanup(void);
|
||||
/* management of individual TTY ports */
|
||||
int gserial_alloc_line(unsigned char *port_line);
|
||||
void gserial_free_line(unsigned char port_line);
|
||||
|
||||
/* connect/disconnect is handled by individual functions */
|
||||
int gserial_connect(struct gserial *, u8 port_num);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user