mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
USB merge for 3.7-rc1
Here is the big USB pull request for 3.7-rc1
There are lots of gadget driver changes (including copying a bunch of
files into the drivers/staging/ccg/ directory so that the other gadget
drivers can be fixed up properly without breaking that driver), and we
remove the old obsolete ub.c driver from the tree. There are also the
usual XHCI set of updates, and other various driver changes and updates.
We also are trying hard to remove the old dbg() macro, but the final
bits of that removal will be coming in through the networking tree
before we can delete it for good.
All of these patches have been in the linux-next tree.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.19 (GNU/Linux)
iEYEABECAAYFAlBp3+AACgkQMUfUDdst+ym5vwCfe93FyJyXn/RDkGz7iBemvWFd
vrwAoIxjaOa4/yWZWcgrWc5bP4aO3ssc
=jYDr
-----END PGP SIGNATURE-----
Merge tag 'usb-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB changes from Greg Kroah-Hartman:
"Here is the big USB pull request for 3.7-rc1
There are lots of gadget driver changes (including copying a bunch of
files into the drivers/staging/ccg/ directory so that the other gadget
drivers can be fixed up properly without breaking that driver), and we
remove the old obsolete ub.c driver from the tree.
There are also the usual XHCI set of updates, and other various driver
changes and updates. We also are trying hard to remove the old dbg()
macro, but the final bits of that removal will be coming in through
the networking tree before we can delete it for good.
All of these patches have been in the linux-next tree.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
Fix up several annoying - but fairly mindless - conflicts due to the
termios structure having moved into the tty device, and often clashing
with dbg -> dev_dbg conversion.
* tag 'usb-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (339 commits)
USB: ezusb: move ezusb.c from drivers/usb/serial to drivers/usb/misc
USB: uas: fix gcc warning
USB: uas: fix locking
USB: Fix race condition when removing host controllers
USB: uas: add locking
USB: uas: fix abort
USB: uas: remove aborted field, replace with status bit.
USB: uas: fix task management
USB: uas: keep track of command urbs
xhci: Intel Panther Point BEI quirk.
powerpc/usb: remove checking PHY_CLK_VALID for UTMI PHY
USB: ftdi_sio: add TIAO USB Multi-Protocol Adapter (TUMPA) support
Revert "usb : Add sysfs files to control port power."
USB: serial: remove vizzini driver
usb: host: xhci: Fix Null pointer dereferencing with 71c731a
for non-x86 systems
Increase XHCI suspend timeout to 16ms
USB: ohci-at91: fix null pointer in ohci_hcd_at91_overcurrent_irq
USB: sierra_ms: don't keep unused variable
fsl/usb: Add support for USB controller version 2.4
USB: qcaux: add Pantech vendor class match
...
This commit is contained in:
commit
d9a807461f
@ -220,3 +220,10 @@ Description:
|
||||
If the device doesn't support LTM, the file will read "no".
|
||||
The file will be present for all speeds of USB devices, and will
|
||||
always read "no" for USB 1.1 and USB 2.0 devices.
|
||||
|
||||
What: /sys/bus/usb/devices/.../(hub interface)/portX
|
||||
Date: August 2012
|
||||
Contact: Lan Tianyu <tianyu.lan@intel.com>
|
||||
Description:
|
||||
The /sys/bus/usb/devices/.../(hub interface)/portX
|
||||
is usb port device's sysfs directory.
|
||||
|
14
Documentation/devicetree/bindings/usb/am33xx-usb.txt
Normal file
14
Documentation/devicetree/bindings/usb/am33xx-usb.txt
Normal file
@ -0,0 +1,14 @@
|
||||
AM33XX MUSB GLUE
|
||||
- compatible : Should be "ti,musb-am33xx"
|
||||
- ti,hwmods : must be "usb_otg_hs"
|
||||
- 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
|
||||
MUSB configuration-specific setting. Should be set to "16"
|
||||
- ram_bits : Specifies the ram address size. Should be set to "12"
|
||||
- port0_mode : Should be "3" to represent OTG. "1" signifies HOST and "2"
|
||||
represents PERIPHERAL.
|
||||
- port1_mode : Should be "1" to represent HOST. "3" signifies OTG and "2"
|
||||
represents PERIPHERAL.
|
||||
- power : Should be "250". This signifies the controller can supply upto
|
||||
500mA when operating in host mode.
|
@ -7,7 +7,10 @@ Required properties:
|
||||
|
||||
Optional properties:
|
||||
- fsl,usbphy: phandler of usb phy that connects to the only one port
|
||||
- fsl,usbmisc: phandler of non-core register device, with one argument
|
||||
that indicate usb controller index
|
||||
- vbus-supply: regulator for vbus
|
||||
- disable-over-current: disable over current detect
|
||||
|
||||
Examples:
|
||||
usb@02184000 { /* USB OTG */
|
||||
@ -15,4 +18,6 @@ usb@02184000 { /* USB OTG */
|
||||
reg = <0x02184000 0x200>;
|
||||
interrupts = <0 43 0x04>;
|
||||
fsl,usbphy = <&usbphy1>;
|
||||
fsl,usbmisc = <&usbmisc 0>;
|
||||
disable-over-current;
|
||||
};
|
||||
|
33
Documentation/devicetree/bindings/usb/omap-usb.txt
Normal file
33
Documentation/devicetree/bindings/usb/omap-usb.txt
Normal file
@ -0,0 +1,33 @@
|
||||
OMAP GLUE
|
||||
|
||||
OMAP MUSB GLUE
|
||||
- compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
|
||||
- ti,hwmods : must be "usb_otg_hs"
|
||||
- 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
|
||||
MUSB configuration-specific setting. Should be set to "16"
|
||||
- ram_bits : Specifies the ram address size. Should be set to "12"
|
||||
- interface_type : This is a board specific setting to describe the type of
|
||||
interface between the controller and the phy. It should be "0" or "1"
|
||||
specifying ULPI and UTMI respectively.
|
||||
- mode : Should be "3" to represent OTG. "1" signifies HOST and "2"
|
||||
represents PERIPHERAL.
|
||||
- power : Should be "50". This signifies the controller can supply upto
|
||||
100mA when operating in host mode.
|
||||
|
||||
SOC specific device node entry
|
||||
usb_otg_hs: usb_otg_hs@4a0ab000 {
|
||||
compatible = "ti,omap4-musb";
|
||||
ti,hwmods = "usb_otg_hs";
|
||||
multipoint = <1>;
|
||||
num_eps = <16>;
|
||||
ram_bits = <12>;
|
||||
};
|
||||
|
||||
Board specific device node entry
|
||||
&usb_otg_hs {
|
||||
interface_type = <1>;
|
||||
mode = <3>;
|
||||
power = <50>;
|
||||
};
|
12
Documentation/devicetree/bindings/usb/platform-uhci.txt
Normal file
12
Documentation/devicetree/bindings/usb/platform-uhci.txt
Normal file
@ -0,0 +1,12 @@
|
||||
Generic Platform UHCI controllers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "platform-uhci".
|
||||
- reg: Address range of the uhci registers
|
||||
- interrupts: Should contain the uhci interrupt.
|
||||
|
||||
usb: uhci@D8007301 {
|
||||
compatible = "platform-uhci", "usb-uhci";
|
||||
reg = <0xD8007301 0x200>;
|
||||
interrupts = <0>;
|
||||
};
|
31
Documentation/devicetree/bindings/usb/pxa-usb.txt
Normal file
31
Documentation/devicetree/bindings/usb/pxa-usb.txt
Normal file
@ -0,0 +1,31 @@
|
||||
PXA USB controllers
|
||||
|
||||
OHCI
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "marvell,pxa-ohci" for USB controllers
|
||||
used in host mode.
|
||||
|
||||
Optional properties:
|
||||
- "marvell,enable-port1", "marvell,enable-port2", "marvell,enable-port3"
|
||||
If present, enables the appropriate USB port of the controller.
|
||||
- "marvell,port-mode" selects the mode of the ports:
|
||||
1 = PMM_NPS_MODE
|
||||
2 = PMM_GLOBAL_MODE
|
||||
3 = PMM_PERPORT_MODE
|
||||
- "marvell,power-sense-low" - power sense pin is low-active.
|
||||
- "marvell,power-control-low" - power control pin is low-active.
|
||||
- "marvell,no-oc-protection" - disable over-current protection.
|
||||
- "marvell,oc-mode-perport" - enable per-port over-current protection.
|
||||
- "marvell,power_on_delay" Power On to Power Good time - in ms.
|
||||
|
||||
Example:
|
||||
|
||||
usb0: ohci@4c000000 {
|
||||
compatible = "marvell,pxa-ohci", "usb-ohci";
|
||||
reg = <0x4c000000 0x100000>;
|
||||
interrupts = <18>;
|
||||
marvell,enable-port1;
|
||||
marvell,port-mode = <2>; /* PMM_GLOBAL_MODE */
|
||||
};
|
||||
|
40
Documentation/devicetree/bindings/usb/twlxxxx-usb.txt
Normal file
40
Documentation/devicetree/bindings/usb/twlxxxx-usb.txt
Normal file
@ -0,0 +1,40 @@
|
||||
USB COMPARATOR OF TWL CHIPS
|
||||
|
||||
TWL6030 USB COMPARATOR
|
||||
- compatible : Should be "ti,twl6030-usb"
|
||||
- interrupts : Two interrupt numbers to the cpu should be specified. First
|
||||
interrupt number is the otg interrupt number that raises ID interrupts when
|
||||
the controller has to act as host and the second interrupt number is the
|
||||
usb interrupt number that raises VBUS interrupts when the controller has to
|
||||
act as device
|
||||
- usb-supply : phandle to the regulator device tree node. It should be vusb
|
||||
if it is twl6030 or ldousb if it is twl6025 subclass.
|
||||
|
||||
twl6030-usb {
|
||||
compatible = "ti,twl6030-usb";
|
||||
interrupts = < 4 10 >;
|
||||
};
|
||||
|
||||
Board specific device node entry
|
||||
&twl6030-usb {
|
||||
usb-supply = <&vusb>;
|
||||
};
|
||||
|
||||
TWL4030 USB PHY AND COMPARATOR
|
||||
- compatible : Should be "ti,twl4030-usb"
|
||||
- interrupts : The interrupt numbers to the cpu should be specified. First
|
||||
interrupt number is the otg interrupt number that raises ID interrupts
|
||||
and VBUS interrupts. The second interrupt number is optional.
|
||||
- <supply-name>-supply : phandle to the regulator device tree node.
|
||||
<supply-name> should be vusb1v5, vusb1v8 and vusb3v1
|
||||
- usb_mode : The mode used by the phy to connect to the controller. "1"
|
||||
specifies "ULPI" mode and "2" specifies "CEA2011_3PIN" mode.
|
||||
|
||||
twl4030-usb {
|
||||
compatible = "ti,twl4030-usb";
|
||||
interrupts = < 10 4 >;
|
||||
usb1v5-supply = <&vusb1v5>;
|
||||
usb1v8-supply = <&vusb1v8>;
|
||||
usb3v1-supply = <&vusb3v1>;
|
||||
usb_mode = <1>;
|
||||
};
|
17
Documentation/devicetree/bindings/usb/usb-phy.txt
Normal file
17
Documentation/devicetree/bindings/usb/usb-phy.txt
Normal file
@ -0,0 +1,17 @@
|
||||
USB PHY
|
||||
|
||||
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
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
compatible = "ti,omap-usb2";
|
||||
reg = <0x4a0ad080 0x58>,
|
||||
<0x4a002300 0x4>;
|
||||
};
|
14
Documentation/devicetree/bindings/usb/usbmisc-imx.txt
Normal file
14
Documentation/devicetree/bindings/usb/usbmisc-imx.txt
Normal file
@ -0,0 +1,14 @@
|
||||
* Freescale i.MX non-core registers
|
||||
|
||||
Required properties:
|
||||
- #index-cells: Cells used to descibe usb controller index. Should be <1>
|
||||
- compatible: Should be one of below:
|
||||
"fsl,imx6q-usbmisc" for imx6q
|
||||
- reg: Should contain registers location and length
|
||||
|
||||
Examples:
|
||||
usbmisc@02184800 {
|
||||
#index-cells = <1>;
|
||||
compatible = "fsl,imx6q-usbmisc";
|
||||
reg = <0x02184800 0x200>;
|
||||
};
|
12
Documentation/devicetree/bindings/usb/vt8500-ehci.txt
Normal file
12
Documentation/devicetree/bindings/usb/vt8500-ehci.txt
Normal file
@ -0,0 +1,12 @@
|
||||
VIA VT8500 and Wondermedia WM8xxx SoC USB controllers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci".
|
||||
- reg: Address range of the ehci registers. size should be 0x200
|
||||
- interrupts: Should contain the ehci interrupt.
|
||||
|
||||
usb: ehci@D8007100 {
|
||||
compatible = "wm,prizm-ehci", "usb-ehci";
|
||||
reg = <0xD8007100 0x200>;
|
||||
interrupts = <1>;
|
||||
};
|
@ -463,17 +463,6 @@ Who: Bjorn Helgaas <bhelgaas@google.com>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: Low Performance USB Block driver ("CONFIG_BLK_DEV_UB")
|
||||
When: 3.6
|
||||
Why: This driver provides support for USB storage devices like "USB
|
||||
sticks". As of now, it is deactivated in Debian, Fedora and
|
||||
Ubuntu. All current users can switch over to usb-storage
|
||||
(CONFIG_USB_STORAGE) which only drawback is the additional SCSI
|
||||
stack.
|
||||
Who: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: get_robust_list syscall
|
||||
When: 2013
|
||||
Why: There appear to be no production users of the get_robust_list syscall,
|
||||
|
@ -155,6 +155,9 @@ If the kernel gets fooled in this way, it's almost certain to cause
|
||||
data corruption and to crash your system. You'll have no one to blame
|
||||
but yourself.
|
||||
|
||||
For those devices with avoid_reset_quirk attribute being set, persist
|
||||
maybe fail because they may morph after reset.
|
||||
|
||||
YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK!
|
||||
|
||||
That having been said, most of the time there shouldn't be any trouble
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/spi/ads7846.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/nop-usb-xceiv.h>
|
||||
#include <linux/smsc911x.h>
|
||||
|
||||
#include <linux/wl12xx.h>
|
||||
|
@ -5890,6 +5890,12 @@ static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
|
||||
.pa_end = 0x4a0ab003,
|
||||
.flags = ADDR_TYPE_RT
|
||||
},
|
||||
{
|
||||
/* XXX: Remove this once control module driver is in place */
|
||||
.pa_start = 0x4a00233c,
|
||||
.pa_end = 0x4a00233f,
|
||||
.flags = ADDR_TYPE_RT
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -31,144 +31,6 @@
|
||||
#include <plat/usb.h>
|
||||
#include "control.h"
|
||||
|
||||
/* OMAP control module register for UTMI PHY */
|
||||
#define CONTROL_DEV_CONF 0x300
|
||||
#define PHY_PD 0x1
|
||||
|
||||
#define USBOTGHS_CONTROL 0x33c
|
||||
#define AVALID BIT(0)
|
||||
#define BVALID BIT(1)
|
||||
#define VBUSVALID BIT(2)
|
||||
#define SESSEND BIT(3)
|
||||
#define IDDIG BIT(4)
|
||||
|
||||
static struct clk *phyclk, *clk48m, *clk32k;
|
||||
static void __iomem *ctrl_base;
|
||||
static int usbotghs_control;
|
||||
|
||||
int omap4430_phy_init(struct device *dev)
|
||||
{
|
||||
ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K);
|
||||
if (!ctrl_base) {
|
||||
pr_err("control module ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Power down the phy */
|
||||
__raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
|
||||
|
||||
if (!dev) {
|
||||
iounmap(ctrl_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
phyclk = clk_get(dev, "ocp2scp_usb_phy_ick");
|
||||
if (IS_ERR(phyclk)) {
|
||||
dev_err(dev, "cannot clk_get ocp2scp_usb_phy_ick\n");
|
||||
iounmap(ctrl_base);
|
||||
return PTR_ERR(phyclk);
|
||||
}
|
||||
|
||||
clk48m = clk_get(dev, "ocp2scp_usb_phy_phy_48m");
|
||||
if (IS_ERR(clk48m)) {
|
||||
dev_err(dev, "cannot clk_get ocp2scp_usb_phy_phy_48m\n");
|
||||
clk_put(phyclk);
|
||||
iounmap(ctrl_base);
|
||||
return PTR_ERR(clk48m);
|
||||
}
|
||||
|
||||
clk32k = clk_get(dev, "usb_phy_cm_clk32k");
|
||||
if (IS_ERR(clk32k)) {
|
||||
dev_err(dev, "cannot clk_get usb_phy_cm_clk32k\n");
|
||||
clk_put(phyclk);
|
||||
clk_put(clk48m);
|
||||
iounmap(ctrl_base);
|
||||
return PTR_ERR(clk32k);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap4430_phy_set_clk(struct device *dev, int on)
|
||||
{
|
||||
static int state;
|
||||
|
||||
if (on && !state) {
|
||||
/* Enable the phy clocks */
|
||||
clk_enable(phyclk);
|
||||
clk_enable(clk48m);
|
||||
clk_enable(clk32k);
|
||||
state = 1;
|
||||
} else if (state) {
|
||||
/* Disable the phy clocks */
|
||||
clk_disable(phyclk);
|
||||
clk_disable(clk48m);
|
||||
clk_disable(clk32k);
|
||||
state = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap4430_phy_power(struct device *dev, int ID, int on)
|
||||
{
|
||||
if (on) {
|
||||
if (ID)
|
||||
/* enable VBUS valid, IDDIG groung */
|
||||
__raw_writel(AVALID | VBUSVALID, ctrl_base +
|
||||
USBOTGHS_CONTROL);
|
||||
else
|
||||
/*
|
||||
* Enable VBUS Valid, AValid and IDDIG
|
||||
* high impedance
|
||||
*/
|
||||
__raw_writel(IDDIG | AVALID | VBUSVALID,
|
||||
ctrl_base + USBOTGHS_CONTROL);
|
||||
} else {
|
||||
/* Enable session END and IDIG to high impedance. */
|
||||
__raw_writel(SESSEND | IDDIG, ctrl_base +
|
||||
USBOTGHS_CONTROL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap4430_phy_suspend(struct device *dev, int suspend)
|
||||
{
|
||||
if (suspend) {
|
||||
/* Disable the clocks */
|
||||
omap4430_phy_set_clk(dev, 0);
|
||||
/* Power down the phy */
|
||||
__raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
|
||||
|
||||
/* save the context */
|
||||
usbotghs_control = __raw_readl(ctrl_base + USBOTGHS_CONTROL);
|
||||
} else {
|
||||
/* Enable the internel phy clcoks */
|
||||
omap4430_phy_set_clk(dev, 1);
|
||||
/* power on the phy */
|
||||
if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) {
|
||||
__raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF);
|
||||
mdelay(200);
|
||||
}
|
||||
|
||||
/* restore the context */
|
||||
__raw_writel(usbotghs_control, ctrl_base + USBOTGHS_CONTROL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap4430_phy_exit(struct device *dev)
|
||||
{
|
||||
if (ctrl_base)
|
||||
iounmap(ctrl_base);
|
||||
if (phyclk)
|
||||
clk_put(phyclk);
|
||||
if (clk48m)
|
||||
clk_put(clk48m);
|
||||
if (clk32k)
|
||||
clk_put(clk32k);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void am35x_musb_reset(void)
|
||||
{
|
||||
u32 regval;
|
||||
|
@ -251,11 +251,6 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP4)
|
||||
static struct twl4030_usb_data omap4_usb_pdata = {
|
||||
.phy_init = omap4430_phy_init,
|
||||
.phy_exit = omap4430_phy_exit,
|
||||
.phy_power = omap4430_phy_power,
|
||||
.phy_set_clock = omap4430_phy_set_clk,
|
||||
.phy_suspend = omap4430_phy_suspend,
|
||||
};
|
||||
|
||||
static struct regulator_init_data omap4_vdac_idata = {
|
||||
|
@ -117,7 +117,4 @@ void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
|
||||
dev->dma_mask = &musb_dmamask;
|
||||
dev->coherent_dma_mask = musb_dmamask;
|
||||
put_device(dev);
|
||||
|
||||
if (cpu_is_omap44xx())
|
||||
omap4430_phy_init(dev);
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o
|
||||
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
|
||||
obj-$(CONFIG_TEGRA_PCI) += pcie.o
|
||||
obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
|
||||
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-dt-tegra20.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include <mach/irqs.h>
|
||||
#include <mach/iomap.h>
|
||||
#include <mach/dma.h>
|
||||
#include <mach/usb_phy.h>
|
||||
#include <linux/usb/tegra_usb_phy.h>
|
||||
|
||||
#include "gpio-names.h"
|
||||
#include "devices.h"
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/tegra_usb.h>
|
||||
|
||||
#include <mach/usb_phy.h>
|
||||
#include <linux/usb/tegra_usb_phy.h>
|
||||
|
||||
extern struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config;
|
||||
|
||||
|
@ -33,6 +33,7 @@ static struct platform_device *devices[] __initdata = {
|
||||
&vt8500_device_uart0,
|
||||
&vt8500_device_lcdc,
|
||||
&vt8500_device_ehci,
|
||||
&vt8500_device_uhci,
|
||||
&vt8500_device_ge_rops,
|
||||
&vt8500_device_pwm,
|
||||
&vt8500_device_pwmbl,
|
||||
|
@ -48,6 +48,11 @@ void __init vt8500_set_resources(void)
|
||||
tmp[1] = wmt_irq_res(IRQ_EHCI);
|
||||
wmt_res_add(&vt8500_device_ehci, tmp, 2);
|
||||
|
||||
/* vt8500 uses a single IRQ for both EHCI and UHCI controllers */
|
||||
tmp[0] = wmt_mmio_res(VT8500_UHCI_BASE, SZ_512);
|
||||
tmp[1] = wmt_irq_res(IRQ_EHCI);
|
||||
wmt_res_add(&vt8500_device_uhci, tmp, 2);
|
||||
|
||||
tmp[0] = wmt_mmio_res(VT8500_GEGEA_BASE, SZ_256);
|
||||
wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
|
||||
|
||||
|
@ -55,6 +55,10 @@ void __init wm8505_set_resources(void)
|
||||
tmp[1] = wmt_irq_res(IRQ_EHCI);
|
||||
wmt_res_add(&vt8500_device_ehci, tmp, 2);
|
||||
|
||||
tmp[0] = wmt_mmio_res(WM8505_UHCI_BASE, SZ_512);
|
||||
tmp[1] = wmt_irq_res(IRQ_UHCI);
|
||||
wmt_res_add(&vt8500_device_uhci, tmp, 2);
|
||||
|
||||
tmp[0] = wmt_mmio_res(WM8505_GEGEA_BASE, SZ_256);
|
||||
wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
|
||||
|
||||
|
@ -204,6 +204,17 @@ struct platform_device vt8500_device_ehci = {
|
||||
},
|
||||
};
|
||||
|
||||
static u64 uhci_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
struct platform_device vt8500_device_uhci = {
|
||||
.name = "platform-uhci",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &uhci_dma_mask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
},
|
||||
};
|
||||
|
||||
struct platform_device vt8500_device_ge_rops = {
|
||||
.name = "wmt_ge_rops",
|
||||
.id = -1,
|
||||
|
@ -81,6 +81,7 @@ extern struct platform_device vt8500_device_uart5;
|
||||
extern struct platform_device vt8500_device_lcdc;
|
||||
extern struct platform_device vt8500_device_wm8505_fb;
|
||||
extern struct platform_device vt8500_device_ehci;
|
||||
extern struct platform_device vt8500_device_uhci;
|
||||
extern struct platform_device vt8500_device_ge_rops;
|
||||
extern struct platform_device vt8500_device_pwm;
|
||||
extern struct platform_device vt8500_device_pwmbl;
|
||||
|
@ -32,6 +32,7 @@ static void __iomem *pmc_hiber;
|
||||
static struct platform_device *devices[] __initdata = {
|
||||
&vt8500_device_uart0,
|
||||
&vt8500_device_ehci,
|
||||
&vt8500_device_uhci,
|
||||
&vt8500_device_wm8505_fb,
|
||||
&vt8500_device_ge_rops,
|
||||
&vt8500_device_pwm,
|
||||
|
@ -353,18 +353,6 @@ config BLK_DEV_SX8
|
||||
|
||||
Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
|
||||
|
||||
config BLK_DEV_UB
|
||||
tristate "Low Performance USB Block driver (deprecated)"
|
||||
depends on USB
|
||||
help
|
||||
This driver supports certain USB attached storage devices
|
||||
such as flash keys.
|
||||
|
||||
If you enable this driver, it is recommended to avoid conflicts
|
||||
with usb-storage by enabling USB_LIBUSUAL.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config BLK_DEV_RAM
|
||||
tristate "RAM block device support"
|
||||
---help---
|
||||
|
@ -33,7 +33,6 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
|
||||
|
||||
obj-$(CONFIG_VIODASD) += viodasd.o
|
||||
obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
|
||||
obj-$(CONFIG_BLK_DEV_UB) += ub.o
|
||||
obj-$(CONFIG_BLK_DEV_HD) += hd.o
|
||||
|
||||
obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o
|
||||
|
2474
drivers/block/ub.c
2474
drivers/block/ub.c
File diff suppressed because it is too large
Load Diff
@ -17,4 +17,9 @@ config USB_G_CCG
|
||||
Configurable Composite Gadget can be compiled "M" only
|
||||
or not at all.
|
||||
|
||||
BIG FAT NOTE: DON'T RELY ON THIS USERINTERFACE HERE! AS PART
|
||||
OF THE REWORK DONE HERE WILL BE A NEW USER INTERFACE WITHOUT ANY
|
||||
COMPATIBILITY TO THIS SYSFS INTERFACE HERE. BE AWARE OF THIS
|
||||
BEFORE SELECTING THIS.
|
||||
|
||||
endif # USB_GADGET
|
||||
|
@ -1,4 +1,2 @@
|
||||
g_ccg-y := ccg.o
|
||||
ccflags-y += -Idrivers/usb/gadget
|
||||
|
||||
obj-$(CONFIG_USB_G_CCG) += g_ccg.o
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include "composite.h"
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
@ -44,19 +44,19 @@
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "../../usb/gadget/usbstring.c"
|
||||
#include "../../usb/gadget/config.c"
|
||||
#include "../../usb/gadget/epautoconf.c"
|
||||
#include "../../usb/gadget/composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
#include "composite.c"
|
||||
|
||||
#include "../../usb/gadget/f_mass_storage.c"
|
||||
#include "../../usb/gadget/u_serial.c"
|
||||
#include "../../usb/gadget/f_acm.c"
|
||||
#include "f_mass_storage.c"
|
||||
#include "u_serial.c"
|
||||
#include "f_acm.c"
|
||||
#define USB_ETH_RNDIS y
|
||||
#include "../../usb/gadget/f_rndis.c"
|
||||
#include "../../usb/gadget/rndis.c"
|
||||
#include "../../usb/gadget/u_ether.c"
|
||||
#include "../../usb/gadget/f_fs.c"
|
||||
#include "f_rndis.c"
|
||||
#include "rndis.c"
|
||||
#include "u_ether.c"
|
||||
#include "f_fs.c"
|
||||
|
||||
MODULE_AUTHOR("Mike Lockwood, Andrzej Pietrasiewicz");
|
||||
MODULE_DESCRIPTION("Configurable Composite USB Gadget");
|
||||
@ -1156,6 +1156,7 @@ static int ccg_usb_unbind(struct usb_composite_dev *cdev)
|
||||
static struct usb_composite_driver ccg_usb_driver = {
|
||||
.name = "configurable_usb",
|
||||
.dev = &device_desc,
|
||||
.bind = ccg_bind,
|
||||
.unbind = ccg_usb_unbind,
|
||||
.needs_serial = true,
|
||||
.iManufacturer = "Linux Foundation",
|
||||
@ -1271,7 +1272,7 @@ static int __init init(void)
|
||||
composite_driver.setup = ccg_setup;
|
||||
composite_driver.disconnect = ccg_disconnect;
|
||||
|
||||
err = usb_composite_probe(&ccg_usb_driver, ccg_bind);
|
||||
err = usb_composite_probe(&ccg_usb_driver);
|
||||
if (err) {
|
||||
class_destroy(ccg_class);
|
||||
kfree(dev);
|
||||
|
1688
drivers/staging/ccg/composite.c
Normal file
1688
drivers/staging/ccg/composite.c
Normal file
File diff suppressed because it is too large
Load Diff
395
drivers/staging/ccg/composite.h
Normal file
395
drivers/staging/ccg/composite.h
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* composite.h -- framework for usb gadgets which are composite devices
|
||||
*
|
||||
* Copyright (C) 2006-2008 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_COMPOSITE_H
|
||||
#define __LINUX_USB_COMPOSITE_H
|
||||
|
||||
/*
|
||||
* This framework is an optional layer on top of the USB Gadget interface,
|
||||
* making it easier to build (a) Composite devices, supporting multiple
|
||||
* functions within any single configuration, and (b) Multi-configuration
|
||||
* devices, also supporting multiple functions but without necessarily
|
||||
* having more than one function per configuration.
|
||||
*
|
||||
* Example: a device with a single configuration supporting both network
|
||||
* link and mass storage functions is a composite device. Those functions
|
||||
* might alternatively be packaged in individual configurations, but in
|
||||
* the composite model the host can use both functions at the same time.
|
||||
*/
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/*
|
||||
* USB function drivers should return USB_GADGET_DELAYED_STATUS if they
|
||||
* wish to delay the data/status stages of the control transfer till they
|
||||
* are ready. The control transfer will then be kept from completing till
|
||||
* all the function drivers that requested for USB_GADGET_DELAYED_STAUS
|
||||
* invoke usb_composite_setup_continue().
|
||||
*/
|
||||
#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
|
||||
|
||||
struct usb_configuration;
|
||||
|
||||
/**
|
||||
* struct usb_function - describes one function of a configuration
|
||||
* @name: For diagnostics, identifies the function.
|
||||
* @strings: tables of strings, keyed by identifiers assigned during bind()
|
||||
* and by language IDs provided in control requests
|
||||
* @descriptors: Table of full (or low) speed descriptors, using interface and
|
||||
* string identifiers assigned during @bind(). If this pointer is null,
|
||||
* the function will not be available at full speed (or at low speed).
|
||||
* @hs_descriptors: Table of high speed descriptors, using interface and
|
||||
* string identifiers assigned during @bind(). If this pointer is null,
|
||||
* the function will not be available at high speed.
|
||||
* @ss_descriptors: Table of super speed descriptors, using interface and
|
||||
* string identifiers assigned during @bind(). If this
|
||||
* pointer is null after initiation, the function will not
|
||||
* be available at super speed.
|
||||
* @config: assigned when @usb_add_function() is called; this is the
|
||||
* configuration with which this function is associated.
|
||||
* @bind: Before the gadget can register, all of its functions bind() to the
|
||||
* available resources including string and interface identifiers used
|
||||
* in interface or class descriptors; endpoints; I/O buffers; and so on.
|
||||
* @unbind: Reverses @bind; called as a side effect of unregistering the
|
||||
* driver which added this function.
|
||||
* @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
|
||||
* initialize usb_ep.driver data at this time (when it is used).
|
||||
* Note that setting an interface to its current altsetting resets
|
||||
* interface state, and that all interfaces have a disabled state.
|
||||
* @get_alt: Returns the active altsetting. If this is not provided,
|
||||
* then only altsetting zero is supported.
|
||||
* @disable: (REQUIRED) Indicates the function should be disabled. Reasons
|
||||
* include host resetting or reconfiguring the gadget, and disconnection.
|
||||
* @setup: Used for interface-specific control requests.
|
||||
* @suspend: Notifies functions when the host stops sending USB traffic.
|
||||
* @resume: Notifies functions when the host restarts USB traffic.
|
||||
* @get_status: Returns function status as a reply to
|
||||
* GetStatus() request when the recepient is Interface.
|
||||
* @func_suspend: callback to be called when
|
||||
* SetFeature(FUNCTION_SUSPEND) is reseived
|
||||
*
|
||||
* A single USB function uses one or more interfaces, and should in most
|
||||
* cases support operation at both full and high speeds. Each function is
|
||||
* associated by @usb_add_function() with a one configuration; that function
|
||||
* causes @bind() to be called so resources can be allocated as part of
|
||||
* setting up a gadget driver. Those resources include endpoints, which
|
||||
* should be allocated using @usb_ep_autoconfig().
|
||||
*
|
||||
* To support dual speed operation, a function driver provides descriptors
|
||||
* for both high and full speed operation. Except in rare cases that don't
|
||||
* involve bulk endpoints, each speed needs different endpoint descriptors.
|
||||
*
|
||||
* Function drivers choose their own strategies for managing instance data.
|
||||
* The simplest strategy just declares it "static', which means the function
|
||||
* can only be activated once. If the function needs to be exposed in more
|
||||
* than one configuration at a given speed, it needs to support multiple
|
||||
* usb_function structures (one for each configuration).
|
||||
*
|
||||
* A more complex strategy might encapsulate a @usb_function structure inside
|
||||
* a driver-specific instance structure to allows multiple activations. An
|
||||
* example of multiple activations might be a CDC ACM function that supports
|
||||
* two or more distinct instances within the same configuration, providing
|
||||
* several independent logical data links to a USB host.
|
||||
*/
|
||||
struct usb_function {
|
||||
const char *name;
|
||||
struct usb_gadget_strings **strings;
|
||||
struct usb_descriptor_header **descriptors;
|
||||
struct usb_descriptor_header **hs_descriptors;
|
||||
struct usb_descriptor_header **ss_descriptors;
|
||||
|
||||
struct usb_configuration *config;
|
||||
|
||||
/* REVISIT: bind() functions can be marked __init, which
|
||||
* makes trouble for section mismatch analysis. See if
|
||||
* we can't restructure things to avoid mismatching.
|
||||
* Related: unbind() may kfree() but bind() won't...
|
||||
*/
|
||||
|
||||
/* configuration management: bind/unbind */
|
||||
int (*bind)(struct usb_configuration *,
|
||||
struct usb_function *);
|
||||
void (*unbind)(struct usb_configuration *,
|
||||
struct usb_function *);
|
||||
|
||||
/* runtime state management */
|
||||
int (*set_alt)(struct usb_function *,
|
||||
unsigned interface, unsigned alt);
|
||||
int (*get_alt)(struct usb_function *,
|
||||
unsigned interface);
|
||||
void (*disable)(struct usb_function *);
|
||||
int (*setup)(struct usb_function *,
|
||||
const struct usb_ctrlrequest *);
|
||||
void (*suspend)(struct usb_function *);
|
||||
void (*resume)(struct usb_function *);
|
||||
|
||||
/* USB 3.0 additions */
|
||||
int (*get_status)(struct usb_function *);
|
||||
int (*func_suspend)(struct usb_function *,
|
||||
u8 suspend_opt);
|
||||
/* private: */
|
||||
/* internals */
|
||||
struct list_head list;
|
||||
DECLARE_BITMAP(endpoints, 32);
|
||||
};
|
||||
|
||||
int usb_add_function(struct usb_configuration *, struct usb_function *);
|
||||
|
||||
int usb_function_deactivate(struct usb_function *);
|
||||
int usb_function_activate(struct usb_function *);
|
||||
|
||||
int usb_interface_id(struct usb_configuration *, struct usb_function *);
|
||||
|
||||
int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
|
||||
struct usb_ep *_ep);
|
||||
|
||||
#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
|
||||
|
||||
/**
|
||||
* struct usb_configuration - represents one gadget configuration
|
||||
* @label: For diagnostics, describes the configuration.
|
||||
* @strings: Tables of strings, keyed by identifiers assigned during @bind()
|
||||
* and by language IDs provided in control requests.
|
||||
* @descriptors: Table of descriptors preceding all function descriptors.
|
||||
* Examples include OTG and vendor-specific descriptors.
|
||||
* @unbind: Reverses @bind; called as a side effect of unregistering the
|
||||
* driver which added this configuration.
|
||||
* @setup: Used to delegate control requests that aren't handled by standard
|
||||
* device infrastructure or directed at a specific interface.
|
||||
* @bConfigurationValue: Copied into configuration descriptor.
|
||||
* @iConfiguration: Copied into configuration descriptor.
|
||||
* @bmAttributes: Copied into configuration descriptor.
|
||||
* @bMaxPower: Copied into configuration descriptor.
|
||||
* @cdev: assigned by @usb_add_config() before calling @bind(); this is
|
||||
* the device associated with this configuration.
|
||||
*
|
||||
* Configurations are building blocks for gadget drivers structured around
|
||||
* function drivers. Simple USB gadgets require only one function and one
|
||||
* configuration, and handle dual-speed hardware by always providing the same
|
||||
* functionality. Slightly more complex gadgets may have more than one
|
||||
* single-function configuration at a given speed; or have configurations
|
||||
* that only work at one speed.
|
||||
*
|
||||
* Composite devices are, by definition, ones with configurations which
|
||||
* include more than one function.
|
||||
*
|
||||
* The lifecycle of a usb_configuration includes allocation, initialization
|
||||
* of the fields described above, and calling @usb_add_config() to set up
|
||||
* internal data and bind it to a specific device. The configuration's
|
||||
* @bind() method is then used to initialize all the functions and then
|
||||
* call @usb_add_function() for them.
|
||||
*
|
||||
* Those functions would normally be independent of each other, but that's
|
||||
* not mandatory. CDC WMC devices are an example where functions often
|
||||
* depend on other functions, with some functions subsidiary to others.
|
||||
* Such interdependency may be managed in any way, so long as all of the
|
||||
* descriptors complete by the time the composite driver returns from
|
||||
* its bind() routine.
|
||||
*/
|
||||
struct usb_configuration {
|
||||
const char *label;
|
||||
struct usb_gadget_strings **strings;
|
||||
const struct usb_descriptor_header **descriptors;
|
||||
|
||||
/* REVISIT: bind() functions can be marked __init, which
|
||||
* makes trouble for section mismatch analysis. See if
|
||||
* we can't restructure things to avoid mismatching...
|
||||
*/
|
||||
|
||||
/* configuration management: unbind/setup */
|
||||
void (*unbind)(struct usb_configuration *);
|
||||
int (*setup)(struct usb_configuration *,
|
||||
const struct usb_ctrlrequest *);
|
||||
|
||||
/* fields in the config descriptor */
|
||||
u8 bConfigurationValue;
|
||||
u8 iConfiguration;
|
||||
u8 bmAttributes;
|
||||
u8 bMaxPower;
|
||||
|
||||
struct usb_composite_dev *cdev;
|
||||
|
||||
/* private: */
|
||||
/* internals */
|
||||
struct list_head list;
|
||||
struct list_head functions;
|
||||
u8 next_interface_id;
|
||||
unsigned superspeed:1;
|
||||
unsigned highspeed:1;
|
||||
unsigned fullspeed:1;
|
||||
struct usb_function *interface[MAX_CONFIG_INTERFACES];
|
||||
};
|
||||
|
||||
int usb_add_config(struct usb_composite_dev *,
|
||||
struct usb_configuration *,
|
||||
int (*)(struct usb_configuration *));
|
||||
|
||||
void usb_remove_config(struct usb_composite_dev *,
|
||||
struct usb_configuration *);
|
||||
|
||||
/**
|
||||
* struct usb_composite_driver - groups configurations into a gadget
|
||||
* @name: For diagnostics, identifies the driver.
|
||||
* @iProduct: Used as iProduct override if @dev->iProduct is not set.
|
||||
* If NULL value of @name is taken.
|
||||
* @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is
|
||||
* not set. If NULL a default "<system> <release> with <udc>" value
|
||||
* will be used.
|
||||
* @iSerialNumber: Used as iSerialNumber override if @dev->iSerialNumber is
|
||||
* not set.
|
||||
* @dev: Template descriptor for the device, including default device
|
||||
* identifiers.
|
||||
* @strings: tables of strings, keyed by identifiers assigned during @bind
|
||||
* and language IDs provided in control requests
|
||||
* @max_speed: Highest speed the driver supports.
|
||||
* @needs_serial: set to 1 if the gadget needs userspace to provide
|
||||
* a serial number. If one is not provided, warning will be printed.
|
||||
* @bind: (REQUIRED) Used to allocate resources that are shared across the
|
||||
* whole device, such as string IDs, and add its configurations using
|
||||
* @usb_add_config(). This may fail by returning a negative errno
|
||||
* value; it should return zero on successful initialization.
|
||||
* @unbind: Reverses @bind; called as a side effect of unregistering
|
||||
* this driver.
|
||||
* @disconnect: optional driver disconnect method
|
||||
* @suspend: Notifies when the host stops sending USB traffic,
|
||||
* after function notifications
|
||||
* @resume: Notifies configuration when the host restarts USB traffic,
|
||||
* before function notifications
|
||||
*
|
||||
* Devices default to reporting self powered operation. Devices which rely
|
||||
* on bus powered operation should report this in their @bind method.
|
||||
*
|
||||
* Before returning from @bind, various fields in the template descriptor
|
||||
* may be overridden. These include the idVendor/idProduct/bcdDevice values
|
||||
* normally to bind the appropriate host side driver, and the three strings
|
||||
* (iManufacturer, iProduct, iSerialNumber) normally used to provide user
|
||||
* meaningful device identifiers. (The strings will not be defined unless
|
||||
* they are defined in @dev and @strings.) The correct ep0 maxpacket size
|
||||
* is also reported, as defined by the underlying controller driver.
|
||||
*/
|
||||
struct usb_composite_driver {
|
||||
const char *name;
|
||||
const char *iProduct;
|
||||
const char *iManufacturer;
|
||||
const char *iSerialNumber;
|
||||
const struct usb_device_descriptor *dev;
|
||||
struct usb_gadget_strings **strings;
|
||||
enum usb_device_speed max_speed;
|
||||
unsigned needs_serial:1;
|
||||
|
||||
int (*bind)(struct usb_composite_dev *cdev);
|
||||
int (*unbind)(struct usb_composite_dev *);
|
||||
|
||||
void (*disconnect)(struct usb_composite_dev *);
|
||||
|
||||
/* global suspend hooks */
|
||||
void (*suspend)(struct usb_composite_dev *);
|
||||
void (*resume)(struct usb_composite_dev *);
|
||||
};
|
||||
|
||||
extern int usb_composite_probe(struct usb_composite_driver *driver);
|
||||
extern void usb_composite_unregister(struct usb_composite_driver *driver);
|
||||
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
|
||||
|
||||
|
||||
/**
|
||||
* struct usb_composite_device - represents one composite usb gadget
|
||||
* @gadget: read-only, abstracts the gadget's usb peripheral controller
|
||||
* @req: used for control responses; buffer is pre-allocated
|
||||
* @bufsiz: size of buffer pre-allocated in @req
|
||||
* @config: the currently active configuration
|
||||
*
|
||||
* One of these devices is allocated and initialized before the
|
||||
* associated device driver's bind() is called.
|
||||
*
|
||||
* OPEN ISSUE: it appears that some WUSB devices will need to be
|
||||
* built by combining a normal (wired) gadget with a wireless one.
|
||||
* This revision of the gadget framework should probably try to make
|
||||
* sure doing that won't hurt too much.
|
||||
*
|
||||
* One notion for how to handle Wireless USB devices involves:
|
||||
* (a) a second gadget here, discovery mechanism TBD, but likely
|
||||
* needing separate "register/unregister WUSB gadget" calls;
|
||||
* (b) updates to usb_gadget to include flags "is it wireless",
|
||||
* "is it wired", plus (presumably in a wrapper structure)
|
||||
* bandgroup and PHY info;
|
||||
* (c) presumably a wireless_ep wrapping a usb_ep, and reporting
|
||||
* wireless-specific parameters like maxburst and maxsequence;
|
||||
* (d) configurations that are specific to wireless links;
|
||||
* (e) function drivers that understand wireless configs and will
|
||||
* support wireless for (additional) function instances;
|
||||
* (f) a function to support association setup (like CBAF), not
|
||||
* necessarily requiring a wireless adapter;
|
||||
* (g) composite device setup that can create one or more wireless
|
||||
* configs, including appropriate association setup support;
|
||||
* (h) more, TBD.
|
||||
*/
|
||||
struct usb_composite_dev {
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_request *req;
|
||||
unsigned bufsiz;
|
||||
|
||||
struct usb_configuration *config;
|
||||
|
||||
/* private: */
|
||||
/* internals */
|
||||
unsigned int suspended:1;
|
||||
struct usb_device_descriptor desc;
|
||||
struct list_head configs;
|
||||
struct usb_composite_driver *driver;
|
||||
u8 next_string_id;
|
||||
u8 manufacturer_override;
|
||||
u8 product_override;
|
||||
u8 serial_override;
|
||||
|
||||
/* the gadget driver won't enable the data pullup
|
||||
* while the deactivation count is nonzero.
|
||||
*/
|
||||
unsigned deactivations;
|
||||
|
||||
/* the composite driver won't complete the control transfer's
|
||||
* data/status stages till delayed_status is zero.
|
||||
*/
|
||||
int delayed_status;
|
||||
|
||||
/* protects deactivations and delayed_status counts*/
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
extern int usb_string_id(struct usb_composite_dev *c);
|
||||
extern int usb_string_ids_tab(struct usb_composite_dev *c,
|
||||
struct usb_string *str);
|
||||
extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
|
||||
|
||||
|
||||
/* messaging utils */
|
||||
#define DBG(d, fmt, args...) \
|
||||
dev_dbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define VDBG(d, fmt, args...) \
|
||||
dev_vdbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define ERROR(d, fmt, args...) \
|
||||
dev_err(&(d)->gadget->dev , fmt , ## args)
|
||||
#define WARNING(d, fmt, args...) \
|
||||
dev_warn(&(d)->gadget->dev , fmt , ## args)
|
||||
#define INFO(d, fmt, args...) \
|
||||
dev_info(&(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
#endif /* __LINUX_USB_COMPOSITE_H */
|
158
drivers/staging/ccg/config.c
Normal file
158
drivers/staging/ccg/config.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* usb/gadget/config.c -- simplify building config descriptors
|
||||
*
|
||||
* Copyright (C) 2003 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
|
||||
/**
|
||||
* usb_descriptor_fillbuf - fill buffer with descriptors
|
||||
* @buf: Buffer to be filled
|
||||
* @buflen: Size of buf
|
||||
* @src: Array of descriptor pointers, terminated by null pointer.
|
||||
*
|
||||
* Copies descriptors into the buffer, returning the length or a
|
||||
* negative error code if they can't all be copied. Useful when
|
||||
* assembling descriptors for an associated set of interfaces used
|
||||
* as part of configuring a composite device; or in other cases where
|
||||
* sets of descriptors need to be marshaled.
|
||||
*/
|
||||
int
|
||||
usb_descriptor_fillbuf(void *buf, unsigned buflen,
|
||||
const struct usb_descriptor_header **src)
|
||||
{
|
||||
u8 *dest = buf;
|
||||
|
||||
if (!src)
|
||||
return -EINVAL;
|
||||
|
||||
/* fill buffer from src[] until null descriptor ptr */
|
||||
for (; NULL != *src; src++) {
|
||||
unsigned len = (*src)->bLength;
|
||||
|
||||
if (len > buflen)
|
||||
return -EINVAL;
|
||||
memcpy(dest, *src, len);
|
||||
buflen -= len;
|
||||
dest += len;
|
||||
}
|
||||
return dest - (u8 *)buf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_gadget_config_buf - builts a complete configuration descriptor
|
||||
* @config: Header for the descriptor, including characteristics such
|
||||
* as power requirements and number of interfaces.
|
||||
* @desc: Null-terminated vector of pointers to the descriptors (interface,
|
||||
* endpoint, etc) defining all functions in this device configuration.
|
||||
* @buf: Buffer for the resulting configuration descriptor.
|
||||
* @length: Length of buffer. If this is not big enough to hold the
|
||||
* entire configuration descriptor, an error code will be returned.
|
||||
*
|
||||
* This copies descriptors into the response buffer, building a descriptor
|
||||
* for that configuration. It returns the buffer length or a negative
|
||||
* status code. The config.wTotalLength field is set to match the length
|
||||
* of the result, but other descriptor fields (including power usage and
|
||||
* interface count) must be set by the caller.
|
||||
*
|
||||
* Gadget drivers could use this when constructing a config descriptor
|
||||
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
|
||||
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
|
||||
*/
|
||||
int usb_gadget_config_buf(
|
||||
const struct usb_config_descriptor *config,
|
||||
void *buf,
|
||||
unsigned length,
|
||||
const struct usb_descriptor_header **desc
|
||||
)
|
||||
{
|
||||
struct usb_config_descriptor *cp = buf;
|
||||
int len;
|
||||
|
||||
/* config descriptor first */
|
||||
if (length < USB_DT_CONFIG_SIZE || !desc)
|
||||
return -EINVAL;
|
||||
*cp = *config;
|
||||
|
||||
/* then interface/endpoint/class/vendor/... */
|
||||
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
|
||||
length - USB_DT_CONFIG_SIZE, desc);
|
||||
if (len < 0)
|
||||
return len;
|
||||
len += USB_DT_CONFIG_SIZE;
|
||||
if (len > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
/* patch up the config descriptor */
|
||||
cp->bLength = USB_DT_CONFIG_SIZE;
|
||||
cp->bDescriptorType = USB_DT_CONFIG;
|
||||
cp->wTotalLength = cpu_to_le16(len);
|
||||
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_copy_descriptors - copy a vector of USB descriptors
|
||||
* @src: null-terminated vector to copy
|
||||
* Context: initialization code, which may sleep
|
||||
*
|
||||
* This makes a copy of a vector of USB descriptors. Its primary use
|
||||
* is to support usb_function objects which can have multiple copies,
|
||||
* each needing different descriptors. Functions may have static
|
||||
* tables of descriptors, which are used as templates and customized
|
||||
* with identifiers (for interfaces, strings, endpoints, and more)
|
||||
* as needed by a given function instance.
|
||||
*/
|
||||
struct usb_descriptor_header **
|
||||
usb_copy_descriptors(struct usb_descriptor_header **src)
|
||||
{
|
||||
struct usb_descriptor_header **tmp;
|
||||
unsigned bytes;
|
||||
unsigned n_desc;
|
||||
void *mem;
|
||||
struct usb_descriptor_header **ret;
|
||||
|
||||
/* count descriptors and their sizes; then add vector size */
|
||||
for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
|
||||
bytes += (*tmp)->bLength;
|
||||
bytes += (n_desc + 1) * sizeof(*tmp);
|
||||
|
||||
mem = kmalloc(bytes, GFP_KERNEL);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
|
||||
/* fill in pointers starting at "tmp",
|
||||
* to descriptors copied starting at "mem";
|
||||
* and return "ret"
|
||||
*/
|
||||
tmp = mem;
|
||||
ret = mem;
|
||||
mem += (n_desc + 1) * sizeof(*tmp);
|
||||
while (*src) {
|
||||
memcpy(mem, *src, (*src)->bLength);
|
||||
*tmp = mem;
|
||||
tmp++;
|
||||
mem += (*src)->bLength;
|
||||
src++;
|
||||
}
|
||||
*tmp = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
393
drivers/staging/ccg/epautoconf.c
Normal file
393
drivers/staging/ccg/epautoconf.c
Normal file
@ -0,0 +1,393 @@
|
||||
/*
|
||||
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
|
||||
*
|
||||
* Copyright (C) 2004 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/* we must assign addresses for configurable endpoints (like net2280) */
|
||||
static unsigned epnum;
|
||||
|
||||
// #define MANY_ENDPOINTS
|
||||
#ifdef MANY_ENDPOINTS
|
||||
/* more than 15 configurable endpoints */
|
||||
static unsigned in_epnum;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* This should work with endpoints from controller drivers sharing the
|
||||
* same endpoint naming convention. By example:
|
||||
*
|
||||
* - ep1, ep2, ... address is fixed, not direction or type
|
||||
* - ep1in, ep2out, ... address and direction are fixed, not type
|
||||
* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
|
||||
* - ep1in-bulk, ep2out-iso, ... all three are fixed
|
||||
* - ep-* ... no functionality restrictions
|
||||
*
|
||||
* Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
|
||||
* Less common restrictions are implied by gadget_is_*().
|
||||
*
|
||||
* NOTE: each endpoint is unidirectional, as specified by its USB
|
||||
* descriptor; and isn't specific to a configuration or altsetting.
|
||||
*/
|
||||
static int
|
||||
ep_matches (
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_ep *ep,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
struct usb_ss_ep_comp_descriptor *ep_comp
|
||||
)
|
||||
{
|
||||
u8 type;
|
||||
const char *tmp;
|
||||
u16 max;
|
||||
|
||||
int num_req_streams = 0;
|
||||
|
||||
/* endpoint already claimed? */
|
||||
if (NULL != ep->driver_data)
|
||||
return 0;
|
||||
|
||||
/* only support ep0 for portable CONTROL traffic */
|
||||
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
if (USB_ENDPOINT_XFER_CONTROL == type)
|
||||
return 0;
|
||||
|
||||
/* some other naming convention */
|
||||
if ('e' != ep->name[0])
|
||||
return 0;
|
||||
|
||||
/* type-restriction: "-iso", "-bulk", or "-int".
|
||||
* direction-restriction: "in", "out".
|
||||
*/
|
||||
if ('-' != ep->name[2]) {
|
||||
tmp = strrchr (ep->name, '-');
|
||||
if (tmp) {
|
||||
switch (type) {
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* bulk endpoints handle interrupt transfers,
|
||||
* except the toggle-quirky iso-synch kind
|
||||
*/
|
||||
if ('s' == tmp[2]) // == "-iso"
|
||||
return 0;
|
||||
/* for now, avoid PXA "interrupt-in";
|
||||
* it's documented as never using DATA1.
|
||||
*/
|
||||
if (gadget_is_pxa (gadget)
|
||||
&& 'i' == tmp [1])
|
||||
return 0;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if ('b' != tmp[1]) // != "-bulk"
|
||||
return 0;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if ('s' != tmp[2]) // != "-iso"
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
tmp = ep->name + strlen (ep->name);
|
||||
}
|
||||
|
||||
/* direction-restriction: "..in-..", "out-.." */
|
||||
tmp--;
|
||||
if (!isdigit (*tmp)) {
|
||||
if (desc->bEndpointAddress & USB_DIR_IN) {
|
||||
if ('n' != *tmp)
|
||||
return 0;
|
||||
} else {
|
||||
if ('t' != *tmp)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the number of required streams from the EP companion
|
||||
* descriptor and see if the EP matches it
|
||||
*/
|
||||
if (usb_endpoint_xfer_bulk(desc)) {
|
||||
if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) {
|
||||
num_req_streams = ep_comp->bmAttributes & 0x1f;
|
||||
if (num_req_streams > ep->max_streams)
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If the protocol driver hasn't yet decided on wMaxPacketSize
|
||||
* and wants to know the maximum possible, provide the info.
|
||||
*/
|
||||
if (desc->wMaxPacketSize == 0)
|
||||
desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
|
||||
|
||||
/* endpoint maxpacket size is an input parameter, except for bulk
|
||||
* where it's an output parameter representing the full speed limit.
|
||||
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
|
||||
*/
|
||||
max = 0x7ff & usb_endpoint_maxp(desc);
|
||||
switch (type) {
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* INT: limit 64 bytes full speed, 1024 high/super speed */
|
||||
if (!gadget_is_dualspeed(gadget) && max > 64)
|
||||
return 0;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
|
||||
if (ep->maxpacket < max)
|
||||
return 0;
|
||||
if (!gadget_is_dualspeed(gadget) && max > 1023)
|
||||
return 0;
|
||||
|
||||
/* BOTH: "high bandwidth" works only at high speed */
|
||||
if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
return 0;
|
||||
/* configure your hardware with enough buffering!! */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* MATCH!! */
|
||||
|
||||
/* report address */
|
||||
desc->bEndpointAddress &= USB_DIR_IN;
|
||||
if (isdigit (ep->name [2])) {
|
||||
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
|
||||
desc->bEndpointAddress |= num;
|
||||
#ifdef MANY_ENDPOINTS
|
||||
} else if (desc->bEndpointAddress & USB_DIR_IN) {
|
||||
if (++in_epnum > 15)
|
||||
return 0;
|
||||
desc->bEndpointAddress = USB_DIR_IN | in_epnum;
|
||||
#endif
|
||||
} else {
|
||||
if (++epnum > 15)
|
||||
return 0;
|
||||
desc->bEndpointAddress |= epnum;
|
||||
}
|
||||
|
||||
/* report (variable) full speed bulk maxpacket */
|
||||
if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
|
||||
int size = ep->maxpacket;
|
||||
|
||||
/* min() doesn't work on bitfields with gcc-3.5 */
|
||||
if (size > 64)
|
||||
size = 64;
|
||||
desc->wMaxPacketSize = cpu_to_le16(size);
|
||||
}
|
||||
ep->address = desc->bEndpointAddress;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct usb_ep *
|
||||
find_ep (struct usb_gadget *gadget, const char *name)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
if (0 == strcmp (ep->name, name))
|
||||
return ep;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig_ss() - choose an endpoint matching the ep
|
||||
* descriptor and ep companion descriptor
|
||||
* @gadget: The device to which the endpoint must belong.
|
||||
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
|
||||
* initialized. For periodic transfers, the maximum packet
|
||||
* size must also be initialized. This is modified on
|
||||
* success.
|
||||
* @ep_comp: Endpoint companion descriptor, with the required
|
||||
* number of streams. Will be modified when the chosen EP
|
||||
* supports a different number of streams.
|
||||
*
|
||||
* This routine replaces the usb_ep_autoconfig when needed
|
||||
* superspeed enhancments. If such enhancemnets are required,
|
||||
* the FD should call usb_ep_autoconfig_ss directly and provide
|
||||
* the additional ep_comp parameter.
|
||||
*
|
||||
* By choosing an endpoint to use with the specified descriptor,
|
||||
* this routine simplifies writing gadget drivers that work with
|
||||
* multiple USB device controllers. The endpoint would be
|
||||
* passed later to usb_ep_enable(), along with some descriptor.
|
||||
*
|
||||
* That second descriptor won't always be the same as the first one.
|
||||
* For example, isochronous endpoints can be autoconfigured for high
|
||||
* bandwidth, and then used in several lower bandwidth altsettings.
|
||||
* Also, high and full speed descriptors will be different.
|
||||
*
|
||||
* Be sure to examine and test the results of autoconfiguration
|
||||
* on your hardware. This code may not make the best choices
|
||||
* about how to use the USB controller, and it can't know all
|
||||
* the restrictions that may apply. Some combinations of driver
|
||||
* and hardware won't be able to autoconfigure.
|
||||
*
|
||||
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
|
||||
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
|
||||
* is initialized as if the endpoint were used at full speed and
|
||||
* the bmAttribute field in the ep companion descriptor is
|
||||
* updated with the assigned number of streams if it is
|
||||
* different from the original value. To prevent the endpoint
|
||||
* from being returned by a later autoconfig call, claim it by
|
||||
* assigning ep->driver_data to some non-null value.
|
||||
*
|
||||
* On failure, this returns a null endpoint descriptor.
|
||||
*/
|
||||
struct usb_ep *usb_ep_autoconfig_ss(
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
struct usb_ss_ep_comp_descriptor *ep_comp
|
||||
)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
u8 type;
|
||||
|
||||
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
|
||||
/* First, apply chip-specific "best usage" knowledge.
|
||||
* This might make a good usb_gadget_ops hook ...
|
||||
*/
|
||||
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
|
||||
/* ep-e, ep-f are PIO with only 64 byte fifos */
|
||||
ep = find_ep (gadget, "ep-e");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
ep = find_ep (gadget, "ep-f");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
|
||||
} else if (gadget_is_goku (gadget)) {
|
||||
if (USB_ENDPOINT_XFER_INT == type) {
|
||||
/* single buffering is enough */
|
||||
ep = find_ep(gadget, "ep3-bulk");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
} else if (USB_ENDPOINT_XFER_BULK == type
|
||||
&& (USB_DIR_IN & desc->bEndpointAddress)) {
|
||||
/* DMA may be available */
|
||||
ep = find_ep(gadget, "ep2-bulk");
|
||||
if (ep && ep_matches(gadget, ep, desc,
|
||||
ep_comp))
|
||||
goto found_ep;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLACKFIN
|
||||
} else if (gadget_is_musbhdrc(gadget)) {
|
||||
if ((USB_ENDPOINT_XFER_BULK == type) ||
|
||||
(USB_ENDPOINT_XFER_ISOC == type)) {
|
||||
if (USB_DIR_IN & desc->bEndpointAddress)
|
||||
ep = find_ep (gadget, "ep5in");
|
||||
else
|
||||
ep = find_ep (gadget, "ep6out");
|
||||
} else if (USB_ENDPOINT_XFER_INT == type) {
|
||||
if (USB_DIR_IN & desc->bEndpointAddress)
|
||||
ep = find_ep(gadget, "ep1in");
|
||||
else
|
||||
ep = find_ep(gadget, "ep2out");
|
||||
} else
|
||||
ep = NULL;
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Second, look at endpoints until an unclaimed one looks usable */
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
if (ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
}
|
||||
|
||||
/* Fail */
|
||||
return NULL;
|
||||
found_ep:
|
||||
ep->desc = NULL;
|
||||
ep->comp_desc = NULL;
|
||||
return ep;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig() - choose an endpoint matching the
|
||||
* descriptor
|
||||
* @gadget: The device to which the endpoint must belong.
|
||||
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
|
||||
* initialized. For periodic transfers, the maximum packet
|
||||
* size must also be initialized. This is modified on success.
|
||||
*
|
||||
* By choosing an endpoint to use with the specified descriptor, this
|
||||
* routine simplifies writing gadget drivers that work with multiple
|
||||
* USB device controllers. The endpoint would be passed later to
|
||||
* usb_ep_enable(), along with some descriptor.
|
||||
*
|
||||
* That second descriptor won't always be the same as the first one.
|
||||
* For example, isochronous endpoints can be autoconfigured for high
|
||||
* bandwidth, and then used in several lower bandwidth altsettings.
|
||||
* Also, high and full speed descriptors will be different.
|
||||
*
|
||||
* Be sure to examine and test the results of autoconfiguration on your
|
||||
* hardware. This code may not make the best choices about how to use the
|
||||
* USB controller, and it can't know all the restrictions that may apply.
|
||||
* Some combinations of driver and hardware won't be able to autoconfigure.
|
||||
*
|
||||
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
|
||||
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
|
||||
* is initialized as if the endpoint were used at full speed. To prevent
|
||||
* the endpoint from being returned by a later autoconfig call, claim it
|
||||
* by assigning ep->driver_data to some non-null value.
|
||||
*
|
||||
* On failure, this returns a null endpoint descriptor.
|
||||
*/
|
||||
struct usb_ep *usb_ep_autoconfig(
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc
|
||||
)
|
||||
{
|
||||
return usb_ep_autoconfig_ss(gadget, desc, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
|
||||
* @gadget: device for which autoconfig state will be reset
|
||||
*
|
||||
* Use this for devices where one configuration may need to assign
|
||||
* endpoint resources very differently from the next one. It clears
|
||||
* state such as ep->driver_data and the record of assigned endpoints
|
||||
* used by usb_ep_autoconfig().
|
||||
*/
|
||||
void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
#ifdef MANY_ENDPOINTS
|
||||
in_epnum = 0;
|
||||
#endif
|
||||
epnum = 0;
|
||||
}
|
||||
|
814
drivers/staging/ccg/f_acm.c
Normal file
814
drivers/staging/ccg/f_acm.c
Normal file
@ -0,0 +1,814 @@
|
||||
/*
|
||||
* f_acm.c -- USB CDC serial (ACM) function driver
|
||||
*
|
||||
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
|
||||
* Copyright (C) 2008 by David Brownell
|
||||
* Copyright (C) 2008 by Nokia Corporation
|
||||
* Copyright (C) 2009 by Samsung Electronics
|
||||
* Author: Michal Nazarewicz (mina86@mina86.com)
|
||||
*
|
||||
* This software is distributed under the terms of the GNU General
|
||||
* Public License ("GPL") as published by the Free Software Foundation,
|
||||
* either version 2 of that License or (at your option) any later version.
|
||||
*/
|
||||
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/*
|
||||
* This CDC ACM function support just wraps control functions and
|
||||
* notifications around the generic serial-over-usb code.
|
||||
*
|
||||
* Because CDC ACM is standardized by the USB-IF, many host operating
|
||||
* systems have drivers for it. Accordingly, ACM is the preferred
|
||||
* interop solution for serial-port type connections. The control
|
||||
* models are often not necessary, and in any case don't do much in
|
||||
* this bare-bones implementation.
|
||||
*
|
||||
* Note that even MS-Windows has some support for ACM. However, that
|
||||
* support is somewhat broken because when you use ACM in a composite
|
||||
* device, having multiple interfaces confuses the poor OS. It doesn't
|
||||
* seem to understand CDC Union descriptors. The new "association"
|
||||
* descriptors (roughly equivalent to CDC Unions) may sometimes help.
|
||||
*/
|
||||
|
||||
struct f_acm {
|
||||
struct gserial port;
|
||||
u8 ctrl_id, data_id;
|
||||
u8 port_num;
|
||||
|
||||
u8 pending;
|
||||
|
||||
/* lock is mostly for pending and notify_req ... they get accessed
|
||||
* by callbacks both from tty (open/close/break) under its spinlock,
|
||||
* and notify_req.complete() which can't use that lock.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
struct usb_ep *notify;
|
||||
struct usb_request *notify_req;
|
||||
|
||||
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
|
||||
|
||||
/* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
|
||||
u16 port_handshake_bits;
|
||||
#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
|
||||
#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
|
||||
|
||||
/* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
|
||||
u16 serial_state;
|
||||
#define ACM_CTRL_OVERRUN (1 << 6)
|
||||
#define ACM_CTRL_PARITY (1 << 5)
|
||||
#define ACM_CTRL_FRAMING (1 << 4)
|
||||
#define ACM_CTRL_RI (1 << 3)
|
||||
#define ACM_CTRL_BRK (1 << 2)
|
||||
#define ACM_CTRL_DSR (1 << 1)
|
||||
#define ACM_CTRL_DCD (1 << 0)
|
||||
};
|
||||
|
||||
static inline struct f_acm *func_to_acm(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct f_acm, port.func);
|
||||
}
|
||||
|
||||
static inline struct f_acm *port_to_acm(struct gserial *p)
|
||||
{
|
||||
return container_of(p, struct f_acm, port);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* notification endpoint uses smallish and infrequent fixed-size messages */
|
||||
|
||||
#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
|
||||
#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
|
||||
|
||||
/* interface and class descriptors: */
|
||||
|
||||
static struct usb_interface_assoc_descriptor
|
||||
acm_iad_descriptor = {
|
||||
.bLength = sizeof acm_iad_descriptor,
|
||||
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
||||
|
||||
/* .bFirstInterface = DYNAMIC, */
|
||||
.bInterfaceCount = 2, // control + data
|
||||
.bFunctionClass = USB_CLASS_COMM,
|
||||
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
|
||||
/* .iFunction = DYNAMIC */
|
||||
};
|
||||
|
||||
|
||||
static struct usb_interface_descriptor acm_control_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_COMM,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor acm_data_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_header_desc acm_header_desc = {
|
||||
.bLength = sizeof(acm_header_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||
.bcdCDC = cpu_to_le16(0x0110),
|
||||
};
|
||||
|
||||
static struct usb_cdc_call_mgmt_descriptor
|
||||
acm_call_mgmt_descriptor = {
|
||||
.bLength = sizeof(acm_call_mgmt_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
|
||||
.bmCapabilities = 0,
|
||||
/* .bDataInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_acm_descriptor acm_descriptor = {
|
||||
.bLength = sizeof(acm_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||
.bmCapabilities = USB_CDC_CAP_LINE,
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc acm_union_desc = {
|
||||
.bLength = sizeof(acm_union_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||
/* .bMasterInterface0 = DYNAMIC */
|
||||
/* .bSlaveInterface0 = DYNAMIC */
|
||||
};
|
||||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor acm_fs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
|
||||
.bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_fs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_fs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *acm_fs_function[] = {
|
||||
(struct usb_descriptor_header *) &acm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_control_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_header_desc,
|
||||
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_union_desc,
|
||||
(struct usb_descriptor_header *) &acm_fs_notify_desc,
|
||||
(struct usb_descriptor_header *) &acm_data_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_fs_in_desc,
|
||||
(struct usb_descriptor_header *) &acm_fs_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor acm_hs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
|
||||
.bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_hs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_hs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *acm_hs_function[] = {
|
||||
(struct usb_descriptor_header *) &acm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_control_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_header_desc,
|
||||
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_union_desc,
|
||||
(struct usb_descriptor_header *) &acm_hs_notify_desc,
|
||||
(struct usb_descriptor_header *) &acm_data_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_hs_in_desc,
|
||||
(struct usb_descriptor_header *) &acm_hs_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_ss_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_ss_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
|
||||
.bLength = sizeof acm_ss_bulk_comp_desc,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *acm_ss_function[] = {
|
||||
(struct usb_descriptor_header *) &acm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_control_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_header_desc,
|
||||
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_union_desc,
|
||||
(struct usb_descriptor_header *) &acm_hs_notify_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
|
||||
(struct usb_descriptor_header *) &acm_data_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_in_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_out_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* string descriptors: */
|
||||
|
||||
#define ACM_CTRL_IDX 0
|
||||
#define ACM_DATA_IDX 1
|
||||
#define ACM_IAD_IDX 2
|
||||
|
||||
/* static strings, in UTF-8 */
|
||||
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 = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = acm_string_defs,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *acm_strings[] = {
|
||||
&acm_string_table,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* ACM control ... data handling is delegated to tty library code.
|
||||
* The main task of this function is to activate and deactivate
|
||||
* that code based on device state; track parameters like line
|
||||
* speed, handshake state, and so on; and issue notifications.
|
||||
*/
|
||||
|
||||
static void acm_complete_set_line_coding(struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
struct f_acm *acm = ep->driver_data;
|
||||
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
|
||||
|
||||
if (req->status != 0) {
|
||||
DBG(cdev, "acm ttyGS%d completion, err %d\n",
|
||||
acm->port_num, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* normal completion */
|
||||
if (req->actual != sizeof(acm->port_line_coding)) {
|
||||
DBG(cdev, "acm ttyGS%d short resp, len %d\n",
|
||||
acm->port_num, req->actual);
|
||||
usb_ep_set_halt(ep);
|
||||
} else {
|
||||
struct usb_cdc_line_coding *value = req->buf;
|
||||
|
||||
/* REVISIT: we currently just remember this data.
|
||||
* If we change that, (a) validate it first, then
|
||||
* (b) update whatever hardware needs updating,
|
||||
* (c) worry about locking. This is information on
|
||||
* the order of 9600-8-N-1 ... most of which means
|
||||
* nothing unless we control a real RS232 line.
|
||||
*/
|
||||
acm->port_line_coding = *value;
|
||||
}
|
||||
}
|
||||
|
||||
static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
/* composite driver infrastructure handles everything except
|
||||
* CDC class messages; interface activation uses set_alt().
|
||||
*
|
||||
* Note CDC spec table 4 lists the ACM request profile. It requires
|
||||
* encapsulated command support ... we don't handle any, and respond
|
||||
* to them by stalling. Options include get/set/clear comm features
|
||||
* (not that useful) and SEND_BREAK.
|
||||
*/
|
||||
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
|
||||
|
||||
/* SET_LINE_CODING ... just read and save what the host sends */
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
|
||||
| USB_CDC_REQ_SET_LINE_CODING:
|
||||
if (w_length != sizeof(struct usb_cdc_line_coding)
|
||||
|| w_index != acm->ctrl_id)
|
||||
goto invalid;
|
||||
|
||||
value = w_length;
|
||||
cdev->gadget->ep0->driver_data = acm;
|
||||
req->complete = acm_complete_set_line_coding;
|
||||
break;
|
||||
|
||||
/* GET_LINE_CODING ... return what host sent, or initial value */
|
||||
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
|
||||
| USB_CDC_REQ_GET_LINE_CODING:
|
||||
if (w_index != acm->ctrl_id)
|
||||
goto invalid;
|
||||
|
||||
value = min_t(unsigned, w_length,
|
||||
sizeof(struct usb_cdc_line_coding));
|
||||
memcpy(req->buf, &acm->port_line_coding, value);
|
||||
break;
|
||||
|
||||
/* SET_CONTROL_LINE_STATE ... save what the host sent */
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
|
||||
| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
|
||||
if (w_index != acm->ctrl_id)
|
||||
goto invalid;
|
||||
|
||||
value = 0;
|
||||
|
||||
/* FIXME we should not allow data to flow until the
|
||||
* host sets the ACM_CTRL_DTR bit; and when it clears
|
||||
* that bit, we should return to that no-flow state.
|
||||
*/
|
||||
acm->port_handshake_bits = w_value;
|
||||
break;
|
||||
|
||||
default:
|
||||
invalid:
|
||||
VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
}
|
||||
|
||||
/* respond with data transfer or status phase? */
|
||||
if (value >= 0) {
|
||||
DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
|
||||
acm->port_num, ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
req->zero = 0;
|
||||
req->length = value;
|
||||
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
if (value < 0)
|
||||
ERROR(cdev, "acm response on ttyGS%d, err %d\n",
|
||||
acm->port_num, value);
|
||||
}
|
||||
|
||||
/* device either stalls (value < 0) or reports success */
|
||||
return value;
|
||||
}
|
||||
|
||||
static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
/* we know alt == 0, so this is an activation or a reset */
|
||||
|
||||
if (intf == acm->ctrl_id) {
|
||||
if (acm->notify->driver_data) {
|
||||
VDBG(cdev, "reset acm control interface %d\n", intf);
|
||||
usb_ep_disable(acm->notify);
|
||||
} else {
|
||||
VDBG(cdev, "init acm ctrl interface %d\n", intf);
|
||||
if (config_ep_by_speed(cdev->gadget, f, acm->notify))
|
||||
return -EINVAL;
|
||||
}
|
||||
usb_ep_enable(acm->notify);
|
||||
acm->notify->driver_data = acm;
|
||||
|
||||
} else if (intf == acm->data_id) {
|
||||
if (acm->port.in->driver_data) {
|
||||
DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
|
||||
gserial_disconnect(&acm->port);
|
||||
}
|
||||
if (!acm->port.in->desc || !acm->port.out->desc) {
|
||||
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
|
||||
if (config_ep_by_speed(cdev->gadget, f,
|
||||
acm->port.in) ||
|
||||
config_ep_by_speed(cdev->gadget, f,
|
||||
acm->port.out)) {
|
||||
acm->port.in->desc = NULL;
|
||||
acm->port.out->desc = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
gserial_connect(&acm->port, acm->port_num);
|
||||
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acm_disable(struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
|
||||
gserial_disconnect(&acm->port);
|
||||
usb_ep_disable(acm->notify);
|
||||
acm->notify->driver_data = NULL;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* acm_cdc_notify - issue CDC notification to host
|
||||
* @acm: wraps host to be notified
|
||||
* @type: notification type
|
||||
* @value: Refer to cdc specs, wValue field.
|
||||
* @data: data to be sent
|
||||
* @length: size of data
|
||||
* Context: irqs blocked, acm->lock held, acm_notify_req non-null
|
||||
*
|
||||
* Returns zero on success or a negative errno.
|
||||
*
|
||||
* See section 6.3.5 of the CDC 1.1 specification for information
|
||||
* about the only notification we issue: SerialState change.
|
||||
*/
|
||||
static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
|
||||
void *data, unsigned length)
|
||||
{
|
||||
struct usb_ep *ep = acm->notify;
|
||||
struct usb_request *req;
|
||||
struct usb_cdc_notification *notify;
|
||||
const unsigned len = sizeof(*notify) + length;
|
||||
void *buf;
|
||||
int status;
|
||||
|
||||
req = acm->notify_req;
|
||||
acm->notify_req = NULL;
|
||||
acm->pending = false;
|
||||
|
||||
req->length = len;
|
||||
notify = req->buf;
|
||||
buf = notify + 1;
|
||||
|
||||
notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
|
||||
| USB_RECIP_INTERFACE;
|
||||
notify->bNotificationType = type;
|
||||
notify->wValue = cpu_to_le16(value);
|
||||
notify->wIndex = cpu_to_le16(acm->ctrl_id);
|
||||
notify->wLength = cpu_to_le16(length);
|
||||
memcpy(buf, data, length);
|
||||
|
||||
/* ep_queue() can complete immediately if it fills the fifo... */
|
||||
spin_unlock(&acm->lock);
|
||||
status = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
spin_lock(&acm->lock);
|
||||
|
||||
if (status < 0) {
|
||||
ERROR(acm->port.func.config->cdev,
|
||||
"acm ttyGS%d can't notify serial state, %d\n",
|
||||
acm->port_num, status);
|
||||
acm->notify_req = req;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acm_notify_serial_state(struct f_acm *acm)
|
||||
{
|
||||
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
|
||||
int status;
|
||||
|
||||
spin_lock(&acm->lock);
|
||||
if (acm->notify_req) {
|
||||
DBG(cdev, "acm ttyGS%d serial state %04x\n",
|
||||
acm->port_num, acm->serial_state);
|
||||
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
|
||||
0, &acm->serial_state, sizeof(acm->serial_state));
|
||||
} else {
|
||||
acm->pending = true;
|
||||
status = 0;
|
||||
}
|
||||
spin_unlock(&acm->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_acm *acm = req->context;
|
||||
u8 doit = false;
|
||||
|
||||
/* on this call path we do NOT hold the port spinlock,
|
||||
* which is why ACM needs its own spinlock
|
||||
*/
|
||||
spin_lock(&acm->lock);
|
||||
if (req->status != -ESHUTDOWN)
|
||||
doit = acm->pending;
|
||||
acm->notify_req = req;
|
||||
spin_unlock(&acm->lock);
|
||||
|
||||
if (doit)
|
||||
acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
/* connect == the TTY link is open */
|
||||
|
||||
static void acm_connect(struct gserial *port)
|
||||
{
|
||||
struct f_acm *acm = port_to_acm(port);
|
||||
|
||||
acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
|
||||
acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
static void acm_disconnect(struct gserial *port)
|
||||
{
|
||||
struct f_acm *acm = port_to_acm(port);
|
||||
|
||||
acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
|
||||
acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
static int acm_send_break(struct gserial *port, int duration)
|
||||
{
|
||||
struct f_acm *acm = port_to_acm(port);
|
||||
u16 state;
|
||||
|
||||
state = acm->serial_state;
|
||||
state &= ~ACM_CTRL_BRK;
|
||||
if (duration)
|
||||
state |= ACM_CTRL_BRK;
|
||||
|
||||
acm->serial_state = state;
|
||||
return acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* ACM function driver setup/binding */
|
||||
static int
|
||||
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);
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
acm->ctrl_id = status;
|
||||
acm_iad_descriptor.bFirstInterface = status;
|
||||
|
||||
acm_control_interface_desc.bInterfaceNumber = status;
|
||||
acm_union_desc .bMasterInterface0 = status;
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
acm->data_id = status;
|
||||
|
||||
acm_data_interface_desc.bInterfaceNumber = status;
|
||||
acm_union_desc.bSlaveInterface0 = status;
|
||||
acm_call_mgmt_descriptor.bDataInterface = status;
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
/* allocate instance-specific endpoints */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
acm->port.in = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
acm->port.out = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
acm->notify = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
/* allocate notification */
|
||||
acm->notify_req = gs_alloc_req(ep,
|
||||
sizeof(struct usb_cdc_notification) + 2,
|
||||
GFP_KERNEL);
|
||||
if (!acm->notify_req)
|
||||
goto fail;
|
||||
|
||||
acm->notify_req->complete = acm_cdc_notify_complete;
|
||||
acm->notify_req->context = acm;
|
||||
|
||||
/* copy descriptors */
|
||||
f->descriptors = usb_copy_descriptors(acm_fs_function);
|
||||
if (!f->descriptors)
|
||||
goto fail;
|
||||
|
||||
/* support all relevant hardware speeds... we expect that when
|
||||
* hardware is dual speed, all bulk-capable endpoints work at
|
||||
* both speeds
|
||||
*/
|
||||
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
||||
acm_hs_in_desc.bEndpointAddress =
|
||||
acm_fs_in_desc.bEndpointAddress;
|
||||
acm_hs_out_desc.bEndpointAddress =
|
||||
acm_fs_out_desc.bEndpointAddress;
|
||||
acm_hs_notify_desc.bEndpointAddress =
|
||||
acm_fs_notify_desc.bEndpointAddress;
|
||||
|
||||
/* copy descriptors */
|
||||
f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
|
||||
}
|
||||
if (gadget_is_superspeed(c->cdev->gadget)) {
|
||||
acm_ss_in_desc.bEndpointAddress =
|
||||
acm_fs_in_desc.bEndpointAddress;
|
||||
acm_ss_out_desc.bEndpointAddress =
|
||||
acm_fs_out_desc.bEndpointAddress;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->ss_descriptors = usb_copy_descriptors(acm_ss_function);
|
||||
if (!f->ss_descriptors)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
acm->port_num,
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
acm->port.in->name, acm->port.out->name,
|
||||
acm->notify->name);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (acm->notify_req)
|
||||
gs_free_req(acm->notify, acm->notify_req);
|
||||
|
||||
/* we might as well release our claims on endpoints */
|
||||
if (acm->notify)
|
||||
acm->notify->driver_data = NULL;
|
||||
if (acm->port.out)
|
||||
acm->port.out->driver_data = NULL;
|
||||
if (acm->port.in)
|
||||
acm->port.in->driver_data = NULL;
|
||||
|
||||
ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
acm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
if (gadget_is_dualspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
if (gadget_is_superspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
usb_free_descriptors(f->descriptors);
|
||||
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
|
||||
* @port_num: /dev/ttyGS* port this interface will use
|
||||
* 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 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[ACM_CTRL_IDX].id == 0) {
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
acm_string_defs[ACM_CTRL_IDX].id = status;
|
||||
|
||||
acm_control_interface_desc.iInterface = status;
|
||||
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
acm_string_defs[ACM_DATA_IDX].id = status;
|
||||
|
||||
acm_data_interface_desc.iInterface = status;
|
||||
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
acm_string_defs[ACM_IAD_IDX].id = status;
|
||||
|
||||
acm_iad_descriptor.iFunction = status;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
acm = kzalloc(sizeof *acm, GFP_KERNEL);
|
||||
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;
|
||||
|
||||
status = usb_add_function(c, &acm->port.func);
|
||||
if (status)
|
||||
kfree(acm);
|
||||
return status;
|
||||
}
|
2455
drivers/staging/ccg/f_fs.c
Normal file
2455
drivers/staging/ccg/f_fs.c
Normal file
File diff suppressed because it is too large
Load Diff
3135
drivers/staging/ccg/f_mass_storage.c
Normal file
3135
drivers/staging/ccg/f_mass_storage.c
Normal file
File diff suppressed because it is too large
Load Diff
918
drivers/staging/ccg/f_rndis.c
Normal file
918
drivers/staging/ccg/f_rndis.c
Normal file
@ -0,0 +1,918 @@
|
||||
/*
|
||||
* f_rndis.c -- RNDIS link function driver
|
||||
*
|
||||
* Copyright (C) 2003-2005,2008 David Brownell
|
||||
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz (mina86@mina86.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "rndis.h"
|
||||
|
||||
|
||||
/*
|
||||
* This function is an RNDIS Ethernet port -- a Microsoft protocol that's
|
||||
* been promoted instead of the standard CDC Ethernet. The published RNDIS
|
||||
* spec is ambiguous, incomplete, and needlessly complex. Variants such as
|
||||
* ActiveSync have even worse status in terms of specification.
|
||||
*
|
||||
* In short: it's a protocol controlled by (and for) Microsoft, not for an
|
||||
* Open ecosystem or markets. Linux supports it *only* because Microsoft
|
||||
* doesn't support the CDC Ethernet standard.
|
||||
*
|
||||
* The RNDIS data transfer model is complex, with multiple Ethernet packets
|
||||
* per USB message, and out of band data. The control model is built around
|
||||
* what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM
|
||||
* (modem, not Ethernet) veneer, with those ACM descriptors being entirely
|
||||
* useless (they're ignored). RNDIS expects to be the only function in its
|
||||
* configuration, so it's no real help if you need composite devices; and
|
||||
* it expects to be the first configuration too.
|
||||
*
|
||||
* There is a single technical advantage of RNDIS over CDC Ethernet, if you
|
||||
* discount the fluff that its RPC can be made to deliver: it doesn't need
|
||||
* a NOP altsetting for the data interface. That lets it work on some of the
|
||||
* "so smart it's stupid" hardware which takes over configuration changes
|
||||
* from the software, and adds restrictions like "no altsettings".
|
||||
*
|
||||
* Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and
|
||||
* have all sorts of contrary-to-specification oddities that can prevent
|
||||
* them from working sanely. Since bugfixes (or accurate specs, letting
|
||||
* Linux work around those bugs) are unlikely to ever come from MSFT, you
|
||||
* may want to avoid using RNDIS on purely operational grounds.
|
||||
*
|
||||
* Omissions from the RNDIS 1.0 specification include:
|
||||
*
|
||||
* - Power management ... references data that's scattered around lots
|
||||
* of other documentation, which is incorrect/incomplete there too.
|
||||
*
|
||||
* - There are various undocumented protocol requirements, like the need
|
||||
* to send garbage in some control-OUT messages.
|
||||
*
|
||||
* - MS-Windows drivers sometimes emit undocumented requests.
|
||||
*/
|
||||
|
||||
struct f_rndis {
|
||||
struct gether port;
|
||||
u8 ctrl_id, data_id;
|
||||
u8 ethaddr[ETH_ALEN];
|
||||
u32 vendorID;
|
||||
const char *manufacturer;
|
||||
int config;
|
||||
|
||||
struct usb_ep *notify;
|
||||
struct usb_request *notify_req;
|
||||
atomic_t notify_count;
|
||||
};
|
||||
|
||||
static inline struct f_rndis *func_to_rndis(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct f_rndis, port.func);
|
||||
}
|
||||
|
||||
/* peak (theoretical) bulk transfer rate in bits-per-second */
|
||||
static unsigned int bitrate(struct usb_gadget *g)
|
||||
{
|
||||
if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
|
||||
return 13 * 1024 * 8 * 1000 * 8;
|
||||
else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return 13 * 512 * 8 * 1000 * 8;
|
||||
else
|
||||
return 19 * 64 * 1 * 1000 * 8;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
|
||||
#define STATUS_BYTECOUNT 8 /* 8 bytes data */
|
||||
|
||||
|
||||
/* interface descriptor: */
|
||||
|
||||
static struct usb_interface_descriptor rndis_control_intf = {
|
||||
.bLength = sizeof rndis_control_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
/* status endpoint is optional; this could be patched later */
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_COMM,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_header_desc header_desc = {
|
||||
.bLength = sizeof header_desc,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||
|
||||
.bcdCDC = cpu_to_le16(0x0110),
|
||||
};
|
||||
|
||||
static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
|
||||
.bLength = sizeof call_mgmt_descriptor,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
|
||||
|
||||
.bmCapabilities = 0x00,
|
||||
.bDataInterface = 0x01,
|
||||
};
|
||||
|
||||
static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
|
||||
.bLength = sizeof rndis_acm_descriptor,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||
|
||||
.bmCapabilities = 0x00,
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc rndis_union_desc = {
|
||||
.bLength = sizeof(rndis_union_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||
/* .bMasterInterface0 = DYNAMIC */
|
||||
/* .bSlaveInterface0 = DYNAMIC */
|
||||
};
|
||||
|
||||
/* the data interface has two bulk endpoints */
|
||||
|
||||
static struct usb_interface_descriptor rndis_data_intf = {
|
||||
.bLength = sizeof rndis_data_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
|
||||
static struct usb_interface_assoc_descriptor
|
||||
rndis_iad_descriptor = {
|
||||
.bLength = sizeof rndis_iad_descriptor,
|
||||
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
||||
|
||||
.bFirstInterface = 0, /* XXX, hardcoded */
|
||||
.bInterfaceCount = 2, // control + data
|
||||
.bFunctionClass = USB_CLASS_COMM,
|
||||
.bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
|
||||
.bFunctionProtocol = USB_CDC_PROTO_NONE,
|
||||
/* .iFunction = DYNAMIC */
|
||||
};
|
||||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor fs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
|
||||
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eth_fs_function[] = {
|
||||
(struct usb_descriptor_header *) &rndis_iad_descriptor,
|
||||
|
||||
/* control interface matches ACM, not Ethernet */
|
||||
(struct usb_descriptor_header *) &rndis_control_intf,
|
||||
(struct usb_descriptor_header *) &header_desc,
|
||||
(struct usb_descriptor_header *) &call_mgmt_descriptor,
|
||||
(struct usb_descriptor_header *) &rndis_acm_descriptor,
|
||||
(struct usb_descriptor_header *) &rndis_union_desc,
|
||||
(struct usb_descriptor_header *) &fs_notify_desc,
|
||||
|
||||
/* data interface has no altsetting */
|
||||
(struct usb_descriptor_header *) &rndis_data_intf,
|
||||
(struct usb_descriptor_header *) &fs_in_desc,
|
||||
(struct usb_descriptor_header *) &fs_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor hs_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
|
||||
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eth_hs_function[] = {
|
||||
(struct usb_descriptor_header *) &rndis_iad_descriptor,
|
||||
|
||||
/* control interface matches ACM, not Ethernet */
|
||||
(struct usb_descriptor_header *) &rndis_control_intf,
|
||||
(struct usb_descriptor_header *) &header_desc,
|
||||
(struct usb_descriptor_header *) &call_mgmt_descriptor,
|
||||
(struct usb_descriptor_header *) &rndis_acm_descriptor,
|
||||
(struct usb_descriptor_header *) &rndis_union_desc,
|
||||
(struct usb_descriptor_header *) &hs_notify_desc,
|
||||
|
||||
/* data interface has no altsetting */
|
||||
(struct usb_descriptor_header *) &rndis_data_intf,
|
||||
(struct usb_descriptor_header *) &hs_in_desc,
|
||||
(struct usb_descriptor_header *) &hs_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* super speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor ss_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
|
||||
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
|
||||
.bLength = sizeof ss_intr_comp_desc,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
/* the following 3 values can be tweaked if necessary */
|
||||
/* .bMaxBurst = 0, */
|
||||
/* .bmAttributes = 0, */
|
||||
.wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
|
||||
.bLength = sizeof ss_bulk_comp_desc,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
/* the following 2 values can be tweaked if necessary */
|
||||
/* .bMaxBurst = 0, */
|
||||
/* .bmAttributes = 0, */
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eth_ss_function[] = {
|
||||
(struct usb_descriptor_header *) &rndis_iad_descriptor,
|
||||
|
||||
/* control interface matches ACM, not Ethernet */
|
||||
(struct usb_descriptor_header *) &rndis_control_intf,
|
||||
(struct usb_descriptor_header *) &header_desc,
|
||||
(struct usb_descriptor_header *) &call_mgmt_descriptor,
|
||||
(struct usb_descriptor_header *) &rndis_acm_descriptor,
|
||||
(struct usb_descriptor_header *) &rndis_union_desc,
|
||||
(struct usb_descriptor_header *) &ss_notify_desc,
|
||||
(struct usb_descriptor_header *) &ss_intr_comp_desc,
|
||||
|
||||
/* data interface has no altsetting */
|
||||
(struct usb_descriptor_header *) &rndis_data_intf,
|
||||
(struct usb_descriptor_header *) &ss_in_desc,
|
||||
(struct usb_descriptor_header *) &ss_bulk_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_out_desc,
|
||||
(struct usb_descriptor_header *) &ss_bulk_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* string descriptors: */
|
||||
|
||||
static struct usb_string rndis_string_defs[] = {
|
||||
[0].s = "RNDIS Communications Control",
|
||||
[1].s = "RNDIS Ethernet Data",
|
||||
[2].s = "RNDIS",
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings rndis_string_table = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = rndis_string_defs,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *rndis_strings[] = {
|
||||
&rndis_string_table,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct sk_buff *rndis_add_header(struct gether *port,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *skb2;
|
||||
|
||||
skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
|
||||
if (skb2)
|
||||
rndis_add_hdr(skb2);
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
return skb2;
|
||||
}
|
||||
|
||||
static void rndis_response_available(void *_rndis)
|
||||
{
|
||||
struct f_rndis *rndis = _rndis;
|
||||
struct usb_request *req = rndis->notify_req;
|
||||
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
|
||||
__le32 *data = req->buf;
|
||||
int status;
|
||||
|
||||
if (atomic_inc_return(&rndis->notify_count) != 1)
|
||||
return;
|
||||
|
||||
/* Send RNDIS RESPONSE_AVAILABLE notification; a
|
||||
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
|
||||
*
|
||||
* This is the only notification defined by RNDIS.
|
||||
*/
|
||||
data[0] = cpu_to_le32(1);
|
||||
data[1] = cpu_to_le32(0);
|
||||
|
||||
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
|
||||
if (status) {
|
||||
atomic_dec(&rndis->notify_count);
|
||||
DBG(cdev, "notify/0 --> %d\n", status);
|
||||
}
|
||||
}
|
||||
|
||||
static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_rndis *rndis = req->context;
|
||||
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
|
||||
int status = req->status;
|
||||
|
||||
/* after TX:
|
||||
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
|
||||
* - RNDIS_RESPONSE_AVAILABLE (status/irq)
|
||||
*/
|
||||
switch (status) {
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
/* connection gone */
|
||||
atomic_set(&rndis->notify_count, 0);
|
||||
break;
|
||||
default:
|
||||
DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
|
||||
ep->name, status,
|
||||
req->actual, req->length);
|
||||
/* FALLTHROUGH */
|
||||
case 0:
|
||||
if (ep != rndis->notify)
|
||||
break;
|
||||
|
||||
/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
|
||||
* notifications by resending until we're done
|
||||
*/
|
||||
if (atomic_dec_and_test(&rndis->notify_count))
|
||||
break;
|
||||
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
|
||||
if (status) {
|
||||
atomic_dec(&rndis->notify_count);
|
||||
DBG(cdev, "notify/1 --> %d\n", status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_rndis *rndis = req->context;
|
||||
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
|
||||
int status;
|
||||
|
||||
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
|
||||
// spin_lock(&dev->lock);
|
||||
status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
|
||||
if (status < 0)
|
||||
ERROR(cdev, "RNDIS command error %d, %d/%d\n",
|
||||
status, req->actual, req->length);
|
||||
// spin_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
/* composite driver infrastructure handles everything except
|
||||
* CDC class messages; interface activation uses set_alt().
|
||||
*/
|
||||
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
|
||||
|
||||
/* RNDIS uses the CDC command encapsulation mechanism to implement
|
||||
* an RPC scheme, with much getting/setting of attributes by OID.
|
||||
*/
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
|
||||
| USB_CDC_SEND_ENCAPSULATED_COMMAND:
|
||||
if (w_value || w_index != rndis->ctrl_id)
|
||||
goto invalid;
|
||||
/* read the request; process it later */
|
||||
value = w_length;
|
||||
req->complete = rndis_command_complete;
|
||||
req->context = rndis;
|
||||
/* later, rndis_response_available() sends a notification */
|
||||
break;
|
||||
|
||||
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
|
||||
| USB_CDC_GET_ENCAPSULATED_RESPONSE:
|
||||
if (w_value || w_index != rndis->ctrl_id)
|
||||
goto invalid;
|
||||
else {
|
||||
u8 *buf;
|
||||
u32 n;
|
||||
|
||||
/* return the result */
|
||||
buf = rndis_get_next_response(rndis->config, &n);
|
||||
if (buf) {
|
||||
memcpy(req->buf, buf, n);
|
||||
req->complete = rndis_response_complete;
|
||||
req->context = rndis;
|
||||
rndis_free_response(rndis->config, buf);
|
||||
value = n;
|
||||
}
|
||||
/* else stalls ... spec says to avoid that */
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
invalid:
|
||||
VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
}
|
||||
|
||||
/* respond with data transfer or status phase? */
|
||||
if (value >= 0) {
|
||||
DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
req->zero = (value < w_length);
|
||||
req->length = value;
|
||||
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
if (value < 0)
|
||||
ERROR(cdev, "rndis response on err %d\n", value);
|
||||
}
|
||||
|
||||
/* device either stalls (value < 0) or reports success */
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
/* we know alt == 0 */
|
||||
|
||||
if (intf == rndis->ctrl_id) {
|
||||
if (rndis->notify->driver_data) {
|
||||
VDBG(cdev, "reset rndis control %d\n", intf);
|
||||
usb_ep_disable(rndis->notify);
|
||||
}
|
||||
if (!rndis->notify->desc) {
|
||||
VDBG(cdev, "init rndis ctrl %d\n", intf);
|
||||
if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
|
||||
goto fail;
|
||||
}
|
||||
usb_ep_enable(rndis->notify);
|
||||
rndis->notify->driver_data = rndis;
|
||||
|
||||
} else if (intf == rndis->data_id) {
|
||||
struct net_device *net;
|
||||
|
||||
if (rndis->port.in_ep->driver_data) {
|
||||
DBG(cdev, "reset rndis\n");
|
||||
gether_disconnect(&rndis->port);
|
||||
}
|
||||
|
||||
if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
|
||||
DBG(cdev, "init rndis\n");
|
||||
if (config_ep_by_speed(cdev->gadget, f,
|
||||
rndis->port.in_ep) ||
|
||||
config_ep_by_speed(cdev->gadget, f,
|
||||
rndis->port.out_ep)) {
|
||||
rndis->port.in_ep->desc = NULL;
|
||||
rndis->port.out_ep->desc = NULL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid ZLPs; they can be troublesome. */
|
||||
rndis->port.is_zlp_ok = false;
|
||||
|
||||
/* RNDIS should be in the "RNDIS uninitialized" state,
|
||||
* either never activated or after rndis_uninit().
|
||||
*
|
||||
* We don't want data to flow here until a nonzero packet
|
||||
* filter is set, at which point it enters "RNDIS data
|
||||
* initialized" state ... but we do want the endpoints
|
||||
* to be activated. It's a strange little state.
|
||||
*
|
||||
* REVISIT the RNDIS gadget code has done this wrong for a
|
||||
* very long time. We need another call to the link layer
|
||||
* code -- gether_updown(...bool) maybe -- to do it right.
|
||||
*/
|
||||
rndis->port.cdc_filter = 0;
|
||||
|
||||
DBG(cdev, "RNDIS RX/TX early activation ... \n");
|
||||
net = gether_connect(&rndis->port);
|
||||
if (IS_ERR(net))
|
||||
return PTR_ERR(net);
|
||||
|
||||
rndis_set_param_dev(rndis->config, net,
|
||||
&rndis->port.cdc_filter);
|
||||
} else
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void rndis_disable(struct usb_function *f)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
if (!rndis->notify->driver_data)
|
||||
return;
|
||||
|
||||
DBG(cdev, "rndis deactivated\n");
|
||||
|
||||
rndis_uninit(rndis->config);
|
||||
gether_disconnect(&rndis->port);
|
||||
|
||||
usb_ep_disable(rndis->notify);
|
||||
rndis->notify->driver_data = NULL;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* This isn't quite the same mechanism as CDC Ethernet, since the
|
||||
* notification scheme passes less data, but the same set of link
|
||||
* states must be tested. A key difference is that altsettings are
|
||||
* not used to tell whether the link should send packets or not.
|
||||
*/
|
||||
|
||||
static void rndis_open(struct gether *geth)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(&geth->func);
|
||||
struct usb_composite_dev *cdev = geth->func.config->cdev;
|
||||
|
||||
DBG(cdev, "%s\n", __func__);
|
||||
|
||||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
|
||||
bitrate(cdev->gadget) / 100);
|
||||
rndis_signal_connect(rndis->config);
|
||||
}
|
||||
|
||||
static void rndis_close(struct gether *geth)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(&geth->func);
|
||||
|
||||
DBG(geth->func.config->cdev, "%s\n", __func__);
|
||||
|
||||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_signal_disconnect(rndis->config);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* ethernet function driver setup/binding */
|
||||
|
||||
static int
|
||||
rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
rndis->ctrl_id = status;
|
||||
rndis_iad_descriptor.bFirstInterface = status;
|
||||
|
||||
rndis_control_intf.bInterfaceNumber = status;
|
||||
rndis_union_desc.bMasterInterface0 = status;
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
rndis->data_id = status;
|
||||
|
||||
rndis_data_intf.bInterfaceNumber = status;
|
||||
rndis_union_desc.bSlaveInterface0 = status;
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
/* allocate instance-specific endpoints */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
rndis->port.in_ep = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
rndis->port.out_ep = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
/* NOTE: a status/notification endpoint is, strictly speaking,
|
||||
* optional. We don't treat it that way though! It's simpler,
|
||||
* and some newer profiles don't treat it as optional.
|
||||
*/
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
rndis->notify = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
status = -ENOMEM;
|
||||
|
||||
/* allocate notification request and buffer */
|
||||
rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
|
||||
if (!rndis->notify_req)
|
||||
goto fail;
|
||||
rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
|
||||
if (!rndis->notify_req->buf)
|
||||
goto fail;
|
||||
rndis->notify_req->length = STATUS_BYTECOUNT;
|
||||
rndis->notify_req->context = rndis;
|
||||
rndis->notify_req->complete = rndis_response_complete;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->descriptors = usb_copy_descriptors(eth_fs_function);
|
||||
if (!f->descriptors)
|
||||
goto fail;
|
||||
|
||||
/* support all relevant hardware speeds... we expect that when
|
||||
* hardware is dual speed, all bulk-capable endpoints work at
|
||||
* both speeds
|
||||
*/
|
||||
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
||||
hs_in_desc.bEndpointAddress =
|
||||
fs_in_desc.bEndpointAddress;
|
||||
hs_out_desc.bEndpointAddress =
|
||||
fs_out_desc.bEndpointAddress;
|
||||
hs_notify_desc.bEndpointAddress =
|
||||
fs_notify_desc.bEndpointAddress;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
|
||||
if (!f->hs_descriptors)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (gadget_is_superspeed(c->cdev->gadget)) {
|
||||
ss_in_desc.bEndpointAddress =
|
||||
fs_in_desc.bEndpointAddress;
|
||||
ss_out_desc.bEndpointAddress =
|
||||
fs_out_desc.bEndpointAddress;
|
||||
ss_notify_desc.bEndpointAddress =
|
||||
fs_notify_desc.bEndpointAddress;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->ss_descriptors = usb_copy_descriptors(eth_ss_function);
|
||||
if (!f->ss_descriptors)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rndis->port.open = rndis_open;
|
||||
rndis->port.close = rndis_close;
|
||||
|
||||
status = rndis_register(rndis_response_available, rndis);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
rndis->config = status;
|
||||
|
||||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_set_host_mac(rndis->config, rndis->ethaddr);
|
||||
|
||||
if (rndis->manufacturer && rndis->vendorID &&
|
||||
rndis_set_param_vendor(rndis->config, rndis->vendorID,
|
||||
rndis->manufacturer))
|
||||
goto fail;
|
||||
|
||||
/* NOTE: all that is done without knowing or caring about
|
||||
* the network link ... which is unavailable to this code
|
||||
* until we're activated via set_alt().
|
||||
*/
|
||||
|
||||
DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
rndis->port.in_ep->name, rndis->port.out_ep->name,
|
||||
rndis->notify->name);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors)
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
if (f->descriptors)
|
||||
usb_free_descriptors(f->descriptors);
|
||||
|
||||
if (rndis->notify_req) {
|
||||
kfree(rndis->notify_req->buf);
|
||||
usb_ep_free_request(rndis->notify, rndis->notify_req);
|
||||
}
|
||||
|
||||
/* we might as well release our claims on endpoints */
|
||||
if (rndis->notify)
|
||||
rndis->notify->driver_data = NULL;
|
||||
if (rndis->port.out_ep->desc)
|
||||
rndis->port.out_ep->driver_data = NULL;
|
||||
if (rndis->port.in_ep->desc)
|
||||
rndis->port.in_ep->driver_data = NULL;
|
||||
|
||||
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
rndis_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
|
||||
rndis_deregister(rndis->config);
|
||||
rndis_exit();
|
||||
rndis_string_defs[0].id = 0;
|
||||
|
||||
if (gadget_is_superspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
if (gadget_is_dualspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
usb_free_descriptors(f->descriptors);
|
||||
|
||||
kfree(rndis->notify_req->buf);
|
||||
usb_ep_free_request(rndis->notify, rndis->notify_req);
|
||||
|
||||
kfree(rndis);
|
||||
}
|
||||
|
||||
/* Some controllers can't support RNDIS ... */
|
||||
static inline bool can_support_rndis(struct usb_configuration *c)
|
||||
{
|
||||
/* everything else is *presumably* fine */
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer)
|
||||
{
|
||||
struct f_rndis *rndis;
|
||||
int status;
|
||||
|
||||
if (!can_support_rndis(c) || !ethaddr)
|
||||
return -EINVAL;
|
||||
|
||||
/* maybe allocate device-global string IDs */
|
||||
if (rndis_string_defs[0].id == 0) {
|
||||
|
||||
/* ... and setup RNDIS itself */
|
||||
status = rndis_init();
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/* control interface label */
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
rndis_string_defs[0].id = status;
|
||||
rndis_control_intf.iInterface = status;
|
||||
|
||||
/* data interface label */
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
rndis_string_defs[1].id = status;
|
||||
rndis_data_intf.iInterface = status;
|
||||
|
||||
/* IAD iFunction label */
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
rndis_string_defs[2].id = status;
|
||||
rndis_iad_descriptor.iFunction = status;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
status = -ENOMEM;
|
||||
rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
|
||||
if (!rndis)
|
||||
goto fail;
|
||||
|
||||
memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
|
||||
rndis->vendorID = vendorID;
|
||||
rndis->manufacturer = manufacturer;
|
||||
|
||||
/* RNDIS activates when the host changes this filter */
|
||||
rndis->port.cdc_filter = 0;
|
||||
|
||||
/* RNDIS has special (and complex) framing */
|
||||
rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
|
||||
rndis->port.wrap = rndis_add_header;
|
||||
rndis->port.unwrap = rndis_rm_hdr;
|
||||
|
||||
rndis->port.func.name = "rndis";
|
||||
rndis->port.func.strings = rndis_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
rndis->port.func.bind = rndis_bind;
|
||||
rndis->port.func.unbind = rndis_unbind;
|
||||
rndis->port.func.set_alt = rndis_set_alt;
|
||||
rndis->port.func.setup = rndis_setup;
|
||||
rndis->port.func.disable = rndis_disable;
|
||||
|
||||
status = usb_add_function(c, &rndis->port.func);
|
||||
if (status) {
|
||||
kfree(rndis);
|
||||
fail:
|
||||
rndis_exit();
|
||||
}
|
||||
return status;
|
||||
}
|
150
drivers/staging/ccg/gadget_chips.h
Normal file
150
drivers/staging/ccg/gadget_chips.h
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* USB device controllers have lots of quirks. Use these macros in
|
||||
* gadget drivers or other code that needs to deal with them, and which
|
||||
* autoconfigures instead of using early binding to the hardware.
|
||||
*
|
||||
* This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
|
||||
* some config file that gets updated as new hardware is supported.
|
||||
* (And avoiding all runtime comparisons in typical one-choice configs!)
|
||||
*
|
||||
* NOTE: some of these controller drivers may not be available yet.
|
||||
* Some are available on 2.4 kernels; several are available, but not
|
||||
* yet pushed in the 2.6 mainline tree.
|
||||
*/
|
||||
|
||||
#ifndef __GADGET_CHIPS_H
|
||||
#define __GADGET_CHIPS_H
|
||||
|
||||
/*
|
||||
* NOTICE: the entries below are alphabetical and should be kept
|
||||
* that way.
|
||||
*
|
||||
* Always be sure to add new entries to the correct position or
|
||||
* accept the bashing later.
|
||||
*
|
||||
* If you have forgotten the alphabetical order let VIM/EMACS
|
||||
* do that for you.
|
||||
*/
|
||||
#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
|
||||
#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
|
||||
#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
|
||||
#define gadget_is_bcm63xx(g) (!strcmp("bcm63xx_udc", (g)->name))
|
||||
#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
|
||||
#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
|
||||
#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
|
||||
#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
|
||||
#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
|
||||
#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
|
||||
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
|
||||
#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
|
||||
#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
|
||||
#define gadget_is_lpc32xx(g) (!strcmp("lpc32xx_udc", (g)->name))
|
||||
#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
|
||||
#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
|
||||
#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
|
||||
#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
|
||||
#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
|
||||
#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
|
||||
#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
|
||||
#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
|
||||
#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name))
|
||||
#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
|
||||
#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
|
||||
#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
|
||||
#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
|
||||
|
||||
/**
|
||||
* usb_gadget_controller_number - support bcdDevice id convention
|
||||
* @gadget: the controller being driven
|
||||
*
|
||||
* Return a 2-digit BCD value associated with the peripheral controller,
|
||||
* suitable for use as part of a bcdDevice value, or a negative error code.
|
||||
*
|
||||
* NOTE: this convention is purely optional, and has no meaning in terms of
|
||||
* any USB specification. If you want to use a different convention in your
|
||||
* gadget driver firmware -- maybe a more formal revision ID -- feel free.
|
||||
*
|
||||
* Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
|
||||
* to change their behavior accordingly. For example it might help avoiding
|
||||
* some chip bug.
|
||||
*/
|
||||
static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget_is_net2280(gadget))
|
||||
return 0x01;
|
||||
else if (gadget_is_dummy(gadget))
|
||||
return 0x02;
|
||||
else if (gadget_is_pxa(gadget))
|
||||
return 0x03;
|
||||
else if (gadget_is_goku(gadget))
|
||||
return 0x06;
|
||||
else if (gadget_is_omap(gadget))
|
||||
return 0x08;
|
||||
else if (gadget_is_pxa27x(gadget))
|
||||
return 0x11;
|
||||
else if (gadget_is_s3c2410(gadget))
|
||||
return 0x12;
|
||||
else if (gadget_is_at91(gadget))
|
||||
return 0x13;
|
||||
else if (gadget_is_imx(gadget))
|
||||
return 0x14;
|
||||
else if (gadget_is_musbhdrc(gadget))
|
||||
return 0x16;
|
||||
else if (gadget_is_atmel_usba(gadget))
|
||||
return 0x18;
|
||||
else if (gadget_is_fsl_usb2(gadget))
|
||||
return 0x19;
|
||||
else if (gadget_is_amd5536udc(gadget))
|
||||
return 0x20;
|
||||
else if (gadget_is_m66592(gadget))
|
||||
return 0x21;
|
||||
else if (gadget_is_fsl_qe(gadget))
|
||||
return 0x22;
|
||||
else if (gadget_is_ci13xxx_pci(gadget))
|
||||
return 0x23;
|
||||
else if (gadget_is_langwell(gadget))
|
||||
return 0x24;
|
||||
else if (gadget_is_r8a66597(gadget))
|
||||
return 0x25;
|
||||
else if (gadget_is_s3c_hsotg(gadget))
|
||||
return 0x26;
|
||||
else if (gadget_is_pch(gadget))
|
||||
return 0x27;
|
||||
else if (gadget_is_ci13xxx_msm(gadget))
|
||||
return 0x28;
|
||||
else if (gadget_is_renesas_usbhs(gadget))
|
||||
return 0x29;
|
||||
else if (gadget_is_s3c_hsudc(gadget))
|
||||
return 0x30;
|
||||
else if (gadget_is_net2272(gadget))
|
||||
return 0x31;
|
||||
else if (gadget_is_dwc3(gadget))
|
||||
return 0x32;
|
||||
else if (gadget_is_lpc32xx(gadget))
|
||||
return 0x33;
|
||||
else if (gadget_is_bcm63xx(gadget))
|
||||
return 0x34;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gadget_supports_altsettings - return true if altsettings work
|
||||
* @gadget: the gadget in question
|
||||
*/
|
||||
static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
|
||||
{
|
||||
/* PXA 21x/25x/26x has no altsettings at all */
|
||||
if (gadget_is_pxa(gadget))
|
||||
return false;
|
||||
|
||||
/* PXA 27x and 3xx have *broken* altsetting support */
|
||||
if (gadget_is_pxa27x(gadget))
|
||||
return false;
|
||||
|
||||
/* Everything else is *presumably* fine ... */
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __GADGET_CHIPS_H */
|
47
drivers/staging/ccg/ndis.h
Normal file
47
drivers/staging/ccg/ndis.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ndis.h
|
||||
*
|
||||
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
|
||||
*
|
||||
* Thanks to the cygwin development team,
|
||||
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
|
||||
*
|
||||
* THIS SOFTWARE IS NOT COPYRIGHTED
|
||||
*
|
||||
* This source code is offered for use in the public domain. You may
|
||||
* use, modify or distribute it freely.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_NDIS_H
|
||||
#define _LINUX_NDIS_H
|
||||
|
||||
enum NDIS_DEVICE_POWER_STATE {
|
||||
NdisDeviceStateUnspecified = 0,
|
||||
NdisDeviceStateD0,
|
||||
NdisDeviceStateD1,
|
||||
NdisDeviceStateD2,
|
||||
NdisDeviceStateD3,
|
||||
NdisDeviceStateMaximum
|
||||
};
|
||||
|
||||
struct NDIS_PM_WAKE_UP_CAPABILITIES {
|
||||
enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
|
||||
enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
|
||||
enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
|
||||
};
|
||||
|
||||
struct NDIS_PNP_CAPABILITIES {
|
||||
__le32 Flags;
|
||||
struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
|
||||
};
|
||||
|
||||
struct NDIS_PM_PACKET_PATTERN {
|
||||
__le32 Priority;
|
||||
__le32 Reserved;
|
||||
__le32 MaskSize;
|
||||
__le32 PatternOffset;
|
||||
__le32 PatternSize;
|
||||
__le32 PatternFlags;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_NDIS_H */
|
1175
drivers/staging/ccg/rndis.c
Normal file
1175
drivers/staging/ccg/rndis.c
Normal file
File diff suppressed because it is too large
Load Diff
222
drivers/staging/ccg/rndis.h
Normal file
222
drivers/staging/ccg/rndis.h
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* RNDIS Definitions for Remote NDIS
|
||||
*
|
||||
* Authors: Benedikt Spranger, Pengutronix
|
||||
* Robert Schwebel, Pengutronix
|
||||
*
|
||||
* 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 software was originally developed in conformance with
|
||||
* Microsoft's Remote NDIS Specification License Agreement.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_RNDIS_H
|
||||
#define _LINUX_RNDIS_H
|
||||
|
||||
#include <linux/rndis.h>
|
||||
#include "ndis.h"
|
||||
|
||||
#define RNDIS_MAXIMUM_FRAME_SIZE 1518
|
||||
#define RNDIS_MAX_TOTAL_SIZE 1558
|
||||
|
||||
typedef struct rndis_init_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
__le32 MajorVersion;
|
||||
__le32 MinorVersion;
|
||||
__le32 MaxTransferSize;
|
||||
} rndis_init_msg_type;
|
||||
|
||||
typedef struct rndis_init_cmplt_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
__le32 Status;
|
||||
__le32 MajorVersion;
|
||||
__le32 MinorVersion;
|
||||
__le32 DeviceFlags;
|
||||
__le32 Medium;
|
||||
__le32 MaxPacketsPerTransfer;
|
||||
__le32 MaxTransferSize;
|
||||
__le32 PacketAlignmentFactor;
|
||||
__le32 AFListOffset;
|
||||
__le32 AFListSize;
|
||||
} rndis_init_cmplt_type;
|
||||
|
||||
typedef struct rndis_halt_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
} rndis_halt_msg_type;
|
||||
|
||||
typedef struct rndis_query_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
__le32 OID;
|
||||
__le32 InformationBufferLength;
|
||||
__le32 InformationBufferOffset;
|
||||
__le32 DeviceVcHandle;
|
||||
} rndis_query_msg_type;
|
||||
|
||||
typedef struct rndis_query_cmplt_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
__le32 Status;
|
||||
__le32 InformationBufferLength;
|
||||
__le32 InformationBufferOffset;
|
||||
} rndis_query_cmplt_type;
|
||||
|
||||
typedef struct rndis_set_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
__le32 OID;
|
||||
__le32 InformationBufferLength;
|
||||
__le32 InformationBufferOffset;
|
||||
__le32 DeviceVcHandle;
|
||||
} rndis_set_msg_type;
|
||||
|
||||
typedef struct rndis_set_cmplt_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
__le32 Status;
|
||||
} rndis_set_cmplt_type;
|
||||
|
||||
typedef struct rndis_reset_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 Reserved;
|
||||
} rndis_reset_msg_type;
|
||||
|
||||
typedef struct rndis_reset_cmplt_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 Status;
|
||||
__le32 AddressingReset;
|
||||
} rndis_reset_cmplt_type;
|
||||
|
||||
typedef struct rndis_indicate_status_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 Status;
|
||||
__le32 StatusBufferLength;
|
||||
__le32 StatusBufferOffset;
|
||||
} rndis_indicate_status_msg_type;
|
||||
|
||||
typedef struct rndis_keepalive_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
} rndis_keepalive_msg_type;
|
||||
|
||||
typedef struct rndis_keepalive_cmplt_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 RequestID;
|
||||
__le32 Status;
|
||||
} rndis_keepalive_cmplt_type;
|
||||
|
||||
struct rndis_packet_msg_type
|
||||
{
|
||||
__le32 MessageType;
|
||||
__le32 MessageLength;
|
||||
__le32 DataOffset;
|
||||
__le32 DataLength;
|
||||
__le32 OOBDataOffset;
|
||||
__le32 OOBDataLength;
|
||||
__le32 NumOOBDataElements;
|
||||
__le32 PerPacketInfoOffset;
|
||||
__le32 PerPacketInfoLength;
|
||||
__le32 VcHandle;
|
||||
__le32 Reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_config_parameter
|
||||
{
|
||||
__le32 ParameterNameOffset;
|
||||
__le32 ParameterNameLength;
|
||||
__le32 ParameterType;
|
||||
__le32 ParameterValueOffset;
|
||||
__le32 ParameterValueLength;
|
||||
};
|
||||
|
||||
/* implementation specific */
|
||||
enum rndis_state
|
||||
{
|
||||
RNDIS_UNINITIALIZED,
|
||||
RNDIS_INITIALIZED,
|
||||
RNDIS_DATA_INITIALIZED,
|
||||
};
|
||||
|
||||
typedef struct rndis_resp_t
|
||||
{
|
||||
struct list_head list;
|
||||
u8 *buf;
|
||||
u32 length;
|
||||
int send;
|
||||
} rndis_resp_t;
|
||||
|
||||
typedef struct rndis_params
|
||||
{
|
||||
u8 confignr;
|
||||
u8 used;
|
||||
u16 saved_filter;
|
||||
enum rndis_state state;
|
||||
u32 medium;
|
||||
u32 speed;
|
||||
u32 media_state;
|
||||
|
||||
const u8 *host_mac;
|
||||
u16 *filter;
|
||||
struct net_device *dev;
|
||||
|
||||
u32 vendorID;
|
||||
const char *vendorDescr;
|
||||
void (*resp_avail)(void *v);
|
||||
void *v;
|
||||
struct list_head resp_queue;
|
||||
} rndis_params;
|
||||
|
||||
/* RNDIS Message parser and other useless functions */
|
||||
int rndis_msg_parser (u8 configNr, u8 *buf);
|
||||
int rndis_register(void (*resp_avail)(void *v), void *v);
|
||||
void rndis_deregister (int configNr);
|
||||
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
|
||||
u16 *cdc_filter);
|
||||
int rndis_set_param_vendor (u8 configNr, u32 vendorID,
|
||||
const char *vendorDescr);
|
||||
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
|
||||
void rndis_add_hdr (struct sk_buff *skb);
|
||||
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
|
||||
struct sk_buff_head *list);
|
||||
u8 *rndis_get_next_response (int configNr, u32 *length);
|
||||
void rndis_free_response (int configNr, u8 *buf);
|
||||
|
||||
void rndis_uninit (int configNr);
|
||||
int rndis_signal_connect (int configNr);
|
||||
int rndis_signal_disconnect (int configNr);
|
||||
int rndis_state (int configNr);
|
||||
extern void rndis_set_host_mac (int configNr, const u8 *addr);
|
||||
|
||||
int rndis_init(void);
|
||||
void rndis_exit (void);
|
||||
|
||||
#endif /* _LINUX_RNDIS_H */
|
893
drivers/staging/ccg/storage_common.c
Normal file
893
drivers/staging/ccg/storage_common.c
Normal file
@ -0,0 +1,893 @@
|
||||
/*
|
||||
* storage_common.c -- Common definitions for mass storage functionality
|
||||
*
|
||||
* Copyright (C) 2003-2008 Alan Stern
|
||||
* Copyeight (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz (mina86@mina86.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* This file requires the following identifiers used in USB strings to
|
||||
* be defined (each of type pointer to char):
|
||||
* - fsg_string_manufacturer -- name of the manufacturer
|
||||
* - fsg_string_product -- name of the product
|
||||
* - fsg_string_config -- name of the configuration
|
||||
* - fsg_string_interface -- name of the interface
|
||||
* The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS
|
||||
* macro is defined prior to including this file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and
|
||||
* fsg_hs_intr_in_desc objects as well as
|
||||
* FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES
|
||||
* macros are not defined.
|
||||
*
|
||||
* When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER,
|
||||
* FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not
|
||||
* defined (as well as corresponding entries in string tables are
|
||||
* missing) and FSG_STRING_INTERFACE has value of zero.
|
||||
*
|
||||
* When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
|
||||
*/
|
||||
|
||||
/*
|
||||
* When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
|
||||
* sets the number of pipeline buffers (length of the fsg_buffhd array).
|
||||
* The valid range of num_buffers is: num >= 2 && num <= 4.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/usb/storage.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
||||
/*
|
||||
* Thanks to NetChip Technologies for donating this product ID.
|
||||
*
|
||||
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
|
||||
* Instead: allocate your own, using normal USB-IF procedures.
|
||||
*/
|
||||
#define FSG_VENDOR_ID 0x0525 /* NetChip */
|
||||
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef DEBUG
|
||||
#undef VERBOSE_DEBUG
|
||||
#undef DUMP_MSGS
|
||||
#endif /* !DEBUG */
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define VLDBG LDBG
|
||||
#else
|
||||
#define VLDBG(lun, fmt, args...) do { } while (0)
|
||||
#endif /* VERBOSE_DEBUG */
|
||||
|
||||
#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
|
||||
#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
|
||||
#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
|
||||
#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
|
||||
|
||||
/*
|
||||
* Keep those macros in sync with those in
|
||||
* include/linux/usb/composite.h or else GCC will complain. If they
|
||||
* are identical (the same names of arguments, white spaces in the
|
||||
* same places) GCC will allow redefinition otherwise (even if some
|
||||
* white space is removed or added) warning will be issued.
|
||||
*
|
||||
* Those macros are needed here because File Storage Gadget does not
|
||||
* include the composite.h header. For composite gadgets those macros
|
||||
* are redundant since composite.h is included any way.
|
||||
*
|
||||
* One could check whether those macros are already defined (which
|
||||
* would indicate composite.h had been included) or not (which would
|
||||
* indicate we were in FSG) but this is not done because a warning is
|
||||
* desired if definitions here differ from the ones in composite.h.
|
||||
*
|
||||
* We want the definitions to match and be the same in File Storage
|
||||
* Gadget as well as Mass Storage Function (and so composite gadgets
|
||||
* using MSF). If someone changes them in composite.h it will produce
|
||||
* a warning in this file when building MSF.
|
||||
*/
|
||||
#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args)
|
||||
#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args)
|
||||
#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
|
||||
|
||||
#ifdef DUMP_MSGS
|
||||
|
||||
# define dump_msg(fsg, /* const char * */ label, \
|
||||
/* const u8 * */ buf, /* unsigned */ length) do { \
|
||||
if (length < 512) { \
|
||||
DBG(fsg, "%s, length %u:\n", label, length); \
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
|
||||
16, 1, buf, length, 0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
# define dump_cdb(fsg) do { } while (0)
|
||||
|
||||
#else
|
||||
|
||||
# define dump_msg(fsg, /* const char * */ label, \
|
||||
/* const u8 * */ buf, /* unsigned */ length) do { } while (0)
|
||||
|
||||
# ifdef VERBOSE_DEBUG
|
||||
|
||||
# define dump_cdb(fsg) \
|
||||
print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
|
||||
16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
|
||||
|
||||
# else
|
||||
|
||||
# define dump_cdb(fsg) do { } while (0)
|
||||
|
||||
# endif /* VERBOSE_DEBUG */
|
||||
|
||||
#endif /* DUMP_MSGS */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* 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
|
||||
|
||||
/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
|
||||
#define SS_NO_SENSE 0
|
||||
#define SS_COMMUNICATION_FAILURE 0x040800
|
||||
#define SS_INVALID_COMMAND 0x052000
|
||||
#define SS_INVALID_FIELD_IN_CDB 0x052400
|
||||
#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
|
||||
#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
|
||||
#define SS_MEDIUM_NOT_PRESENT 0x023a00
|
||||
#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
|
||||
#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
|
||||
#define SS_RESET_OCCURRED 0x062900
|
||||
#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
|
||||
#define SS_UNRECOVERED_READ_ERROR 0x031100
|
||||
#define SS_WRITE_ERROR 0x030c02
|
||||
#define SS_WRITE_PROTECTED 0x072700
|
||||
|
||||
#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
|
||||
#define ASC(x) ((u8) ((x) >> 8))
|
||||
#define ASCQ(x) ((u8) (x))
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
struct fsg_lun {
|
||||
struct file *filp;
|
||||
loff_t file_length;
|
||||
loff_t num_sectors;
|
||||
|
||||
unsigned int initially_ro:1;
|
||||
unsigned int ro:1;
|
||||
unsigned int removable:1;
|
||||
unsigned int cdrom:1;
|
||||
unsigned int prevent_medium_removal:1;
|
||||
unsigned int registered:1;
|
||||
unsigned int info_valid:1;
|
||||
unsigned int nofua:1;
|
||||
|
||||
u32 sense_data;
|
||||
u32 sense_data_info;
|
||||
u32 unit_attention_data;
|
||||
|
||||
unsigned int blkbits; /* Bits of logical block size of bound block device */
|
||||
unsigned int blksize; /* logical block size of bound block device */
|
||||
struct device dev;
|
||||
};
|
||||
|
||||
#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
|
||||
|
||||
static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct fsg_lun, dev);
|
||||
}
|
||||
|
||||
|
||||
/* Big enough to hold our biggest descriptor */
|
||||
#define EP0_BUFSIZE 256
|
||||
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
|
||||
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
|
||||
module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Number of buffers we will use.
|
||||
* 2 is usually enough for good buffering pipeline
|
||||
*/
|
||||
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
|
||||
|
||||
#endif /* CONFIG_USB_DEBUG */
|
||||
|
||||
/* check if fsg_num_buffers is within a valid range */
|
||||
static inline int fsg_num_buffers_validate(void)
|
||||
{
|
||||
if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
|
||||
return 0;
|
||||
pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
|
||||
fsg_num_buffers, 2 ,4);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Default size of buffer length. */
|
||||
#define FSG_BUFLEN ((u32)16384)
|
||||
|
||||
/* Maximal number of LUNs supported in mass storage function */
|
||||
#define FSG_MAX_LUNS 8
|
||||
|
||||
enum fsg_buffer_state {
|
||||
BUF_STATE_EMPTY = 0,
|
||||
BUF_STATE_FULL,
|
||||
BUF_STATE_BUSY
|
||||
};
|
||||
|
||||
struct fsg_buffhd {
|
||||
void *buf;
|
||||
enum fsg_buffer_state state;
|
||||
struct fsg_buffhd *next;
|
||||
|
||||
/*
|
||||
* The NetChip 2280 is faster, and handles some protocol faults
|
||||
* better, if we don't submit any short bulk-out read requests.
|
||||
* So we will record the intended request length here.
|
||||
*/
|
||||
unsigned int bulk_out_intended_length;
|
||||
|
||||
struct usb_request *inreq;
|
||||
int inreq_busy;
|
||||
struct usb_request *outreq;
|
||||
int outreq_busy;
|
||||
};
|
||||
|
||||
enum fsg_state {
|
||||
/* This one isn't used anywhere */
|
||||
FSG_STATE_COMMAND_PHASE = -10,
|
||||
FSG_STATE_DATA_PHASE,
|
||||
FSG_STATE_STATUS_PHASE,
|
||||
|
||||
FSG_STATE_IDLE = 0,
|
||||
FSG_STATE_ABORT_BULK_OUT,
|
||||
FSG_STATE_RESET,
|
||||
FSG_STATE_INTERFACE_CHANGE,
|
||||
FSG_STATE_CONFIG_CHANGE,
|
||||
FSG_STATE_DISCONNECT,
|
||||
FSG_STATE_EXIT,
|
||||
FSG_STATE_TERMINATED
|
||||
};
|
||||
|
||||
enum data_direction {
|
||||
DATA_DIR_UNKNOWN = 0,
|
||||
DATA_DIR_FROM_HOST,
|
||||
DATA_DIR_TO_HOST,
|
||||
DATA_DIR_NONE
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static inline u32 get_unaligned_be24(u8 *buf)
|
||||
{
|
||||
return 0xffffff & (u32) get_unaligned_be32(buf - 1);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
enum {
|
||||
#ifndef FSG_NO_DEVICE_STRINGS
|
||||
FSG_STRING_MANUFACTURER = 1,
|
||||
FSG_STRING_PRODUCT,
|
||||
FSG_STRING_SERIAL,
|
||||
FSG_STRING_CONFIG,
|
||||
#endif
|
||||
FSG_STRING_INTERFACE
|
||||
};
|
||||
|
||||
|
||||
#ifndef FSG_NO_OTG
|
||||
static struct usb_otg_descriptor
|
||||
fsg_otg_desc = {
|
||||
.bLength = sizeof fsg_otg_desc,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
.bmAttributes = USB_OTG_SRP,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* There is only one interface. */
|
||||
|
||||
static struct usb_interface_descriptor
|
||||
fsg_intf_desc = {
|
||||
.bLength = sizeof fsg_intf_desc,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
.bNumEndpoints = 2, /* Adjusted during fsg_bind() */
|
||||
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
|
||||
.bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */
|
||||
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
|
||||
.iInterface = FSG_STRING_INTERFACE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
|
||||
* interrupt-in.
|
||||
*/
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_fs_bulk_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
/* wMaxPacketSize set by autoconfiguration */
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_fs_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
/* wMaxPacketSize set by autoconfiguration */
|
||||
};
|
||||
|
||||
#ifndef FSG_NO_INTR_EP
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_fs_intr_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(2),
|
||||
.bInterval = 32, /* frames -> 32 ms */
|
||||
};
|
||||
|
||||
#ifndef FSG_NO_OTG
|
||||
# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2
|
||||
#else
|
||||
# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static struct usb_descriptor_header *fsg_fs_function[] = {
|
||||
#ifndef FSG_NO_OTG
|
||||
(struct usb_descriptor_header *) &fsg_otg_desc,
|
||||
#endif
|
||||
(struct usb_descriptor_header *) &fsg_intf_desc,
|
||||
(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
|
||||
(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
|
||||
#ifndef FSG_NO_INTR_EP
|
||||
(struct usb_descriptor_header *) &fsg_fs_intr_in_desc,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* USB 2.0 devices need to expose both high speed and full speed
|
||||
* descriptors, unless they only run at full speed.
|
||||
*
|
||||
* That means alternate endpoint descriptors (bigger packets)
|
||||
* and a "device qualifier" ... plus more construction options
|
||||
* for the configuration descriptor.
|
||||
*/
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_hs_bulk_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_hs_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
.bInterval = 1, /* NAK every 1 uframe */
|
||||
};
|
||||
|
||||
#ifndef FSG_NO_INTR_EP
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_hs_intr_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
/* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(2),
|
||||
.bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
|
||||
};
|
||||
|
||||
#ifndef FSG_NO_OTG
|
||||
# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2
|
||||
#else
|
||||
# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static struct usb_descriptor_header *fsg_hs_function[] = {
|
||||
#ifndef FSG_NO_OTG
|
||||
(struct usb_descriptor_header *) &fsg_otg_desc,
|
||||
#endif
|
||||
(struct usb_descriptor_header *) &fsg_intf_desc,
|
||||
(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
|
||||
(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
|
||||
#ifndef FSG_NO_INTR_EP
|
||||
(struct usb_descriptor_header *) &fsg_hs_intr_in_desc,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_ss_bulk_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
|
||||
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
/*.bMaxBurst = DYNAMIC, */
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_ss_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
|
||||
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
/*.bMaxBurst = DYNAMIC, */
|
||||
};
|
||||
|
||||
#ifndef FSG_NO_INTR_EP
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_ss_intr_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
/* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(2),
|
||||
.bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = {
|
||||
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
.wBytesPerInterval = cpu_to_le16(2),
|
||||
};
|
||||
|
||||
#ifndef FSG_NO_OTG
|
||||
# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2
|
||||
#else
|
||||
# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
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[] = {
|
||||
#ifndef FSG_NO_OTG
|
||||
(struct usb_descriptor_header *) &fsg_otg_desc,
|
||||
#endif
|
||||
(struct usb_descriptor_header *) &fsg_intf_desc,
|
||||
(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
|
||||
(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
|
||||
(struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
|
||||
(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
|
||||
#ifndef FSG_NO_INTR_EP
|
||||
(struct usb_descriptor_header *) &fsg_ss_intr_in_desc,
|
||||
(struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc,
|
||||
#endif
|
||||
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[] = {
|
||||
#ifndef FSG_NO_DEVICE_STRINGS
|
||||
{FSG_STRING_MANUFACTURER, fsg_string_manufacturer},
|
||||
{FSG_STRING_PRODUCT, fsg_string_product},
|
||||
{FSG_STRING_SERIAL, ""},
|
||||
{FSG_STRING_CONFIG, fsg_string_config},
|
||||
#endif
|
||||
{FSG_STRING_INTERFACE, fsg_string_interface},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings fsg_stringtab = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = fsg_strings,
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* If the next two routines are called while the gadget is registered,
|
||||
* the caller must own fsg->filesem for writing.
|
||||
*/
|
||||
|
||||
static void fsg_lun_close(struct fsg_lun *curlun)
|
||||
{
|
||||
if (curlun->filp) {
|
||||
LDBG(curlun, "close backing file\n");
|
||||
fput(curlun->filp);
|
||||
curlun->filp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||
{
|
||||
int ro;
|
||||
struct file *filp = NULL;
|
||||
int rc = -EINVAL;
|
||||
struct inode *inode = NULL;
|
||||
loff_t size;
|
||||
loff_t num_sectors;
|
||||
loff_t min_sectors;
|
||||
unsigned int blkbits;
|
||||
unsigned int blksize;
|
||||
|
||||
/* R/W if we can, R/O if we must */
|
||||
ro = curlun->initially_ro;
|
||||
if (!ro) {
|
||||
filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
|
||||
if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
|
||||
ro = 1;
|
||||
}
|
||||
if (ro)
|
||||
filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
|
||||
if (IS_ERR(filp)) {
|
||||
LINFO(curlun, "unable to open backing file: %s\n", filename);
|
||||
return PTR_ERR(filp);
|
||||
}
|
||||
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
ro = 1;
|
||||
|
||||
inode = filp->f_path.dentry->d_inode;
|
||||
if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
|
||||
LINFO(curlun, "invalid file type: %s\n", filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we can't read the file, it's no good.
|
||||
* If we can't write the file, use it read-only.
|
||||
*/
|
||||
if (!(filp->f_op->read || filp->f_op->aio_read)) {
|
||||
LINFO(curlun, "file not readable: %s\n", filename);
|
||||
goto out;
|
||||
}
|
||||
if (!(filp->f_op->write || filp->f_op->aio_write))
|
||||
ro = 1;
|
||||
|
||||
size = i_size_read(inode->i_mapping->host);
|
||||
if (size < 0) {
|
||||
LINFO(curlun, "unable to find file size: %s\n", filename);
|
||||
rc = (int) size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (curlun->cdrom) {
|
||||
blksize = 2048;
|
||||
blkbits = 11;
|
||||
} else if (inode->i_bdev) {
|
||||
blksize = bdev_logical_block_size(inode->i_bdev);
|
||||
blkbits = blksize_bits(blksize);
|
||||
} else {
|
||||
blksize = 512;
|
||||
blkbits = 9;
|
||||
}
|
||||
|
||||
num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
|
||||
min_sectors = 1;
|
||||
if (curlun->cdrom) {
|
||||
min_sectors = 300; /* Smallest track is 300 frames */
|
||||
if (num_sectors >= 256*60*75) {
|
||||
num_sectors = 256*60*75 - 1;
|
||||
LINFO(curlun, "file too big: %s\n", filename);
|
||||
LINFO(curlun, "using only first %d blocks\n",
|
||||
(int) num_sectors);
|
||||
}
|
||||
}
|
||||
if (num_sectors < min_sectors) {
|
||||
LINFO(curlun, "file too small: %s\n", filename);
|
||||
rc = -ETOOSMALL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fsg_lun_is_open(curlun))
|
||||
fsg_lun_close(curlun);
|
||||
|
||||
curlun->blksize = blksize;
|
||||
curlun->blkbits = blkbits;
|
||||
curlun->ro = ro;
|
||||
curlun->filp = filp;
|
||||
curlun->file_length = size;
|
||||
curlun->num_sectors = num_sectors;
|
||||
LDBG(curlun, "open backing file: %s\n", filename);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
fput(filp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Sync the file data, don't bother with the metadata.
|
||||
* This code was copied from fs/buffer.c:sys_fdatasync().
|
||||
*/
|
||||
static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
|
||||
{
|
||||
struct file *filp = curlun->filp;
|
||||
|
||||
if (curlun->ro || !filp)
|
||||
return 0;
|
||||
return vfs_fsync(filp, 1);
|
||||
}
|
||||
|
||||
static void store_cdrom_address(u8 *dest, int msf, u32 addr)
|
||||
{
|
||||
if (msf) {
|
||||
/* Convert to Minutes-Seconds-Frames */
|
||||
addr >>= 2; /* Convert to 2048-byte frames */
|
||||
addr += 2*75; /* Lead-in occupies 2 seconds */
|
||||
dest[3] = addr % 75; /* Frames */
|
||||
addr /= 75;
|
||||
dest[2] = addr % 60; /* Seconds */
|
||||
addr /= 60;
|
||||
dest[1] = addr; /* Minutes */
|
||||
dest[0] = 0; /* Reserved */
|
||||
} else {
|
||||
/* Absolute sector */
|
||||
put_unaligned_be32(addr, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
|
||||
? curlun->ro
|
||||
: curlun->initially_ro);
|
||||
}
|
||||
|
||||
static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", curlun->nofua);
|
||||
}
|
||||
|
||||
static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
char *p;
|
||||
ssize_t rc;
|
||||
|
||||
down_read(filesem);
|
||||
if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */
|
||||
p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
|
||||
if (IS_ERR(p))
|
||||
rc = PTR_ERR(p);
|
||||
else {
|
||||
rc = strlen(p);
|
||||
memmove(buf, p, rc);
|
||||
buf[rc] = '\n'; /* Add a newline */
|
||||
buf[++rc] = 0;
|
||||
}
|
||||
} else { /* No file, return 0 bytes */
|
||||
*buf = 0;
|
||||
rc = 0;
|
||||
}
|
||||
up_read(filesem);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
ssize_t rc;
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
unsigned ro;
|
||||
|
||||
rc = kstrtouint(buf, 2, &ro);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Allow the write-enable status to change only while the
|
||||
* backing file is closed.
|
||||
*/
|
||||
down_read(filesem);
|
||||
if (fsg_lun_is_open(curlun)) {
|
||||
LDBG(curlun, "read-only status change prevented\n");
|
||||
rc = -EBUSY;
|
||||
} else {
|
||||
curlun->ro = ro;
|
||||
curlun->initially_ro = ro;
|
||||
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
|
||||
rc = count;
|
||||
}
|
||||
up_read(filesem);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t fsg_store_nofua(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
unsigned nofua;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 2, &nofua);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Sync data when switching from async mode to sync */
|
||||
if (!nofua && curlun->nofua)
|
||||
fsg_lun_fsync_sub(curlun);
|
||||
|
||||
curlun->nofua = nofua;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
int rc = 0;
|
||||
|
||||
if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
|
||||
LDBG(curlun, "eject attempt prevented\n");
|
||||
return -EBUSY; /* "Door is locked" */
|
||||
}
|
||||
|
||||
/* Remove a trailing newline */
|
||||
if (count > 0 && buf[count-1] == '\n')
|
||||
((char *) buf)[count-1] = 0; /* Ugh! */
|
||||
|
||||
/* Load new medium */
|
||||
down_write(filesem);
|
||||
if (count > 0 && buf[0]) {
|
||||
/* fsg_lun_open() will close existing file if any. */
|
||||
rc = fsg_lun_open(curlun, buf);
|
||||
if (rc == 0)
|
||||
curlun->unit_attention_data =
|
||||
SS_NOT_READY_TO_READY_TRANSITION;
|
||||
} else if (fsg_lun_is_open(curlun)) {
|
||||
fsg_lun_close(curlun);
|
||||
curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
|
||||
}
|
||||
up_write(filesem);
|
||||
return (rc < 0 ? rc : count);
|
||||
}
|
986
drivers/staging/ccg/u_ether.c
Normal file
986
drivers/staging/ccg/u_ether.c
Normal file
@ -0,0 +1,986 @@
|
||||
/*
|
||||
* u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
|
||||
*
|
||||
* Copyright (C) 2003-2005,2008 David Brownell
|
||||
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
|
||||
|
||||
/*
|
||||
* This component encapsulates the Ethernet link glue needed to provide
|
||||
* one (!) network link through the USB gadget stack, normally "usb0".
|
||||
*
|
||||
* The control and data models are handled by the function driver which
|
||||
* connects to this code; such as CDC Ethernet (ECM or EEM),
|
||||
* "CDC Subset", or RNDIS. That includes all descriptor and endpoint
|
||||
* management.
|
||||
*
|
||||
* Link level addressing is handled by this component using module
|
||||
* parameters; if no such parameters are provided, random link level
|
||||
* addresses are used. Each end of the link uses one address. The
|
||||
* host end address is exported in various ways, and is often recorded
|
||||
* in configuration databases.
|
||||
*
|
||||
* The driver which assembles each configuration using such a link is
|
||||
* responsible for ensuring that each configuration includes at most one
|
||||
* instance of is network link. (The network layer provides ways for
|
||||
* this single "physical" link to be used by multiple virtual links.)
|
||||
*/
|
||||
|
||||
#define UETH__VERSION "29-May-2008"
|
||||
|
||||
struct eth_dev {
|
||||
/* lock is held while accessing port_usb
|
||||
* or updating its backlink port_usb->ioport
|
||||
*/
|
||||
spinlock_t lock;
|
||||
struct gether *port_usb;
|
||||
|
||||
struct net_device *net;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
spinlock_t req_lock; /* guard {rx,tx}_reqs */
|
||||
struct list_head tx_reqs, rx_reqs;
|
||||
atomic_t tx_qlen;
|
||||
|
||||
struct sk_buff_head rx_frames;
|
||||
|
||||
unsigned header_len;
|
||||
struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
|
||||
int (*unwrap)(struct gether *,
|
||||
struct sk_buff *skb,
|
||||
struct sk_buff_head *list);
|
||||
|
||||
struct work_struct work;
|
||||
|
||||
unsigned long todo;
|
||||
#define WORK_RX_MEMORY 0
|
||||
|
||||
bool zlp;
|
||||
u8 host_mac[ETH_ALEN];
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define RX_EXTRA 20 /* bytes guarding against rx overflows */
|
||||
|
||||
#define DEFAULT_QLEN 2 /* double buffering by default */
|
||||
|
||||
static unsigned qmult = 5;
|
||||
module_param(qmult, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
|
||||
|
||||
/* for dual-speed hardware, use deeper queues at high/super speed */
|
||||
static inline int qlen(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
|
||||
gadget->speed == USB_SPEED_SUPER))
|
||||
return qmult * DEFAULT_QLEN;
|
||||
else
|
||||
return DEFAULT_QLEN;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* REVISIT there must be a better way than having two sets
|
||||
* of debug calls ...
|
||||
*/
|
||||
|
||||
#undef DBG
|
||||
#undef VDBG
|
||||
#undef ERROR
|
||||
#undef INFO
|
||||
|
||||
#define xprintk(d, level, fmt, args...) \
|
||||
printk(level "%s: " fmt , (d)->net->name , ## args)
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef DEBUG
|
||||
#define DBG(dev, fmt, args...) \
|
||||
xprintk(dev , KERN_DEBUG , fmt , ## args)
|
||||
#else
|
||||
#define DBG(dev, fmt, args...) \
|
||||
do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define VDBG DBG
|
||||
#else
|
||||
#define VDBG(dev, fmt, args...) \
|
||||
do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#define ERROR(dev, fmt, args...) \
|
||||
xprintk(dev , KERN_ERR , fmt , ## args)
|
||||
#define INFO(dev, fmt, args...) \
|
||||
xprintk(dev , KERN_INFO , fmt , ## args)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
|
||||
|
||||
static int ueth_change_mtu(struct net_device *net, int new_mtu)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
|
||||
/* don't change MTU on "live" link (peer won't know) */
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb)
|
||||
status = -EBUSY;
|
||||
else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
|
||||
status = -ERANGE;
|
||||
else
|
||||
net->mtu = new_mtu;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
|
||||
strlcpy(p->driver, "g_ether", sizeof p->driver);
|
||||
strlcpy(p->version, UETH__VERSION, sizeof p->version);
|
||||
strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);
|
||||
strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);
|
||||
}
|
||||
|
||||
/* REVISIT can also support:
|
||||
* - WOL (by tracking suspends and issuing remote wakeup)
|
||||
* - msglevel (implies updated messaging)
|
||||
* - ... probably more ethtool ops
|
||||
*/
|
||||
|
||||
static const struct ethtool_ops ops = {
|
||||
.get_drvinfo = eth_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
};
|
||||
|
||||
static void defer_kevent(struct eth_dev *dev, int flag)
|
||||
{
|
||||
if (test_and_set_bit(flag, &dev->todo))
|
||||
return;
|
||||
if (!schedule_work(&dev->work))
|
||||
ERROR(dev, "kevent %d may have been dropped\n", flag);
|
||||
else
|
||||
DBG(dev, "kevent %d scheduled\n", flag);
|
||||
}
|
||||
|
||||
static void rx_complete(struct usb_ep *ep, struct usb_request *req);
|
||||
|
||||
static int
|
||||
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int retval = -ENOMEM;
|
||||
size_t size = 0;
|
||||
struct usb_ep *out;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb)
|
||||
out = dev->port_usb->out_ep;
|
||||
else
|
||||
out = NULL;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (!out)
|
||||
return -ENOTCONN;
|
||||
|
||||
|
||||
/* Padding up to RX_EXTRA handles minor disagreements with host.
|
||||
* Normally we use the USB "terminate on short read" convention;
|
||||
* so allow up to (N*maxpacket), since that memory is normally
|
||||
* already allocated. Some hardware doesn't deal well with short
|
||||
* reads (e.g. DMA must be N*maxpacket), so for now don't trim a
|
||||
* byte off the end (to force hardware errors on overflow).
|
||||
*
|
||||
* RNDIS uses internal framing, and explicitly allows senders to
|
||||
* pad to end-of-packet. That's potentially nice for speed, but
|
||||
* means receivers can't recover lost synch on their own (because
|
||||
* new packets don't only start after a short RX).
|
||||
*/
|
||||
size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
|
||||
size += dev->port_usb->header_len;
|
||||
size += out->maxpacket - 1;
|
||||
size -= size % out->maxpacket;
|
||||
|
||||
if (dev->port_usb->is_fixed)
|
||||
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
|
||||
|
||||
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
|
||||
if (skb == NULL) {
|
||||
DBG(dev, "no rx skb\n");
|
||||
goto enomem;
|
||||
}
|
||||
|
||||
/* Some platforms perform better when IP packets are aligned,
|
||||
* but on at least one, checksumming fails otherwise. Note:
|
||||
* RNDIS headers involve variable numbers of LE32 values.
|
||||
*/
|
||||
skb_reserve(skb, NET_IP_ALIGN);
|
||||
|
||||
req->buf = skb->data;
|
||||
req->length = size;
|
||||
req->complete = rx_complete;
|
||||
req->context = skb;
|
||||
|
||||
retval = usb_ep_queue(out, req, gfp_flags);
|
||||
if (retval == -ENOMEM)
|
||||
enomem:
|
||||
defer_kevent(dev, WORK_RX_MEMORY);
|
||||
if (retval) {
|
||||
DBG(dev, "rx submit --> %d\n", retval);
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct sk_buff *skb = req->context, *skb2;
|
||||
struct eth_dev *dev = ep->driver_data;
|
||||
int status = req->status;
|
||||
|
||||
switch (status) {
|
||||
|
||||
/* normal completion */
|
||||
case 0:
|
||||
skb_put(skb, req->actual);
|
||||
|
||||
if (dev->unwrap) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb) {
|
||||
status = dev->unwrap(dev->port_usb,
|
||||
skb,
|
||||
&dev->rx_frames);
|
||||
} else {
|
||||
dev_kfree_skb_any(skb);
|
||||
status = -ENOTCONN;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
} else {
|
||||
skb_queue_tail(&dev->rx_frames, skb);
|
||||
}
|
||||
skb = NULL;
|
||||
|
||||
skb2 = skb_dequeue(&dev->rx_frames);
|
||||
while (skb2) {
|
||||
if (status < 0
|
||||
|| ETH_HLEN > skb2->len
|
||||
|| skb2->len > ETH_FRAME_LEN) {
|
||||
dev->net->stats.rx_errors++;
|
||||
dev->net->stats.rx_length_errors++;
|
||||
DBG(dev, "rx length %d\n", skb2->len);
|
||||
dev_kfree_skb_any(skb2);
|
||||
goto next_frame;
|
||||
}
|
||||
skb2->protocol = eth_type_trans(skb2, dev->net);
|
||||
dev->net->stats.rx_packets++;
|
||||
dev->net->stats.rx_bytes += skb2->len;
|
||||
|
||||
/* no buffer copies needed, unless hardware can't
|
||||
* use skb buffers.
|
||||
*/
|
||||
status = netif_rx(skb2);
|
||||
next_frame:
|
||||
skb2 = skb_dequeue(&dev->rx_frames);
|
||||
}
|
||||
break;
|
||||
|
||||
/* software-driven interface shutdown */
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ESHUTDOWN: /* disconnect etc */
|
||||
VDBG(dev, "rx shutdown, code %d\n", status);
|
||||
goto quiesce;
|
||||
|
||||
/* for hardware automagic (such as pxa) */
|
||||
case -ECONNABORTED: /* endpoint reset */
|
||||
DBG(dev, "rx %s reset\n", ep->name);
|
||||
defer_kevent(dev, WORK_RX_MEMORY);
|
||||
quiesce:
|
||||
dev_kfree_skb_any(skb);
|
||||
goto clean;
|
||||
|
||||
/* data overrun */
|
||||
case -EOVERFLOW:
|
||||
dev->net->stats.rx_over_errors++;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
default:
|
||||
dev->net->stats.rx_errors++;
|
||||
DBG(dev, "rx status %d\n", status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!netif_running(dev->net)) {
|
||||
clean:
|
||||
spin_lock(&dev->req_lock);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock(&dev->req_lock);
|
||||
req = NULL;
|
||||
}
|
||||
if (req)
|
||||
rx_submit(dev, req, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
|
||||
{
|
||||
unsigned i;
|
||||
struct usb_request *req;
|
||||
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
/* queue/recycle up to N requests */
|
||||
i = n;
|
||||
list_for_each_entry(req, list, list) {
|
||||
if (i-- == 0)
|
||||
goto extra;
|
||||
}
|
||||
while (i--) {
|
||||
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (!req)
|
||||
return list_empty(list) ? -ENOMEM : 0;
|
||||
list_add(&req->list, list);
|
||||
}
|
||||
return 0;
|
||||
|
||||
extra:
|
||||
/* free extras */
|
||||
for (;;) {
|
||||
struct list_head *next;
|
||||
|
||||
next = req->list.next;
|
||||
list_del(&req->list);
|
||||
usb_ep_free_request(ep, req);
|
||||
|
||||
if (next == list)
|
||||
break;
|
||||
|
||||
req = container_of(next, struct usb_request, list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
|
||||
{
|
||||
int status;
|
||||
|
||||
spin_lock(&dev->req_lock);
|
||||
status = prealloc(&dev->tx_reqs, link->in_ep, n);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
status = prealloc(&dev->rx_reqs, link->out_ep, n);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
goto done;
|
||||
fail:
|
||||
DBG(dev, "can't alloc requests\n");
|
||||
done:
|
||||
spin_unlock(&dev->req_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
|
||||
{
|
||||
struct usb_request *req;
|
||||
unsigned long flags;
|
||||
|
||||
/* fill unused rxq slots with some skb */
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
while (!list_empty(&dev->rx_reqs)) {
|
||||
req = container_of(dev->rx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_del_init(&req->list);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
|
||||
if (rx_submit(dev, req, gfp_flags) < 0) {
|
||||
defer_kevent(dev, WORK_RX_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
|
||||
static void eth_work(struct work_struct *work)
|
||||
{
|
||||
struct eth_dev *dev = container_of(work, struct eth_dev, work);
|
||||
|
||||
if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
|
||||
if (netif_running(dev->net))
|
||||
rx_fill(dev, GFP_KERNEL);
|
||||
}
|
||||
|
||||
if (dev->todo)
|
||||
DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
|
||||
}
|
||||
|
||||
static void tx_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct sk_buff *skb = req->context;
|
||||
struct eth_dev *dev = ep->driver_data;
|
||||
|
||||
switch (req->status) {
|
||||
default:
|
||||
dev->net->stats.tx_errors++;
|
||||
VDBG(dev, "tx err %d\n", req->status);
|
||||
/* FALLTHROUGH */
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ESHUTDOWN: /* disconnect etc */
|
||||
break;
|
||||
case 0:
|
||||
dev->net->stats.tx_bytes += skb->len;
|
||||
}
|
||||
dev->net->stats.tx_packets++;
|
||||
|
||||
spin_lock(&dev->req_lock);
|
||||
list_add(&req->list, &dev->tx_reqs);
|
||||
spin_unlock(&dev->req_lock);
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
atomic_dec(&dev->tx_qlen);
|
||||
if (netif_carrier_ok(dev->net))
|
||||
netif_wake_queue(dev->net);
|
||||
}
|
||||
|
||||
static inline int is_promisc(u16 cdc_filter)
|
||||
{
|
||||
return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
|
||||
}
|
||||
|
||||
static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *net)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
int length = skb->len;
|
||||
int retval;
|
||||
struct usb_request *req = NULL;
|
||||
unsigned long flags;
|
||||
struct usb_ep *in;
|
||||
u16 cdc_filter;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb) {
|
||||
in = dev->port_usb->in_ep;
|
||||
cdc_filter = dev->port_usb->cdc_filter;
|
||||
} else {
|
||||
in = NULL;
|
||||
cdc_filter = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (!in) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* apply outgoing CDC or RNDIS filters */
|
||||
if (!is_promisc(cdc_filter)) {
|
||||
u8 *dest = skb->data;
|
||||
|
||||
if (is_multicast_ether_addr(dest)) {
|
||||
u16 type;
|
||||
|
||||
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
|
||||
* SET_ETHERNET_MULTICAST_FILTERS requests
|
||||
*/
|
||||
if (is_broadcast_ether_addr(dest))
|
||||
type = USB_CDC_PACKET_TYPE_BROADCAST;
|
||||
else
|
||||
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
|
||||
if (!(cdc_filter & type)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
}
|
||||
/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
/*
|
||||
* this freelist can be empty if an interrupt triggered disconnect()
|
||||
* and reconfigured the gadget (shutting down this queue) after the
|
||||
* network stack decided to xmit but before we got the spinlock.
|
||||
*/
|
||||
if (list_empty(&dev->tx_reqs)) {
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
req = container_of(dev->tx_reqs.next, struct usb_request, list);
|
||||
list_del(&req->list);
|
||||
|
||||
/* temporarily stop TX queue when the freelist empties */
|
||||
if (list_empty(&dev->tx_reqs))
|
||||
netif_stop_queue(net);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
|
||||
/* no buffer copies needed, unless the network stack did it
|
||||
* or the hardware can't use skb buffers.
|
||||
* or there's not enough space for extra headers we need
|
||||
*/
|
||||
if (dev->wrap) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb)
|
||||
skb = dev->wrap(dev->port_usb, skb);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
if (!skb)
|
||||
goto drop;
|
||||
|
||||
length = skb->len;
|
||||
}
|
||||
req->buf = skb->data;
|
||||
req->context = skb;
|
||||
req->complete = tx_complete;
|
||||
|
||||
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
|
||||
if (dev->port_usb->is_fixed &&
|
||||
length == dev->port_usb->fixed_in_len &&
|
||||
(length % in->maxpacket) == 0)
|
||||
req->zero = 0;
|
||||
else
|
||||
req->zero = 1;
|
||||
|
||||
/* use zlp framing on tx for strict CDC-Ether conformance,
|
||||
* though any robust network rx path ignores extra padding.
|
||||
* and some hardware doesn't like to write zlps.
|
||||
*/
|
||||
if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
|
||||
length++;
|
||||
|
||||
req->length = length;
|
||||
|
||||
/* throttle high/super speed IRQ rate back slightly */
|
||||
if (gadget_is_dualspeed(dev->gadget))
|
||||
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
|
||||
dev->gadget->speed == USB_SPEED_SUPER)
|
||||
? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
|
||||
: 0;
|
||||
|
||||
retval = usb_ep_queue(in, req, GFP_ATOMIC);
|
||||
switch (retval) {
|
||||
default:
|
||||
DBG(dev, "tx queue err %d\n", retval);
|
||||
break;
|
||||
case 0:
|
||||
net->trans_start = jiffies;
|
||||
atomic_inc(&dev->tx_qlen);
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
dev_kfree_skb_any(skb);
|
||||
drop:
|
||||
dev->net->stats.tx_dropped++;
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
if (list_empty(&dev->tx_reqs))
|
||||
netif_start_queue(net);
|
||||
list_add(&req->list, &dev->tx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
|
||||
{
|
||||
DBG(dev, "%s\n", __func__);
|
||||
|
||||
/* fill the rx queue */
|
||||
rx_fill(dev, gfp_flags);
|
||||
|
||||
/* and open the tx floodgates */
|
||||
atomic_set(&dev->tx_qlen, 0);
|
||||
netif_wake_queue(dev->net);
|
||||
}
|
||||
|
||||
static int eth_open(struct net_device *net)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
struct gether *link;
|
||||
|
||||
DBG(dev, "%s\n", __func__);
|
||||
if (netif_carrier_ok(dev->net))
|
||||
eth_start(dev, GFP_KERNEL);
|
||||
|
||||
spin_lock_irq(&dev->lock);
|
||||
link = dev->port_usb;
|
||||
if (link && link->open)
|
||||
link->open(link);
|
||||
spin_unlock_irq(&dev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_stop(struct net_device *net)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
unsigned long flags;
|
||||
|
||||
VDBG(dev, "%s\n", __func__);
|
||||
netif_stop_queue(net);
|
||||
|
||||
DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
|
||||
dev->net->stats.rx_packets, dev->net->stats.tx_packets,
|
||||
dev->net->stats.rx_errors, dev->net->stats.tx_errors
|
||||
);
|
||||
|
||||
/* ensure there are no more active requests */
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb) {
|
||||
struct gether *link = dev->port_usb;
|
||||
|
||||
if (link->close)
|
||||
link->close(link);
|
||||
|
||||
/* NOTE: we have no abort-queue primitive we could use
|
||||
* to cancel all pending I/O. Instead, we disable then
|
||||
* reenable the endpoints ... this idiom may leave toggle
|
||||
* wrong, but that's a self-correcting error.
|
||||
*
|
||||
* REVISIT: we *COULD* just let the transfers complete at
|
||||
* their own pace; the network stack can handle old packets.
|
||||
* For the moment we leave this here, since it works.
|
||||
*/
|
||||
usb_ep_disable(link->in_ep);
|
||||
usb_ep_disable(link->out_ep);
|
||||
if (netif_carrier_ok(net)) {
|
||||
DBG(dev, "host still using in/out endpoints\n");
|
||||
usb_ep_enable(link->in_ep);
|
||||
usb_ep_enable(link->out_ep);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
|
||||
static char *dev_addr;
|
||||
module_param(dev_addr, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
|
||||
|
||||
/* this address is invisible to ifconfig */
|
||||
static char *host_addr;
|
||||
module_param(host_addr, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
|
||||
|
||||
static int get_ether_addr(const char *str, u8 *dev_addr)
|
||||
{
|
||||
if (str) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
unsigned char num;
|
||||
|
||||
if ((*str == '.') || (*str == ':'))
|
||||
str++;
|
||||
num = hex_to_bin(*str++) << 4;
|
||||
num |= hex_to_bin(*str++);
|
||||
dev_addr [i] = num;
|
||||
}
|
||||
if (is_valid_ether_addr(dev_addr))
|
||||
return 0;
|
||||
}
|
||||
eth_random_addr(dev_addr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct eth_dev *the_dev;
|
||||
|
||||
static const struct net_device_ops eth_netdev_ops = {
|
||||
.ndo_open = eth_open,
|
||||
.ndo_stop = eth_stop,
|
||||
.ndo_start_xmit = eth_start_xmit,
|
||||
.ndo_change_mtu = ueth_change_mtu,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
static struct device_type gadget_type = {
|
||||
.name = "gadget",
|
||||
};
|
||||
|
||||
/**
|
||||
* gether_setup_name - initialize one ethernet-over-usb link
|
||||
* @g: gadget to associated with these links
|
||||
* @ethaddr: NULL, or a buffer in which the ethernet address of the
|
||||
* host side of the link is recorded
|
||||
* @netname: name for network device (for example, "usb")
|
||||
* Context: may sleep
|
||||
*
|
||||
* This sets up the single network link that may be exported by a
|
||||
* gadget driver using this framework. The link layer addresses are
|
||||
* set up using module parameters.
|
||||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
const char *netname)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
struct net_device *net;
|
||||
int status;
|
||||
|
||||
if (the_dev)
|
||||
return -EBUSY;
|
||||
|
||||
net = alloc_etherdev(sizeof *dev);
|
||||
if (!net)
|
||||
return -ENOMEM;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
spin_lock_init(&dev->lock);
|
||||
spin_lock_init(&dev->req_lock);
|
||||
INIT_WORK(&dev->work, eth_work);
|
||||
INIT_LIST_HEAD(&dev->tx_reqs);
|
||||
INIT_LIST_HEAD(&dev->rx_reqs);
|
||||
|
||||
skb_queue_head_init(&dev->rx_frames);
|
||||
|
||||
/* network device setup */
|
||||
dev->net = net;
|
||||
snprintf(net->name, sizeof(net->name), "%s%%d", netname);
|
||||
|
||||
if (get_ether_addr(dev_addr, net->dev_addr))
|
||||
dev_warn(&g->dev,
|
||||
"using random %s ethernet address\n", "self");
|
||||
if (get_ether_addr(host_addr, dev->host_mac))
|
||||
dev_warn(&g->dev,
|
||||
"using random %s ethernet address\n", "host");
|
||||
|
||||
if (ethaddr)
|
||||
memcpy(ethaddr, dev->host_mac, ETH_ALEN);
|
||||
|
||||
net->netdev_ops = ð_netdev_ops;
|
||||
|
||||
SET_ETHTOOL_OPS(net, &ops);
|
||||
|
||||
dev->gadget = g;
|
||||
SET_NETDEV_DEV(net, &g->dev);
|
||||
SET_NETDEV_DEVTYPE(net, &gadget_type);
|
||||
|
||||
status = register_netdev(net);
|
||||
if (status < 0) {
|
||||
dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
|
||||
free_netdev(net);
|
||||
} else {
|
||||
INFO(dev, "MAC %pM\n", net->dev_addr);
|
||||
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
|
||||
|
||||
the_dev = dev;
|
||||
|
||||
/* two kinds of host-initiated state changes:
|
||||
* - iff DATA transfer is active, carrier is "on"
|
||||
* - tx queueing enabled if open *and* carrier is "on"
|
||||
*/
|
||||
netif_carrier_off(net);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* gether_cleanup - remove Ethernet-over-USB device
|
||||
* Context: may sleep
|
||||
*
|
||||
* This is called to free all resources allocated by @gether_setup().
|
||||
*/
|
||||
void gether_cleanup(void)
|
||||
{
|
||||
if (!the_dev)
|
||||
return;
|
||||
|
||||
unregister_netdev(the_dev->net);
|
||||
flush_work_sync(&the_dev->work);
|
||||
free_netdev(the_dev->net);
|
||||
|
||||
the_dev = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gether_connect - notify network layer that USB link is active
|
||||
* @link: the USB link, set up with endpoints, descriptors matching
|
||||
* current device speed, and any framing wrapper(s) set up.
|
||||
* Context: irqs blocked
|
||||
*
|
||||
* This is called to activate endpoints and let the network layer know
|
||||
* the connection is active ("carrier detect"). It may cause the I/O
|
||||
* queues to open and start letting network packets flow, but will in
|
||||
* any case activate the endpoints so that they respond properly to the
|
||||
* USB host.
|
||||
*
|
||||
* Verify net_device pointer returned using IS_ERR(). If it doesn't
|
||||
* indicate some error code (negative errno), ep->driver_data values
|
||||
* have been overwritten.
|
||||
*/
|
||||
struct net_device *gether_connect(struct gether *link)
|
||||
{
|
||||
struct eth_dev *dev = the_dev;
|
||||
int result = 0;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
link->in_ep->driver_data = dev;
|
||||
result = usb_ep_enable(link->in_ep);
|
||||
if (result != 0) {
|
||||
DBG(dev, "enable %s --> %d\n",
|
||||
link->in_ep->name, result);
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
link->out_ep->driver_data = dev;
|
||||
result = usb_ep_enable(link->out_ep);
|
||||
if (result != 0) {
|
||||
DBG(dev, "enable %s --> %d\n",
|
||||
link->out_ep->name, result);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
result = alloc_requests(dev, link, qlen(dev->gadget));
|
||||
|
||||
if (result == 0) {
|
||||
dev->zlp = link->is_zlp_ok;
|
||||
DBG(dev, "qlen %d\n", qlen(dev->gadget));
|
||||
|
||||
dev->header_len = link->header_len;
|
||||
dev->unwrap = link->unwrap;
|
||||
dev->wrap = link->wrap;
|
||||
|
||||
spin_lock(&dev->lock);
|
||||
dev->port_usb = link;
|
||||
link->ioport = dev;
|
||||
if (netif_running(dev->net)) {
|
||||
if (link->open)
|
||||
link->open(link);
|
||||
} else {
|
||||
if (link->close)
|
||||
link->close(link);
|
||||
}
|
||||
spin_unlock(&dev->lock);
|
||||
|
||||
netif_carrier_on(dev->net);
|
||||
if (netif_running(dev->net))
|
||||
eth_start(dev, GFP_ATOMIC);
|
||||
|
||||
/* on error, disable any endpoints */
|
||||
} else {
|
||||
(void) usb_ep_disable(link->out_ep);
|
||||
fail1:
|
||||
(void) usb_ep_disable(link->in_ep);
|
||||
}
|
||||
fail0:
|
||||
/* caller is responsible for cleanup on error */
|
||||
if (result < 0)
|
||||
return ERR_PTR(result);
|
||||
return dev->net;
|
||||
}
|
||||
|
||||
/**
|
||||
* gether_disconnect - notify network layer that USB link is inactive
|
||||
* @link: the USB link, on which gether_connect() was called
|
||||
* Context: irqs blocked
|
||||
*
|
||||
* This is called to deactivate endpoints and let the network layer know
|
||||
* the connection went inactive ("no carrier").
|
||||
*
|
||||
* On return, the state is as if gether_connect() had never been called.
|
||||
* The endpoints are inactive, and accordingly without active USB I/O.
|
||||
* Pointers to endpoint descriptors and endpoint private data are nulled.
|
||||
*/
|
||||
void gether_disconnect(struct gether *link)
|
||||
{
|
||||
struct eth_dev *dev = link->ioport;
|
||||
struct usb_request *req;
|
||||
|
||||
WARN_ON(!dev);
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
DBG(dev, "%s\n", __func__);
|
||||
|
||||
netif_stop_queue(dev->net);
|
||||
netif_carrier_off(dev->net);
|
||||
|
||||
/* disable endpoints, forcing (synchronous) completion
|
||||
* of all pending i/o. then free the request objects
|
||||
* and forget about the endpoints.
|
||||
*/
|
||||
usb_ep_disable(link->in_ep);
|
||||
spin_lock(&dev->req_lock);
|
||||
while (!list_empty(&dev->tx_reqs)) {
|
||||
req = container_of(dev->tx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_del(&req->list);
|
||||
|
||||
spin_unlock(&dev->req_lock);
|
||||
usb_ep_free_request(link->in_ep, req);
|
||||
spin_lock(&dev->req_lock);
|
||||
}
|
||||
spin_unlock(&dev->req_lock);
|
||||
link->in_ep->driver_data = NULL;
|
||||
link->in_ep->desc = NULL;
|
||||
|
||||
usb_ep_disable(link->out_ep);
|
||||
spin_lock(&dev->req_lock);
|
||||
while (!list_empty(&dev->rx_reqs)) {
|
||||
req = container_of(dev->rx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_del(&req->list);
|
||||
|
||||
spin_unlock(&dev->req_lock);
|
||||
usb_ep_free_request(link->out_ep, req);
|
||||
spin_lock(&dev->req_lock);
|
||||
}
|
||||
spin_unlock(&dev->req_lock);
|
||||
link->out_ep->driver_data = NULL;
|
||||
link->out_ep->desc = NULL;
|
||||
|
||||
/* finish forgetting about this USB link episode */
|
||||
dev->header_len = 0;
|
||||
dev->unwrap = NULL;
|
||||
dev->wrap = NULL;
|
||||
|
||||
spin_lock(&dev->lock);
|
||||
dev->port_usb = NULL;
|
||||
link->ioport = NULL;
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
154
drivers/staging/ccg/u_ether.h
Normal file
154
drivers/staging/ccg/u_ether.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* u_ether.h -- interface to USB gadget "ethernet link" utilities
|
||||
*
|
||||
* Copyright (C) 2003-2005,2008 David Brownell
|
||||
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __U_ETHER_H
|
||||
#define __U_ETHER_H
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/*
|
||||
* This represents the USB side of an "ethernet" link, managed by a USB
|
||||
* function which provides control and (maybe) framing. Two functions
|
||||
* in different configurations could share the same ethernet link/netdev,
|
||||
* using different host interaction models.
|
||||
*
|
||||
* There is a current limitation that only one instance of this link may
|
||||
* be present in any given configuration. When that's a problem, network
|
||||
* layer facilities can be used to package multiple logical links on this
|
||||
* single "physical" one.
|
||||
*/
|
||||
struct gether {
|
||||
struct usb_function func;
|
||||
|
||||
/* updated by gether_{connect,disconnect} */
|
||||
struct eth_dev *ioport;
|
||||
|
||||
/* endpoints handle full and/or high speeds */
|
||||
struct usb_ep *in_ep;
|
||||
struct usb_ep *out_ep;
|
||||
|
||||
bool is_zlp_ok;
|
||||
|
||||
u16 cdc_filter;
|
||||
|
||||
/* hooks for added framing, as needed for RNDIS and EEM. */
|
||||
u32 header_len;
|
||||
/* NCM requires fixed size bundles */
|
||||
bool is_fixed;
|
||||
u32 fixed_out_len;
|
||||
u32 fixed_in_len;
|
||||
struct sk_buff *(*wrap)(struct gether *port,
|
||||
struct sk_buff *skb);
|
||||
int (*unwrap)(struct gether *port,
|
||||
struct sk_buff *skb,
|
||||
struct sk_buff_head *list);
|
||||
|
||||
/* called on network open/close */
|
||||
void (*open)(struct gether *);
|
||||
void (*close)(struct gether *);
|
||||
};
|
||||
|
||||
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
|
||||
|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
|
||||
|USB_CDC_PACKET_TYPE_PROMISCUOUS \
|
||||
|USB_CDC_PACKET_TYPE_DIRECTED)
|
||||
|
||||
/* variant of gether_setup that allows customizing network device name */
|
||||
int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
const char *netname);
|
||||
|
||||
/* netdev setup/teardown as directed by the gadget driver */
|
||||
/* gether_setup - initialize one ethernet-over-usb link
|
||||
* @g: gadget to associated with these links
|
||||
* @ethaddr: NULL, or a buffer in which the ethernet address of the
|
||||
* host side of the link is recorded
|
||||
* Context: may sleep
|
||||
*
|
||||
* This sets up the single network link that may be exported by a
|
||||
* gadget driver using this framework. The link layer addresses are
|
||||
* set up using module parameters.
|
||||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
static inline int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
return gether_setup_name(g, ethaddr, "usb");
|
||||
}
|
||||
|
||||
void gether_cleanup(void);
|
||||
|
||||
/* connect/disconnect is handled by individual functions */
|
||||
struct net_device *gether_connect(struct gether *);
|
||||
void gether_disconnect(struct gether *);
|
||||
|
||||
/* Some controllers can't support CDC Ethernet (ECM) ... */
|
||||
static inline bool can_support_ecm(struct usb_gadget *gadget)
|
||||
{
|
||||
if (!gadget_supports_altsettings(gadget))
|
||||
return false;
|
||||
|
||||
/* Everything else is *presumably* fine ... but this is a bit
|
||||
* chancy, so be **CERTAIN** there are no hardware issues with
|
||||
* your controller. Add it above if it can't handle CDC.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/* each configuration may bind one instance of an ethernet link */
|
||||
int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int eem_bind_config(struct usb_configuration *c);
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
|
||||
int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer);
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* rndis_bind_config - add RNDIS network link to a configuration
|
||||
* @c: the configuration to support the network link
|
||||
* @ethaddr: a buffer in which the ethernet address of the host side
|
||||
* side of the link was recorded
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
static inline int rndis_bind_config(struct usb_configuration *c,
|
||||
u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
return rndis_bind_config_vendor(c, ethaddr, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
#endif /* __U_ETHER_H */
|
1341
drivers/staging/ccg/u_serial.c
Normal file
1341
drivers/staging/ccg/u_serial.c
Normal file
File diff suppressed because it is too large
Load Diff
65
drivers/staging/ccg/u_serial.h
Normal file
65
drivers/staging/ccg/u_serial.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* u_serial.h - interface to USB gadget "serial port"/TTY utilities
|
||||
*
|
||||
* Copyright (C) 2008 David Brownell
|
||||
* Copyright (C) 2008 by Nokia Corporation
|
||||
*
|
||||
* This software is distributed under the terms of the GNU General
|
||||
* Public License ("GPL") as published by the Free Software Foundation,
|
||||
* either version 2 of that License or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __U_SERIAL_H
|
||||
#define __U_SERIAL_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
|
||||
/*
|
||||
* One non-multiplexed "serial" I/O port ... there can be several of these
|
||||
* on any given USB peripheral device, if it provides enough endpoints.
|
||||
*
|
||||
* The "u_serial" utility component exists to do one thing: manage TTY
|
||||
* style I/O using the USB peripheral endpoints listed here, including
|
||||
* hookups to sysfs and /dev for each logical "tty" device.
|
||||
*
|
||||
* REVISIT at least ACM could support tiocmget() if needed.
|
||||
*
|
||||
* REVISIT someday, allow multiplexing several TTYs over these endpoints.
|
||||
*/
|
||||
struct gserial {
|
||||
struct usb_function func;
|
||||
|
||||
/* port is managed by gserial_{connect,disconnect} */
|
||||
struct gs_port *ioport;
|
||||
|
||||
struct usb_ep *in;
|
||||
struct usb_ep *out;
|
||||
|
||||
/* REVISIT avoid this CDC-ACM support harder ... */
|
||||
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
|
||||
|
||||
/* notification callbacks */
|
||||
void (*connect)(struct gserial *p);
|
||||
void (*disconnect)(struct gserial *p);
|
||||
int (*send_break)(struct gserial *p, int duration);
|
||||
};
|
||||
|
||||
/* utilities to allocate/free request and buffer */
|
||||
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);
|
||||
|
||||
/* connect/disconnect is handled by individual functions */
|
||||
int gserial_connect(struct gserial *, u8 port_num);
|
||||
void gserial_disconnect(struct gserial *);
|
||||
|
||||
/* functions are bound to configurations by a config or gadget driver */
|
||||
int acm_bind_config(struct usb_configuration *c, u8 port_num);
|
||||
int gser_bind_config(struct usb_configuration *c, u8 port_num);
|
||||
int obex_bind_config(struct usb_configuration *c, u8 port_num);
|
||||
|
||||
#endif /* __U_SERIAL_H */
|
71
drivers/staging/ccg/usbstring.c
Normal file
71
drivers/staging/ccg/usbstring.c
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2003 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
|
||||
/**
|
||||
* usb_gadget_get_string - fill out a string descriptor
|
||||
* @table: of c strings encoded using UTF-8
|
||||
* @id: string id, from low byte of wValue in get string descriptor
|
||||
* @buf: at least 256 bytes, must be 16-bit aligned
|
||||
*
|
||||
* Finds the UTF-8 string matching the ID, and converts it into a
|
||||
* string descriptor in utf16-le.
|
||||
* Returns length of descriptor (always even) or negative errno
|
||||
*
|
||||
* If your driver needs stings in multiple languages, you'll probably
|
||||
* "switch (wIndex) { ... }" in your ep0 string descriptor logic,
|
||||
* using this routine after choosing which set of UTF-8 strings to use.
|
||||
* Note that US-ASCII is a strict subset of UTF-8; any string bytes with
|
||||
* the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
|
||||
* characters (which are also widely used in C strings).
|
||||
*/
|
||||
int
|
||||
usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
|
||||
{
|
||||
struct usb_string *s;
|
||||
int len;
|
||||
|
||||
/* descriptor 0 has the language id */
|
||||
if (id == 0) {
|
||||
buf [0] = 4;
|
||||
buf [1] = USB_DT_STRING;
|
||||
buf [2] = (u8) table->language;
|
||||
buf [3] = (u8) (table->language >> 8);
|
||||
return 4;
|
||||
}
|
||||
for (s = table->strings; s && s->s; s++)
|
||||
if (s->id == id)
|
||||
break;
|
||||
|
||||
/* unrecognized: stall. */
|
||||
if (!s || !s->s)
|
||||
return -EINVAL;
|
||||
|
||||
/* string descriptors have length, tag, then UTF16-LE text */
|
||||
len = min ((size_t) 126, strlen (s->s));
|
||||
len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN,
|
||||
(wchar_t *) &buf[2], 126);
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
buf [0] = (len + 1) * 2;
|
||||
buf [1] = USB_DT_STRING;
|
||||
return buf [0];
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
|
||||
|
||||
us->subclass = idesc->bInterfaceSubClass;
|
||||
us->protocol = idesc->bInterfaceProtocol;
|
||||
us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
|
||||
us->fflags = id->driver_info;
|
||||
us->Power_IsResum = false;
|
||||
|
||||
if (us->fflags & US_FL_IGNORE_DEVICE)
|
||||
|
@ -16,8 +16,6 @@
|
||||
#include <linux/usb/serial.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
static bool debug;
|
||||
|
||||
/* Version Information */
|
||||
#define DRIVER_VERSION "v2.14"
|
||||
#define DRIVER_AUTHOR "Tim Gobeli, Quatech, Inc"
|
||||
@ -184,11 +182,11 @@ static int port_paranoia_check(struct usb_serial_port *port,
|
||||
const char *function)
|
||||
{
|
||||
if (!port) {
|
||||
dbg("%s - port == NULL", function);
|
||||
pr_debug("%s - port == NULL", function);
|
||||
return -1;
|
||||
}
|
||||
if (!port->serial) {
|
||||
dbg("%s - port->serial == NULL\n", function);
|
||||
pr_debug("%s - port->serial == NULL\n", function);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -199,12 +197,12 @@ static int serial_paranoia_check(struct usb_serial *serial,
|
||||
const char *function)
|
||||
{
|
||||
if (!serial) {
|
||||
dbg("%s - serial == NULL\n", function);
|
||||
pr_debug("%s - serial == NULL\n", function);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!serial->type) {
|
||||
dbg("%s - serial->type == NULL!", function);
|
||||
pr_debug("%s - serial->type == NULL!", function);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -274,7 +272,7 @@ static void qt_write_bulk_callback(struct urb *urb)
|
||||
status = urb->status;
|
||||
|
||||
if (status) {
|
||||
dbg("nonzero write bulk status received:%d\n", status);
|
||||
dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -307,8 +305,8 @@ static void qt_read_bulk_callback(struct urb *urb)
|
||||
|
||||
if (urb->status) {
|
||||
qt_port->ReadBulkStopped = 1;
|
||||
dbg("%s - nonzero write bulk status received: %d\n",
|
||||
__func__, urb->status);
|
||||
dev_dbg(&urb->dev->dev, "%s - nonzero write bulk status received: %d\n",
|
||||
__func__, urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -323,21 +321,19 @@ static void qt_read_bulk_callback(struct urb *urb)
|
||||
/* index = MINOR(port->tty->device) - serial->minor; */
|
||||
index = tty->index - serial->minor;
|
||||
|
||||
dbg("%s - port->RxHolding = %d\n", __func__, qt_port->RxHolding);
|
||||
dev_dbg(&port->dev, "%s - port->RxHolding = %d\n", __func__, qt_port->RxHolding);
|
||||
|
||||
if (port_paranoia_check(port, __func__) != 0) {
|
||||
dbg("%s - port_paranoia_check, exiting\n", __func__);
|
||||
qt_port->ReadBulkStopped = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!serial) {
|
||||
dbg("%s - bad serial pointer, exiting\n", __func__);
|
||||
if (!serial)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (qt_port->closePending == 1) {
|
||||
/* Were closing , stop reading */
|
||||
dbg("%s - (qt_port->closepending == 1\n", __func__);
|
||||
dev_dbg(&port->dev, "%s - (qt_port->closepending == 1\n", __func__);
|
||||
qt_port->ReadBulkStopped = 1;
|
||||
goto exit;
|
||||
}
|
||||
@ -355,8 +351,8 @@ static void qt_read_bulk_callback(struct urb *urb)
|
||||
if (urb->status) {
|
||||
qt_port->ReadBulkStopped = 1;
|
||||
|
||||
dbg("%s - nonzero read bulk status received: %d\n",
|
||||
__func__, urb->status);
|
||||
dev_dbg(&port->dev, "%s - nonzero read bulk status received: %d\n",
|
||||
__func__, urb->status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@ -371,7 +367,7 @@ static void qt_read_bulk_callback(struct urb *urb)
|
||||
case 0x00:
|
||||
/* line status change 4th byte must follow */
|
||||
if (i > (RxCount - 4)) {
|
||||
dbg("Illegal escape seuences in received data\n");
|
||||
dev_dbg(&port->dev, "Illegal escape seuences in received data\n");
|
||||
break;
|
||||
}
|
||||
ProcessLineStatus(qt_port, data[i + 3]);
|
||||
@ -381,9 +377,9 @@ static void qt_read_bulk_callback(struct urb *urb)
|
||||
|
||||
case 0x01:
|
||||
/* Modem status status change 4th byte must follow */
|
||||
dbg("Modem status status.\n");
|
||||
dev_dbg(&port->dev, "Modem status status.\n");
|
||||
if (i > (RxCount - 4)) {
|
||||
dbg("Illegal escape sequences in received data\n");
|
||||
dev_dbg(&port->dev, "Illegal escape sequences in received data\n");
|
||||
break;
|
||||
}
|
||||
ProcessModemStatus(qt_port,
|
||||
@ -392,7 +388,7 @@ static void qt_read_bulk_callback(struct urb *urb)
|
||||
flag = 1;
|
||||
break;
|
||||
case 0xff:
|
||||
dbg("No status sequence.\n");
|
||||
dev_dbg(&port->dev, "No status sequence.\n");
|
||||
|
||||
if (tty) {
|
||||
ProcessRxChar(tty, port, data[i]);
|
||||
@ -421,8 +417,8 @@ static void qt_read_bulk_callback(struct urb *urb)
|
||||
qt_read_bulk_callback, port);
|
||||
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dbg("%s - failed resubmitting read urb, error %d",
|
||||
__func__, result);
|
||||
dev_dbg(&port->dev, "%s - failed resubmitting read urb, error %d",
|
||||
__func__, result);
|
||||
else {
|
||||
if (RxCount) {
|
||||
tty_flip_buffer_push(tty);
|
||||
@ -517,7 +513,6 @@ static int qt_set_device(struct usb_serial *serial,
|
||||
PortSettings += ((__u16) (device_data->porta));
|
||||
|
||||
length = sizeof(struct qt_get_device_data);
|
||||
dbg("%s - PortSettings = 0x%x\n", __func__, PortSettings);
|
||||
|
||||
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
||||
QT_SET_GET_DEVICE, 0x40, PortSettings,
|
||||
@ -691,6 +686,7 @@ static int BoxDisable_SW_FlowCtrl(struct usb_serial *serial, __u16 index)
|
||||
|
||||
static int qt_startup(struct usb_serial *serial)
|
||||
{
|
||||
struct device *dev = &serial->dev->dev;
|
||||
struct usb_serial_port *port;
|
||||
struct quatech_port *qt_port;
|
||||
struct qt_get_device_data DeviceData;
|
||||
@ -702,8 +698,6 @@ static int qt_startup(struct usb_serial *serial)
|
||||
port = serial->port[i];
|
||||
qt_port = kzalloc(sizeof(*qt_port), GFP_KERNEL);
|
||||
if (!qt_port) {
|
||||
dbg("%s: kzalloc for quatech_port (%d) failed!.",
|
||||
__func__, i);
|
||||
for (--i; i >= 0; i--) {
|
||||
port = serial->port[i];
|
||||
kfree(usb_get_serial_port_data(port));
|
||||
@ -718,25 +712,23 @@ static int qt_startup(struct usb_serial *serial)
|
||||
}
|
||||
|
||||
status = qt_get_device(serial, &DeviceData);
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "box_get_device failed");
|
||||
if (status < 0)
|
||||
goto startup_error;
|
||||
}
|
||||
|
||||
dbg(__FILE__ "DeviceData.portb = 0x%x", DeviceData.portb);
|
||||
dev_dbg(dev, "DeviceData.portb = 0x%x\n", DeviceData.portb);
|
||||
|
||||
DeviceData.portb &= ~FULLPWRBIT;
|
||||
dbg(__FILE__ "Changing DeviceData.portb to 0x%x", DeviceData.portb);
|
||||
dev_dbg(dev, "Changing DeviceData.portb to 0x%x\n", DeviceData.portb);
|
||||
|
||||
status = qt_set_device(serial, &DeviceData);
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "qt_set_device failed\n");
|
||||
dev_dbg(dev, "qt_set_device failed\n");
|
||||
goto startup_error;
|
||||
}
|
||||
|
||||
status = qt_get_device(serial, &DeviceData);
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "qt_get_device failed");
|
||||
dev_dbg(dev, "qt_get_device failed\n");
|
||||
goto startup_error;
|
||||
}
|
||||
|
||||
@ -780,29 +772,27 @@ static int qt_startup(struct usb_serial *serial)
|
||||
|
||||
status = BoxSetPrebufferLevel(serial); /* sets to default value */
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "BoxSetPrebufferLevel failed\n");
|
||||
dev_dbg(dev, "BoxSetPrebufferLevel failed\n");
|
||||
goto startup_error;
|
||||
}
|
||||
|
||||
status = BoxSetATC(serial, ATC_DISABLED);
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "BoxSetATC failed\n");
|
||||
dev_dbg(dev, "BoxSetATC failed\n");
|
||||
goto startup_error;
|
||||
}
|
||||
|
||||
dbg(__FILE__ "DeviceData.portb = 0x%x", DeviceData.portb);
|
||||
dev_dbg(dev, "DeviceData.portb = 0x%x\n", DeviceData.portb);
|
||||
|
||||
DeviceData.portb |= NEXT_BOARD_POWER_BIT;
|
||||
dbg(__FILE__ "Changing DeviceData.portb to 0x%x", DeviceData.portb);
|
||||
dev_dbg(dev, "Changing DeviceData.portb to 0x%x\n", DeviceData.portb);
|
||||
|
||||
status = qt_set_device(serial, &DeviceData);
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "qt_set_device failed\n");
|
||||
dev_dbg(dev, "qt_set_device failed\n");
|
||||
goto startup_error;
|
||||
}
|
||||
|
||||
dbg("Exit Success %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
startup_error:
|
||||
@ -813,8 +803,6 @@ startup_error:
|
||||
usb_set_serial_port_data(port, NULL);
|
||||
}
|
||||
|
||||
dbg("Exit fail %s\n", __func__);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -869,10 +857,10 @@ static int qt_open(struct tty_struct *tty,
|
||||
/* Port specific setups */
|
||||
result = qt_open_channel(serial, port->number, &ChannelData);
|
||||
if (result < 0) {
|
||||
dbg(__FILE__ "qt_open_channel failed\n");
|
||||
dev_dbg(&port->dev, "qt_open_channel failed\n");
|
||||
return result;
|
||||
}
|
||||
dbg(__FILE__ "qt_open_channel completed.\n");
|
||||
dev_dbg(&port->dev, "qt_open_channel completed.\n");
|
||||
|
||||
/* FIXME: are these needed? Does it even do anything useful? */
|
||||
quatech_port->shadowLSR = ChannelData.line_status &
|
||||
@ -884,10 +872,10 @@ static int qt_open(struct tty_struct *tty,
|
||||
/* Set Baud rate to default and turn off (default)flow control here */
|
||||
result = qt_setuart(serial, port->number, DEFAULT_DIVISOR, DEFAULT_LCR);
|
||||
if (result < 0) {
|
||||
dbg(__FILE__ "qt_setuart failed\n");
|
||||
dev_dbg(&port->dev, "qt_setuart failed\n");
|
||||
return result;
|
||||
}
|
||||
dbg(__FILE__ "qt_setuart completed.\n");
|
||||
dev_dbg(&port->dev, "qt_setuart completed.\n");
|
||||
|
||||
/*
|
||||
* Put this here to make it responsive to stty and defaults set by
|
||||
@ -922,12 +910,12 @@ static int qt_open(struct tty_struct *tty,
|
||||
|
||||
}
|
||||
|
||||
dbg("port number is %d\n", port->number);
|
||||
dbg("serial number is %d\n", port->serial->minor);
|
||||
dbg("Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
|
||||
dbg("BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
|
||||
dbg("Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
|
||||
dbg("port's number in the device is %d\n", quatech_port->port_num);
|
||||
dev_dbg(&port->dev, "port number is %d\n", port->number);
|
||||
dev_dbg(&port->dev, "serial number is %d\n", port->serial->minor);
|
||||
dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
|
||||
dev_dbg(&port->dev, "BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
|
||||
dev_dbg(&port->dev, "Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
|
||||
dev_dbg(&port->dev, "port's number in the device is %d\n", quatech_port->port_num);
|
||||
quatech_port->read_urb = port->read_urb;
|
||||
|
||||
/* set up our bulk in urb */
|
||||
@ -940,7 +928,7 @@ static int qt_open(struct tty_struct *tty,
|
||||
quatech_port->read_urb->transfer_buffer_length,
|
||||
qt_read_bulk_callback, quatech_port);
|
||||
|
||||
dbg("qt_open: bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
|
||||
dev_dbg(&port->dev, "qt_open: bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
|
||||
quatech_port->read_urb_busy = true;
|
||||
result = usb_submit_urb(quatech_port->read_urb, GFP_KERNEL);
|
||||
if (result) {
|
||||
@ -974,8 +962,6 @@ static int qt_chars_in_buffer(struct tty_struct *tty)
|
||||
chars = port->write_urb->transfer_buffer_length;
|
||||
}
|
||||
|
||||
dbg("%s - returns %d\n", __func__, chars);
|
||||
|
||||
return chars;
|
||||
}
|
||||
|
||||
@ -997,7 +983,7 @@ static void qt_block_until_empty(struct tty_struct *tty,
|
||||
|
||||
wait--;
|
||||
if (wait == 0) {
|
||||
dbg("%s - TIMEOUT", __func__);
|
||||
dev_dbg(&qt_port->port->dev, "%s - TIMEOUT", __func__);
|
||||
return;
|
||||
} else {
|
||||
wait = 30;
|
||||
@ -1035,17 +1021,15 @@ static void qt_close(struct usb_serial_port *port)
|
||||
/* Close uart channel */
|
||||
status = qt_close_channel(serial, index);
|
||||
if (status < 0)
|
||||
dbg("%s - port %d qt_close_channel failed.\n",
|
||||
__func__, port->number);
|
||||
dev_dbg(&port->dev, "%s - port %d qt_close_channel failed.\n", __func__, port->number);
|
||||
|
||||
port0->open_ports--;
|
||||
|
||||
dbg("qt_num_open_ports in close%d:in port%d\n",
|
||||
port0->open_ports, port->number);
|
||||
dev_dbg(&port->dev, "qt_num_open_ports in close%d:in port%d\n", port0->open_ports, port->number);
|
||||
|
||||
if (port0->open_ports == 0) {
|
||||
if (serial->port[0]->interrupt_in_urb) {
|
||||
dbg("%s", "Shutdown interrupt_in_urb\n");
|
||||
dev_dbg(&port->dev, "%s", "Shutdown interrupt_in_urb\n");
|
||||
usb_kill_urb(serial->port[0]->interrupt_in_urb);
|
||||
}
|
||||
|
||||
@ -1069,14 +1053,14 @@ static int qt_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
return -ENODEV;
|
||||
|
||||
if (count == 0) {
|
||||
dbg("%s - write request of 0 bytes\n", __func__);
|
||||
dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* only do something if we have a bulk out endpoint */
|
||||
if (serial->num_bulk_out) {
|
||||
if (port->write_urb->status == -EINPROGRESS) {
|
||||
dbg("%s - already writing\n", __func__);
|
||||
dev_dbg(&port->dev, "%s - already writing\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1096,8 +1080,8 @@ static int qt_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
/* send the data out the bulk port */
|
||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dbg("%s - failed submitting write urb, error %d\n",
|
||||
__func__, result);
|
||||
dev_dbg(&port->dev, "%s - failed submitting write urb, error %d\n",
|
||||
__func__, result);
|
||||
else
|
||||
result = count;
|
||||
|
||||
@ -1116,10 +1100,8 @@ static int qt_write_room(struct tty_struct *tty)
|
||||
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (port_paranoia_check(port, __func__)) {
|
||||
dbg("%s", "Invalid port\n");
|
||||
if (port_paranoia_check(port, __func__))
|
||||
return -1;
|
||||
}
|
||||
|
||||
serial = get_usb_serial(port, __func__);
|
||||
|
||||
@ -1148,7 +1130,7 @@ static int qt_ioctl(struct tty_struct *tty,
|
||||
struct usb_serial *serial = get_usb_serial(port, __func__);
|
||||
unsigned int index;
|
||||
|
||||
dbg("%s cmd 0x%04x", __func__, cmd);
|
||||
dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
|
||||
|
||||
index = tty->index - serial->minor;
|
||||
|
||||
@ -1181,7 +1163,7 @@ static int qt_ioctl(struct tty_struct *tty,
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbg("%s -No ioctl for that one. port = %d\n", __func__, port->number);
|
||||
dev_dbg(&port->dev, "%s -No ioctl for that one. port = %d\n", __func__, port->number);
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
@ -1228,7 +1210,7 @@ static void qt_set_termios(struct tty_struct *tty,
|
||||
else
|
||||
new_LCR |= SERIAL_ONE_STOPB;
|
||||
|
||||
dbg("%s - 4\n", __func__);
|
||||
dev_dbg(&port->dev, "%s - 4\n", __func__);
|
||||
|
||||
/* Thats the LCR stuff, go ahead and set it */
|
||||
baud = tty_get_baud_rate(tty);
|
||||
@ -1236,7 +1218,7 @@ static void qt_set_termios(struct tty_struct *tty,
|
||||
/* pick a default, any default... */
|
||||
baud = 9600;
|
||||
|
||||
dbg("%s - got baud = %d\n", __func__, baud);
|
||||
dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
|
||||
|
||||
divisor = MAX_BAUD_RATE / baud;
|
||||
remainder = MAX_BAUD_RATE % baud;
|
||||
@ -1250,30 +1232,28 @@ static void qt_set_termios(struct tty_struct *tty,
|
||||
status =
|
||||
qt_setuart(port->serial, index, (unsigned short)divisor, new_LCR);
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "qt_setuart failed\n");
|
||||
dev_dbg(&port->dev, "qt_setuart failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now determine flow control */
|
||||
if (cflag & CRTSCTS) {
|
||||
dbg("%s - Enabling HW flow control port %d\n", __func__,
|
||||
port->number);
|
||||
dev_dbg(&port->dev, "%s - Enabling HW flow control port %d\n", __func__, port->number);
|
||||
|
||||
/* Enable RTS/CTS flow control */
|
||||
status = BoxSetHW_FlowCtrl(port->serial, index, 1);
|
||||
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "BoxSetHW_FlowCtrl failed\n");
|
||||
dev_dbg(&port->dev, "BoxSetHW_FlowCtrl failed\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Disable RTS/CTS flow control */
|
||||
dbg("%s - disabling HW flow control port %d\n", __func__,
|
||||
port->number);
|
||||
dev_dbg(&port->dev, "%s - disabling HW flow control port %d\n", __func__, port->number);
|
||||
|
||||
status = BoxSetHW_FlowCtrl(port->serial, index, 0);
|
||||
if (status < 0) {
|
||||
dbg(__FILE__ "BoxSetHW_FlowCtrl failed\n");
|
||||
dev_dbg(&port->dev, "BoxSetHW_FlowCtrl failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1288,13 +1268,13 @@ static void qt_set_termios(struct tty_struct *tty,
|
||||
BoxSetSW_FlowCtrl(port->serial, index, stop_char,
|
||||
start_char);
|
||||
if (status < 0)
|
||||
dbg(__FILE__ "BoxSetSW_FlowCtrl (enabled) failed\n");
|
||||
dev_dbg(&port->dev, "BoxSetSW_FlowCtrl (enabled) failed\n");
|
||||
|
||||
} else {
|
||||
/* disable SW flow control */
|
||||
status = BoxDisable_SW_FlowCtrl(port->serial, index);
|
||||
if (status < 0)
|
||||
dbg(__FILE__ "BoxSetSW_FlowCtrl (diabling) failed\n");
|
||||
dev_dbg(&port->dev, "BoxSetSW_FlowCtrl (diabling) failed\n");
|
||||
|
||||
}
|
||||
termios->c_cflag &= ~CMSPAR;
|
||||
@ -1471,10 +1451,10 @@ static void qt_unthrottle(struct tty_struct *tty)
|
||||
mutex_lock(&qt_port->lock);
|
||||
|
||||
if (qt_port->RxHolding == 1) {
|
||||
dbg("%s -qt_port->RxHolding == 1\n", __func__);
|
||||
dev_dbg(&port->dev, "%s -qt_port->RxHolding == 1\n", __func__);
|
||||
|
||||
qt_port->RxHolding = 0;
|
||||
dbg("%s - qt_port->RxHolding = 0\n", __func__);
|
||||
dev_dbg(&port->dev, "%s - qt_port->RxHolding = 0\n", __func__);
|
||||
|
||||
/* if we have a bulk endpoint, start it up */
|
||||
if ((serial->num_bulk_in) && (qt_port->ReadBulkStopped == 1)) {
|
||||
@ -1500,11 +1480,6 @@ static int qt_calc_num_ports(struct usb_serial *serial)
|
||||
{
|
||||
int num_ports;
|
||||
|
||||
dbg("numberofendpoints: %d\n",
|
||||
(int)serial->interface->cur_altsetting->desc.bNumEndpoints);
|
||||
dbg("numberofendpoints: %d\n",
|
||||
(int)serial->interface->altsetting->desc.bNumEndpoints);
|
||||
|
||||
num_ports =
|
||||
(serial->interface->cur_altsetting->desc.bNumEndpoints - 1) / 2;
|
||||
|
||||
@ -1545,6 +1520,3 @@ module_usb_serial_driver(serial_drivers, id_table);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||
|
@ -161,8 +161,7 @@ static void usbip_dump_usb_device(struct usb_device *udev)
|
||||
dev_dbg(dev, "have_langid %d, string_langid %d\n",
|
||||
udev->have_langid, udev->string_langid);
|
||||
|
||||
dev_dbg(dev, "maxchild %d, children %p\n",
|
||||
udev->maxchild, udev->children);
|
||||
dev_dbg(dev, "maxchild %d\n", udev->maxchild);
|
||||
}
|
||||
|
||||
static void usbip_dump_request_type(__u8 rt)
|
||||
|
@ -48,6 +48,7 @@ config USB_ARCH_HAS_EHCI
|
||||
default y if SPARC_LEON
|
||||
default y if ARCH_MMP
|
||||
default y if MACH_LOONGSON1
|
||||
default y if PLAT_ORION
|
||||
default PCI
|
||||
|
||||
# some non-PCI HCDs implement xHCI
|
||||
|
@ -307,6 +307,34 @@ enum {
|
||||
#define FW_GET_BYTE(p) (*((__u8 *) (p)))
|
||||
|
||||
#define FW_DIR "ueagle-atm/"
|
||||
#define EAGLE_FIRMWARE FW_DIR "eagle.fw"
|
||||
#define ADI930_FIRMWARE FW_DIR "adi930.fw"
|
||||
#define EAGLE_I_FIRMWARE FW_DIR "eagleI.fw"
|
||||
#define EAGLE_II_FIRMWARE FW_DIR "eagleII.fw"
|
||||
#define EAGLE_III_FIRMWARE FW_DIR "eagleIII.fw"
|
||||
#define EAGLE_IV_FIRMWARE FW_DIR "eagleIV.fw"
|
||||
|
||||
#define DSP4I_FIRMWARE FW_DIR "DSP4i.bin"
|
||||
#define DSP4P_FIRMWARE FW_DIR "DSP4p.bin"
|
||||
#define DSP9I_FIRMWARE FW_DIR "DSP9i.bin"
|
||||
#define DSP9P_FIRMWARE FW_DIR "DSP9p.bin"
|
||||
#define DSPEI_FIRMWARE FW_DIR "DSPei.bin"
|
||||
#define DSPEP_FIRMWARE FW_DIR "DSPep.bin"
|
||||
#define FPGA930_FIRMWARE FW_DIR "930-fpga.bin"
|
||||
|
||||
#define CMV4P_FIRMWARE FW_DIR "CMV4p.bin"
|
||||
#define CMV4PV2_FIRMWARE FW_DIR "CMV4p.bin.v2"
|
||||
#define CMV4I_FIRMWARE FW_DIR "CMV4i.bin"
|
||||
#define CMV4IV2_FIRMWARE FW_DIR "CMV4i.bin.v2"
|
||||
#define CMV9P_FIRMWARE FW_DIR "CMV9p.bin"
|
||||
#define CMV9PV2_FIRMWARE FW_DIR "CMV9p.bin.v2"
|
||||
#define CMV9I_FIRMWARE FW_DIR "CMV9i.bin"
|
||||
#define CMV9IV2_FIRMWARE FW_DIR "CMV9i.bin.v2"
|
||||
#define CMVEP_FIRMWARE FW_DIR "CMVep.bin"
|
||||
#define CMVEPV2_FIRMWARE FW_DIR "CMVep.bin.v2"
|
||||
#define CMVEI_FIRMWARE FW_DIR "CMVei.bin"
|
||||
#define CMVEIV2_FIRMWARE FW_DIR "CMVei.bin.v2"
|
||||
|
||||
#define UEA_FW_NAME_MAX 30
|
||||
#define NB_MODEM 4
|
||||
|
||||
@ -694,26 +722,26 @@ err:
|
||||
static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
|
||||
{
|
||||
int ret;
|
||||
char *fw_name = FW_DIR "eagle.fw";
|
||||
char *fw_name = EAGLE_FIRMWARE;
|
||||
|
||||
uea_enters(usb);
|
||||
uea_info(usb, "pre-firmware device, uploading firmware\n");
|
||||
|
||||
switch (ver) {
|
||||
case ADI930:
|
||||
fw_name = FW_DIR "adi930.fw";
|
||||
fw_name = ADI930_FIRMWARE;
|
||||
break;
|
||||
case EAGLE_I:
|
||||
fw_name = FW_DIR "eagleI.fw";
|
||||
fw_name = EAGLE_I_FIRMWARE;
|
||||
break;
|
||||
case EAGLE_II:
|
||||
fw_name = FW_DIR "eagleII.fw";
|
||||
fw_name = EAGLE_II_FIRMWARE;
|
||||
break;
|
||||
case EAGLE_III:
|
||||
fw_name = FW_DIR "eagleIII.fw";
|
||||
fw_name = EAGLE_III_FIRMWARE;
|
||||
break;
|
||||
case EAGLE_IV:
|
||||
fw_name = FW_DIR "eagleIV.fw";
|
||||
fw_name = EAGLE_IV_FIRMWARE;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -869,19 +897,19 @@ static int request_dsp(struct uea_softc *sc)
|
||||
|
||||
if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
|
||||
if (IS_ISDN(sc))
|
||||
dsp_name = FW_DIR "DSP4i.bin";
|
||||
dsp_name = DSP4I_FIRMWARE;
|
||||
else
|
||||
dsp_name = FW_DIR "DSP4p.bin";
|
||||
dsp_name = DSP4P_FIRMWARE;
|
||||
} else if (UEA_CHIP_VERSION(sc) == ADI930) {
|
||||
if (IS_ISDN(sc))
|
||||
dsp_name = FW_DIR "DSP9i.bin";
|
||||
dsp_name = DSP9I_FIRMWARE;
|
||||
else
|
||||
dsp_name = FW_DIR "DSP9p.bin";
|
||||
dsp_name = DSP9P_FIRMWARE;
|
||||
} else {
|
||||
if (IS_ISDN(sc))
|
||||
dsp_name = FW_DIR "DSPei.bin";
|
||||
dsp_name = DSPEI_FIRMWARE;
|
||||
else
|
||||
dsp_name = FW_DIR "DSPep.bin";
|
||||
dsp_name = DSPEP_FIRMWARE;
|
||||
}
|
||||
|
||||
ret = request_firmware(&sc->dsp_firm, dsp_name, &sc->usb_dev->dev);
|
||||
@ -1925,7 +1953,7 @@ static int load_XILINX_firmware(struct uea_softc *sc)
|
||||
int ret, size, u, ln;
|
||||
const u8 *pfw;
|
||||
u8 value;
|
||||
char *fw_name = FW_DIR "930-fpga.bin";
|
||||
char *fw_name = FPGA930_FIRMWARE;
|
||||
|
||||
uea_enters(INS_TO_USBDEV(sc));
|
||||
|
||||
@ -2753,3 +2781,28 @@ module_usb_driver(uea_driver);
|
||||
MODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka");
|
||||
MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_FIRMWARE(EAGLE_FIRMWARE);
|
||||
MODULE_FIRMWARE(ADI930_FIRMWARE);
|
||||
MODULE_FIRMWARE(EAGLE_I_FIRMWARE);
|
||||
MODULE_FIRMWARE(EAGLE_II_FIRMWARE);
|
||||
MODULE_FIRMWARE(EAGLE_III_FIRMWARE);
|
||||
MODULE_FIRMWARE(EAGLE_IV_FIRMWARE);
|
||||
MODULE_FIRMWARE(DSP4I_FIRMWARE);
|
||||
MODULE_FIRMWARE(DSP4P_FIRMWARE);
|
||||
MODULE_FIRMWARE(DSP9I_FIRMWARE);
|
||||
MODULE_FIRMWARE(DSP9P_FIRMWARE);
|
||||
MODULE_FIRMWARE(DSPEI_FIRMWARE);
|
||||
MODULE_FIRMWARE(DSPEP_FIRMWARE);
|
||||
MODULE_FIRMWARE(FPGA930_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV4P_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV4PV2_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV4I_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV4IV2_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV9P_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV9PV2_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV9I_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMV9IV2_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMVEP_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMVEPV2_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMVEI_FIRMWARE);
|
||||
MODULE_FIRMWARE(CMVEIV2_FIRMWARE);
|
||||
|
@ -84,7 +84,7 @@
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
static int usbatm_print_packet(const unsigned char *data, int len);
|
||||
static int usbatm_print_packet(struct usbatm_data *instance, const unsigned char *data, int len);
|
||||
#define PACKETDEBUG(arg...) usbatm_print_packet(arg)
|
||||
#define vdbg(arg...) dev_dbg(arg)
|
||||
#else
|
||||
@ -230,8 +230,8 @@ static int usbatm_submit_urb(struct urb *urb)
|
||||
struct usbatm_channel *channel = urb->context;
|
||||
int ret;
|
||||
|
||||
vdbg("%s: submitting urb 0x%p, size %u",
|
||||
__func__, urb, urb->transfer_buffer_length);
|
||||
/* vdbg("%s: submitting urb 0x%p, size %u",
|
||||
__func__, urb, urb->transfer_buffer_length); */
|
||||
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (ret) {
|
||||
@ -261,8 +261,8 @@ static void usbatm_complete(struct urb *urb)
|
||||
unsigned long flags;
|
||||
int status = urb->status;
|
||||
|
||||
vdbg("%s: urb 0x%p, status %d, actual_length %d",
|
||||
__func__, urb, status, urb->actual_length);
|
||||
/* vdbg("%s: urb 0x%p, status %d, actual_length %d",
|
||||
__func__, urb, status, urb->actual_length); */
|
||||
|
||||
/* usually in_interrupt(), but not always */
|
||||
spin_lock_irqsave(&channel->lock, flags);
|
||||
@ -311,7 +311,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
|
||||
int vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
|
||||
u8 pti = ((source[3] & 0xe) >> 1);
|
||||
|
||||
vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
|
||||
vdbg(&instance->usb_intf->dev, "%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
|
||||
|
||||
if ((vci != instance->cached_vci) || (vpi != instance->cached_vpi)) {
|
||||
instance->cached_vpi = vpi;
|
||||
@ -381,7 +381,9 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
|
||||
goto out;
|
||||
}
|
||||
|
||||
vdbg("%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc);
|
||||
vdbg(&instance->usb_intf->dev,
|
||||
"%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)",
|
||||
__func__, length, pdu_length, vcc);
|
||||
|
||||
if (!(skb = dev_alloc_skb(length))) {
|
||||
if (printk_ratelimit())
|
||||
@ -391,7 +393,9 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
|
||||
goto out;
|
||||
}
|
||||
|
||||
vdbg("%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", __func__, skb, skb->truesize);
|
||||
vdbg(&instance->usb_intf->dev,
|
||||
"%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)",
|
||||
__func__, skb, skb->truesize);
|
||||
|
||||
if (!atm_charge(vcc, skb->truesize)) {
|
||||
atm_rldbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n",
|
||||
@ -405,10 +409,11 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
|
||||
length);
|
||||
__skb_put(skb, length);
|
||||
|
||||
vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
|
||||
vdbg(&instance->usb_intf->dev,
|
||||
"%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
|
||||
__func__, skb, skb->len, skb->truesize);
|
||||
|
||||
PACKETDEBUG(skb->data, skb->len);
|
||||
PACKETDEBUG(instance, skb->data, skb->len);
|
||||
|
||||
vcc->push(vcc, skb);
|
||||
|
||||
@ -474,7 +479,8 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
|
||||
unsigned int bytes_written;
|
||||
unsigned int stride = instance->tx_channel.stride;
|
||||
|
||||
vdbg("%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space);
|
||||
vdbg(&instance->usb_intf->dev, "%s: skb->len=%d, avail_space=%u",
|
||||
__func__, skb->len, avail_space);
|
||||
UDSL_ASSERT(instance, !(avail_space % stride));
|
||||
|
||||
for (bytes_written = 0; bytes_written < avail_space && ctrl->len;
|
||||
@ -534,7 +540,8 @@ static void usbatm_rx_process(unsigned long data)
|
||||
struct urb *urb;
|
||||
|
||||
while ((urb = usbatm_pop_urb(&instance->rx_channel))) {
|
||||
vdbg("%s: processing urb 0x%p", __func__, urb);
|
||||
vdbg(&instance->usb_intf->dev,
|
||||
"%s: processing urb 0x%p", __func__, urb);
|
||||
|
||||
if (usb_pipeisoc(urb->pipe)) {
|
||||
unsigned char *merge_start = NULL;
|
||||
@ -608,7 +615,8 @@ static void usbatm_tx_process(unsigned long data)
|
||||
buffer + bytes_written,
|
||||
buf_size - bytes_written);
|
||||
|
||||
vdbg("%s: wrote %u bytes from skb 0x%p to urb 0x%p",
|
||||
vdbg(&instance->usb_intf->dev,
|
||||
"%s: wrote %u bytes from skb 0x%p to urb 0x%p",
|
||||
__func__, bytes_written, skb, urb);
|
||||
|
||||
if (!UDSL_SKB(skb)->len) {
|
||||
@ -664,7 +672,8 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
||||
struct usbatm_control *ctrl = UDSL_SKB(skb);
|
||||
int err;
|
||||
|
||||
vdbg("%s called (skb 0x%p, len %u)", __func__, skb, skb->len);
|
||||
vdbg(&instance->usb_intf->dev, "%s called (skb 0x%p, len %u)", __func__,
|
||||
skb, skb->len);
|
||||
|
||||
/* racy disconnection check - fine */
|
||||
if (!instance || instance->disconnected) {
|
||||
@ -688,7 +697,7 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
PACKETDEBUG(skb->data, skb->len);
|
||||
PACKETDEBUG(instance, skb->data, skb->len);
|
||||
|
||||
/* initialize the control block */
|
||||
ctrl->atm.vcc = vcc;
|
||||
@ -1202,7 +1211,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
|
||||
if (i >= num_rcv_urbs)
|
||||
list_add_tail(&urb->urb_list, &channel->list);
|
||||
|
||||
vdbg("%s: alloced buffer 0x%p buf size %u urb 0x%p",
|
||||
vdbg(&intf->dev, "%s: alloced buffer 0x%p buf size %u urb 0x%p",
|
||||
__func__, urb->transfer_buffer, urb->transfer_buffer_length, urb);
|
||||
}
|
||||
|
||||
@ -1359,7 +1368,8 @@ MODULE_VERSION(DRIVER_VERSION);
|
||||
************/
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
static int usbatm_print_packet(const unsigned char *data, int len)
|
||||
static int usbatm_print_packet(struct usbatm_data *instance,
|
||||
const unsigned char *data, int len)
|
||||
{
|
||||
unsigned char buffer[256];
|
||||
int i = 0, j = 0;
|
||||
@ -1369,7 +1379,7 @@ static int usbatm_print_packet(const unsigned char *data, int len)
|
||||
sprintf(buffer, "%.3d :", i);
|
||||
for (j = 0; (j < 16) && (i < len); j++, i++)
|
||||
sprintf(buffer, "%s %2.2x", buffer, data[i]);
|
||||
dbg("%s", buffer);
|
||||
dev_dbg(&instance->usb_intf->dev, "%s", buffer);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ if USB_CHIPIDEA
|
||||
config USB_CHIPIDEA_UDC
|
||||
bool "ChipIdea device controller"
|
||||
depends on USB_GADGET=y || USB_GADGET=USB_CHIPIDEA
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Say Y here to enable device controller functionality of the
|
||||
ChipIdea driver.
|
||||
|
@ -1,3 +1,5 @@
|
||||
ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
|
||||
|
||||
ci_hdrc-y := core.o
|
||||
@ -15,5 +17,5 @@ ifneq ($(CONFIG_PCI),)
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_OF_DEVICE),)
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx6q.o
|
||||
endif
|
||||
|
@ -139,6 +139,7 @@ struct ci13xxx {
|
||||
enum ci_role role;
|
||||
bool is_otg;
|
||||
struct work_struct work;
|
||||
struct work_struct vbus_work;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
struct dma_pool *qh_pool;
|
||||
|
@ -20,8 +20,10 @@
|
||||
#include <linux/usb/chipidea.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include "ci.h"
|
||||
#include "ci13xxx_imx.h"
|
||||
|
||||
#define pdev_to_phy(pdev) \
|
||||
((struct usb_phy *)platform_get_drvdata(pdev))
|
||||
@ -34,6 +36,55 @@ struct ci13xxx_imx_data {
|
||||
struct regulator *reg_vbus;
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops *usbmisc_ops;
|
||||
|
||||
/* Common functions shared by usbmisc drivers */
|
||||
|
||||
int usbmisc_set_ops(const struct usbmisc_ops *ops)
|
||||
{
|
||||
if (usbmisc_ops)
|
||||
return -EBUSY;
|
||||
|
||||
usbmisc_ops = ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbmisc_set_ops);
|
||||
|
||||
void usbmisc_unset_ops(const struct usbmisc_ops *ops)
|
||||
{
|
||||
usbmisc_ops = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbmisc_unset_ops);
|
||||
|
||||
int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
usbdev->dev = dev;
|
||||
|
||||
ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
|
||||
0, &args);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
|
||||
ret);
|
||||
memset(usbdev, 0, sizeof(*usbdev));
|
||||
return ret;
|
||||
}
|
||||
usbdev->index = args.args[0];
|
||||
of_node_put(args.np);
|
||||
|
||||
if (of_find_property(np, "disable-over-current", NULL))
|
||||
usbdev->disable_oc = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbmisc_get_init_data);
|
||||
|
||||
/* End of common functions shared by usbmisc drivers*/
|
||||
|
||||
static struct ci13xxx_platform_data ci13xxx_imx_platdata __devinitdata = {
|
||||
.name = "ci13xxx_imx",
|
||||
.flags = CI13XXX_REQUIRE_TRANSCEIVER |
|
||||
@ -49,8 +100,13 @@ static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
|
||||
struct device_node *phy_np;
|
||||
struct resource *res;
|
||||
struct regulator *reg_vbus;
|
||||
struct pinctrl *pinctrl;
|
||||
int ret;
|
||||
|
||||
if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL)
|
||||
&& !usbmisc_ops)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(&pdev->dev, "Failed to allocate CI13xxx-IMX data!\n");
|
||||
@ -63,6 +119,11 @@ static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl))
|
||||
dev_warn(&pdev->dev, "pinctrl get/select failed, err=%ld\n",
|
||||
PTR_ERR(pinctrl));
|
||||
|
||||
data->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
@ -120,6 +181,16 @@ static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
|
||||
*pdev->dev.dma_mask = DMA_BIT_MASK(32);
|
||||
dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask);
|
||||
}
|
||||
|
||||
if (usbmisc_ops && usbmisc_ops->init) {
|
||||
ret = usbmisc_ops->init(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"usbmisc init failed, ret=%d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
plat_ci = ci13xxx_add_device(&pdev->dev,
|
||||
pdev->resource, pdev->num_resources,
|
||||
&ci13xxx_imx_platdata);
|
||||
|
28
drivers/usb/chipidea/ci13xxx_imx.h
Normal file
28
drivers/usb/chipidea/ci13xxx_imx.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
/* Used to set SoC specific callbacks */
|
||||
struct usbmisc_ops {
|
||||
/* It's called once when probe a usb device */
|
||||
int (*init)(struct device *dev);
|
||||
};
|
||||
|
||||
struct usbmisc_usb_device {
|
||||
struct device *dev; /* usb controller device */
|
||||
int index;
|
||||
|
||||
int disable_oc:1; /* over current detect disabled */
|
||||
};
|
||||
|
||||
int usbmisc_set_ops(const struct usbmisc_ops *ops);
|
||||
void usbmisc_unset_ops(const struct usbmisc_ops *ops);
|
||||
int
|
||||
usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev);
|
@ -273,14 +273,13 @@ static void ci_role_work(struct work_struct *work)
|
||||
struct ci13xxx *ci = container_of(work, struct ci13xxx, work);
|
||||
enum ci_role role = ci_otg_role(ci);
|
||||
|
||||
hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
|
||||
|
||||
if (role != ci->role) {
|
||||
dev_dbg(ci->dev, "switching from %s to %s\n",
|
||||
ci_role(ci)->name, ci->roles[role]->name);
|
||||
|
||||
ci_role_stop(ci);
|
||||
ci_role_start(ci, role);
|
||||
enable_irq(ci->irq);
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,17 +319,22 @@ static irqreturn_t ci_irq(int irq, void *data)
|
||||
{
|
||||
struct ci13xxx *ci = data;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 otgsc = 0;
|
||||
|
||||
if (ci->is_otg) {
|
||||
u32 sts = hw_read(ci, OP_OTGSC, ~0);
|
||||
if (ci->is_otg)
|
||||
otgsc = hw_read(ci, OP_OTGSC, ~0);
|
||||
|
||||
if (sts & OTGSC_IDIS) {
|
||||
queue_work(ci->wq, &ci->work);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
if (ci->role != CI_ROLE_END)
|
||||
ret = ci_role(ci)->irq(ci);
|
||||
|
||||
if (ci->is_otg && (otgsc & OTGSC_IDIS)) {
|
||||
hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
|
||||
disable_irq_nosync(ci->irq);
|
||||
queue_work(ci->wq, &ci->work);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_IDA(ci_ida);
|
||||
@ -462,6 +466,8 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev)
|
||||
|
||||
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
|
||||
ci->is_otg = true;
|
||||
/* ID pin needs 1ms debouce time, we delay 2ms for safe */
|
||||
mdelay(2);
|
||||
ci->role = ci_otg_role(ci);
|
||||
} else {
|
||||
ci->role = ci->roles[CI_ROLE_HOST]
|
||||
|
@ -305,6 +305,18 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci)
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void hw_enable_vbus_intr(struct ci13xxx *ci)
|
||||
{
|
||||
hw_write(ci, OP_OTGSC, OTGSC_AVVIS, OTGSC_AVVIS);
|
||||
hw_write(ci, OP_OTGSC, OTGSC_AVVIE, OTGSC_AVVIE);
|
||||
queue_work(ci->wq, &ci->vbus_work);
|
||||
}
|
||||
|
||||
static void hw_disable_vbus_intr(struct ci13xxx *ci)
|
||||
{
|
||||
hw_write(ci, OP_OTGSC, OTGSC_AVVIE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_test_and_clear_setup_guard: test & clear setup guard (execute without
|
||||
* interruption)
|
||||
@ -371,6 +383,16 @@ static int hw_usb_reset(struct ci13xxx *ci)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vbus_work(struct work_struct *work)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(work, struct ci13xxx, vbus_work);
|
||||
|
||||
if (hw_read(ci, OP_OTGSC, OTGSC_AVV))
|
||||
usb_gadget_vbus_connect(&ci->gadget);
|
||||
else
|
||||
usb_gadget_vbus_disconnect(&ci->gadget);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* UTIL block
|
||||
*****************************************************************************/
|
||||
@ -1370,6 +1392,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
if (is_active) {
|
||||
pm_runtime_get_sync(&_gadget->dev);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_enable_vbus_intr(ci);
|
||||
hw_device_state(ci, ci->ep0out->qh.dma);
|
||||
} else {
|
||||
hw_device_state(ci, 0);
|
||||
@ -1544,8 +1567,10 @@ static int ci13xxx_start(struct usb_gadget *gadget,
|
||||
pm_runtime_get_sync(&ci->gadget.dev);
|
||||
if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) {
|
||||
if (ci->vbus_active) {
|
||||
if (ci->platdata->flags & CI13XXX_REGS_SHARED)
|
||||
if (ci->platdata->flags & CI13XXX_REGS_SHARED) {
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_enable_vbus_intr(ci);
|
||||
}
|
||||
} else {
|
||||
pm_runtime_put_sync(&ci->gadget.dev);
|
||||
goto done;
|
||||
@ -1651,6 +1676,13 @@ static irqreturn_t udc_irq(struct ci13xxx *ci)
|
||||
} else {
|
||||
retval = IRQ_NONE;
|
||||
}
|
||||
|
||||
intr = hw_read(ci, OP_OTGSC, ~0);
|
||||
hw_write(ci, OP_OTGSC, ~0, intr);
|
||||
|
||||
if (intr & (OTGSC_AVVIE & OTGSC_AVVIS))
|
||||
queue_work(ci->wq, &ci->vbus_work);
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
|
||||
return retval;
|
||||
@ -1726,6 +1758,7 @@ static int udc_start(struct ci13xxx *ci)
|
||||
retval = hw_device_reset(ci, USBMODE_CM_DC);
|
||||
if (retval)
|
||||
goto put_transceiver;
|
||||
hw_enable_vbus_intr(ci);
|
||||
}
|
||||
|
||||
retval = device_register(&ci->gadget.dev);
|
||||
@ -1788,6 +1821,9 @@ static void udc_stop(struct ci13xxx *ci)
|
||||
if (ci == NULL)
|
||||
return;
|
||||
|
||||
hw_disable_vbus_intr(ci);
|
||||
cancel_work_sync(&ci->vbus_work);
|
||||
|
||||
usb_del_gadget_udc(&ci->gadget);
|
||||
|
||||
destroy_eps(ci);
|
||||
@ -1828,6 +1864,7 @@ int ci_hdrc_gadget_init(struct ci13xxx *ci)
|
||||
rdrv->irq = udc_irq;
|
||||
rdrv->name = "gadget";
|
||||
ci->roles[CI_ROLE_GADGET] = rdrv;
|
||||
INIT_WORK(&ci->vbus_work, vbus_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
162
drivers/usb/chipidea/usbmisc_imx6q.c
Normal file
162
drivers/usb/chipidea/usbmisc_imx6q.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "ci13xxx_imx.h"
|
||||
|
||||
#define USB_DEV_MAX 4
|
||||
|
||||
#define BM_OVER_CUR_DIS BIT(7)
|
||||
|
||||
struct imx6q_usbmisc {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
struct clk *clk;
|
||||
struct usbmisc_usb_device usbdev[USB_DEV_MAX];
|
||||
};
|
||||
|
||||
static struct imx6q_usbmisc *usbmisc;
|
||||
|
||||
static struct usbmisc_usb_device *get_usbdev(struct device *dev)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < USB_DEV_MAX; i++) {
|
||||
if (usbmisc->usbdev[i].dev == dev)
|
||||
return &usbmisc->usbdev[i];
|
||||
else if (!usbmisc->usbdev[i].dev)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= USB_DEV_MAX)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return &usbmisc->usbdev[i];
|
||||
}
|
||||
|
||||
static int usbmisc_imx6q_init(struct device *dev)
|
||||
{
|
||||
|
||||
struct usbmisc_usb_device *usbdev;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
usbdev = get_usbdev(dev);
|
||||
if (IS_ERR(usbdev))
|
||||
return PTR_ERR(usbdev);
|
||||
|
||||
if (usbdev->disable_oc) {
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
reg = readl(usbmisc->base + usbdev->index * 4);
|
||||
writel(reg | BM_OVER_CUR_DIS,
|
||||
usbmisc->base + usbdev->index * 4);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct usbmisc_ops imx6q_usbmisc_ops = {
|
||||
.init = usbmisc_imx6q_init,
|
||||
};
|
||||
|
||||
static const struct of_device_id usbmisc_imx6q_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-usbmisc"},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int __devinit usbmisc_imx6q_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct imx6q_usbmisc *data;
|
||||
int ret;
|
||||
|
||||
if (usbmisc)
|
||||
return -EBUSY;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!data->base)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
data->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get clock, err=%ld\n", PTR_ERR(data->clk));
|
||||
return PTR_ERR(data->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"clk_prepare_enable failed, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usbmisc_set_ops(&imx6q_usbmisc_ops);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(data->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbmisc = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit usbmisc_imx6q_remove(struct platform_device *pdev)
|
||||
{
|
||||
usbmisc_unset_ops(&imx6q_usbmisc_ops);
|
||||
clk_disable_unprepare(usbmisc->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver usbmisc_imx6q_driver = {
|
||||
.probe = usbmisc_imx6q_probe,
|
||||
.remove = __devexit_p(usbmisc_imx6q_remove),
|
||||
.driver = {
|
||||
.name = "usbmisc_imx6q",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = usbmisc_imx6q_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
int __init usbmisc_imx6q_drv_init(void)
|
||||
{
|
||||
return platform_driver_register(&usbmisc_imx6q_driver);
|
||||
}
|
||||
subsys_initcall(usbmisc_imx6q_drv_init);
|
||||
|
||||
void __exit usbmisc_imx6q_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&usbmisc_imx6q_driver);
|
||||
}
|
||||
module_exit(usbmisc_imx6q_drv_exit);
|
||||
|
||||
MODULE_ALIAS("platform:usbmisc-imx6q");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("driver for imx6q usb non-core registers");
|
||||
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
|
@ -39,7 +39,6 @@
|
||||
#include <linux/serial.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -56,7 +56,7 @@ config USB_SUSPEND
|
||||
|
||||
config USB_OTG
|
||||
bool "OTG support"
|
||||
depends on USB && EXPERIMENTAL
|
||||
depends on USB
|
||||
depends on USB_SUSPEND
|
||||
default n
|
||||
help
|
||||
|
@ -702,6 +702,8 @@ int usb_get_configuration(struct usb_device *dev)
|
||||
if (result < 0) {
|
||||
dev_err(ddev, "unable to read config index %d "
|
||||
"descriptor/%s: %d\n", cfgno, "start", result);
|
||||
if (result != -EPIPE)
|
||||
goto err;
|
||||
dev_err(ddev, "chopping to %d config(s)\n", cfgno);
|
||||
dev->descriptor.bNumConfigurations = cfgno;
|
||||
break;
|
||||
|
@ -496,6 +496,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
||||
char *pages_start, *data_end, *speed;
|
||||
unsigned int length;
|
||||
ssize_t total_written = 0;
|
||||
struct usb_device *childdev = NULL;
|
||||
|
||||
/* don't bother with anything else if we're not writing any data */
|
||||
if (*nbytes <= 0)
|
||||
@ -589,14 +590,12 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
||||
free_pages((unsigned long)pages_start, 1);
|
||||
|
||||
/* Now look at all of this device's children. */
|
||||
for (chix = 0; chix < usbdev->maxchild; chix++) {
|
||||
struct usb_device *childdev = usbdev->children[chix];
|
||||
|
||||
usb_hub_for_each_child(usbdev, chix, childdev) {
|
||||
if (childdev) {
|
||||
usb_lock_device(childdev);
|
||||
ret = usb_device_dump(buffer, nbytes, skip_bytes,
|
||||
file_offset, childdev, bus,
|
||||
level + 1, chix, ++cnt);
|
||||
level + 1, chix - 1, ++cnt);
|
||||
usb_unlock_device(childdev);
|
||||
if (ret == -EFAULT)
|
||||
return total_written;
|
||||
|
@ -1928,6 +1928,38 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_disconnect_claim(struct dev_state *ps, void __user *arg)
|
||||
{
|
||||
struct usbdevfs_disconnect_claim dc;
|
||||
struct usb_interface *intf;
|
||||
|
||||
if (copy_from_user(&dc, arg, sizeof(dc)))
|
||||
return -EFAULT;
|
||||
|
||||
intf = usb_ifnum_to_if(ps->dev, dc.interface);
|
||||
if (!intf)
|
||||
return -EINVAL;
|
||||
|
||||
if (intf->dev.driver) {
|
||||
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
|
||||
strncmp(dc.driver, intf->dev.driver->name,
|
||||
sizeof(dc.driver)) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) &&
|
||||
strncmp(dc.driver, intf->dev.driver->name,
|
||||
sizeof(dc.driver)) == 0)
|
||||
return -EBUSY;
|
||||
|
||||
dev_dbg(&intf->dev, "disconnect by usbfs\n");
|
||||
usb_driver_release_interface(driver, intf);
|
||||
}
|
||||
|
||||
return claimintf(ps, dc.interface);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: All requests here that have interface numbers as parameters
|
||||
* are assuming that somehow the configuration has been prevented from
|
||||
@ -2101,6 +2133,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||
case USBDEVFS_GET_CAPABILITIES:
|
||||
ret = proc_get_capabilities(ps, p);
|
||||
break;
|
||||
case USBDEVFS_DISCONNECT_CLAIM:
|
||||
ret = proc_disconnect_claim(ps, p);
|
||||
break;
|
||||
}
|
||||
usb_unlock_device(dev);
|
||||
if (ret >= 0)
|
||||
|
@ -125,10 +125,9 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
|
||||
{
|
||||
struct usb_dynid *dynid, *n;
|
||||
struct usb_driver *usb_driver = to_usb_driver(driver);
|
||||
u32 idVendor = 0;
|
||||
u32 idProduct = 0;
|
||||
int fields = 0;
|
||||
int retval = 0;
|
||||
u32 idVendor;
|
||||
u32 idProduct;
|
||||
int fields;
|
||||
|
||||
fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
|
||||
if (fields < 2)
|
||||
@ -141,14 +140,10 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
|
||||
(id->idProduct == idProduct)) {
|
||||
list_del(&dynid->node);
|
||||
kfree(dynid);
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&usb_driver->dynids.lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
return count;
|
||||
}
|
||||
static DRIVER_ATTR(remove_id, S_IRUGO | S_IWUSR, show_dynids, store_remove_id);
|
||||
|
@ -24,10 +24,6 @@ struct ep_device {
|
||||
#define to_ep_device(_dev) \
|
||||
container_of(_dev, struct ep_device, dev)
|
||||
|
||||
struct device_type usb_ep_device_type = {
|
||||
.name = "usb_endpoint",
|
||||
};
|
||||
|
||||
struct ep_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct usb_device *,
|
||||
@ -172,6 +168,11 @@ static void ep_device_release(struct device *dev)
|
||||
kfree(ep_dev);
|
||||
}
|
||||
|
||||
struct device_type usb_ep_device_type = {
|
||||
.name = "usb_endpoint",
|
||||
.release = ep_device_release,
|
||||
};
|
||||
|
||||
int usb_create_ep_devs(struct device *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev)
|
||||
@ -190,7 +191,6 @@ int usb_create_ep_devs(struct device *parent,
|
||||
ep_dev->dev.groups = ep_dev_groups;
|
||||
ep_dev->dev.type = &usb_ep_device_type;
|
||||
ep_dev->dev.parent = parent;
|
||||
ep_dev->dev.release = ep_device_release;
|
||||
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
|
||||
retval = device_register(&ep_dev->dev);
|
||||
|
@ -22,6 +22,7 @@
|
||||
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -123,9 +124,8 @@ static inline int is_root_hub(struct usb_device *udev)
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
|
||||
#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
|
||||
#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
|
||||
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
|
||||
|
||||
/* usb 3.0 root hub device descriptor */
|
||||
static const u8 usb3_rh_dev_descriptor[18] = {
|
||||
@ -2151,15 +2151,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
|
||||
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
|
||||
{
|
||||
struct usb_hcd *hcd = __hcd;
|
||||
unsigned long flags;
|
||||
irqreturn_t rc;
|
||||
|
||||
/* IRQF_DISABLED doesn't work correctly with shared IRQs
|
||||
* when the first handler doesn't use it. So let's just
|
||||
* assume it's never used.
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
|
||||
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
|
||||
rc = IRQ_NONE;
|
||||
else if (hcd->driver->irq(hcd) == IRQ_NONE)
|
||||
@ -2167,7 +2160,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
|
||||
else
|
||||
rc = IRQ_HANDLED;
|
||||
|
||||
local_irq_restore(flags);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_irq);
|
||||
@ -2355,14 +2347,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
|
||||
int retval;
|
||||
|
||||
if (hcd->driver->irq) {
|
||||
|
||||
/* IRQF_DISABLED doesn't work as advertised when used together
|
||||
* with IRQF_SHARED. As usb_hcd_irq() will always disable
|
||||
* interrupts we can remove it here.
|
||||
*/
|
||||
if (irqflags & IRQF_SHARED)
|
||||
irqflags &= ~IRQF_DISABLED;
|
||||
|
||||
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
|
||||
hcd->driver->description, hcd->self.busnum);
|
||||
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
|
||||
|
@ -39,6 +39,13 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct usb_port {
|
||||
struct usb_device *child;
|
||||
struct device dev;
|
||||
struct dev_state *port_owner;
|
||||
enum usb_port_connect_type connect_type;
|
||||
};
|
||||
|
||||
struct usb_hub {
|
||||
struct device *intfdev; /* the "interface" device */
|
||||
struct usb_device *hdev;
|
||||
@ -83,7 +90,7 @@ struct usb_hub {
|
||||
u8 indicator[USB_MAXCHILDREN];
|
||||
struct delayed_work leds;
|
||||
struct delayed_work init_work;
|
||||
struct dev_state **port_owners;
|
||||
struct usb_port **ports;
|
||||
};
|
||||
|
||||
static inline int hub_is_superspeed(struct usb_device *hdev)
|
||||
@ -156,6 +163,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
|
||||
#define HUB_DEBOUNCE_STEP 25
|
||||
#define HUB_DEBOUNCE_STABLE 100
|
||||
|
||||
#define to_usb_port(_dev) \
|
||||
container_of(_dev, struct usb_port, dev)
|
||||
|
||||
static int usb_reset_and_verify_device(struct usb_device *udev);
|
||||
|
||||
@ -174,7 +183,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
|
||||
/* Note that hdev or one of its children must be locked! */
|
||||
static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
|
||||
{
|
||||
if (!hdev || !hdev->actconfig)
|
||||
if (!hdev || !hdev->actconfig || !hdev->maxchild)
|
||||
return NULL;
|
||||
return usb_get_intfdata(hdev->actconfig->interface[0]);
|
||||
}
|
||||
@ -869,8 +878,8 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int ret = 0;
|
||||
|
||||
if (hdev->children[port1-1] && set_state)
|
||||
usb_set_device_state(hdev->children[port1-1],
|
||||
if (hub->ports[port1 - 1]->child && set_state)
|
||||
usb_set_device_state(hub->ports[port1 - 1]->child,
|
||||
USB_STATE_NOTATTACHED);
|
||||
if (!hub->error && !hub_is_superspeed(hub->hdev))
|
||||
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
|
||||
@ -1026,7 +1035,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
||||
* which ports need attention.
|
||||
*/
|
||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||
struct usb_device *udev = hdev->children[port1-1];
|
||||
struct usb_device *udev = hub->ports[port1 - 1]->child;
|
||||
u16 portstatus, portchange;
|
||||
|
||||
portstatus = portchange = 0;
|
||||
@ -1191,8 +1200,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
|
||||
if (type != HUB_SUSPEND) {
|
||||
/* Disconnect all the children */
|
||||
for (i = 0; i < hdev->maxchild; ++i) {
|
||||
if (hdev->children[i])
|
||||
usb_disconnect(&hdev->children[i]);
|
||||
if (hub->ports[i]->child)
|
||||
usb_disconnect(&hub->ports[i]->child);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1222,6 +1231,52 @@ static int hub_post_reset(struct usb_interface *intf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_port_device_release(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
|
||||
kfree(port_dev);
|
||||
}
|
||||
|
||||
static void usb_hub_remove_port_device(struct usb_hub *hub,
|
||||
int port1)
|
||||
{
|
||||
device_unregister(&hub->ports[port1 - 1]->dev);
|
||||
}
|
||||
|
||||
struct device_type usb_port_device_type = {
|
||||
.name = "usb_port",
|
||||
.release = usb_port_device_release,
|
||||
};
|
||||
|
||||
static 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->dev.parent = hub->intfdev;
|
||||
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;
|
||||
return 0;
|
||||
|
||||
error_register:
|
||||
put_device(&port_dev->dev);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hub_configure(struct usb_hub *hub,
|
||||
struct usb_endpoint_descriptor *endpoint)
|
||||
{
|
||||
@ -1231,7 +1286,7 @@ static int hub_configure(struct usb_hub *hub,
|
||||
u16 hubstatus, hubchange;
|
||||
u16 wHubCharacteristics;
|
||||
unsigned int pipe;
|
||||
int maxp, ret;
|
||||
int maxp, ret, i;
|
||||
char *message = "out of memory";
|
||||
|
||||
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
|
||||
@ -1271,11 +1326,9 @@ static int hub_configure(struct usb_hub *hub,
|
||||
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
|
||||
(hdev->maxchild == 1) ? "" : "s");
|
||||
|
||||
hdev->children = kzalloc(hdev->maxchild *
|
||||
sizeof(struct usb_device *), GFP_KERNEL);
|
||||
hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
|
||||
GFP_KERNEL);
|
||||
if (!hdev->children || !hub->port_owners) {
|
||||
hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
|
||||
GFP_KERNEL);
|
||||
if (!hub->ports) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
@ -1484,6 +1537,11 @@ static int hub_configure(struct usb_hub *hub,
|
||||
if (hub->has_indicators && blinkenlights)
|
||||
hub->indicator [0] = INDICATOR_CYCLE;
|
||||
|
||||
for (i = 0; i < hdev->maxchild; i++)
|
||||
if (usb_hub_create_port_device(hub, i + 1) < 0)
|
||||
dev_err(hub->intfdev,
|
||||
"couldn't create port%d device.\n", i + 1);
|
||||
|
||||
hub_activate(hub, HUB_INIT);
|
||||
return 0;
|
||||
|
||||
@ -1508,6 +1566,7 @@ static void hub_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||
struct usb_device *hdev = interface_to_usbdev(intf);
|
||||
int i;
|
||||
|
||||
/* Take the hub off the event list and don't let it be added again */
|
||||
spin_lock_irq(&hub_event_lock);
|
||||
@ -1523,14 +1582,16 @@ static void hub_disconnect(struct usb_interface *intf)
|
||||
hub_quiesce(hub, HUB_DISCONNECT);
|
||||
|
||||
usb_set_intfdata (intf, NULL);
|
||||
|
||||
for (i = 0; i < hdev->maxchild; i++)
|
||||
usb_hub_remove_port_device(hub, i + 1);
|
||||
hub->hdev->maxchild = 0;
|
||||
|
||||
if (hub->hdev->speed == USB_SPEED_HIGH)
|
||||
highspeed_hubs--;
|
||||
|
||||
usb_free_urb(hub->urb);
|
||||
kfree(hdev->children);
|
||||
kfree(hub->port_owners);
|
||||
kfree(hub->ports);
|
||||
kfree(hub->descriptor);
|
||||
kfree(hub->status);
|
||||
kfree(hub->buffer);
|
||||
@ -1617,6 +1678,7 @@ static int
|
||||
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
|
||||
{
|
||||
struct usb_device *hdev = interface_to_usbdev (intf);
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
/* assert ifno == 0 (part of hub spec) */
|
||||
switch (code) {
|
||||
@ -1630,11 +1692,11 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
|
||||
else {
|
||||
info->nports = hdev->maxchild;
|
||||
for (i = 0; i < info->nports; i++) {
|
||||
if (hdev->children[i] == NULL)
|
||||
if (hub->ports[i]->child == NULL)
|
||||
info->port[i] = 0;
|
||||
else
|
||||
info->port[i] =
|
||||
hdev->children[i]->devnum;
|
||||
hub->ports[i]->child->devnum;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&device_state_lock);
|
||||
@ -1662,7 +1724,7 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
|
||||
/* This assumes that devices not managed by the hub driver
|
||||
* will always have maxchild equal to 0.
|
||||
*/
|
||||
*ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
|
||||
*ppowner = &(hdev_to_hub(hdev)->ports[port1 - 1]->port_owner);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1699,16 +1761,14 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
|
||||
|
||||
void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
int n;
|
||||
struct dev_state **powner;
|
||||
|
||||
n = find_port_owner(hdev, 1, &powner);
|
||||
if (n == 0) {
|
||||
for (; n < hdev->maxchild; (++n, ++powner)) {
|
||||
if (*powner == owner)
|
||||
*powner = NULL;
|
||||
}
|
||||
for (n = 0; n < hdev->maxchild; n++) {
|
||||
if (hub->ports[n]->port_owner == owner)
|
||||
hub->ports[n]->port_owner = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The caller must hold udev's lock */
|
||||
@ -1719,17 +1779,17 @@ bool usb_device_is_owned(struct usb_device *udev)
|
||||
if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
|
||||
return false;
|
||||
hub = hdev_to_hub(udev->parent);
|
||||
return !!hub->port_owners[udev->portnum - 1];
|
||||
return !!hub->ports[udev->portnum - 1]->port_owner;
|
||||
}
|
||||
|
||||
|
||||
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(udev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < udev->maxchild; ++i) {
|
||||
if (udev->children[i])
|
||||
recursively_mark_NOTATTACHED(udev->children[i]);
|
||||
if (hub->ports[i]->child)
|
||||
recursively_mark_NOTATTACHED(hub->ports[i]->child);
|
||||
}
|
||||
if (udev->state == USB_STATE_SUSPENDED)
|
||||
udev->active_duration -= jiffies;
|
||||
@ -1893,6 +1953,7 @@ static void hub_free_dev(struct usb_device *udev)
|
||||
void usb_disconnect(struct usb_device **pdev)
|
||||
{
|
||||
struct usb_device *udev = *pdev;
|
||||
struct usb_hub *hub = hdev_to_hub(udev);
|
||||
int i;
|
||||
|
||||
/* mark the device as inactive, so any further urb submissions for
|
||||
@ -1907,8 +1968,8 @@ void usb_disconnect(struct usb_device **pdev)
|
||||
|
||||
/* Free up all the children before we remove this device */
|
||||
for (i = 0; i < udev->maxchild; i++) {
|
||||
if (udev->children[i])
|
||||
usb_disconnect(&udev->children[i]);
|
||||
if (hub->ports[i]->child)
|
||||
usb_disconnect(&hub->ports[i]->child);
|
||||
}
|
||||
|
||||
/* deallocate hcd/hardware state ... nuking all pending urbs and
|
||||
@ -2113,7 +2174,8 @@ static void set_usb_port_removable(struct usb_device *udev)
|
||||
return;
|
||||
|
||||
if (hub_is_superspeed(hdev)) {
|
||||
if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
|
||||
if (le16_to_cpu(hub->descriptor->u.ss.DeviceRemovable)
|
||||
& (1 << port))
|
||||
removable = false;
|
||||
} else {
|
||||
if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
|
||||
@ -3072,7 +3134,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = hdev->children [port1-1];
|
||||
udev = hub->ports[port1 - 1]->child;
|
||||
if (udev && udev->can_submit) {
|
||||
dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
|
||||
if (PMSG_IS_AUTO(msg))
|
||||
@ -3999,7 +4061,7 @@ hub_power_remaining (struct usb_hub *hub)
|
||||
|
||||
remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
|
||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||
struct usb_device *udev = hdev->children[port1 - 1];
|
||||
struct usb_device *udev = hub->ports[port1 - 1]->child;
|
||||
int delta;
|
||||
|
||||
if (!udev)
|
||||
@ -4063,7 +4125,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
#endif
|
||||
|
||||
/* Try to resuscitate an existing device */
|
||||
udev = hdev->children[port1-1];
|
||||
udev = hub->ports[port1 - 1]->child;
|
||||
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
|
||||
udev->state != USB_STATE_NOTATTACHED) {
|
||||
usb_lock_device(udev);
|
||||
@ -4092,7 +4154,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
|
||||
/* Disconnect any existing devices under this port */
|
||||
if (udev)
|
||||
usb_disconnect(&hdev->children[port1-1]);
|
||||
usb_disconnect(&hub->ports[port1 - 1]->child);
|
||||
clear_bit(port1, hub->change_bits);
|
||||
|
||||
/* We can forget about a "removed" device when there's a physical
|
||||
@ -4228,7 +4290,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
if (hdev->state == USB_STATE_NOTATTACHED)
|
||||
status = -ENOTCONN;
|
||||
else
|
||||
hdev->children[port1-1] = udev;
|
||||
hub->ports[port1 - 1]->child = udev;
|
||||
spin_unlock_irq(&device_state_lock);
|
||||
|
||||
/* Run it through the hoops (find a driver, etc) */
|
||||
@ -4236,7 +4298,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
status = usb_new_device(udev);
|
||||
if (status) {
|
||||
spin_lock_irq(&device_state_lock);
|
||||
hdev->children[port1-1] = NULL;
|
||||
hub->ports[port1 - 1]->child = NULL;
|
||||
spin_unlock_irq(&device_state_lock);
|
||||
}
|
||||
}
|
||||
@ -4282,7 +4344,7 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
|
||||
int ret;
|
||||
|
||||
hdev = hub->hdev;
|
||||
udev = hdev->children[port-1];
|
||||
udev = hub->ports[port - 1]->child;
|
||||
if (!hub_is_superspeed(hdev)) {
|
||||
if (!(portchange & USB_PORT_STAT_C_SUSPEND))
|
||||
return 0;
|
||||
@ -4436,7 +4498,7 @@ static void hub_events(void)
|
||||
*/
|
||||
if (!(portstatus & USB_PORT_STAT_ENABLE)
|
||||
&& !connect_change
|
||||
&& hdev->children[i-1]) {
|
||||
&& hub->ports[i - 1]->child) {
|
||||
dev_err (hub_dev,
|
||||
"port %i "
|
||||
"disabled by hub (EMI?), "
|
||||
@ -4993,3 +5055,75 @@ void usb_queue_reset_device(struct usb_interface *iface)
|
||||
schedule_work(&iface->reset_ws);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_queue_reset_device);
|
||||
|
||||
/**
|
||||
* usb_hub_find_child - Get the pointer of child device
|
||||
* attached to the port which is specified by @port1.
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @port1: port num to indicate which port the child device
|
||||
* is attached to.
|
||||
*
|
||||
* USB drivers call this function to get hub's child device
|
||||
* pointer.
|
||||
*
|
||||
* Return NULL if input param is invalid and
|
||||
* child's usb_device pointer if non-NULL.
|
||||
*/
|
||||
struct usb_device *usb_hub_find_child(struct usb_device *hdev,
|
||||
int port1)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
if (port1 < 1 || port1 > hdev->maxchild)
|
||||
return NULL;
|
||||
return hub->ports[port1 - 1]->child;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hub_find_child);
|
||||
|
||||
/**
|
||||
* usb_set_hub_port_connect_type - set hub port connect type.
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @port1: port num of the port
|
||||
* @type: connect type of the port
|
||||
*/
|
||||
void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
|
||||
enum usb_port_connect_type type)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
hub->ports[port1 - 1]->connect_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_get_hub_port_connect_type - Get the port's connect type
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @port1: port num of the port
|
||||
*
|
||||
* Return connect type of the port and if input params are
|
||||
* invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN.
|
||||
*/
|
||||
enum usb_port_connect_type
|
||||
usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
return hub->ports[port1 - 1]->connect_type;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/**
|
||||
* usb_get_hub_port_acpi_handle - Get the usb port's acpi handle
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @port1: port num of the port
|
||||
*
|
||||
* Return port's acpi handle if successful, NULL if params are
|
||||
* invaild.
|
||||
*/
|
||||
acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
|
||||
int port1)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
|
||||
}
|
||||
#endif
|
||||
|
@ -146,8 +146,6 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
|
||||
dr->wIndex = cpu_to_le16(index);
|
||||
dr->wLength = cpu_to_le16(size);
|
||||
|
||||
/* dbg("usb_control_msg"); */
|
||||
|
||||
ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
|
||||
|
||||
kfree(dr);
|
||||
|
@ -209,7 +209,7 @@ void usb_detect_quirks(struct usb_device *udev)
|
||||
* for all devices. It will affect things like hub resets
|
||||
* and EMF-related port disables.
|
||||
*/
|
||||
if (!(udev->quirks & USB_QUIRK_RESET_MORPHS))
|
||||
if (!(udev->quirks & USB_QUIRK_RESET))
|
||||
udev->persist_enabled = 1;
|
||||
#endif /* CONFIG_PM */
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char *
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET_MORPHS));
|
||||
return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -204,15 +204,15 @@ set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int config;
|
||||
int val;
|
||||
|
||||
if (sscanf(buf, "%d", &config) != 1 || config < 0 || config > 1)
|
||||
if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
usb_lock_device(udev);
|
||||
if (config)
|
||||
udev->quirks |= USB_QUIRK_RESET_MORPHS;
|
||||
if (val)
|
||||
udev->quirks |= USB_QUIRK_RESET;
|
||||
else
|
||||
udev->quirks &= ~USB_QUIRK_RESET_MORPHS;
|
||||
udev->quirks &= ~USB_QUIRK_RESET;
|
||||
usb_unlock_device(udev);
|
||||
return count;
|
||||
}
|
||||
|
@ -19,20 +19,91 @@
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
|
||||
/**
|
||||
* usb_acpi_power_manageable - check whether usb port has
|
||||
* acpi power resource.
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @index: port index based zero
|
||||
*
|
||||
* Return true if the port has acpi power resource and false if no.
|
||||
*/
|
||||
bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
|
||||
{
|
||||
acpi_handle port_handle;
|
||||
int port1 = index + 1;
|
||||
|
||||
port_handle = usb_get_hub_port_acpi_handle(hdev,
|
||||
port1);
|
||||
if (port_handle)
|
||||
return acpi_bus_power_manageable(port_handle);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
|
||||
|
||||
/**
|
||||
* usb_acpi_set_power_state - control usb port's power via acpi power
|
||||
* resource
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @index: port index based zero
|
||||
* @enable: power state expected to be set
|
||||
*
|
||||
* Notice to use usb_acpi_power_manageable() to check whether the usb port
|
||||
* has acpi power resource before invoking this function.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
|
||||
{
|
||||
acpi_handle port_handle;
|
||||
unsigned char state;
|
||||
int port1 = index + 1;
|
||||
int error = -EINVAL;
|
||||
|
||||
port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev,
|
||||
port1);
|
||||
if (!port_handle)
|
||||
return error;
|
||||
|
||||
if (enable)
|
||||
state = ACPI_STATE_D0;
|
||||
else
|
||||
state = ACPI_STATE_D3_COLD;
|
||||
|
||||
error = acpi_bus_set_power(port_handle, state);
|
||||
if (!error)
|
||||
dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n",
|
||||
port1, enable);
|
||||
else
|
||||
dev_dbg(&hdev->dev, "The power of hub port failed to be set\n");
|
||||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
|
||||
|
||||
static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
|
||||
acpi_handle handle, int port1)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *upc;
|
||||
struct acpi_pld pld;
|
||||
int ret = 0;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
|
||||
|
||||
/*
|
||||
* Accoding to ACPI Spec 9.13. PLD indicates whether usb port is
|
||||
* user visible and _UPC indicates whether it is connectable. If
|
||||
* the port was visible and connectable, it could be freely connected
|
||||
* and disconnected with USB devices. If no visible and connectable,
|
||||
* a usb device is directly hard-wired to the port. If no visible and
|
||||
* no connectable, the port would be not used.
|
||||
*/
|
||||
status = acpi_get_physical_device_location(handle, &pld);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
|
||||
upc = buffer.pointer;
|
||||
|
||||
if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
|
||||
|| upc->package.count != 4) {
|
||||
ret = -EINVAL;
|
||||
@ -40,69 +111,107 @@ static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
|
||||
}
|
||||
|
||||
if (upc->package.elements[0].integer.value)
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
if (pld.user_visible)
|
||||
usb_set_hub_port_connect_type(hdev, port1,
|
||||
USB_PORT_CONNECT_TYPE_HOT_PLUG);
|
||||
else
|
||||
usb_set_hub_port_connect_type(hdev, port1,
|
||||
USB_PORT_CONNECT_TYPE_HARD_WIRED);
|
||||
else if (!pld.user_visible)
|
||||
usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
|
||||
|
||||
out:
|
||||
kfree(upc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_pld pld;
|
||||
|
||||
status = acpi_get_physical_device_location(handle, &pld);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
if (pld.user_visible)
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct device *parent;
|
||||
acpi_handle *parent_handle;
|
||||
|
||||
if (!is_usb_device(dev))
|
||||
return -ENODEV;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
parent = dev->parent;
|
||||
parent_handle = DEVICE_ACPI_HANDLE(parent);
|
||||
|
||||
if (!parent_handle)
|
||||
return -ENODEV;
|
||||
|
||||
*handle = acpi_get_child(parent_handle, udev->portnum);
|
||||
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
int port_num;
|
||||
|
||||
/*
|
||||
* PLD will tell us whether a port is removable to the user or
|
||||
* not. If we don't get an answer from PLD (it's not present
|
||||
* or it's malformed) then try to infer it from UPC. If a
|
||||
* device isn't connectable then it's probably not removable.
|
||||
* In the ACPI DSDT table, only usb root hub and usb ports are
|
||||
* acpi device nodes. The hierarchy like following.
|
||||
* Device (EHC1)
|
||||
* Device (HUBN)
|
||||
* Device (PR01)
|
||||
* Device (PR11)
|
||||
* Device (PR12)
|
||||
* Device (PR13)
|
||||
* ...
|
||||
* So all binding process is divided into two parts. binding
|
||||
* root hub and usb ports.
|
||||
*/
|
||||
if (usb_acpi_check_pld(udev, *handle) != 0)
|
||||
usb_acpi_check_upc(udev, *handle);
|
||||
if (is_usb_device(dev)) {
|
||||
udev = to_usb_device(dev);
|
||||
if (udev->parent) {
|
||||
enum usb_port_connect_type type;
|
||||
|
||||
/*
|
||||
* According usb port's connect type to set usb device's
|
||||
* removability.
|
||||
*/
|
||||
type = usb_get_hub_port_connect_type(udev->parent,
|
||||
udev->portnum);
|
||||
switch (type) {
|
||||
case USB_PORT_CONNECT_TYPE_HOT_PLUG:
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
break;
|
||||
case USB_PORT_CONNECT_TYPE_HARD_WIRED:
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
break;
|
||||
default:
|
||||
udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* root hub's parent is the usb hcd. */
|
||||
parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
|
||||
*handle = acpi_get_child(parent_handle, udev->portnum);
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
} else if (is_usb_port(dev)) {
|
||||
sscanf(dev_name(dev), "port%d", &port_num);
|
||||
/* Get the struct usb_device point of port's hub */
|
||||
udev = to_usb_device(dev->parent->parent);
|
||||
|
||||
/*
|
||||
* The root hub ports' parent is the root hub. The non-root-hub
|
||||
* ports' parent is the parent hub port which the hub is
|
||||
* connected to.
|
||||
*/
|
||||
if (!udev->parent) {
|
||||
*handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
|
||||
port_num);
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
} else {
|
||||
parent_handle =
|
||||
usb_get_hub_port_acpi_handle(udev->parent,
|
||||
udev->portnum);
|
||||
if (!parent_handle)
|
||||
return -ENODEV;
|
||||
|
||||
*handle = acpi_get_child(parent_handle, port_num);
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
}
|
||||
usb_acpi_check_port_connect_type(udev, *handle, port_num);
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_bus_type usb_acpi_bus = {
|
||||
.bus = &usb_bus_type,
|
||||
.find_bridge = NULL,
|
||||
.find_bridge = usb_acpi_find_device,
|
||||
.find_device = usb_acpi_find_device,
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <linux/pm.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
struct dev_state;
|
||||
|
||||
@ -115,6 +116,7 @@ extern struct bus_type usb_bus_type;
|
||||
extern struct device_type usb_device_type;
|
||||
extern struct device_type usb_if_device_type;
|
||||
extern struct device_type usb_ep_device_type;
|
||||
extern struct device_type usb_port_device_type;
|
||||
extern struct usb_device_driver usb_generic_driver;
|
||||
|
||||
static inline int is_usb_device(const struct device *dev)
|
||||
@ -132,6 +134,11 @@ static inline int is_usb_endpoint(const struct device *dev)
|
||||
return dev->type == &usb_ep_device_type;
|
||||
}
|
||||
|
||||
static inline int is_usb_port(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb_port_device_type;
|
||||
}
|
||||
|
||||
/* Do the same for device drivers and interface drivers. */
|
||||
|
||||
static inline int is_usb_device_driver(struct device_driver *drv)
|
||||
@ -162,10 +169,16 @@ extern void usb_notify_add_device(struct usb_device *udev);
|
||||
extern void usb_notify_remove_device(struct usb_device *udev);
|
||||
extern void usb_notify_add_bus(struct usb_bus *ubus);
|
||||
extern void usb_notify_remove_bus(struct usb_bus *ubus);
|
||||
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);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int usb_acpi_register(void);
|
||||
extern void usb_acpi_unregister(void);
|
||||
extern acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
|
||||
int port1);
|
||||
#else
|
||||
static inline int usb_acpi_register(void) { return 0; };
|
||||
static inline void usb_acpi_unregister(void) { };
|
||||
|
@ -2,8 +2,6 @@ config USB_DWC3
|
||||
tristate "DesignWare USB3 DRD Core Support"
|
||||
depends on (USB && USB_GADGET)
|
||||
select USB_OTG_UTILS
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SUPERSPEED
|
||||
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
|
||||
help
|
||||
Say Y or M here if your system has a Dual Role SuperSpeed
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
@ -99,6 +100,7 @@ void dwc3_put_device_id(int id)
|
||||
|
||||
ret = test_bit(id, dwc3_devs);
|
||||
WARN(!ret, "dwc3: ID %d not in use\n", id);
|
||||
smp_mb__before_clear_bit();
|
||||
clear_bit(id, dwc3_devs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_put_device_id);
|
||||
@ -136,6 +138,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
mdelay(100);
|
||||
|
||||
/* Clear USB3 PHY reset */
|
||||
@ -464,12 +468,24 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
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;
|
||||
}
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
|
||||
|
@ -457,7 +457,6 @@ enum dwc3_phy {
|
||||
enum dwc3_ep0_next {
|
||||
DWC3_EP0_UNKNOWN = 0,
|
||||
DWC3_EP0_COMPLETE,
|
||||
DWC3_EP0_NRDY_SETUP,
|
||||
DWC3_EP0_NRDY_DATA,
|
||||
DWC3_EP0_NRDY_STATUS,
|
||||
};
|
||||
@ -624,6 +623,8 @@ struct dwc3_scratchpad_array {
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @revision: revision register contents
|
||||
* @mode: mode of operation
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
@ -667,6 +668,9 @@ struct dwc3 {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *gadget_driver;
|
||||
|
||||
struct usb_phy *usb2_phy;
|
||||
struct usb_phy *usb3_phy;
|
||||
|
||||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
@ -779,7 +783,6 @@ struct dwc3_event_depevt {
|
||||
#define DEPEVT_STREAMEVT_NOTFOUND 2
|
||||
|
||||
/* Control-only Status */
|
||||
#define DEPEVT_STATUS_CONTROL_SETUP 0
|
||||
#define DEPEVT_STATUS_CONTROL_DATA 1
|
||||
#define DEPEVT_STATUS_CONTROL_STATUS 2
|
||||
|
||||
|
@ -19,16 +19,74 @@
|
||||
#include <linux/platform_data/dwc3-exynos.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/nop-usb-xceiv.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
struct dwc3_exynos {
|
||||
struct platform_device *dwc3;
|
||||
struct platform_device *usb2_phy;
|
||||
struct platform_device *usb3_phy;
|
||||
struct device *dev;
|
||||
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int __devinit dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
|
||||
{
|
||||
struct nop_usb_xceiv_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 0);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
exynos->usb2_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 1);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
exynos->usb3_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB3;
|
||||
|
||||
ret = platform_device_add_data(exynos->usb3_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(exynos->usb2_phy);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(exynos->usb3_phy);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
platform_device_del(exynos->usb2_phy);
|
||||
|
||||
err2:
|
||||
platform_device_put(exynos->usb3_phy);
|
||||
|
||||
err1:
|
||||
platform_device_put(exynos->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
|
||||
@ -51,6 +109,12 @@ static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
|
||||
if (devid < 0)
|
||||
goto err1;
|
||||
|
||||
ret = dwc3_exynos_register_phys(exynos);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "couldn't register PHYs\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", devid);
|
||||
if (!dwc3) {
|
||||
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
|
||||
@ -120,6 +184,8 @@ static int __devexit dwc3_exynos_remove(struct platform_device *pdev)
|
||||
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
platform_device_unregister(exynos->dwc3);
|
||||
platform_device_unregister(exynos->usb2_phy);
|
||||
platform_device_unregister(exynos->usb3_phy);
|
||||
|
||||
dwc3_put_device_id(exynos->dwc3->id);
|
||||
|
||||
|
@ -48,6 +48,9 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/nop-usb-xceiv.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/*
|
||||
@ -131,6 +134,8 @@ struct dwc3_omap {
|
||||
spinlock_t lock;
|
||||
|
||||
struct platform_device *dwc3;
|
||||
struct platform_device *usb2_phy;
|
||||
struct platform_device *usb3_phy;
|
||||
struct device *dev;
|
||||
|
||||
int irq;
|
||||
@ -152,6 +157,59 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
static int __devinit dwc3_omap_register_phys(struct dwc3_omap *omap)
|
||||
{
|
||||
struct nop_usb_xceiv_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 0);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
omap->usb2_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 1);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
omap->usb3_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB3;
|
||||
|
||||
ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(omap->usb2_phy);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(omap->usb3_phy);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
platform_device_del(omap->usb2_phy);
|
||||
|
||||
err2:
|
||||
platform_device_put(omap->usb3_phy);
|
||||
|
||||
err1:
|
||||
platform_device_put(omap->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
{
|
||||
@ -251,6 +309,12 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = dwc3_omap_register_phys(omap);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
devid = dwc3_get_device_id();
|
||||
if (devid < 0)
|
||||
return -ENODEV;
|
||||
@ -371,6 +435,8 @@ static int __devexit 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);
|
||||
|
||||
dwc3_put_device_id(omap->dwc3->id);
|
||||
|
||||
|
@ -42,6 +42,9 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/nop-usb-xceiv.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/* FIXME define these in <linux/pci_ids.h> */
|
||||
@ -51,8 +54,64 @@
|
||||
struct dwc3_pci {
|
||||
struct device *dev;
|
||||
struct platform_device *dwc3;
|
||||
struct platform_device *usb2_phy;
|
||||
struct platform_device *usb3_phy;
|
||||
};
|
||||
|
||||
static int __devinit dwc3_pci_register_phys(struct dwc3_pci *glue)
|
||||
{
|
||||
struct nop_usb_xceiv_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 0);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
glue->usb2_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", 1);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
glue->usb3_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB3;
|
||||
|
||||
ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(glue->usb2_phy);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(glue->usb3_phy);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
platform_device_del(glue->usb2_phy);
|
||||
|
||||
err2:
|
||||
platform_device_put(glue->usb3_phy);
|
||||
|
||||
err1:
|
||||
platform_device_put(glue->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit dwc3_pci_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
@ -80,6 +139,12 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_set_master(pci);
|
||||
|
||||
ret = dwc3_pci_register_phys(glue);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
devid = dwc3_get_device_id();
|
||||
if (devid < 0) {
|
||||
ret = -ENOMEM;
|
||||
@ -144,6 +209,8 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct dwc3_pci *glue = pci_get_drvdata(pci);
|
||||
|
||||
platform_device_unregister(glue->usb2_phy);
|
||||
platform_device_unregister(glue->usb3_phy);
|
||||
dwc3_put_device_id(glue->dwc3->id);
|
||||
platform_device_unregister(glue->dwc3);
|
||||
pci_set_drvdata(pci, NULL);
|
||||
|
@ -125,7 +125,6 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
||||
struct dwc3_request *req)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int ret = 0;
|
||||
|
||||
req->request.actual = 0;
|
||||
req->request.status = -EINPROGRESS;
|
||||
@ -156,16 +155,72 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
||||
|
||||
dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
|
||||
DWC3_EP0_DIR_IN);
|
||||
} else if (dwc->delayed_status) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case gadget driver asked us to delay the STATUS phase,
|
||||
* handle it here.
|
||||
*/
|
||||
if (dwc->delayed_status) {
|
||||
unsigned direction;
|
||||
|
||||
direction = !dwc->ep0_expect_in;
|
||||
dwc->delayed_status = false;
|
||||
|
||||
if (dwc->ep0state == EP0_STATUS_PHASE)
|
||||
__dwc3_ep0_do_control_status(dwc, dwc->eps[1]);
|
||||
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
|
||||
else
|
||||
dev_dbg(dwc->dev, "too early for delayed status\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
/*
|
||||
* Unfortunately we have uncovered a limitation wrt the Data Phase.
|
||||
*
|
||||
* Section 9.4 says we can wait for the XferNotReady(DATA) event to
|
||||
* come before issueing Start Transfer command, but if we do, we will
|
||||
* miss situations where the host starts another SETUP phase instead of
|
||||
* the DATA phase. Such cases happen at least on TD.7.6 of the Link
|
||||
* Layer Compliance Suite.
|
||||
*
|
||||
* The problem surfaces due to the fact that in case of back-to-back
|
||||
* SETUP packets there will be no XferNotReady(DATA) generated and we
|
||||
* will be stuck waiting for XferNotReady(DATA) forever.
|
||||
*
|
||||
* By looking at tables 9-13 and 9-14 of the Databook, we can see that
|
||||
* it tells us to start Data Phase right away. It also mentions that if
|
||||
* we receive a SETUP phase instead of the DATA phase, core will issue
|
||||
* XferComplete for the DATA phase, before actually initiating it in
|
||||
* the wire, with the TRB's status set to "SETUP_PENDING". Such status
|
||||
* can only be used to print some debugging logs, as the core expects
|
||||
* us to go through to the STATUS phase and start a CONTROL_STATUS TRB,
|
||||
* just so it completes right away, without transferring anything and,
|
||||
* only then, we can go back to the SETUP phase.
|
||||
*
|
||||
* Because of this scenario, SNPS decided to change the programming
|
||||
* model of control transfers and support on-demand transfers only for
|
||||
* the STATUS phase. To fix the issue we have now, we will always wait
|
||||
* for gadget driver to queue the DATA phase's struct usb_request, then
|
||||
* start it right away.
|
||||
*
|
||||
* If we're actually in a 2-stage transfer, we will wait for
|
||||
* XferNotReady(STATUS).
|
||||
*/
|
||||
if (dwc->three_stage_setup) {
|
||||
unsigned direction;
|
||||
|
||||
direction = dwc->ep0_expect_in;
|
||||
dwc->ep0state = EP0_DATA_PHASE;
|
||||
|
||||
__dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
|
||||
|
||||
dep->flags &= ~DWC3_EP0_DIR_IN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
@ -207,9 +262,14 @@ out:
|
||||
|
||||
static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep = dwc->eps[0];
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
/* reinitialize physical ep1 */
|
||||
dep = dwc->eps[1];
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
|
||||
/* stall is always issued on EP0 */
|
||||
dep = dwc->eps[0];
|
||||
__dwc3_gadget_ep_set_halt(dep, 1);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
dwc->delayed_status = false;
|
||||
@ -698,6 +758,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
||||
struct dwc3_trb *trb;
|
||||
struct dwc3_ep *ep0;
|
||||
u32 transferred;
|
||||
u32 status;
|
||||
u32 length;
|
||||
u8 epnum;
|
||||
|
||||
@ -710,6 +771,17 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
||||
ur = &r->request;
|
||||
|
||||
trb = dwc->ep0_trb;
|
||||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING) {
|
||||
dev_dbg(dwc->dev, "Setup Pending received\n");
|
||||
|
||||
if (r)
|
||||
dwc3_gadget_giveback(ep0, r, -ECONNRESET);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
length = trb->size & DWC3_TRB_SIZE_MASK;
|
||||
|
||||
if (dwc->ep0_bounced) {
|
||||
@ -745,8 +817,11 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
||||
{
|
||||
struct dwc3_request *r;
|
||||
struct dwc3_ep *dep;
|
||||
struct dwc3_trb *trb;
|
||||
u32 status;
|
||||
|
||||
dep = dwc->eps[0];
|
||||
trb = dwc->ep0_trb;
|
||||
|
||||
if (!list_empty(&dep->request_list)) {
|
||||
r = next_request(&dep->request_list);
|
||||
@ -766,6 +841,10 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
||||
}
|
||||
}
|
||||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING)
|
||||
dev_dbg(dwc->dev, "Setup Pending received\n");
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
}
|
||||
@ -799,12 +878,6 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc3_ep0_out_start(dwc);
|
||||
}
|
||||
|
||||
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
{
|
||||
@ -857,29 +930,6 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
WARN_ON(ret < 0);
|
||||
}
|
||||
|
||||
static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
struct dwc3_request *req;
|
||||
|
||||
dep = dwc->eps[0];
|
||||
|
||||
if (list_empty(&dep->request_list)) {
|
||||
dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
|
||||
if (event->endpoint_number)
|
||||
dep->flags |= DWC3_EP0_DIR_IN;
|
||||
return;
|
||||
}
|
||||
|
||||
req = next_request(&dep->request_list);
|
||||
dep = dwc->eps[event->endpoint_number];
|
||||
|
||||
__dwc3_ep0_do_control_data(dwc, dep, req);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
@ -911,100 +961,61 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
|
||||
__dwc3_ep0_do_control_status(dwc, dep);
|
||||
}
|
||||
|
||||
static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 cmd;
|
||||
int ret;
|
||||
|
||||
if (!dep->resource_index)
|
||||
return;
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
||||
cmd |= DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
}
|
||||
|
||||
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc->setup_packet_pending = true;
|
||||
|
||||
/*
|
||||
* This part is very tricky: If we have just handled
|
||||
* XferNotReady(Setup) and we're now expecting a
|
||||
* XferComplete but, instead, we receive another
|
||||
* XferNotReady(Setup), we should STALL and restart
|
||||
* the state machine.
|
||||
*
|
||||
* In all other cases, we just continue waiting
|
||||
* for the XferComplete event.
|
||||
*
|
||||
* We are a little bit unsafe here because we're
|
||||
* not trying to ensure that last event was, indeed,
|
||||
* XferNotReady(Setup).
|
||||
*
|
||||
* Still, we don't expect any condition where that
|
||||
* should happen and, even if it does, it would be
|
||||
* another error condition.
|
||||
*/
|
||||
if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) {
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_SETUP:
|
||||
dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n");
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
break;
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
/* FALLTHROUGH */
|
||||
case DEPEVT_STATUS_CONTROL_STATUS:
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
dev_vdbg(dwc->dev, "waiting for XferComplete\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_SETUP:
|
||||
dev_vdbg(dwc->dev, "Control Setup\n");
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
|
||||
dwc3_ep0_do_control_setup(dwc, event);
|
||||
break;
|
||||
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
dev_vdbg(dwc->dev, "Control Data\n");
|
||||
|
||||
dwc->ep0state = EP0_DATA_PHASE;
|
||||
|
||||
if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) {
|
||||
dev_vdbg(dwc->dev, "Expected %d got %d\n",
|
||||
dwc->ep0_next_event,
|
||||
DWC3_EP0_NRDY_DATA);
|
||||
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* One of the possible error cases is when Host _does_
|
||||
* request for Data Phase, but it does so on the wrong
|
||||
* direction.
|
||||
* We already have a DATA transfer in the controller's cache,
|
||||
* if we receive a XferNotReady(DATA) we will ignore it, unless
|
||||
* it's for the wrong direction.
|
||||
*
|
||||
* Here, we already know ep0_next_event is DATA (see above),
|
||||
* so we only need to check for direction.
|
||||
* In that case, we must issue END_TRANSFER command to the Data
|
||||
* Phase we already have started and issue SetStall on the
|
||||
* control endpoint.
|
||||
*/
|
||||
if (dwc->ep0_expect_in != event->endpoint_number) {
|
||||
struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
|
||||
|
||||
dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
|
||||
dwc3_ep0_end_control_data(dwc, dep);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
}
|
||||
|
||||
dwc3_ep0_do_control_data(dwc, event);
|
||||
break;
|
||||
|
||||
case DEPEVT_STATUS_CONTROL_STATUS:
|
||||
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
|
||||
return;
|
||||
|
||||
dev_vdbg(dwc->dev, "Control Status\n");
|
||||
|
||||
dwc->ep0state = EP0_STATUS_PHASE;
|
||||
|
||||
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) {
|
||||
dev_vdbg(dwc->dev, "Expected %d got %d\n",
|
||||
dwc->ep0_next_event,
|
||||
DWC3_EP0_NRDY_STATUS);
|
||||
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dwc->delayed_status) {
|
||||
WARN_ON_ONCE(event->endpoint_number != 1);
|
||||
dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
|
||||
|
@ -434,15 +434,25 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
|
||||
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc)
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
||||
bool ignore)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
|
||||
memset(¶ms, 0x00, sizeof(params));
|
||||
|
||||
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
|
||||
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
|
||||
| DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1);
|
||||
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
|
||||
|
||||
/* Burst size is only needed in SuperSpeed mode */
|
||||
if (dwc->gadget.speed == USB_SPEED_SUPER) {
|
||||
u32 burst = dep->endpoint.maxburst - 1;
|
||||
|
||||
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
|
||||
}
|
||||
|
||||
if (ignore)
|
||||
params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
|
||||
|
||||
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
|
||||
| DWC3_DEPCFG_XFER_NOT_READY_EN;
|
||||
@ -501,7 +511,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
*/
|
||||
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc)
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
||||
bool ignore)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
@ -513,7 +524,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc);
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -561,27 +572,7 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
if (!list_empty(&dep->req_queued)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
|
||||
/*
|
||||
* NOTICE: We are violating what the Databook says about the
|
||||
* EndTransfer command. Ideally we would _always_ wait for the
|
||||
* EndTransfer Command Completion IRQ, but that's causing too
|
||||
* much trouble synchronizing between us and gadget driver.
|
||||
*
|
||||
* We have discussed this with the IP Provider and it was
|
||||
* suggested to giveback all requests here, but give HW some
|
||||
* extra time to synchronize with the interconnect. We're using
|
||||
* an arbitraty 100us delay for that.
|
||||
*
|
||||
* Note also that a similar handling was tested by Synopsys
|
||||
* (thanks a lot Paul) and nothing bad has come out of it.
|
||||
* In short, what we're doing is:
|
||||
*
|
||||
* - Issue EndTransfer WITH CMDIOC bit set
|
||||
* - Wait 100us
|
||||
* - giveback all requests to gadget driver
|
||||
*/
|
||||
udelay(100);
|
||||
|
||||
/* - giveback all requests to gadget driver */
|
||||
while (!list_empty(&dep->req_queued)) {
|
||||
req = next_request(&dep->req_queued);
|
||||
|
||||
@ -660,6 +651,12 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
||||
dep = to_dwc3_ep(ep);
|
||||
dwc = dep->dwc;
|
||||
|
||||
if (dep->flags & DWC3_EP_ENABLED) {
|
||||
dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
|
||||
dep->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
strlcat(dep->name, "-control", sizeof(dep->name));
|
||||
@ -677,16 +674,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
||||
dev_err(dwc->dev, "invalid endpoint transfer type\n");
|
||||
}
|
||||
|
||||
if (dep->flags & DWC3_EP_ENABLED) {
|
||||
dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
|
||||
dep->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -1105,12 +1096,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
}
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
|
||||
if (ret && ret != -EBUSY) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
if (ret && ret != -EBUSY)
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1119,16 +1107,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
* core may not see the modified TRB(s).
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
(dep->flags & DWC3_EP_BUSY)) {
|
||||
(dep->flags & DWC3_EP_BUSY) &&
|
||||
!(dep->flags & DWC3_EP_MISSED_ISOC)) {
|
||||
WARN_ON_ONCE(!dep->resource_index);
|
||||
ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
|
||||
false);
|
||||
if (ret && ret != -EBUSY) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
if (ret && ret != -EBUSY)
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1533,14 +1519,14 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err1;
|
||||
@ -1765,7 +1751,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
|
||||
struct dwc3_ep *dep = dwc->eps[i];
|
||||
dep = dwc->eps[i];
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
@ -1892,6 +1878,25 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
|
||||
if (!dep->resource_index)
|
||||
return;
|
||||
|
||||
/*
|
||||
* NOTICE: We are violating what the Databook says about the
|
||||
* EndTransfer command. Ideally we would _always_ wait for the
|
||||
* EndTransfer Command Completion IRQ, but that's causing too
|
||||
* much trouble synchronizing between us and gadget driver.
|
||||
*
|
||||
* We have discussed this with the IP Provider and it was
|
||||
* suggested to giveback all requests here, but give HW some
|
||||
* extra time to synchronize with the interconnect. We're using
|
||||
* an arbitraty 100us delay for that.
|
||||
*
|
||||
* Note also that a similar handling was tested by Synopsys
|
||||
* (thanks a lot Paul) and nothing bad has come out of it.
|
||||
* In short, what we're doing is:
|
||||
*
|
||||
* - Issue EndTransfer WITH CMDIOC bit set
|
||||
* - Wait 100us
|
||||
*/
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
||||
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
@ -1899,6 +1904,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
|
||||
@ -2156,14 +2163,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
}
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
|
@ -491,7 +491,7 @@ static int ehci_wait_for_port(int port);
|
||||
* Return -ENODEV for any general failure
|
||||
* Return -EIO if wait for port fails
|
||||
*/
|
||||
int dbgp_external_startup(void)
|
||||
static int _dbgp_external_startup(void)
|
||||
{
|
||||
int devnum;
|
||||
struct usb_debug_descriptor dbgp_desc;
|
||||
@ -613,6 +613,11 @@ err:
|
||||
goto try_again;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int dbgp_external_startup(struct usb_hcd *hcd)
|
||||
{
|
||||
return xen_dbgp_external_startup(hcd) ?: _dbgp_external_startup();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dbgp_external_startup);
|
||||
|
||||
static int ehci_reset_port(int port)
|
||||
@ -804,7 +809,7 @@ try_next_port:
|
||||
dbgp_ehci_status("ehci skip - already configured");
|
||||
}
|
||||
|
||||
ret = dbgp_external_startup();
|
||||
ret = _dbgp_external_startup();
|
||||
if (ret == -EIO)
|
||||
goto next_debug_port;
|
||||
|
||||
@ -934,7 +939,7 @@ static void early_dbgp_write(struct console *con, const char *str, u32 n)
|
||||
ctrl = readl(&ehci_debug->control);
|
||||
if (!(ctrl & DBGP_ENABLED)) {
|
||||
dbgp_not_safe = 1;
|
||||
dbgp_external_startup();
|
||||
_dbgp_external_startup();
|
||||
} else {
|
||||
cmd |= CMD_RUN;
|
||||
writel(cmd, &ehci_regs->command);
|
||||
@ -974,10 +979,14 @@ struct console early_dbgp_console = {
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
int dbgp_reset_prep(void)
|
||||
int dbgp_reset_prep(struct usb_hcd *hcd)
|
||||
{
|
||||
int ret = xen_dbgp_reset_prep(hcd);
|
||||
u32 ctrl;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dbgp_not_safe = 1;
|
||||
if (!ehci_debug)
|
||||
return 0;
|
||||
|
@ -154,16 +154,25 @@ config USB_LPC32XX
|
||||
|
||||
config USB_ATMEL_USBA
|
||||
tristate "Atmel USBA"
|
||||
select USB_GADGET_DUALSPEED
|
||||
depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
|
||||
help
|
||||
USBA is the integrated high-speed USB Device controller on
|
||||
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
|
||||
|
||||
config USB_BCM63XX_UDC
|
||||
tristate "Broadcom BCM63xx Peripheral Controller"
|
||||
depends on BCM63XX
|
||||
help
|
||||
Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
|
||||
high speed USB Device Port with support for four fixed endpoints
|
||||
(plus endpoint zero).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "bcm63xx_udc".
|
||||
|
||||
config USB_FSL_USB2
|
||||
tristate "Freescale Highspeed USB DR Peripheral Controller"
|
||||
depends on FSL_SOC || ARCH_MXC
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
help
|
||||
Some of Freescale PowerPC and i.MX processors have a High Speed
|
||||
@ -179,7 +188,6 @@ config USB_FSL_USB2
|
||||
config USB_FUSB300
|
||||
tristate "Faraday FUSB300 USB Peripheral Controller"
|
||||
depends on !PHYS_ADDR_T_64BIT
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Faraday usb device controller FUSB300 driver
|
||||
|
||||
@ -227,7 +235,6 @@ config USB_PXA25X_SMALL
|
||||
|
||||
config USB_R8A66597
|
||||
tristate "Renesas R8A66597 USB Peripheral Controller"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
R8A66597 is a discrete USB host and peripheral controller chip that
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
@ -240,7 +247,6 @@ config USB_R8A66597
|
||||
config USB_RENESAS_USBHS_UDC
|
||||
tristate 'Renesas USBHS controller'
|
||||
depends on USB_RENESAS_USBHS
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Renesas USBHS is a discrete USB host and peripheral controller chip
|
||||
that supports both full and high speed USB 2.0 data transfers.
|
||||
@ -268,7 +274,6 @@ config USB_PXA27X
|
||||
config USB_S3C_HSOTG
|
||||
tristate "S3C HS/OtG USB Device controller"
|
||||
depends on S3C_DEV_USB_HSOTG
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
The Samsung S3C64XX USB2.0 high-speed gadget controller
|
||||
integrated into the S3C64XX series SoC.
|
||||
@ -305,7 +310,6 @@ config USB_S3C2410_DEBUG
|
||||
config USB_S3C_HSUDC
|
||||
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
|
||||
depends on ARCH_S3C24XX
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
|
||||
integrated with dual speed USB 2.0 device controller. It has
|
||||
@ -315,7 +319,6 @@ config USB_S3C_HSUDC
|
||||
|
||||
config USB_MV_UDC
|
||||
tristate "Marvell USB2.0 Device Controller"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Marvell Socs (including PXA and MMP series) include a high speed
|
||||
USB2.0 OTG controller, which can be configured as high speed or
|
||||
@ -338,14 +341,12 @@ config USB_MV_U3D
|
||||
config USB_GADGET_MUSB_HDRC
|
||||
tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
|
||||
depends on USB_MUSB_HDRC
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
This OTG-capable silicon IP is used in dual designs including
|
||||
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
|
||||
|
||||
config USB_M66592
|
||||
tristate "Renesas M66592 USB Peripheral Controller"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
M66592 is a discrete USB peripheral controller chip that
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
@ -362,7 +363,6 @@ config USB_M66592
|
||||
config USB_AMD5536UDC
|
||||
tristate "AMD5536 UDC"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
|
||||
It is a USB Highspeed DMA capable USB device controller. Beside ep0
|
||||
@ -389,7 +389,6 @@ config USB_FSL_QE
|
||||
|
||||
config USB_NET2272
|
||||
tristate "PLX NET2272"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
PLX NET2272 is a USB peripheral controller which supports
|
||||
both full and high speed USB 2.0 data transfers.
|
||||
@ -413,7 +412,6 @@ config USB_NET2272_DMA
|
||||
config USB_NET2280
|
||||
tristate "NetChip 228x"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
@ -443,7 +441,6 @@ config USB_GOKU
|
||||
config USB_EG20T
|
||||
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
This is a USB device driver for EG20T PCH.
|
||||
EG20T PCH is the platform controller hub that is used in Intel's
|
||||
@ -470,8 +467,6 @@ config USB_EG20T
|
||||
config USB_DUMMY_HCD
|
||||
tristate "Dummy HCD (DEVELOPMENT)"
|
||||
depends on USB=y || (USB=m && USB_GADGET=m)
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SUPERSPEED
|
||||
help
|
||||
This host controller driver emulates USB, looping all data transfer
|
||||
requests back to a USB "gadget driver" in the same host. The host
|
||||
@ -496,18 +491,15 @@ config USB_DUMMY_HCD
|
||||
|
||||
endmenu
|
||||
|
||||
# Selected by UDC drivers that support high-speed operation.
|
||||
config USB_GADGET_DUALSPEED
|
||||
bool
|
||||
|
||||
# Selected by UDC drivers that support super-speed opperation
|
||||
config USB_GADGET_SUPERSPEED
|
||||
bool
|
||||
depends on USB_GADGET_DUALSPEED
|
||||
|
||||
#
|
||||
# USB Gadget Drivers
|
||||
#
|
||||
|
||||
# composite based drivers
|
||||
config USB_LIBCOMPOSITE
|
||||
tristate
|
||||
depends on USB_GADGET
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
@ -531,6 +523,7 @@ choice
|
||||
|
||||
config USB_ZERO
|
||||
tristate "Gadget Zero (DEVELOPMENT)"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
Gadget Zero is a two-configuration device. It either sinks and
|
||||
sources bulk data; or it loops back a configurable number of
|
||||
@ -564,8 +557,9 @@ config USB_ZERO_HNPTEST
|
||||
one serve as the USB host instead (in the "B-Host" role).
|
||||
|
||||
config USB_AUDIO
|
||||
tristate "Audio Gadget (EXPERIMENTAL)"
|
||||
tristate "Audio Gadget"
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
help
|
||||
This Gadget Audio driver is compatible with USB Audio Class
|
||||
@ -594,6 +588,7 @@ config GADGET_UAC1
|
||||
config USB_ETH
|
||||
tristate "Ethernet Gadget (with CDC Ethernet support)"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select CRC32
|
||||
help
|
||||
This driver implements Ethernet style communication, in one of
|
||||
@ -629,6 +624,7 @@ config USB_ETH
|
||||
config USB_ETH_RNDIS
|
||||
bool "RNDIS support"
|
||||
depends on USB_ETH
|
||||
select USB_LIBCOMPOSITE
|
||||
default y
|
||||
help
|
||||
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
|
||||
@ -647,6 +643,7 @@ config USB_ETH_RNDIS
|
||||
config USB_ETH_EEM
|
||||
bool "Ethernet Emulation Model (EEM) support"
|
||||
depends on USB_ETH
|
||||
select USB_LIBCOMPOSITE
|
||||
default n
|
||||
help
|
||||
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
|
||||
@ -663,6 +660,7 @@ config USB_ETH_EEM
|
||||
config USB_G_NCM
|
||||
tristate "Network Control Model (NCM) support"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select CRC32
|
||||
help
|
||||
This driver implements USB CDC NCM subclass standard. NCM is
|
||||
@ -674,8 +672,7 @@ config USB_G_NCM
|
||||
dynamically linked module called "g_ncm".
|
||||
|
||||
config USB_GADGETFS
|
||||
tristate "Gadget Filesystem (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
tristate "Gadget Filesystem"
|
||||
help
|
||||
This driver provides a filesystem based API that lets user mode
|
||||
programs implement a single-configuration USB device, including
|
||||
@ -683,15 +680,12 @@ config USB_GADGETFS
|
||||
All endpoints, transfer speeds, and transfer types supported by
|
||||
the hardware are available, through read() and write() calls.
|
||||
|
||||
Currently, this option is still labelled as EXPERIMENTAL because
|
||||
of existing race conditions in the underlying in-kernel AIO core.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "gadgetfs".
|
||||
|
||||
config USB_FUNCTIONFS
|
||||
tristate "Function Filesystem (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
tristate "Function Filesystem"
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
|
||||
help
|
||||
The Function Filesystem (FunctionFS) lets one create USB
|
||||
@ -755,6 +749,7 @@ config USB_FILE_STORAGE_TEST
|
||||
config USB_MASS_STORAGE
|
||||
tristate "Mass Storage Gadget"
|
||||
depends on BLOCK
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
|
||||
As its storage repository it can use a regular file or a block
|
||||
@ -770,6 +765,7 @@ config USB_MASS_STORAGE
|
||||
config USB_GADGET_TARGET
|
||||
tristate "USB Gadget Target Fabric Module"
|
||||
depends on TARGET_CORE
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
This fabric is an USB gadget. Two USB protocols are supported that is
|
||||
BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
|
||||
@ -779,6 +775,7 @@ config USB_GADGET_TARGET
|
||||
|
||||
config USB_G_SERIAL
|
||||
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Serial Gadget talks to the Linux-USB generic serial driver.
|
||||
This driver supports a CDC-ACM module option, which can be used
|
||||
@ -797,8 +794,9 @@ config USB_G_SERIAL
|
||||
make MS-Windows work with CDC ACM.
|
||||
|
||||
config USB_MIDI_GADGET
|
||||
tristate "MIDI Gadget (EXPERIMENTAL)"
|
||||
depends on SND && EXPERIMENTAL
|
||||
tristate "MIDI Gadget"
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_RAWMIDI
|
||||
help
|
||||
The MIDI Gadget acts as a USB Audio device, with one MIDI
|
||||
@ -812,6 +810,7 @@ config USB_MIDI_GADGET
|
||||
|
||||
config USB_G_PRINTER
|
||||
tristate "Printer Gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Printer Gadget channels data between the USB host and a
|
||||
userspace program driving the print engine. The user space
|
||||
@ -828,6 +827,7 @@ config USB_G_PRINTER
|
||||
config USB_CDC_COMPOSITE
|
||||
tristate "CDC Composite Device (Ethernet and ACM)"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
|
||||
@ -842,6 +842,7 @@ config USB_CDC_COMPOSITE
|
||||
config USB_G_NOKIA
|
||||
tristate "Nokia composite gadget"
|
||||
depends on PHONET
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Nokia composite gadget provides support for acm, obex
|
||||
and phonet in only one composite gadget driver.
|
||||
@ -852,6 +853,7 @@ config USB_G_NOKIA
|
||||
config USB_G_ACM_MS
|
||||
tristate "CDC Composite Device (ACM and mass storage)"
|
||||
depends on BLOCK
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a mass storage, and a CDC ACM (serial port) link.
|
||||
@ -860,9 +862,10 @@ config USB_G_ACM_MS
|
||||
dynamically linked module called "g_acm_ms".
|
||||
|
||||
config USB_G_MULTI
|
||||
tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
|
||||
tristate "Multifunction Composite Gadget"
|
||||
depends on BLOCK && NET
|
||||
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Multifunction Composite Gadget provides Ethernet (RNDIS
|
||||
and/or CDC Ethernet), mass storage and ACM serial link
|
||||
@ -903,6 +906,7 @@ config USB_G_MULTI_CDC
|
||||
|
||||
config USB_G_HID
|
||||
tristate "HID Gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The HID gadget driver provides generic emulation of USB
|
||||
Human Interface Devices (HID).
|
||||
@ -913,8 +917,10 @@ config USB_G_HID
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_hid".
|
||||
|
||||
# Standalone / single function gadgets
|
||||
config USB_G_DBGP
|
||||
tristate "EHCI Debug Device Gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
This gadget emulates an EHCI Debug device. This is useful when you want
|
||||
to interact with an EHCI Debug Port.
|
||||
|
@ -4,6 +4,9 @@
|
||||
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
|
||||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
@ -16,6 +19,7 @@ obj-$(CONFIG_USB_OMAP) += omap_udc.o
|
||||
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
|
||||
obj-$(CONFIG_USB_AT91) += at91_udc.o
|
||||
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
||||
obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
|
||||
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
|
||||
fsl_usb2_udc-y := fsl_udc_core.o
|
||||
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
||||
@ -41,15 +41,12 @@
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
#include "u_serial.c"
|
||||
#include "f_acm.c"
|
||||
#include "f_mass_storage.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
@ -89,17 +86,11 @@ static const struct usb_descriptor_header *otg_desc[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
[USB_GADGET_MANUFACTURER_IDX].s = "",
|
||||
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
[USB_GADGET_SERIAL_IDX].s = "",
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
@ -157,7 +148,6 @@ static struct usb_configuration acm_ms_config_driver = {
|
||||
|
||||
static int __init acm_ms_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status;
|
||||
void *retp;
|
||||
@ -174,44 +164,22 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev)
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
/* set bcdDevice */
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
if (gcnum >= 0) {
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
} else {
|
||||
WARNING(cdev, "controller '%s' not recognized; trying %s\n",
|
||||
gadget->name,
|
||||
acm_ms_config_driver.label);
|
||||
device_desc.bcdDevice =
|
||||
cpu_to_le16(0x0300 | 0x0099);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
*/
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
||||
|
||||
/* register our configuration */
|
||||
status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
|
||||
DRIVER_DESC);
|
||||
fsg_common_put(&fsg_common);
|
||||
@ -232,11 +200,12 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver acm_ms_driver = {
|
||||
static __refdata struct usb_composite_driver acm_ms_driver = {
|
||||
.name = "g_acm_ms",
|
||||
.dev = &device_desc,
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
.strings = dev_strings,
|
||||
.bind = acm_ms_bind,
|
||||
.unbind = __exit_p(acm_ms_unbind),
|
||||
};
|
||||
|
||||
@ -246,7 +215,7 @@ MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
|
||||
return usb_composite_probe(&acm_ms_driver);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
|
@ -1401,7 +1401,7 @@ static int udc_wakeup(struct usb_gadget *gadget)
|
||||
}
|
||||
|
||||
static int amd5536_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *));
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
|
||||
static int amd5536_stop(struct usb_gadget_driver *driver);
|
||||
/* gadget operations */
|
||||
static const struct usb_gadget_ops udc_ops = {
|
||||
@ -1914,7 +1914,7 @@ 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 *))
|
||||
int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
|
||||
{
|
||||
struct udc *dev = udc;
|
||||
int retval;
|
||||
@ -1932,7 +1932,7 @@ static int amd5536_start(struct usb_gadget_driver *driver,
|
||||
dev->driver = driver;
|
||||
dev->gadget.dev.driver = &driver->driver;
|
||||
|
||||
retval = bind(&dev->gadget);
|
||||
retval = bind(&dev->gadget, driver);
|
||||
|
||||
/* Some gadget drivers use both ep0 directions.
|
||||
* NOTE: to gadget driver, ep0 is just one endpoint...
|
||||
|
@ -469,7 +469,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
|
||||
struct at91_udc *udc = ep->udc;
|
||||
struct at91_udc *udc;
|
||||
u16 maxpacket;
|
||||
u32 tmp;
|
||||
unsigned long flags;
|
||||
@ -483,6 +483,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
udc = ep->udc;
|
||||
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
DBG("bogus device state\n");
|
||||
return -ESHUTDOWN;
|
||||
@ -1699,7 +1700,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev)
|
||||
int retval;
|
||||
struct resource *res;
|
||||
|
||||
if (!dev->platform_data) {
|
||||
if (!dev->platform_data && !pdev->dev.of_node) {
|
||||
/* small (so we copy it) but critical! */
|
||||
DBG("missing platform_data\n");
|
||||
return -ENODEV;
|
||||
|
@ -12,35 +12,21 @@
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
#define DRIVER_DESC "Linux USB Audio Gadget"
|
||||
#define DRIVER_VERSION "Feb 2, 2012"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
[USB_GADGET_MANUFACTURER_IDX].s = "",
|
||||
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
[USB_GADGET_SERIAL_IDX].s = "",
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
@ -149,39 +135,18 @@ static struct usb_configuration audio_config_driver = {
|
||||
|
||||
static int __init audio_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
int status;
|
||||
|
||||
gcnum = usb_gadget_controller_number(cdev->gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
else {
|
||||
ERROR(cdev, "controller '%s' not recognized; trying %s\n",
|
||||
cdev->gadget->name,
|
||||
audio_config_driver.label);
|
||||
device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(0x0300 | 0x0099);
|
||||
}
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
cdev->gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
||||
|
||||
status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
|
||||
INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
|
||||
return 0;
|
||||
@ -198,17 +163,18 @@ static int __exit audio_unbind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver audio_driver = {
|
||||
static __refdata struct usb_composite_driver audio_driver = {
|
||||
.name = "g_audio",
|
||||
.dev = &device_desc,
|
||||
.strings = audio_strings,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.bind = audio_bind,
|
||||
.unbind = __exit_p(audio_unbind),
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return usb_composite_probe(&audio_driver, audio_bind);
|
||||
return usb_composite_probe(&audio_driver);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
|
2464
drivers/usb/gadget/bcm63xx_udc.c
Normal file
2464
drivers/usb/gadget/bcm63xx_udc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
@ -34,6 +33,7 @@
|
||||
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
@ -43,10 +43,6 @@
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
#include "u_serial.c"
|
||||
#include "f_acm.c"
|
||||
#include "f_ecm.c"
|
||||
@ -92,15 +88,10 @@ static const struct usb_descriptor_header *otg_desc[] = {
|
||||
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
[USB_GADGET_MANUFACTURER_IDX].s = "",
|
||||
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
[USB_GADGET_SERIAL_IDX].s = "",
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
@ -152,7 +143,6 @@ static struct usb_configuration cdc_config_driver = {
|
||||
|
||||
static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status;
|
||||
|
||||
@ -172,47 +162,22 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
if (status < 0)
|
||||
goto fail0;
|
||||
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
else {
|
||||
/* We assume that can_support_ecm() tells the truth;
|
||||
* but if the controller isn't recognized at all then
|
||||
* that assumption is a bit more likely to be wrong.
|
||||
*/
|
||||
WARNING(cdev, "controller '%s' not recognized; trying %s\n",
|
||||
gadget->name,
|
||||
cdc_config_driver.label);
|
||||
device_desc.bcdDevice =
|
||||
cpu_to_le16(0x0300 | 0x0099);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
*/
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
||||
|
||||
/* register our configuration */
|
||||
status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
|
||||
DRIVER_DESC);
|
||||
|
||||
@ -232,11 +197,12 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver cdc_driver = {
|
||||
static __refdata struct usb_composite_driver cdc_driver = {
|
||||
.name = "g_cdc",
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.bind = cdc_bind,
|
||||
.unbind = __exit_p(cdc_unbind),
|
||||
};
|
||||
|
||||
@ -246,7 +212,7 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return usb_composite_probe(&cdc_driver, cdc_bind);
|
||||
return usb_composite_probe(&cdc_driver);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
|
@ -28,44 +28,6 @@
|
||||
* with the relevant device-wide data.
|
||||
*/
|
||||
|
||||
/* big enough to hold our biggest descriptor */
|
||||
#define USB_BUFSIZ 1024
|
||||
|
||||
static struct usb_composite_driver *composite;
|
||||
static int (*composite_gadget_bind)(struct usb_composite_dev *cdev);
|
||||
|
||||
/* Some systems will need runtime overrides for the product identifiers
|
||||
* published in the device descriptor, either numbers or strings or both.
|
||||
* String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
|
||||
*/
|
||||
|
||||
static ushort idVendor;
|
||||
module_param(idVendor, ushort, 0644);
|
||||
MODULE_PARM_DESC(idVendor, "USB Vendor ID");
|
||||
|
||||
static ushort idProduct;
|
||||
module_param(idProduct, ushort, 0644);
|
||||
MODULE_PARM_DESC(idProduct, "USB Product ID");
|
||||
|
||||
static ushort bcdDevice;
|
||||
module_param(bcdDevice, ushort, 0644);
|
||||
MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
|
||||
|
||||
static char *iManufacturer;
|
||||
module_param(iManufacturer, charp, 0644);
|
||||
MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
|
||||
|
||||
static char *iProduct;
|
||||
module_param(iProduct, charp, 0644);
|
||||
MODULE_PARM_DESC(iProduct, "USB Product string");
|
||||
|
||||
static char *iSerialNumber;
|
||||
module_param(iSerialNumber, charp, 0644);
|
||||
MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
|
||||
|
||||
static char composite_manufacturer[50];
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/**
|
||||
* next_ep_desc() - advance to the next EP descriptor
|
||||
* @t: currect pointer within descriptor array
|
||||
@ -192,6 +154,7 @@ ep_found:
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(config_ep_by_speed);
|
||||
|
||||
/**
|
||||
* usb_add_function() - add a function to a configuration
|
||||
@ -250,6 +213,7 @@ done:
|
||||
function->name, function, value);
|
||||
return value;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_function);
|
||||
|
||||
/**
|
||||
* usb_function_deactivate - prevent function and gadget enumeration
|
||||
@ -286,6 +250,7 @@ int usb_function_deactivate(struct usb_function *function)
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_function_deactivate);
|
||||
|
||||
/**
|
||||
* usb_function_activate - allow function and gadget enumeration
|
||||
@ -300,9 +265,10 @@ int usb_function_deactivate(struct usb_function *function)
|
||||
int usb_function_activate(struct usb_function *function)
|
||||
{
|
||||
struct usb_composite_dev *cdev = function->config->cdev;
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
|
||||
spin_lock(&cdev->lock);
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
|
||||
if (WARN_ON(cdev->deactivations == 0))
|
||||
status = -EINVAL;
|
||||
@ -312,9 +278,10 @@ int usb_function_activate(struct usb_function *function)
|
||||
status = usb_gadget_connect(cdev->gadget);
|
||||
}
|
||||
|
||||
spin_unlock(&cdev->lock);
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_function_activate);
|
||||
|
||||
/**
|
||||
* usb_interface_id() - allocate an unused interface ID
|
||||
@ -351,16 +318,18 @@ int usb_interface_id(struct usb_configuration *config,
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_interface_id);
|
||||
|
||||
static int config_buf(struct usb_configuration *config,
|
||||
enum usb_device_speed speed, void *buf, u8 type)
|
||||
{
|
||||
struct usb_config_descriptor *c = buf;
|
||||
void *next = buf + USB_DT_CONFIG_SIZE;
|
||||
int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
|
||||
int len;
|
||||
struct usb_function *f;
|
||||
int status;
|
||||
|
||||
len = USB_COMP_EP0_BUFSIZ - USB_DT_CONFIG_SIZE;
|
||||
/* write the config descriptor */
|
||||
c = buf;
|
||||
c->bLength = USB_DT_CONFIG_SIZE;
|
||||
@ -790,6 +759,7 @@ done:
|
||||
config->bConfigurationValue, status);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_config);
|
||||
|
||||
static void remove_config(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *config)
|
||||
@ -889,10 +859,10 @@ static int lookup_string(
|
||||
static int get_string(struct usb_composite_dev *cdev,
|
||||
void *buf, u16 language, int id)
|
||||
{
|
||||
struct usb_composite_driver *composite = cdev->driver;
|
||||
struct usb_configuration *c;
|
||||
struct usb_function *f;
|
||||
int len;
|
||||
const char *str;
|
||||
|
||||
/* Yes, not only is USB's I18N support probably more than most
|
||||
* folk will ever care about ... also, it's all supported here.
|
||||
@ -932,26 +902,6 @@ static int get_string(struct usb_composite_dev *cdev,
|
||||
return s->bLength;
|
||||
}
|
||||
|
||||
/* Otherwise, look up and return a specified string. First
|
||||
* check if the string has not been overridden.
|
||||
*/
|
||||
if (cdev->manufacturer_override == id)
|
||||
str = iManufacturer ?: composite->iManufacturer ?:
|
||||
composite_manufacturer;
|
||||
else if (cdev->product_override == id)
|
||||
str = iProduct ?: composite->iProduct;
|
||||
else if (cdev->serial_override == id)
|
||||
str = iSerialNumber ?: composite->iSerialNumber;
|
||||
else
|
||||
str = NULL;
|
||||
if (str) {
|
||||
struct usb_gadget_strings strings = {
|
||||
.language = language,
|
||||
.strings = &(struct usb_string) { 0xff, str }
|
||||
};
|
||||
return usb_gadget_get_string(&strings, 0xff, buf);
|
||||
}
|
||||
|
||||
/* String IDs are device-scoped, so we look up each string
|
||||
* table we're told about. These lookups are infrequent;
|
||||
* simpler-is-better here.
|
||||
@ -1003,6 +953,7 @@ int usb_string_id(struct usb_composite_dev *cdev)
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_string_id);
|
||||
|
||||
/**
|
||||
* usb_string_ids() - allocate unused string IDs in batch
|
||||
@ -1034,6 +985,7 @@ int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_string_ids_tab);
|
||||
|
||||
/**
|
||||
* usb_string_ids_n() - allocate unused string IDs in batch
|
||||
@ -1062,7 +1014,7 @@ int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
|
||||
c->next_string_id += n;
|
||||
return next + 1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_string_ids_n);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -1359,8 +1311,8 @@ static void composite_disconnect(struct usb_gadget *gadget)
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
if (cdev->config)
|
||||
reset_config(cdev);
|
||||
if (composite->disconnect)
|
||||
composite->disconnect(cdev);
|
||||
if (cdev->driver->disconnect)
|
||||
cdev->driver->disconnect(cdev);
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
}
|
||||
|
||||
@ -1396,35 +1348,67 @@ composite_unbind(struct usb_gadget *gadget)
|
||||
struct usb_configuration, list);
|
||||
remove_config(cdev, c);
|
||||
}
|
||||
if (composite->unbind)
|
||||
composite->unbind(cdev);
|
||||
if (cdev->driver->unbind)
|
||||
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);
|
||||
kfree(cdev->def_manufacturer);
|
||||
kfree(cdev);
|
||||
set_gadget_data(gadget, NULL);
|
||||
composite = NULL;
|
||||
}
|
||||
|
||||
static u8 override_id(struct usb_composite_dev *cdev, u8 *desc)
|
||||
static void update_unchanged_dev_desc(struct usb_device_descriptor *new,
|
||||
const struct usb_device_descriptor *old)
|
||||
{
|
||||
if (!*desc) {
|
||||
int ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
WARNING(cdev, "failed to override string ID\n");
|
||||
else
|
||||
*desc = ret;
|
||||
}
|
||||
__le16 idVendor;
|
||||
__le16 idProduct;
|
||||
__le16 bcdDevice;
|
||||
u8 iSerialNumber;
|
||||
u8 iManufacturer;
|
||||
u8 iProduct;
|
||||
|
||||
return *desc;
|
||||
/*
|
||||
* these variables may have been set in
|
||||
* usb_composite_overwrite_options()
|
||||
*/
|
||||
idVendor = new->idVendor;
|
||||
idProduct = new->idProduct;
|
||||
bcdDevice = new->bcdDevice;
|
||||
iSerialNumber = new->iSerialNumber;
|
||||
iManufacturer = new->iManufacturer;
|
||||
iProduct = new->iProduct;
|
||||
|
||||
*new = *old;
|
||||
if (idVendor)
|
||||
new->idVendor = idVendor;
|
||||
if (idProduct)
|
||||
new->idProduct = idProduct;
|
||||
if (bcdDevice)
|
||||
new->bcdDevice = bcdDevice;
|
||||
else
|
||||
new->bcdDevice = cpu_to_le16(get_default_bcdDevice());
|
||||
if (iSerialNumber)
|
||||
new->iSerialNumber = iSerialNumber;
|
||||
if (iManufacturer)
|
||||
new->iManufacturer = iManufacturer;
|
||||
if (iProduct)
|
||||
new->iProduct = iProduct;
|
||||
}
|
||||
|
||||
static int composite_bind(struct usb_gadget *gadget)
|
||||
static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv)
|
||||
{
|
||||
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);
|
||||
@ -1440,13 +1424,12 @@ static int composite_bind(struct usb_gadget *gadget)
|
||||
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
|
||||
if (!cdev->req)
|
||||
goto fail;
|
||||
cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
|
||||
cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
|
||||
if (!cdev->req->buf)
|
||||
goto fail;
|
||||
cdev->req->complete = composite_setup_complete;
|
||||
gadget->ep0->driver_data = cdev;
|
||||
|
||||
cdev->bufsiz = USB_BUFSIZ;
|
||||
cdev->driver = composite;
|
||||
|
||||
/*
|
||||
@ -1467,49 +1450,11 @@ static int composite_bind(struct usb_gadget *gadget)
|
||||
* serial number), register function drivers, potentially update
|
||||
* power state and consumption, etc
|
||||
*/
|
||||
status = composite_gadget_bind(cdev);
|
||||
status = composite->bind(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
cdev->desc = *composite->dev;
|
||||
|
||||
/* standardized runtime overrides for device ID data */
|
||||
if (idVendor)
|
||||
cdev->desc.idVendor = cpu_to_le16(idVendor);
|
||||
else
|
||||
idVendor = le16_to_cpu(cdev->desc.idVendor);
|
||||
if (idProduct)
|
||||
cdev->desc.idProduct = cpu_to_le16(idProduct);
|
||||
else
|
||||
idProduct = le16_to_cpu(cdev->desc.idProduct);
|
||||
if (bcdDevice)
|
||||
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
|
||||
else
|
||||
bcdDevice = le16_to_cpu(cdev->desc.bcdDevice);
|
||||
|
||||
/* string overrides */
|
||||
if (iManufacturer || !cdev->desc.iManufacturer) {
|
||||
if (!iManufacturer && !composite->iManufacturer &&
|
||||
!*composite_manufacturer)
|
||||
snprintf(composite_manufacturer,
|
||||
sizeof composite_manufacturer,
|
||||
"%s %s with %s",
|
||||
init_utsname()->sysname,
|
||||
init_utsname()->release,
|
||||
gadget->name);
|
||||
|
||||
cdev->manufacturer_override =
|
||||
override_id(cdev, &cdev->desc.iManufacturer);
|
||||
}
|
||||
|
||||
if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
|
||||
cdev->product_override =
|
||||
override_id(cdev, &cdev->desc.iProduct);
|
||||
|
||||
if (iSerialNumber ||
|
||||
(!cdev->desc.iSerialNumber && composite->iSerialNumber))
|
||||
cdev->serial_override =
|
||||
override_id(cdev, &cdev->desc.iSerialNumber);
|
||||
update_unchanged_dev_desc(&cdev->desc, composite->dev);
|
||||
|
||||
/* has userspace failed to provide a serial number? */
|
||||
if (composite->needs_serial && !cdev->desc.iSerialNumber)
|
||||
@ -1546,8 +1491,8 @@ composite_suspend(struct usb_gadget *gadget)
|
||||
f->suspend(f);
|
||||
}
|
||||
}
|
||||
if (composite->suspend)
|
||||
composite->suspend(cdev);
|
||||
if (cdev->driver->suspend)
|
||||
cdev->driver->suspend(cdev);
|
||||
|
||||
cdev->suspended = 1;
|
||||
|
||||
@ -1565,8 +1510,8 @@ composite_resume(struct usb_gadget *gadget)
|
||||
* suspend/resume callbacks?
|
||||
*/
|
||||
DBG(cdev, "resume\n");
|
||||
if (composite->resume)
|
||||
composite->resume(cdev);
|
||||
if (cdev->driver->resume)
|
||||
cdev->driver->resume(cdev);
|
||||
if (cdev->config) {
|
||||
list_for_each_entry(f, &cdev->config->functions, list) {
|
||||
if (f->resume)
|
||||
@ -1584,13 +1529,8 @@ composite_resume(struct usb_gadget *gadget)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_gadget_driver composite_driver = {
|
||||
#ifdef CONFIG_USB_GADGET_SUPERSPEED
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
#else
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
#endif
|
||||
|
||||
static const struct usb_gadget_driver composite_driver_template = {
|
||||
.bind = composite_bind,
|
||||
.unbind = composite_unbind,
|
||||
|
||||
.setup = composite_setup,
|
||||
@ -1623,25 +1563,26 @@ static struct usb_gadget_driver composite_driver = {
|
||||
* while it was binding. That would usually be done in order to wait for
|
||||
* some userspace participation.
|
||||
*/
|
||||
int usb_composite_probe(struct usb_composite_driver *driver,
|
||||
int (*bind)(struct usb_composite_dev *cdev))
|
||||
int usb_composite_probe(struct usb_composite_driver *driver)
|
||||
{
|
||||
if (!driver || !driver->dev || !bind || composite)
|
||||
struct usb_gadget_driver *gadget_driver;
|
||||
|
||||
if (!driver || !driver->dev || !driver->bind)
|
||||
return -EINVAL;
|
||||
|
||||
if (!driver->name)
|
||||
driver->name = "composite";
|
||||
if (!driver->iProduct)
|
||||
driver->iProduct = driver->name;
|
||||
composite_driver.function = (char *) driver->name;
|
||||
composite_driver.driver.name = driver->name;
|
||||
composite_driver.max_speed =
|
||||
min_t(u8, composite_driver.max_speed, driver->max_speed);
|
||||
composite = driver;
|
||||
composite_gadget_bind = bind;
|
||||
|
||||
return usb_gadget_probe_driver(&composite_driver, composite_bind);
|
||||
driver->gadget_driver = composite_driver_template;
|
||||
gadget_driver = &driver->gadget_driver;
|
||||
|
||||
gadget_driver->function = (char *) driver->name;
|
||||
gadget_driver->driver.name = driver->name;
|
||||
gadget_driver->max_speed = driver->max_speed;
|
||||
|
||||
return usb_gadget_probe_driver(gadget_driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_composite_probe);
|
||||
|
||||
/**
|
||||
* usb_composite_unregister() - unregister a composite driver
|
||||
@ -1652,10 +1593,9 @@ int usb_composite_probe(struct usb_composite_driver *driver,
|
||||
*/
|
||||
void usb_composite_unregister(struct usb_composite_driver *driver)
|
||||
{
|
||||
if (composite != driver)
|
||||
return;
|
||||
usb_gadget_unregister_driver(&composite_driver);
|
||||
usb_gadget_unregister_driver(&driver->gadget_driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_composite_unregister);
|
||||
|
||||
/**
|
||||
* usb_composite_setup_continue() - Continue with the control transfer
|
||||
@ -1692,4 +1632,60 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
|
||||
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_composite_setup_continue);
|
||||
|
||||
static char *composite_default_mfr(struct usb_gadget *gadget)
|
||||
{
|
||||
char *mfr;
|
||||
int len;
|
||||
|
||||
len = snprintf(NULL, 0, "%s %s with %s", init_utsname()->sysname,
|
||||
init_utsname()->release, gadget->name);
|
||||
len++;
|
||||
mfr = kmalloc(len, GFP_KERNEL);
|
||||
if (!mfr)
|
||||
return NULL;
|
||||
snprintf(mfr, len, "%s %s with %s", init_utsname()->sysname,
|
||||
init_utsname()->release, gadget->name);
|
||||
return mfr;
|
||||
}
|
||||
|
||||
void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
|
||||
struct usb_composite_overwrite *covr)
|
||||
{
|
||||
struct usb_device_descriptor *desc = &cdev->desc;
|
||||
struct usb_gadget_strings *gstr = cdev->driver->strings[0];
|
||||
struct usb_string *dev_str = gstr->strings;
|
||||
|
||||
if (covr->idVendor)
|
||||
desc->idVendor = cpu_to_le16(covr->idVendor);
|
||||
|
||||
if (covr->idProduct)
|
||||
desc->idProduct = cpu_to_le16(covr->idProduct);
|
||||
|
||||
if (covr->bcdDevice)
|
||||
desc->bcdDevice = cpu_to_le16(covr->bcdDevice);
|
||||
|
||||
if (covr->serial_number) {
|
||||
desc->iSerialNumber = dev_str[USB_GADGET_SERIAL_IDX].id;
|
||||
dev_str[USB_GADGET_SERIAL_IDX].s = covr->serial_number;
|
||||
}
|
||||
if (covr->manufacturer) {
|
||||
desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer;
|
||||
|
||||
} else if (!strlen(dev_str[USB_GADGET_MANUFACTURER_IDX].s)) {
|
||||
desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
cdev->def_manufacturer = composite_default_mfr(cdev->gadget);
|
||||
dev_str[USB_GADGET_MANUFACTURER_IDX].s = cdev->def_manufacturer;
|
||||
}
|
||||
|
||||
if (covr->product) {
|
||||
desc->iProduct = dev_str[USB_GADGET_PRODUCT_IDX].id;
|
||||
dev_str[USB_GADGET_PRODUCT_IDX].s = covr->product;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_composite_overwrite_options);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/device.h>
|
||||
@ -53,7 +54,7 @@ usb_descriptor_fillbuf(void *buf, unsigned buflen,
|
||||
}
|
||||
return dest - (u8 *)buf;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf);
|
||||
|
||||
/**
|
||||
* usb_gadget_config_buf - builts a complete configuration descriptor
|
||||
@ -106,6 +107,7 @@ int usb_gadget_config_buf(
|
||||
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_config_buf);
|
||||
|
||||
/**
|
||||
* usb_copy_descriptors - copy a vector of USB descriptors
|
||||
@ -155,4 +157,4 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_copy_descriptors);
|
||||
|
@ -13,9 +13,6 @@
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* See comments in "zero.c" */
|
||||
#include "epautoconf.c"
|
||||
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
#include "u_serial.c"
|
||||
#endif
|
||||
@ -292,7 +289,8 @@ fail_1:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int __init dbgp_bind(struct usb_gadget *gadget)
|
||||
static int __init dbgp_bind(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
int err, stp;
|
||||
|
||||
@ -402,9 +400,10 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct usb_gadget_driver dbgp_driver = {
|
||||
static __refdata struct usb_gadget_driver dbgp_driver = {
|
||||
.function = "dbgp",
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.bind = dbgp_bind,
|
||||
.unbind = dbgp_unbind,
|
||||
.setup = dbgp_setup,
|
||||
.disconnect = dbgp_disconnect,
|
||||
@ -416,7 +415,7 @@ static struct usb_gadget_driver dbgp_driver = {
|
||||
|
||||
static int __init dbgp_init(void)
|
||||
{
|
||||
return usb_gadget_probe_driver(&dbgp_driver, dbgp_bind);
|
||||
return usb_gadget_probe_driver(&dbgp_driver);
|
||||
}
|
||||
|
||||
static void __exit dbgp_exit(void)
|
||||
|
@ -909,6 +909,7 @@ static int dummy_udc_start(struct usb_gadget *g,
|
||||
dum->devstatus = 0;
|
||||
|
||||
dum->driver = driver;
|
||||
dum->gadget.dev.driver = &driver->driver;
|
||||
dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
@ -923,6 +924,7 @@ static int dummy_udc_stop(struct usb_gadget *g,
|
||||
dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
dum->gadget.dev.driver = NULL;
|
||||
dum->driver = NULL;
|
||||
|
||||
return 0;
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
@ -22,17 +23,6 @@
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/* we must assign addresses for configurable endpoints (like net2280) */
|
||||
static unsigned epnum;
|
||||
|
||||
// #define MANY_ENDPOINTS
|
||||
#ifdef MANY_ENDPOINTS
|
||||
/* more than 15 configurable endpoints */
|
||||
static unsigned in_epnum;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* This should work with endpoints from controller drivers sharing the
|
||||
* same endpoint naming convention. By example:
|
||||
@ -176,16 +166,14 @@ ep_matches (
|
||||
if (isdigit (ep->name [2])) {
|
||||
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
|
||||
desc->bEndpointAddress |= num;
|
||||
#ifdef MANY_ENDPOINTS
|
||||
} else if (desc->bEndpointAddress & USB_DIR_IN) {
|
||||
if (++in_epnum > 15)
|
||||
if (++gadget->in_epnum > 15)
|
||||
return 0;
|
||||
desc->bEndpointAddress = USB_DIR_IN | in_epnum;
|
||||
#endif
|
||||
desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;
|
||||
} else {
|
||||
if (++epnum > 15)
|
||||
if (++gadget->out_epnum > 15)
|
||||
return 0;
|
||||
desc->bEndpointAddress |= epnum;
|
||||
desc->bEndpointAddress |= gadget->out_epnum;
|
||||
}
|
||||
|
||||
/* report (variable) full speed bulk maxpacket */
|
||||
@ -328,6 +316,7 @@ found_ep:
|
||||
ep->comp_desc = NULL;
|
||||
return ep;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig() - choose an endpoint matching the
|
||||
@ -367,7 +356,7 @@ struct usb_ep *usb_ep_autoconfig(
|
||||
{
|
||||
return usb_ep_autoconfig_ss(gadget, desc, NULL);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
|
||||
@ -385,9 +374,7 @@ void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
#ifdef MANY_ENDPOINTS
|
||||
in_epnum = 0;
|
||||
#endif
|
||||
epnum = 0;
|
||||
gadget->in_epnum = 0;
|
||||
gadget->out_epnum = 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user