提交 f7a0d426 编写于 作者: G Greg Kroah-Hartman

Merge 3.3-rc7 into usb-next

This resolves the conflict with drivers/usb/host/ehci-fsl.h that
happened with changes in Linus's and this branch at the same time.
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
...@@ -182,3 +182,14 @@ Description: ...@@ -182,3 +182,14 @@ Description:
USB2 hardware LPM is enabled for the device. Developer can USB2 hardware LPM is enabled for the device. Developer can
write y/Y/1 or n/N/0 to the file to enable/disable the write y/Y/1 or n/N/0 to the file to enable/disable the
feature. feature.
What: /sys/bus/usb/devices/.../removable
Date: February 2012
Contact: Matthew Garrett <mjg@redhat.com>
Description:
Some information about whether a given USB device is
physically fixed to the platform can be inferred from a
combination of hub decriptor bits and platform-specific data
such as ACPI. This file will read either "removable" or
"fixed" if the information is available, and "unknown"
otherwise.
\ No newline at end of file
...@@ -158,7 +158,7 @@ static int devboard_usbh1_hw_init(struct platform_device *pdev) ...@@ -158,7 +158,7 @@ static int devboard_usbh1_hw_init(struct platform_device *pdev)
#define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B) #define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B)
#define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE) #define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE)
static int devboard_isp1105_init(struct otg_transceiver *otg) static int devboard_isp1105_init(struct usb_phy *otg)
{ {
int ret = gpio_request(USBH1_MODE, "usbh1-mode"); int ret = gpio_request(USBH1_MODE, "usbh1-mode");
if (ret) if (ret)
...@@ -177,7 +177,7 @@ static int devboard_isp1105_init(struct otg_transceiver *otg) ...@@ -177,7 +177,7 @@ static int devboard_isp1105_init(struct otg_transceiver *otg)
} }
static int devboard_isp1105_set_vbus(struct otg_transceiver *otg, bool on) static int devboard_isp1105_set_vbus(struct usb_otg *otg, bool on)
{ {
if (on) if (on)
gpio_set_value(USBH1_VBUSEN_B, 0); gpio_set_value(USBH1_VBUSEN_B, 0);
...@@ -194,18 +194,24 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = { ...@@ -194,18 +194,24 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = {
static int __init devboard_usbh1_init(void) static int __init devboard_usbh1_init(void)
{ {
struct otg_transceiver *otg; struct usb_phy *phy;
struct platform_device *pdev; struct platform_device *pdev;
otg = kzalloc(sizeof(*otg), GFP_KERNEL); phy = kzalloc(sizeof(*phy), GFP_KERNEL);
if (!otg) if (!phy)
return -ENOMEM; return -ENOMEM;
otg->label = "ISP1105"; phy->otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
otg->init = devboard_isp1105_init; if (!phy->otg) {
otg->set_vbus = devboard_isp1105_set_vbus; kfree(phy);
return -ENOMEM;
}
phy->label = "ISP1105";
phy->init = devboard_isp1105_init;
phy->otg->set_vbus = devboard_isp1105_set_vbus;
usbh1_pdata.otg = otg; usbh1_pdata.otg = phy;
pdev = imx31_add_mxc_ehci_hs(1, &usbh1_pdata); pdev = imx31_add_mxc_ehci_hs(1, &usbh1_pdata);
if (IS_ERR(pdev)) if (IS_ERR(pdev))
......
...@@ -272,7 +272,7 @@ static int marxbot_usbh1_hw_init(struct platform_device *pdev) ...@@ -272,7 +272,7 @@ static int marxbot_usbh1_hw_init(struct platform_device *pdev)
#define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B) #define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B)
#define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE) #define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE)
static int marxbot_isp1105_init(struct otg_transceiver *otg) static int marxbot_isp1105_init(struct usb_phy *otg)
{ {
int ret = gpio_request(USBH1_MODE, "usbh1-mode"); int ret = gpio_request(USBH1_MODE, "usbh1-mode");
if (ret) if (ret)
...@@ -291,7 +291,7 @@ static int marxbot_isp1105_init(struct otg_transceiver *otg) ...@@ -291,7 +291,7 @@ static int marxbot_isp1105_init(struct otg_transceiver *otg)
} }
static int marxbot_isp1105_set_vbus(struct otg_transceiver *otg, bool on) static int marxbot_isp1105_set_vbus(struct usb_otg *otg, bool on)
{ {
if (on) if (on)
gpio_set_value(USBH1_VBUSEN_B, 0); gpio_set_value(USBH1_VBUSEN_B, 0);
...@@ -308,18 +308,24 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = { ...@@ -308,18 +308,24 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = {
static int __init marxbot_usbh1_init(void) static int __init marxbot_usbh1_init(void)
{ {
struct otg_transceiver *otg; struct usb_phy *phy;
struct platform_device *pdev; struct platform_device *pdev;
otg = kzalloc(sizeof(*otg), GFP_KERNEL); phy = kzalloc(sizeof(*phy), GFP_KERNEL);
if (!otg) if (!phy)
return -ENOMEM; return -ENOMEM;
otg->label = "ISP1105"; phy->otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
otg->init = marxbot_isp1105_init; if (!phy->otg) {
otg->set_vbus = marxbot_isp1105_set_vbus; kfree(phy);
return -ENOMEM;
}
phy->label = "ISP1105";
phy->init = marxbot_isp1105_init;
phy->otg->set_vbus = marxbot_isp1105_set_vbus;
usbh1_pdata.otg = otg; usbh1_pdata.otg = phy;
pdev = imx31_add_mxc_ehci_hs(1, &usbh1_pdata); pdev = imx31_add_mxc_ehci_hs(1, &usbh1_pdata);
if (IS_ERR(pdev)) if (IS_ERR(pdev))
......
...@@ -33,7 +33,7 @@ struct pxa3xx_u2d_ulpi { ...@@ -33,7 +33,7 @@ struct pxa3xx_u2d_ulpi {
struct clk *clk; struct clk *clk;
void __iomem *mmio_base; void __iomem *mmio_base;
struct otg_transceiver *otg; struct usb_phy *otg;
unsigned int ulpi_mode; unsigned int ulpi_mode;
}; };
...@@ -79,7 +79,7 @@ static int pxa310_ulpi_poll(void) ...@@ -79,7 +79,7 @@ static int pxa310_ulpi_poll(void)
return -ETIMEDOUT; return -ETIMEDOUT;
} }
static int pxa310_ulpi_read(struct otg_transceiver *otg, u32 reg) static int pxa310_ulpi_read(struct usb_phy *otg, u32 reg)
{ {
int err; int err;
...@@ -98,7 +98,7 @@ static int pxa310_ulpi_read(struct otg_transceiver *otg, u32 reg) ...@@ -98,7 +98,7 @@ static int pxa310_ulpi_read(struct otg_transceiver *otg, u32 reg)
return u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA; return u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA;
} }
static int pxa310_ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) static int pxa310_ulpi_write(struct usb_phy *otg, u32 val, u32 reg)
{ {
if (pxa310_ulpi_get_phymode() != SYNCH) { if (pxa310_ulpi_get_phymode() != SYNCH) {
pr_warning("%s: PHY is not in SYNCH mode!\n", __func__); pr_warning("%s: PHY is not in SYNCH mode!\n", __func__);
...@@ -111,7 +111,7 @@ static int pxa310_ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) ...@@ -111,7 +111,7 @@ static int pxa310_ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
return pxa310_ulpi_poll(); return pxa310_ulpi_poll();
} }
struct otg_io_access_ops pxa310_ulpi_access_ops = { struct usb_phy_io_ops pxa310_ulpi_access_ops = {
.read = pxa310_ulpi_read, .read = pxa310_ulpi_read,
.write = pxa310_ulpi_write, .write = pxa310_ulpi_write,
}; };
...@@ -139,19 +139,19 @@ static int pxa310_start_otg_host_transcvr(struct usb_bus *host) ...@@ -139,19 +139,19 @@ static int pxa310_start_otg_host_transcvr(struct usb_bus *host)
pxa310_otg_transceiver_rtsm(); pxa310_otg_transceiver_rtsm();
err = otg_init(u2d->otg); err = usb_phy_init(u2d->otg);
if (err) { if (err) {
pr_err("OTG transceiver init failed"); pr_err("OTG transceiver init failed");
return err; return err;
} }
err = otg_set_vbus(u2d->otg, 1); err = otg_set_vbus(u2d->otg->otg, 1);
if (err) { if (err) {
pr_err("OTG transceiver VBUS set failed"); pr_err("OTG transceiver VBUS set failed");
return err; return err;
} }
err = otg_set_host(u2d->otg, host); err = otg_set_host(u2d->otg->otg, host);
if (err) if (err)
pr_err("OTG transceiver Host mode set failed"); pr_err("OTG transceiver Host mode set failed");
...@@ -189,9 +189,9 @@ static void pxa310_stop_otg_hc(void) ...@@ -189,9 +189,9 @@ static void pxa310_stop_otg_hc(void)
{ {
pxa310_otg_transceiver_rtsm(); pxa310_otg_transceiver_rtsm();
otg_set_host(u2d->otg, NULL); otg_set_host(u2d->otg->otg, NULL);
otg_set_vbus(u2d->otg, 0); otg_set_vbus(u2d->otg->otg, 0);
otg_shutdown(u2d->otg); usb_phy_shutdown(u2d->otg);
} }
static void pxa310_u2d_setup_otg_hc(void) static void pxa310_u2d_setup_otg_hc(void)
......
...@@ -58,7 +58,7 @@ struct tegra_usb_phy { ...@@ -58,7 +58,7 @@ struct tegra_usb_phy {
struct clk *pad_clk; struct clk *pad_clk;
enum tegra_usb_phy_mode mode; enum tegra_usb_phy_mode mode;
void *config; void *config;
struct otg_transceiver *ulpi; struct usb_phy *ulpi;
}; };
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
......
...@@ -608,13 +608,13 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) ...@@ -608,13 +608,13 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
writel(val, base + ULPI_TIMING_CTRL_1); writel(val, base + ULPI_TIMING_CTRL_1);
/* Fix VbusInvalid due to floating VBUS */ /* Fix VbusInvalid due to floating VBUS */
ret = otg_io_write(phy->ulpi, 0x40, 0x08); ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
if (ret) { if (ret) {
pr_err("%s: ulpi write failed\n", __func__); pr_err("%s: ulpi write failed\n", __func__);
return ret; return ret;
} }
ret = otg_io_write(phy->ulpi, 0x80, 0x0B); ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
if (ret) { if (ret) {
pr_err("%s: ulpi write failed\n", __func__); pr_err("%s: ulpi write failed\n", __func__);
return ret; return ret;
......
...@@ -44,7 +44,7 @@ struct mxc_usbh_platform_data { ...@@ -44,7 +44,7 @@ struct mxc_usbh_platform_data {
int (*exit)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev);
unsigned int portsc; unsigned int portsc;
struct otg_transceiver *otg; struct usb_phy *otg;
}; };
int mx51_initialize_usb_hw(int port, unsigned int flags); int mx51_initialize_usb_hw(int port, unsigned int flags);
......
...@@ -2,15 +2,15 @@ ...@@ -2,15 +2,15 @@
#define __MACH_ULPI_H #define __MACH_ULPI_H
#ifdef CONFIG_USB_ULPI #ifdef CONFIG_USB_ULPI
struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags); struct usb_phy *imx_otg_ulpi_create(unsigned int flags);
#else #else
static inline struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags) static inline struct usb_phy *imx_otg_ulpi_create(unsigned int flags)
{ {
return NULL; return NULL;
} }
#endif #endif
extern struct otg_io_access_ops mxc_ulpi_access_ops; extern struct usb_phy_io_ops mxc_ulpi_access_ops;
#endif /* __MACH_ULPI_H */ #endif /* __MACH_ULPI_H */
...@@ -58,7 +58,7 @@ static int ulpi_poll(void __iomem *view, u32 bit) ...@@ -58,7 +58,7 @@ static int ulpi_poll(void __iomem *view, u32 bit)
return -ETIMEDOUT; return -ETIMEDOUT;
} }
static int ulpi_read(struct otg_transceiver *otg, u32 reg) static int ulpi_read(struct usb_phy *otg, u32 reg)
{ {
int ret; int ret;
void __iomem *view = otg->io_priv; void __iomem *view = otg->io_priv;
...@@ -84,7 +84,7 @@ static int ulpi_read(struct otg_transceiver *otg, u32 reg) ...@@ -84,7 +84,7 @@ static int ulpi_read(struct otg_transceiver *otg, u32 reg)
return (__raw_readl(view) >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK; return (__raw_readl(view) >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK;
} }
static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) static int ulpi_write(struct usb_phy *otg, u32 val, u32 reg)
{ {
int ret; int ret;
void __iomem *view = otg->io_priv; void __iomem *view = otg->io_priv;
...@@ -106,13 +106,13 @@ static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) ...@@ -106,13 +106,13 @@ static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
return ulpi_poll(view, ULPIVW_RUN); return ulpi_poll(view, ULPIVW_RUN);
} }
struct otg_io_access_ops mxc_ulpi_access_ops = { struct usb_phy_io_ops mxc_ulpi_access_ops = {
.read = ulpi_read, .read = ulpi_read,
.write = ulpi_write, .write = ulpi_write,
}; };
EXPORT_SYMBOL_GPL(mxc_ulpi_access_ops); EXPORT_SYMBOL_GPL(mxc_ulpi_access_ops);
struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags) struct usb_phy *imx_otg_ulpi_create(unsigned int flags)
{ {
return otg_ulpi_create(&mxc_ulpi_access_ops, flags); return otg_ulpi_create(&mxc_ulpi_access_ops, flags);
} }
...@@ -117,43 +117,6 @@ ...@@ -117,43 +117,6 @@
#define UB_SENSE_SIZE 18 #define UB_SENSE_SIZE 18
/*
*/
/* command block wrapper */
struct bulk_cb_wrap {
__le32 Signature; /* contains 'USBC' */
u32 Tag; /* unique per command id */
__le32 DataTransferLength; /* size of data */
u8 Flags; /* direction in bit 0 */
u8 Lun; /* LUN */
u8 Length; /* of of the CDB */
u8 CDB[UB_MAX_CDB_SIZE]; /* max command */
};
#define US_BULK_CB_WRAP_LEN 31
#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */
#define US_BULK_FLAG_IN 1
#define US_BULK_FLAG_OUT 0
/* command status wrapper */
struct bulk_cs_wrap {
__le32 Signature; /* should = 'USBS' */
u32 Tag; /* same as original command */
__le32 Residue; /* amount not transferred */
u8 Status; /* see below */
};
#define US_BULK_CS_WRAP_LEN 13
#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
#define US_BULK_STAT_OK 0
#define US_BULK_STAT_FAIL 1
#define US_BULK_STAT_PHASE 2
/* bulk-only class specific requests */
#define US_BULK_RESET_REQUEST 0xff
#define US_BULK_GET_MAX_LUN 0xfe
/* /*
*/ */
struct ub_dev; struct ub_dev;
......
...@@ -398,6 +398,27 @@ config USB_NET_KALMIA ...@@ -398,6 +398,27 @@ config USB_NET_KALMIA
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called kalmia. module will be called kalmia.
config USB_NET_QMI_WWAN
tristate "QMI WWAN driver for Qualcomm MSM based 3G and LTE modems"
depends on USB_USBNET
help
Support WWAN LTE/3G devices based on Qualcomm Mobile Data Modem
(MDM) chipsets. Examples of such devices are
* Huawei E392/E398
This driver will only drive the ethernet part of the chips.
The devices require additional configuration to be usable.
Multiple management interfaces with linux drivers are
available:
* option: AT commands on /dev/ttyUSBx
* cdc-wdm: Qualcomm MSM Interface (QMI) protocol on /dev/cdc-wdmx
A modem manager with support for QMI is recommended.
To compile this driver as a module, choose M here: the
module will be called qmi_wwan.
config USB_HSO config USB_HSO
tristate "Option USB High Speed Mobile Devices" tristate "Option USB High Speed Mobile Devices"
depends on USB && RFKILL depends on USB && RFKILL
...@@ -461,4 +482,5 @@ config USB_VL600 ...@@ -461,4 +482,5 @@ config USB_VL600
http://ubuntuforums.org/showpost.php?p=10589647&postcount=17 http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
endmenu endmenu
...@@ -29,4 +29,5 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o ...@@ -29,4 +29,5 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
obj-$(CONFIG_USB_VL600) += lg-vl600.o obj-$(CONFIG_USB_VL600) += lg-vl600.o
obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
/*
* Copyright (c) 2012 Bjørn Mork <bjorn@mork.no>
*
* 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.
*/
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/usb/usbnet.h>
#include <linux/usb/cdc-wdm.h>
/* The name of the CDC Device Management driver */
#define DM_DRIVER "cdc_wdm"
/*
* This driver supports wwan (3G/LTE/?) devices using a vendor
* specific management protocol called Qualcomm MSM Interface (QMI) -
* in addition to the more common AT commands over serial interface
* management
*
* QMI is wrapped in CDC, using CDC encapsulated commands on the
* control ("master") interface of a two-interface CDC Union
* resembling standard CDC ECM. The devices do not use the control
* interface for any other CDC messages. Most likely because the
* management protocol is used in place of the standard CDC
* notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE
*
* Handling a protocol like QMI is out of the scope for any driver.
* It can be exported as a character device using the cdc-wdm driver,
* which will enable userspace applications ("modem managers") to
* handle it. This may be required to use the network interface
* provided by the driver.
*
* These devices may alternatively/additionally be configured using AT
* commands on any of the serial interfaces driven by the option driver
*
* This driver binds only to the data ("slave") interface to enable
* the cdc-wdm driver to bind to the control interface. It still
* parses the CDC functional descriptors on the control interface to
* a) verify that this is indeed a handled interface (CDC Union
* header lists it as slave)
* b) get MAC address and other ethernet config from the CDC Ethernet
* header
* c) enable user bind requests against the control interface, which
* is the common way to bind to CDC Ethernet Control Model type
* interfaces
* d) provide a hint to the user about which interface is the
* corresponding management interface
*/
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status = -1;
struct usb_interface *control = NULL;
u8 *buf = intf->cur_altsetting->extra;
int len = intf->cur_altsetting->extralen;
struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
struct usb_cdc_union_desc *cdc_union = NULL;
struct usb_cdc_ether_desc *cdc_ether = NULL;
u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE;
u32 found = 0;
atomic_t *pmcount = (void *)&dev->data[1];
atomic_set(pmcount, 0);
/*
* assume a data interface has no additional descriptors and
* that the control and data interface are numbered
* consecutively - this holds for the Huawei device at least
*/
if (len == 0 && desc->bInterfaceNumber > 0) {
control = usb_ifnum_to_if(dev->udev, desc->bInterfaceNumber - 1);
if (!control)
goto err;
buf = control->cur_altsetting->extra;
len = control->cur_altsetting->extralen;
dev_dbg(&intf->dev, "guessing \"control\" => %s, \"data\" => this\n",
dev_name(&control->dev));
}
while (len > 3) {
struct usb_descriptor_header *h = (void *)buf;
/* ignore any misplaced descriptors */
if (h->bDescriptorType != USB_DT_CS_INTERFACE)
goto next_desc;
/* buf[2] is CDC descriptor subtype */
switch (buf[2]) {
case USB_CDC_HEADER_TYPE:
if (found & 1 << USB_CDC_HEADER_TYPE) {
dev_dbg(&intf->dev, "extra CDC header\n");
goto err;
}
if (h->bLength != sizeof(struct usb_cdc_header_desc)) {
dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength);
goto err;
}
break;
case USB_CDC_UNION_TYPE:
if (found & 1 << USB_CDC_UNION_TYPE) {
dev_dbg(&intf->dev, "extra CDC union\n");
goto err;
}
if (h->bLength != sizeof(struct usb_cdc_union_desc)) {
dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength);
goto err;
}
cdc_union = (struct usb_cdc_union_desc *)buf;
break;
case USB_CDC_ETHERNET_TYPE:
if (found & 1 << USB_CDC_ETHERNET_TYPE) {
dev_dbg(&intf->dev, "extra CDC ether\n");
goto err;
}
if (h->bLength != sizeof(struct usb_cdc_ether_desc)) {
dev_dbg(&intf->dev, "CDC ether len %u\n", h->bLength);
goto err;
}
cdc_ether = (struct usb_cdc_ether_desc *)buf;
break;
}
/*
* Remember which CDC functional descriptors we've seen. Works
* for all types we care about, of which USB_CDC_ETHERNET_TYPE
* (0x0f) is the highest numbered
*/
if (buf[2] < 32)
found |= 1 << buf[2];
next_desc:
len -= h->bLength;
buf += h->bLength;
}
/* did we find all the required ones? */
if ((found & required) != required) {
dev_err(&intf->dev, "CDC functional descriptors missing\n");
goto err;
}
/* give the user a helpful hint if trying to bind to the wrong interface */
if (cdc_union && desc->bInterfaceNumber == cdc_union->bMasterInterface0) {
dev_err(&intf->dev, "leaving \"control\" interface for " DM_DRIVER " - try binding to %s instead!\n",
dev_name(&usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0)->dev));
goto err;
}
/* errors aren't fatal - we can live with the dynamic address */
if (cdc_ether) {
dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize);
usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress);
}
/* success! point the user to the management interface */
if (control)
dev_info(&intf->dev, "Use \"" DM_DRIVER "\" for QMI interface %s\n",
dev_name(&control->dev));
/* XXX: add a sysfs symlink somewhere to help management applications find it? */
/* collect bulk endpoints now that we know intf == "data" interface */
status = usbnet_get_endpoints(dev, intf);
err:
return status;
}
/* using a counter to merge subdriver requests with our own into a combined state */
static int qmi_wwan_manage_power(struct usbnet *dev, int on)
{
atomic_t *pmcount = (void *)&dev->data[1];
int rv = 0;
dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(pmcount), on);
if ((on && atomic_add_return(1, pmcount) == 1) || (!on && atomic_dec_and_test(pmcount))) {
/* need autopm_get/put here to ensure the usbcore sees the new value */
rv = usb_autopm_get_interface(dev->intf);
if (rv < 0)
goto err;
dev->intf->needs_remote_wakeup = on;
usb_autopm_put_interface(dev->intf);
}
err:
return rv;
}
static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on)
{
struct usbnet *dev = usb_get_intfdata(intf);
return qmi_wwan_manage_power(dev, on);
}
/* Some devices combine the "control" and "data" functions into a
* single interface with all three endpoints: interrupt + bulk in and
* out
*
* Setting up cdc-wdm as a subdriver owning the interrupt endpoint
* will let it provide userspace access to the encapsulated QMI
* protocol without interfering with the usbnet operations.
*/
static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
{
int rv;
struct usb_driver *subdriver = NULL;
atomic_t *pmcount = (void *)&dev->data[1];
atomic_set(pmcount, 0);
/* collect all three endpoints */
rv = usbnet_get_endpoints(dev, intf);
if (rv < 0)
goto err;
/* require interrupt endpoint for subdriver */
if (!dev->status) {
rv = -EINVAL;
goto err;
}
subdriver = usb_cdc_wdm_register(intf, &dev->status->desc, 512, &qmi_wwan_cdc_wdm_manage_power);
if (IS_ERR(subdriver)) {
rv = PTR_ERR(subdriver);
goto err;
}
/* can't let usbnet use the interrupt endpoint */
dev->status = NULL;
/* save subdriver struct for suspend/resume wrappers */
dev->data[0] = (unsigned long)subdriver;
err:
return rv;
}
/* Gobi devices uses identical class/protocol codes for all interfaces regardless
* of function. Some of these are CDC ACM like and have the exact same endpoints
* we are looking for. This leaves two possible strategies for identifying the
* correct interface:
* a) hardcoding interface number, or
* b) use the fact that the wwan interface is the only one lacking additional
* (CDC functional) descriptors
*
* Let's see if we can get away with the generic b) solution.
*/
static int qmi_wwan_bind_gobi(struct usbnet *dev, struct usb_interface *intf)
{
int rv = -EINVAL;
/* ignore any interface with additional descriptors */
if (intf->cur_altsetting->extralen)
goto err;
rv = qmi_wwan_bind_shared(dev, intf);
err:
return rv;
}
static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *intf)
{
struct usb_driver *subdriver = (void *)dev->data[0];
if (subdriver && subdriver->disconnect)
subdriver->disconnect(intf);
dev->data[0] = (unsigned long)NULL;
}
/* suspend/resume wrappers calling both usbnet and the cdc-wdm
* subdriver if present.
*
* NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide
* wrappers for those without adding usbnet reset support first.
*/
static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
struct usb_driver *subdriver = (void *)dev->data[0];
int ret;
ret = usbnet_suspend(intf, message);
if (ret < 0)
goto err;
if (subdriver && subdriver->suspend)
ret = subdriver->suspend(intf, message);
if (ret < 0)
usbnet_resume(intf);
err:
return ret;
}
static int qmi_wwan_resume(struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
struct usb_driver *subdriver = (void *)dev->data[0];
int ret = 0;
if (subdriver && subdriver->resume)
ret = subdriver->resume(intf);
if (ret < 0)
goto err;
ret = usbnet_resume(intf);
if (ret < 0 && subdriver && subdriver->resume && subdriver->suspend)
subdriver->suspend(intf, PMSG_SUSPEND);
err:
return ret;
}
static const struct driver_info qmi_wwan_info = {
.description = "QMI speaking wwan device",
.flags = FLAG_WWAN,
.bind = qmi_wwan_bind,
.manage_power = qmi_wwan_manage_power,
};
static const struct driver_info qmi_wwan_shared = {
.description = "QMI speaking wwan device with combined interface",
.flags = FLAG_WWAN,
.bind = qmi_wwan_bind_shared,
.unbind = qmi_wwan_unbind_shared,
.manage_power = qmi_wwan_manage_power,
};
static const struct driver_info qmi_wwan_gobi = {
.description = "Qualcomm Gobi wwan/QMI device",
.flags = FLAG_WWAN,
.bind = qmi_wwan_bind_gobi,
.unbind = qmi_wwan_unbind_shared,
.manage_power = qmi_wwan_manage_power,
};
#define HUAWEI_VENDOR_ID 0x12D1
#define QMI_GOBI_DEVICE(vend, prod) \
USB_DEVICE(vend, prod), \
.driver_info = (unsigned long)&qmi_wwan_gobi
static const struct usb_device_id products[] = {
{ /* Huawei E392, E398 and possibly others sharing both device id and more... */
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = HUAWEI_VENDOR_ID,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 8, /* NOTE: This is the *slave* interface of the CDC Union! */
.driver_info = (unsigned long)&qmi_wwan_info,
},
{ /* Huawei E392, E398 and possibly others in "Windows mode"
* using a combined control and data interface without any CDC
* functional descriptors
*/
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = HUAWEI_VENDOR_ID,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 17,
.driver_info = (unsigned long)&qmi_wwan_shared,
},
{ /* Pantech UML290 */
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x106c,
.idProduct = 0x3718,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xf0,
.bInterfaceProtocol = 0xff,
.driver_info = (unsigned long)&qmi_wwan_shared,
},
{QMI_GOBI_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
{QMI_GOBI_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
{QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
{QMI_GOBI_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */
{QMI_GOBI_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */
{QMI_GOBI_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */
{QMI_GOBI_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */
{QMI_GOBI_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9001)}, /* Generic Gobi Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9002)}, /* Generic Gobi Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9202)}, /* Generic Gobi Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9203)}, /* Generic Gobi Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
{QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
{QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
{QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
{QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
{QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
{QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
{QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
{QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
{QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
{QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
{QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
{QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
{ } /* END */
};
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver qmi_wwan_driver = {
.name = "qmi_wwan",
.id_table = products,
.probe = usbnet_probe,
.disconnect = usbnet_disconnect,
.suspend = qmi_wwan_suspend,
.resume = qmi_wwan_resume,
.reset_resume = qmi_wwan_resume,
.supports_autosuspend = 1,
};
static int __init qmi_wwan_init(void)
{
return usb_register(&qmi_wwan_driver);
}
module_init(qmi_wwan_init);
static void __exit qmi_wwan_exit(void)
{
usb_deregister(&qmi_wwan_driver);
}
module_exit(qmi_wwan_exit);
MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>");
MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver");
MODULE_LICENSE("GPL");
...@@ -56,7 +56,7 @@ static u16 isp170x_id[] = { ...@@ -56,7 +56,7 @@ static u16 isp170x_id[] = {
struct isp1704_charger { struct isp1704_charger {
struct device *dev; struct device *dev;
struct power_supply psy; struct power_supply psy;
struct otg_transceiver *otg; struct usb_phy *phy;
struct notifier_block nb; struct notifier_block nb;
struct work_struct work; struct work_struct work;
...@@ -71,6 +71,16 @@ struct isp1704_charger { ...@@ -71,6 +71,16 @@ struct isp1704_charger {
unsigned max_power; unsigned max_power;
}; };
static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
{
return usb_phy_io_read(isp->phy, reg);
}
static inline int isp1704_write(struct isp1704_charger *isp, u32 val, u32 reg)
{
return usb_phy_io_write(isp->phy, val, reg);
}
/* /*
* Disable/enable the power from the isp1704 if a function for it * Disable/enable the power from the isp1704 if a function for it
* has been provided with platform data. * has been provided with platform data.
...@@ -97,31 +107,31 @@ static inline int isp1704_charger_type(struct isp1704_charger *isp) ...@@ -97,31 +107,31 @@ static inline int isp1704_charger_type(struct isp1704_charger *isp)
u8 otg_ctrl; u8 otg_ctrl;
int type = POWER_SUPPLY_TYPE_USB_DCP; int type = POWER_SUPPLY_TYPE_USB_DCP;
func_ctrl = otg_io_read(isp->otg, ULPI_FUNC_CTRL); func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL);
otg_ctrl = otg_io_read(isp->otg, ULPI_OTG_CTRL); otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL);
/* disable pulldowns */ /* disable pulldowns */
reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN; reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN;
otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), reg); isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg);
/* full speed */ /* full speed */
otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL), isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
ULPI_FUNC_CTRL_XCVRSEL_MASK); ULPI_FUNC_CTRL_XCVRSEL_MASK);
otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL),
ULPI_FUNC_CTRL_FULL_SPEED); ULPI_FUNC_CTRL_FULL_SPEED);
/* Enable strong pull-up on DP (1.5K) and reset */ /* Enable strong pull-up on DP (1.5K) and reset */
reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET; reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), reg); isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg);
usleep_range(1000, 2000); usleep_range(1000, 2000);
reg = otg_io_read(isp->otg, ULPI_DEBUG); reg = isp1704_read(isp, ULPI_DEBUG);
if ((reg & 3) != 3) if ((reg & 3) != 3)
type = POWER_SUPPLY_TYPE_USB_CDP; type = POWER_SUPPLY_TYPE_USB_CDP;
/* recover original state */ /* recover original state */
otg_io_write(isp->otg, ULPI_FUNC_CTRL, func_ctrl); isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl);
otg_io_write(isp->otg, ULPI_OTG_CTRL, otg_ctrl); isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl);
return type; return type;
} }
...@@ -136,28 +146,28 @@ static inline int isp1704_charger_verify(struct isp1704_charger *isp) ...@@ -136,28 +146,28 @@ static inline int isp1704_charger_verify(struct isp1704_charger *isp)
u8 r; u8 r;
/* Reset the transceiver */ /* Reset the transceiver */
r = otg_io_read(isp->otg, ULPI_FUNC_CTRL); r = isp1704_read(isp, ULPI_FUNC_CTRL);
r |= ULPI_FUNC_CTRL_RESET; r |= ULPI_FUNC_CTRL_RESET;
otg_io_write(isp->otg, ULPI_FUNC_CTRL, r); isp1704_write(isp, ULPI_FUNC_CTRL, r);
usleep_range(1000, 2000); usleep_range(1000, 2000);
/* Set normal mode */ /* Set normal mode */
r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK); r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
otg_io_write(isp->otg, ULPI_FUNC_CTRL, r); isp1704_write(isp, ULPI_FUNC_CTRL, r);
/* Clear the DP and DM pull-down bits */ /* Clear the DP and DM pull-down bits */
r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN; r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), r); isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r);
/* Enable strong pull-up on DP (1.5K) and reset */ /* Enable strong pull-up on DP (1.5K) and reset */
r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET; r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), r); isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r);
usleep_range(1000, 2000); usleep_range(1000, 2000);
/* Read the line state */ /* Read the line state */
if (!otg_io_read(isp->otg, ULPI_DEBUG)) { if (!isp1704_read(isp, ULPI_DEBUG)) {
/* Disable strong pull-up on DP (1.5K) */ /* Disable strong pull-up on DP (1.5K) */
otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL), isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
ULPI_FUNC_CTRL_TERMSELECT); ULPI_FUNC_CTRL_TERMSELECT);
return 1; return 1;
} }
...@@ -165,23 +175,23 @@ static inline int isp1704_charger_verify(struct isp1704_charger *isp) ...@@ -165,23 +175,23 @@ static inline int isp1704_charger_verify(struct isp1704_charger *isp)
/* Is it a charger or PS/2 connection */ /* Is it a charger or PS/2 connection */
/* Enable weak pull-up resistor on DP */ /* Enable weak pull-up resistor on DP */
otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
ISP1704_PWR_CTRL_DP_WKPU_EN); ISP1704_PWR_CTRL_DP_WKPU_EN);
/* Disable strong pull-up on DP (1.5K) */ /* Disable strong pull-up on DP (1.5K) */
otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL), isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
ULPI_FUNC_CTRL_TERMSELECT); ULPI_FUNC_CTRL_TERMSELECT);
/* Enable weak pull-down resistor on DM */ /* Enable weak pull-down resistor on DM */
otg_io_write(isp->otg, ULPI_SET(ULPI_OTG_CTRL), isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL),
ULPI_OTG_CTRL_DM_PULLDOWN); ULPI_OTG_CTRL_DM_PULLDOWN);
/* It's a charger if the line states are clear */ /* It's a charger if the line states are clear */
if (!(otg_io_read(isp->otg, ULPI_DEBUG))) if (!(isp1704_read(isp, ULPI_DEBUG)))
ret = 1; ret = 1;
/* Disable weak pull-up resistor on DP */ /* Disable weak pull-up resistor on DP */
otg_io_write(isp->otg, ULPI_CLR(ISP1704_PWR_CTRL), isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL),
ISP1704_PWR_CTRL_DP_WKPU_EN); ISP1704_PWR_CTRL_DP_WKPU_EN);
return ret; return ret;
...@@ -193,14 +203,14 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp) ...@@ -193,14 +203,14 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
u8 pwr_ctrl; u8 pwr_ctrl;
int ret = 0; int ret = 0;
pwr_ctrl = otg_io_read(isp->otg, ISP1704_PWR_CTRL); pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL);
/* set SW control bit in PWR_CTRL register */ /* set SW control bit in PWR_CTRL register */
otg_io_write(isp->otg, ISP1704_PWR_CTRL, isp1704_write(isp, ISP1704_PWR_CTRL,
ISP1704_PWR_CTRL_SWCTRL); ISP1704_PWR_CTRL_SWCTRL);
/* enable manual charger detection */ /* enable manual charger detection */
otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
ISP1704_PWR_CTRL_SWCTRL ISP1704_PWR_CTRL_SWCTRL
| ISP1704_PWR_CTRL_DPVSRC_EN); | ISP1704_PWR_CTRL_DPVSRC_EN);
usleep_range(1000, 2000); usleep_range(1000, 2000);
...@@ -208,7 +218,7 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp) ...@@ -208,7 +218,7 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
timeout = jiffies + msecs_to_jiffies(300); timeout = jiffies + msecs_to_jiffies(300);
do { do {
/* Check if there is a charger */ /* Check if there is a charger */
if (otg_io_read(isp->otg, ISP1704_PWR_CTRL) if (isp1704_read(isp, ISP1704_PWR_CTRL)
& ISP1704_PWR_CTRL_VDAT_DET) { & ISP1704_PWR_CTRL_VDAT_DET) {
ret = isp1704_charger_verify(isp); ret = isp1704_charger_verify(isp);
break; break;
...@@ -216,7 +226,7 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp) ...@@ -216,7 +226,7 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
} while (!time_after(jiffies, timeout) && isp->online); } while (!time_after(jiffies, timeout) && isp->online);
/* recover original state */ /* recover original state */
otg_io_write(isp->otg, ISP1704_PWR_CTRL, pwr_ctrl); isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl);
return ret; return ret;
} }
...@@ -264,8 +274,8 @@ static void isp1704_charger_work(struct work_struct *data) ...@@ -264,8 +274,8 @@ static void isp1704_charger_work(struct work_struct *data)
case POWER_SUPPLY_TYPE_USB: case POWER_SUPPLY_TYPE_USB:
default: default:
/* enable data pullups */ /* enable data pullups */
if (isp->otg->gadget) if (isp->phy->otg->gadget)
usb_gadget_connect(isp->otg->gadget); usb_gadget_connect(isp->phy->otg->gadget);
} }
break; break;
case USB_EVENT_NONE: case USB_EVENT_NONE:
...@@ -283,8 +293,8 @@ static void isp1704_charger_work(struct work_struct *data) ...@@ -283,8 +293,8 @@ static void isp1704_charger_work(struct work_struct *data)
* chargers. The pullups may be enabled elsewhere, so this can * chargers. The pullups may be enabled elsewhere, so this can
* not be the final solution. * not be the final solution.
*/ */
if (isp->otg->gadget) if (isp->phy->otg->gadget)
usb_gadget_disconnect(isp->otg->gadget); usb_gadget_disconnect(isp->phy->otg->gadget);
isp1704_charger_set_power(isp, 0); isp1704_charger_set_power(isp, 0);
break; break;
...@@ -364,11 +374,11 @@ static inline int isp1704_test_ulpi(struct isp1704_charger *isp) ...@@ -364,11 +374,11 @@ static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
int ret = -ENODEV; int ret = -ENODEV;
/* Test ULPI interface */ /* Test ULPI interface */
ret = otg_io_write(isp->otg, ULPI_SCRATCH, 0xaa); ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = otg_io_read(isp->otg, ULPI_SCRATCH); ret = isp1704_read(isp, ULPI_SCRATCH);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -376,13 +386,13 @@ static inline int isp1704_test_ulpi(struct isp1704_charger *isp) ...@@ -376,13 +386,13 @@ static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
return -ENODEV; return -ENODEV;
/* Verify the product and vendor id matches */ /* Verify the product and vendor id matches */
vendor = otg_io_read(isp->otg, ULPI_VENDOR_ID_LOW); vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW);
vendor |= otg_io_read(isp->otg, ULPI_VENDOR_ID_HIGH) << 8; vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8;
if (vendor != NXP_VENDOR_ID) if (vendor != NXP_VENDOR_ID)
return -ENODEV; return -ENODEV;
product = otg_io_read(isp->otg, ULPI_PRODUCT_ID_LOW); product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW);
product |= otg_io_read(isp->otg, ULPI_PRODUCT_ID_HIGH) << 8; product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8;
for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) { for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
if (product == isp170x_id[i]) { if (product == isp170x_id[i]) {
...@@ -405,8 +415,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) ...@@ -405,8 +415,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
if (!isp) if (!isp)
return -ENOMEM; return -ENOMEM;
isp->otg = otg_get_transceiver(); isp->phy = usb_get_transceiver();
if (!isp->otg) if (!isp->phy)
goto fail0; goto fail0;
isp->dev = &pdev->dev; isp->dev = &pdev->dev;
...@@ -429,14 +439,14 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) ...@@ -429,14 +439,14 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
goto fail1; goto fail1;
/* /*
* REVISIT: using work in order to allow the otg notifications to be * REVISIT: using work in order to allow the usb notifications to be
* made atomically in the future. * made atomically in the future.
*/ */
INIT_WORK(&isp->work, isp1704_charger_work); INIT_WORK(&isp->work, isp1704_charger_work);
isp->nb.notifier_call = isp1704_notifier_call; isp->nb.notifier_call = isp1704_notifier_call;
ret = otg_register_notifier(isp->otg, &isp->nb); ret = usb_register_notifier(isp->phy, &isp->nb);
if (ret) if (ret)
goto fail2; goto fail2;
...@@ -449,13 +459,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) ...@@ -449,13 +459,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
* enumerated. The charger driver should be always loaded before any * enumerated. The charger driver should be always loaded before any
* gadget is loaded. * gadget is loaded.
*/ */
if (isp->otg->gadget) if (isp->phy->otg->gadget)
usb_gadget_disconnect(isp->otg->gadget); usb_gadget_disconnect(isp->phy->otg->gadget);
/* Detect charger if VBUS is valid (the cable was already plugged). */ /* Detect charger if VBUS is valid (the cable was already plugged). */
ret = otg_io_read(isp->otg, ULPI_USB_INT_STS); ret = isp1704_read(isp, ULPI_USB_INT_STS);
isp1704_charger_set_power(isp, 0); isp1704_charger_set_power(isp, 0);
if ((ret & ULPI_INT_VBUS_VALID) && !isp->otg->default_a) { if ((ret & ULPI_INT_VBUS_VALID) && !isp->phy->otg->default_a) {
isp->event = USB_EVENT_VBUS; isp->event = USB_EVENT_VBUS;
schedule_work(&isp->work); schedule_work(&isp->work);
} }
...@@ -464,7 +474,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) ...@@ -464,7 +474,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
fail2: fail2:
power_supply_unregister(&isp->psy); power_supply_unregister(&isp->psy);
fail1: fail1:
otg_put_transceiver(isp->otg); usb_put_transceiver(isp->phy);
fail0: fail0:
kfree(isp); kfree(isp);
...@@ -477,9 +487,9 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) ...@@ -477,9 +487,9 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev)
{ {
struct isp1704_charger *isp = platform_get_drvdata(pdev); struct isp1704_charger *isp = platform_get_drvdata(pdev);
otg_unregister_notifier(isp->otg, &isp->nb); usb_unregister_notifier(isp->phy, &isp->nb);
power_supply_unregister(&isp->psy); power_supply_unregister(&isp->psy);
otg_put_transceiver(isp->otg); usb_put_transceiver(isp->phy);
isp1704_charger_set_power(isp, 0); isp1704_charger_set_power(isp, 0);
kfree(isp); kfree(isp);
......
...@@ -40,7 +40,7 @@ static struct timer_list polling_timer; ...@@ -40,7 +40,7 @@ static struct timer_list polling_timer;
static int polling; static int polling;
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
static struct otg_transceiver *transceiver; static struct usb_phy *transceiver;
static struct notifier_block otg_nb; static struct notifier_block otg_nb;
#endif #endif
...@@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) ...@@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev)
} }
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
transceiver = otg_get_transceiver(); transceiver = usb_get_transceiver();
if (transceiver && !pdata->is_usb_online) { if (transceiver && !pdata->is_usb_online) {
pdata->is_usb_online = otg_is_usb_online; pdata->is_usb_online = otg_is_usb_online;
} }
...@@ -375,7 +375,7 @@ static int pda_power_probe(struct platform_device *pdev) ...@@ -375,7 +375,7 @@ static int pda_power_probe(struct platform_device *pdev)
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
if (transceiver && pdata->use_otg_notifier) { if (transceiver && pdata->use_otg_notifier) {
otg_nb.notifier_call = otg_handle_notification; otg_nb.notifier_call = otg_handle_notification;
ret = otg_register_notifier(transceiver, &otg_nb); ret = usb_register_notifier(transceiver, &otg_nb);
if (ret) { if (ret) {
dev_err(dev, "failure to register otg notifier\n"); dev_err(dev, "failure to register otg notifier\n");
goto otg_reg_notifier_failed; goto otg_reg_notifier_failed;
...@@ -409,7 +409,7 @@ static int pda_power_probe(struct platform_device *pdev) ...@@ -409,7 +409,7 @@ static int pda_power_probe(struct platform_device *pdev)
free_irq(ac_irq->start, &pda_psy_ac); free_irq(ac_irq->start, &pda_psy_ac);
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
if (transceiver) if (transceiver)
otg_put_transceiver(transceiver); usb_put_transceiver(transceiver);
#endif #endif
ac_irq_failed: ac_irq_failed:
if (pdata->is_ac_online) if (pdata->is_ac_online)
...@@ -444,7 +444,7 @@ static int pda_power_remove(struct platform_device *pdev) ...@@ -444,7 +444,7 @@ static int pda_power_remove(struct platform_device *pdev)
power_supply_unregister(&pda_psy_ac); power_supply_unregister(&pda_psy_ac);
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
if (transceiver) if (transceiver)
otg_put_transceiver(transceiver); usb_put_transceiver(transceiver);
#endif #endif
if (ac_draw) { if (ac_draw) {
regulator_put(ac_draw); regulator_put(ac_draw);
......
...@@ -69,8 +69,8 @@ struct twl4030_bci { ...@@ -69,8 +69,8 @@ struct twl4030_bci {
struct device *dev; struct device *dev;
struct power_supply ac; struct power_supply ac;
struct power_supply usb; struct power_supply usb;
struct otg_transceiver *transceiver; struct usb_phy *transceiver;
struct notifier_block otg_nb; struct notifier_block usb_nb;
struct work_struct work; struct work_struct work;
int irq_chg; int irq_chg;
int irq_bci; int irq_bci;
...@@ -279,7 +279,7 @@ static void twl4030_bci_usb_work(struct work_struct *data) ...@@ -279,7 +279,7 @@ static void twl4030_bci_usb_work(struct work_struct *data)
static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
void *priv) void *priv)
{ {
struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb); struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb);
dev_dbg(bci->dev, "OTG notify %lu\n", val); dev_dbg(bci->dev, "OTG notify %lu\n", val);
...@@ -479,10 +479,10 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -479,10 +479,10 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
INIT_WORK(&bci->work, twl4030_bci_usb_work); INIT_WORK(&bci->work, twl4030_bci_usb_work);
bci->transceiver = otg_get_transceiver(); bci->transceiver = usb_get_transceiver();
if (bci->transceiver != NULL) { if (bci->transceiver != NULL) {
bci->otg_nb.notifier_call = twl4030_bci_usb_ncb; bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
otg_register_notifier(bci->transceiver, &bci->otg_nb); usb_register_notifier(bci->transceiver, &bci->usb_nb);
} }
/* Enable interrupts now. */ /* Enable interrupts now. */
...@@ -508,8 +508,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -508,8 +508,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
fail_unmask_interrupts: fail_unmask_interrupts:
if (bci->transceiver != NULL) { if (bci->transceiver != NULL) {
otg_unregister_notifier(bci->transceiver, &bci->otg_nb); usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
otg_put_transceiver(bci->transceiver); usb_put_transceiver(bci->transceiver);
} }
free_irq(bci->irq_bci, bci); free_irq(bci->irq_bci, bci);
fail_bci_irq: fail_bci_irq:
...@@ -539,8 +539,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) ...@@ -539,8 +539,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
TWL4030_INTERRUPTS_BCIIMR2A); TWL4030_INTERRUPTS_BCIIMR2A);
if (bci->transceiver != NULL) { if (bci->transceiver != NULL) {
otg_unregister_notifier(bci->transceiver, &bci->otg_nb); usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
otg_put_transceiver(bci->transceiver); usb_put_transceiver(bci->transceiver);
} }
free_irq(bci->irq_bci, bci); free_irq(bci->irq_bci, bci);
free_irq(bci->irq_chg, bci); free_irq(bci->irq_chg, bci);
......
...@@ -1295,6 +1295,7 @@ EXPORT_SYMBOL(int_to_scsilun); ...@@ -1295,6 +1295,7 @@ EXPORT_SYMBOL(int_to_scsilun);
* LUNs even if it's older than SCSI-3. * LUNs even if it's older than SCSI-3.
* If BLIST_NOREPORTLUN is set, return 1 always. * If BLIST_NOREPORTLUN is set, return 1 always.
* If BLIST_NOLUN is set, return 0 always. * If BLIST_NOLUN is set, return 0 always.
* If starget->no_report_luns is set, return 1 always.
* *
* Return: * Return:
* 0: scan completed (or no memory, so further scanning is futile) * 0: scan completed (or no memory, so further scanning is futile)
...@@ -1321,6 +1322,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, ...@@ -1321,6 +1322,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set. * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
* Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does
* support more than 8 LUNs. * support more than 8 LUNs.
* Don't attempt if the target doesn't support REPORT LUNS.
*/ */
if (bflags & BLIST_NOREPORTLUN) if (bflags & BLIST_NOREPORTLUN)
return 1; return 1;
...@@ -1332,6 +1334,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, ...@@ -1332,6 +1334,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
return 1; return 1;
if (bflags & BLIST_NOLUN) if (bflags & BLIST_NOLUN)
return 0; return 0;
if (starget->no_report_luns)
return 1;
if (!(sdev = scsi_device_lookup_by_target(starget, 0))) { if (!(sdev = scsi_device_lookup_by_target(starget, 0))) {
sdev = scsi_alloc_sdev(starget, 0, NULL); sdev = scsi_alloc_sdev(starget, 0, NULL);
......
...@@ -2349,7 +2349,7 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp) ...@@ -2349,7 +2349,7 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp)
* some USB ones crash on receiving them, and the pages * some USB ones crash on receiving them, and the pages
* we currently ask for are for SPC-3 and beyond * we currently ask for are for SPC-3 and beyond
*/ */
if (sdp->scsi_level > SCSI_SPC_2) if (sdp->scsi_level > SCSI_SPC_2 && !sdp->skip_vpd_pages)
return 1; return 1;
return 0; return 0;
} }
......
...@@ -3,43 +3,6 @@ ...@@ -3,43 +3,6 @@
#include <linux/blkdev.h> #include <linux/blkdev.h>
/* Bulk only data structures */
/* command block wrapper */
struct bulk_cb_wrap {
__le32 Signature; /* contains 'USBC' */
__u32 Tag; /* unique per command id */
__le32 DataTransferLength; /* size of data */
__u8 Flags; /* direction in bit 0 */
__u8 Lun; /* LUN normally 0 */
__u8 Length; /* of of the CDB */
__u8 CDB[16]; /* max command */
};
#define US_BULK_CB_WRAP_LEN 31
#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */
#define US_BULK_FLAG_IN 1
#define US_BULK_FLAG_OUT 0
/* command status wrapper */
struct bulk_cs_wrap {
__le32 Signature; /* should = 'USBS' */
__u32 Tag; /* same as original command */
__le32 Residue; /* amount not transferred */
__u8 Status; /* see below */
__u8 Filler[18];
};
#define US_BULK_CS_WRAP_LEN 13
#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
#define US_BULK_STAT_OK 0
#define US_BULK_STAT_FAIL 1
#define US_BULK_STAT_PHASE 2
/* bulk-only class specific requests */
#define US_BULK_RESET_REQUEST 0xff
#define US_BULK_GET_MAX_LUN 0xfe
/* usb_stor_bulk_transfer_xxx() return codes, in order of severity */ /* usb_stor_bulk_transfer_xxx() return codes, in order of severity */
#define USB_STOR_XFER_GOOD 0 /* good transfer */ #define USB_STOR_XFER_GOOD 0 /* good transfer */
#define USB_STOR_XFER_SHORT 1 /* transferred less than expected */ #define USB_STOR_XFER_SHORT 1 /* transferred less than expected */
......
...@@ -135,7 +135,6 @@ static struct usb_driver quausb2_usb_driver = { ...@@ -135,7 +135,6 @@ static struct usb_driver quausb2_usb_driver = {
.probe = usb_serial_probe, .probe = usb_serial_probe,
.disconnect = usb_serial_disconnect, .disconnect = usb_serial_disconnect,
.id_table = quausb2_id_table, .id_table = quausb2_id_table,
.no_dynamic_id = 1,
}; };
/** /**
...@@ -1942,7 +1941,6 @@ static struct usb_serial_driver quatech2_device = { ...@@ -1942,7 +1941,6 @@ static struct usb_serial_driver quatech2_device = {
.name = "quatech_usb2", .name = "quatech_usb2",
}, },
.description = DRIVER_DESC, .description = DRIVER_DESC,
.usb_driver = &quausb2_usb_driver,
.id_table = quausb2_id_table, .id_table = quausb2_id_table,
.num_ports = 8, .num_ports = 8,
.open = qt2_open, .open = qt2_open,
...@@ -1964,41 +1962,11 @@ static struct usb_serial_driver quatech2_device = { ...@@ -1964,41 +1962,11 @@ static struct usb_serial_driver quatech2_device = {
.write_bulk_callback = qt2_write_bulk_callback, .write_bulk_callback = qt2_write_bulk_callback,
}; };
static int __init quausb2_usb_init(void) static struct usb_serial_driver * const serial_drivers[] = {
{ &quatech2_device, NULL
int retval; };
dbg("%s\n", __func__);
/* register with usb-serial */
retval = usb_serial_register(&quatech2_device);
if (retval)
goto failed_usb_serial_register;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
/* register with usb */
retval = usb_register(&quausb2_usb_driver);
if (retval == 0)
return 0;
/* if we're here, usb_register() failed */
usb_serial_deregister(&quatech2_device);
failed_usb_serial_register:
return retval;
}
static void __exit quausb2_usb_exit(void)
{
usb_deregister(&quausb2_usb_driver);
usb_serial_deregister(&quatech2_device);
}
module_init(quausb2_usb_init); module_usb_serial_driver(quausb2_usb_driver, serial_drivers);
module_exit(quausb2_usb_exit);
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
......
...@@ -200,7 +200,6 @@ static struct usb_driver serqt_usb_driver = { ...@@ -200,7 +200,6 @@ static struct usb_driver serqt_usb_driver = {
.probe = usb_serial_probe, .probe = usb_serial_probe,
.disconnect = usb_serial_disconnect, .disconnect = usb_serial_disconnect,
.id_table = serqt_id_table, .id_table = serqt_id_table,
.no_dynamic_id = 1,
}; };
static int port_paranoia_check(struct usb_serial_port *port, static int port_paranoia_check(struct usb_serial_port *port,
...@@ -1590,7 +1589,6 @@ static struct usb_serial_driver quatech_device = { ...@@ -1590,7 +1589,6 @@ static struct usb_serial_driver quatech_device = {
.name = "serqt", .name = "serqt",
}, },
.description = DRIVER_DESC, .description = DRIVER_DESC,
.usb_driver = &serqt_usb_driver,
.id_table = serqt_id_table, .id_table = serqt_id_table,
.num_ports = 8, .num_ports = 8,
.open = qt_open, .open = qt_open,
...@@ -1610,41 +1608,11 @@ static struct usb_serial_driver quatech_device = { ...@@ -1610,41 +1608,11 @@ static struct usb_serial_driver quatech_device = {
.release = qt_release, .release = qt_release,
}; };
static int __init serqt_usb_init(void) static struct usb_serial_driver * const serial_drivers[] = {
{ &quatech_device, NULL
int retval; };
dbg("%s\n", __func__);
/* register with usb-serial */
retval = usb_serial_register(&quatech_device);
if (retval)
goto failed_usb_serial_register;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
/* register with usb */
retval = usb_register(&serqt_usb_driver);
if (retval == 0)
return 0;
/* if we're here, usb_register() failed */
usb_serial_deregister(&quatech_device);
failed_usb_serial_register:
return retval;
}
static void __exit serqt_usb_exit(void)
{
usb_deregister(&serqt_usb_driver);
usb_serial_deregister(&quatech_device);
}
module_init(serqt_usb_init); module_usb_serial_driver(serqt_usb_driver, serial_drivers);
module_exit(serqt_usb_exit);
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
......
...@@ -76,6 +76,7 @@ config USB_ARCH_HAS_EHCI ...@@ -76,6 +76,7 @@ config USB_ARCH_HAS_EHCI
default y if MICROBLAZE default y if MICROBLAZE
default y if SPARC_LEON default y if SPARC_LEON
default y if ARCH_MMP default y if ARCH_MMP
default y if MACH_LOONGSON1
default PCI default PCI
# some non-PCI HCDs implement xHCI # some non-PCI HCDs implement xHCI
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/tty_driver.h> #include <linux/tty_driver.h>
#include <linux/tty_flip.h> #include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -773,10 +774,37 @@ static int acm_tty_tiocmset(struct tty_struct *tty, ...@@ -773,10 +774,37 @@ static int acm_tty_tiocmset(struct tty_struct *tty,
return acm_set_control(acm, acm->ctrlout = newctrl); return acm_set_control(acm, acm->ctrlout = newctrl);
} }
static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
{
struct serial_struct tmp;
if (!info)
return -EINVAL;
memset(&tmp, 0, sizeof(tmp));
tmp.flags = ASYNC_LOW_LATENCY;
tmp.xmit_fifo_size = acm->writesize;
tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
if (copy_to_user(info, &tmp, sizeof(tmp)))
return -EFAULT;
else
return 0;
}
static int acm_tty_ioctl(struct tty_struct *tty, static int acm_tty_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
return -ENOIOCTLCMD; struct acm *acm = tty->driver_data;
int rv = -ENOIOCTLCMD;
switch (cmd) {
case TIOCGSERIAL: /* gets serial port data */
rv = get_serial_info(acm, (struct serial_struct __user *) arg);
break;
}
return rv;
} }
static const __u32 acm_tty_speed[] = { static const __u32 acm_tty_speed[] = {
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/usb/cdc.h> #include <linux/usb/cdc.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <linux/usb/cdc-wdm.h>
/* /*
* Version Information * Version Information
...@@ -31,6 +32,8 @@ ...@@ -31,6 +32,8 @@
#define DRIVER_AUTHOR "Oliver Neukum" #define DRIVER_AUTHOR "Oliver Neukum"
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
#define HUAWEI_VENDOR_ID 0x12D1
static const struct usb_device_id wdm_ids[] = { static const struct usb_device_id wdm_ids[] = {
{ {
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
...@@ -38,6 +41,20 @@ static const struct usb_device_id wdm_ids[] = { ...@@ -38,6 +41,20 @@ static const struct usb_device_id wdm_ids[] = {
.bInterfaceClass = USB_CLASS_COMM, .bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
}, },
{
/*
* Huawei E392, E398 and possibly other Qualcomm based modems
* embed the Qualcomm QMI protocol inside CDC on CDC ECM like
* control interfaces. Userspace access to this is required
* to configure the accompanying data interface
*/
.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = HUAWEI_VENDOR_ID,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
},
{ } { }
}; };
...@@ -54,6 +71,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); ...@@ -54,6 +71,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_POLL_RUNNING 6 #define WDM_POLL_RUNNING 6
#define WDM_RESPONDING 7 #define WDM_RESPONDING 7
#define WDM_SUSPENDING 8 #define WDM_SUSPENDING 8
#define WDM_RESETTING 9
#define WDM_MAX 16 #define WDM_MAX 16
...@@ -61,6 +79,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); ...@@ -61,6 +79,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_DEFAULT_BUFSIZE 256 #define WDM_DEFAULT_BUFSIZE 256
static DEFINE_MUTEX(wdm_mutex); static DEFINE_MUTEX(wdm_mutex);
static DEFINE_SPINLOCK(wdm_device_list_lock);
static LIST_HEAD(wdm_device_list);
/* --- method tables --- */ /* --- method tables --- */
...@@ -82,7 +102,6 @@ struct wdm_device { ...@@ -82,7 +102,6 @@ struct wdm_device {
u16 bufsize; u16 bufsize;
u16 wMaxCommand; u16 wMaxCommand;
u16 wMaxPacketSize; u16 wMaxPacketSize;
u16 bMaxPacketSize0;
__le16 inum; __le16 inum;
int reslength; int reslength;
int length; int length;
...@@ -96,10 +115,40 @@ struct wdm_device { ...@@ -96,10 +115,40 @@ struct wdm_device {
struct work_struct rxwork; struct work_struct rxwork;
int werr; int werr;
int rerr; int rerr;
struct list_head device_list;
int (*manage_power)(struct usb_interface *, int);
}; };
static struct usb_driver wdm_driver; static struct usb_driver wdm_driver;
/* return intfdata if we own the interface, else look up intf in the list */
static struct wdm_device *wdm_find_device(struct usb_interface *intf)
{
struct wdm_device *desc = NULL;
spin_lock(&wdm_device_list_lock);
list_for_each_entry(desc, &wdm_device_list, device_list)
if (desc->intf == intf)
break;
spin_unlock(&wdm_device_list_lock);
return desc;
}
static struct wdm_device *wdm_find_device_by_minor(int minor)
{
struct wdm_device *desc = NULL;
spin_lock(&wdm_device_list_lock);
list_for_each_entry(desc, &wdm_device_list, device_list)
if (desc->intf->minor == minor)
break;
spin_unlock(&wdm_device_list_lock);
return desc;
}
/* --- callbacks --- */ /* --- callbacks --- */
static void wdm_out_callback(struct urb *urb) static void wdm_out_callback(struct urb *urb)
{ {
...@@ -162,11 +211,9 @@ static void wdm_int_callback(struct urb *urb) ...@@ -162,11 +211,9 @@ static void wdm_int_callback(struct urb *urb)
int rv = 0; int rv = 0;
int status = urb->status; int status = urb->status;
struct wdm_device *desc; struct wdm_device *desc;
struct usb_ctrlrequest *req;
struct usb_cdc_notification *dr; struct usb_cdc_notification *dr;
desc = urb->context; desc = urb->context;
req = desc->irq;
dr = (struct usb_cdc_notification *)desc->sbuf; dr = (struct usb_cdc_notification *)desc->sbuf;
if (status) { if (status) {
...@@ -213,24 +260,6 @@ static void wdm_int_callback(struct urb *urb) ...@@ -213,24 +260,6 @@ static void wdm_int_callback(struct urb *urb)
goto exit; goto exit;
} }
req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
req->wValue = 0;
req->wIndex = desc->inum;
req->wLength = cpu_to_le16(desc->wMaxCommand);
usb_fill_control_urb(
desc->response,
interface_to_usbdev(desc->intf),
/* using common endpoint 0 */
usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
(unsigned char *)req,
desc->inbuf,
desc->wMaxCommand,
wdm_in_callback,
desc
);
desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
spin_lock(&desc->iuspin); spin_lock(&desc->iuspin);
clear_bit(WDM_READ, &desc->flags); clear_bit(WDM_READ, &desc->flags);
set_bit(WDM_RESPONDING, &desc->flags); set_bit(WDM_RESPONDING, &desc->flags);
...@@ -279,14 +308,11 @@ static void free_urbs(struct wdm_device *desc) ...@@ -279,14 +308,11 @@ static void free_urbs(struct wdm_device *desc)
static void cleanup(struct wdm_device *desc) static void cleanup(struct wdm_device *desc)
{ {
usb_free_coherent(interface_to_usbdev(desc->intf), spin_lock(&wdm_device_list_lock);
desc->wMaxPacketSize, list_del(&desc->device_list);
desc->sbuf, spin_unlock(&wdm_device_list_lock);
desc->validity->transfer_dma); kfree(desc->sbuf);
usb_free_coherent(interface_to_usbdev(desc->intf), kfree(desc->inbuf);
desc->bMaxPacketSize0,
desc->inbuf,
desc->response->transfer_dma);
kfree(desc->orq); kfree(desc->orq);
kfree(desc->irq); kfree(desc->irq);
kfree(desc->ubuf); kfree(desc->ubuf);
...@@ -351,6 +377,10 @@ static ssize_t wdm_write ...@@ -351,6 +377,10 @@ static ssize_t wdm_write
else else
if (test_bit(WDM_IN_USE, &desc->flags)) if (test_bit(WDM_IN_USE, &desc->flags))
r = -EAGAIN; r = -EAGAIN;
if (test_bit(WDM_RESETTING, &desc->flags))
r = -EIO;
if (r < 0) { if (r < 0) {
kfree(buf); kfree(buf);
goto out; goto out;
...@@ -397,7 +427,7 @@ static ssize_t wdm_write ...@@ -397,7 +427,7 @@ static ssize_t wdm_write
static ssize_t wdm_read static ssize_t wdm_read
(struct file *file, char __user *buffer, size_t count, loff_t *ppos) (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{ {
int rv, cntr = 0; int rv, cntr;
int i = 0; int i = 0;
struct wdm_device *desc = file->private_data; struct wdm_device *desc = file->private_data;
...@@ -406,7 +436,8 @@ static ssize_t wdm_read ...@@ -406,7 +436,8 @@ static ssize_t wdm_read
if (rv < 0) if (rv < 0)
return -ERESTARTSYS; return -ERESTARTSYS;
if (desc->length == 0) { cntr = ACCESS_ONCE(desc->length);
if (cntr == 0) {
desc->read = 0; desc->read = 0;
retry: retry:
if (test_bit(WDM_DISCONNECTING, &desc->flags)) { if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
...@@ -430,6 +461,10 @@ static ssize_t wdm_read ...@@ -430,6 +461,10 @@ static ssize_t wdm_read
rv = -ENODEV; rv = -ENODEV;
goto err; goto err;
} }
if (test_bit(WDM_RESETTING, &desc->flags)) {
rv = -EIO;
goto err;
}
usb_mark_last_busy(interface_to_usbdev(desc->intf)); usb_mark_last_busy(interface_to_usbdev(desc->intf));
if (rv < 0) { if (rv < 0) {
rv = -ERESTARTSYS; rv = -ERESTARTSYS;
...@@ -456,26 +491,30 @@ static ssize_t wdm_read ...@@ -456,26 +491,30 @@ static ssize_t wdm_read
spin_unlock_irq(&desc->iuspin); spin_unlock_irq(&desc->iuspin);
goto retry; goto retry;
} }
clear_bit(WDM_READ, &desc->flags); cntr = desc->length;
spin_unlock_irq(&desc->iuspin); spin_unlock_irq(&desc->iuspin);
} }
cntr = count > desc->length ? desc->length : count; if (cntr > count)
cntr = count;
rv = copy_to_user(buffer, desc->ubuf, cntr); rv = copy_to_user(buffer, desc->ubuf, cntr);
if (rv > 0) { if (rv > 0) {
rv = -EFAULT; rv = -EFAULT;
goto err; goto err;
} }
spin_lock_irq(&desc->iuspin);
for (i = 0; i < desc->length - cntr; i++) for (i = 0; i < desc->length - cntr; i++)
desc->ubuf[i] = desc->ubuf[i + cntr]; desc->ubuf[i] = desc->ubuf[i + cntr];
spin_lock_irq(&desc->iuspin);
desc->length -= cntr; desc->length -= cntr;
spin_unlock_irq(&desc->iuspin);
/* in case we had outstanding data */ /* in case we had outstanding data */
if (!desc->length) if (!desc->length)
clear_bit(WDM_READ, &desc->flags); clear_bit(WDM_READ, &desc->flags);
spin_unlock_irq(&desc->iuspin);
rv = cntr; rv = cntr;
err: err:
...@@ -529,11 +568,11 @@ static int wdm_open(struct inode *inode, struct file *file) ...@@ -529,11 +568,11 @@ static int wdm_open(struct inode *inode, struct file *file)
struct wdm_device *desc; struct wdm_device *desc;
mutex_lock(&wdm_mutex); mutex_lock(&wdm_mutex);
intf = usb_find_interface(&wdm_driver, minor); desc = wdm_find_device_by_minor(minor);
if (!intf) if (!desc)
goto out; goto out;
desc = usb_get_intfdata(intf); intf = desc->intf;
if (test_bit(WDM_DISCONNECTING, &desc->flags)) if (test_bit(WDM_DISCONNECTING, &desc->flags))
goto out; goto out;
file->private_data = desc; file->private_data = desc;
...@@ -543,7 +582,6 @@ static int wdm_open(struct inode *inode, struct file *file) ...@@ -543,7 +582,6 @@ static int wdm_open(struct inode *inode, struct file *file)
dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
goto out; goto out;
} }
intf->needs_remote_wakeup = 1;
/* using write lock to protect desc->count */ /* using write lock to protect desc->count */
mutex_lock(&desc->wlock); mutex_lock(&desc->wlock);
...@@ -560,6 +598,8 @@ static int wdm_open(struct inode *inode, struct file *file) ...@@ -560,6 +598,8 @@ static int wdm_open(struct inode *inode, struct file *file)
rv = 0; rv = 0;
} }
mutex_unlock(&desc->wlock); mutex_unlock(&desc->wlock);
if (desc->count == 1)
desc->manage_power(intf, 1);
usb_autopm_put_interface(desc->intf); usb_autopm_put_interface(desc->intf);
out: out:
mutex_unlock(&wdm_mutex); mutex_unlock(&wdm_mutex);
...@@ -581,7 +621,7 @@ static int wdm_release(struct inode *inode, struct file *file) ...@@ -581,7 +621,7 @@ static int wdm_release(struct inode *inode, struct file *file)
dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
kill_urbs(desc); kill_urbs(desc);
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) if (!test_bit(WDM_DISCONNECTING, &desc->flags))
desc->intf->needs_remote_wakeup = 0; desc->manage_power(desc->intf, 0);
} }
mutex_unlock(&wdm_mutex); mutex_unlock(&wdm_mutex);
return 0; return 0;
...@@ -628,71 +668,31 @@ static void wdm_rxwork(struct work_struct *work) ...@@ -628,71 +668,31 @@ static void wdm_rxwork(struct work_struct *work)
/* --- hotplug --- */ /* --- hotplug --- */
static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
u16 bufsize, int (*manage_power)(struct usb_interface *, int))
{ {
int rv = -EINVAL; int rv = -ENOMEM;
struct usb_device *udev = interface_to_usbdev(intf);
struct wdm_device *desc; struct wdm_device *desc;
struct usb_host_interface *iface;
struct usb_endpoint_descriptor *ep;
struct usb_cdc_dmm_desc *dmhd;
u8 *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
u16 maxcom = WDM_DEFAULT_BUFSIZE;
if (!buffer)
goto out;
while (buflen > 2) {
if (buffer [1] != USB_DT_CS_INTERFACE) {
dev_err(&intf->dev, "skipping garbage\n");
goto next_desc;
}
switch (buffer [2]) {
case USB_CDC_HEADER_TYPE:
break;
case USB_CDC_DMM_TYPE:
dmhd = (struct usb_cdc_dmm_desc *)buffer;
maxcom = le16_to_cpu(dmhd->wMaxCommand);
dev_dbg(&intf->dev,
"Finding maximum buffer length: %d", maxcom);
break;
default:
dev_err(&intf->dev,
"Ignoring extra header, type %d, length %d\n",
buffer[2], buffer[0]);
break;
}
next_desc:
buflen -= buffer[0];
buffer += buffer[0];
}
rv = -ENOMEM;
desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
if (!desc) if (!desc)
goto out; goto out;
INIT_LIST_HEAD(&desc->device_list);
mutex_init(&desc->rlock); mutex_init(&desc->rlock);
mutex_init(&desc->wlock); mutex_init(&desc->wlock);
spin_lock_init(&desc->iuspin); spin_lock_init(&desc->iuspin);
init_waitqueue_head(&desc->wait); init_waitqueue_head(&desc->wait);
desc->wMaxCommand = maxcom; desc->wMaxCommand = bufsize;
/* this will be expanded and needed in hardware endianness */ /* this will be expanded and needed in hardware endianness */
desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
desc->intf = intf; desc->intf = intf;
INIT_WORK(&desc->rxwork, wdm_rxwork); INIT_WORK(&desc->rxwork, wdm_rxwork);
rv = -EINVAL; rv = -EINVAL;
iface = intf->cur_altsetting; if (!usb_endpoint_is_int_in(ep))
if (iface->desc.bNumEndpoints != 1)
goto err;
ep = &iface->endpoint[0].desc;
if (!ep || !usb_endpoint_is_int_in(ep))
goto err; goto err;
desc->wMaxPacketSize = usb_endpoint_maxp(ep); desc->wMaxPacketSize = usb_endpoint_maxp(ep);
desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
if (!desc->orq) if (!desc->orq)
...@@ -717,19 +717,13 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -717,19 +717,13 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (!desc->ubuf) if (!desc->ubuf)
goto err; goto err;
desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf), desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
desc->wMaxPacketSize,
GFP_KERNEL,
&desc->validity->transfer_dma);
if (!desc->sbuf) if (!desc->sbuf)
goto err; goto err;
desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
desc->wMaxCommand,
GFP_KERNEL,
&desc->response->transfer_dma);
if (!desc->inbuf) if (!desc->inbuf)
goto err2; goto err;
usb_fill_int_urb( usb_fill_int_urb(
desc->validity, desc->validity,
...@@ -741,45 +735,149 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -741,45 +735,149 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc, desc,
ep->bInterval ep->bInterval
); );
desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_set_intfdata(intf, desc); desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
desc->irq->wValue = 0;
desc->irq->wIndex = desc->inum;
desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
usb_fill_control_urb(
desc->response,
interface_to_usbdev(intf),
/* using common endpoint 0 */
usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
(unsigned char *)desc->irq,
desc->inbuf,
desc->wMaxCommand,
wdm_in_callback,
desc
);
desc->manage_power = manage_power;
spin_lock(&wdm_device_list_lock);
list_add(&desc->device_list, &wdm_device_list);
spin_unlock(&wdm_device_list_lock);
rv = usb_register_dev(intf, &wdm_class); rv = usb_register_dev(intf, &wdm_class);
if (rv < 0) if (rv < 0)
goto err3; goto err;
else else
dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n", dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
intf->minor - WDM_MINOR_BASE);
out: out:
return rv; return rv;
err3:
usb_set_intfdata(intf, NULL);
usb_free_coherent(interface_to_usbdev(desc->intf),
desc->bMaxPacketSize0,
desc->inbuf,
desc->response->transfer_dma);
err2:
usb_free_coherent(interface_to_usbdev(desc->intf),
desc->wMaxPacketSize,
desc->sbuf,
desc->validity->transfer_dma);
err: err:
free_urbs(desc); cleanup(desc);
kfree(desc->ubuf); return rv;
kfree(desc->orq); }
kfree(desc->irq);
kfree(desc); static int wdm_manage_power(struct usb_interface *intf, int on)
{
/* need autopm_get/put here to ensure the usbcore sees the new value */
int rv = usb_autopm_get_interface(intf);
if (rv < 0)
goto err;
intf->needs_remote_wakeup = on;
usb_autopm_put_interface(intf);
err:
return rv;
}
static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
int rv = -EINVAL;
struct usb_host_interface *iface;
struct usb_endpoint_descriptor *ep;
struct usb_cdc_dmm_desc *dmhd;
u8 *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
u16 maxcom = WDM_DEFAULT_BUFSIZE;
if (!buffer)
goto err;
while (buflen > 2) {
if (buffer[1] != USB_DT_CS_INTERFACE) {
dev_err(&intf->dev, "skipping garbage\n");
goto next_desc;
}
switch (buffer[2]) {
case USB_CDC_HEADER_TYPE:
break;
case USB_CDC_DMM_TYPE:
dmhd = (struct usb_cdc_dmm_desc *)buffer;
maxcom = le16_to_cpu(dmhd->wMaxCommand);
dev_dbg(&intf->dev,
"Finding maximum buffer length: %d", maxcom);
break;
default:
dev_err(&intf->dev,
"Ignoring extra header, type %d, length %d\n",
buffer[2], buffer[0]);
break;
}
next_desc:
buflen -= buffer[0];
buffer += buffer[0];
}
iface = intf->cur_altsetting;
if (iface->desc.bNumEndpoints != 1)
goto err;
ep = &iface->endpoint[0].desc;
rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
err:
return rv; return rv;
} }
/**
* usb_cdc_wdm_register - register a WDM subdriver
* @intf: usb interface the subdriver will associate with
* @ep: interrupt endpoint to monitor for notifications
* @bufsize: maximum message size to support for read/write
*
* Create WDM usb class character device and associate it with intf
* without binding, allowing another driver to manage the interface.
*
* The subdriver will manage the given interrupt endpoint exclusively
* and will issue control requests referring to the given intf. It
* will otherwise avoid interferring, and in particular not do
* usb_set_intfdata/usb_get_intfdata on intf.
*
* The return value is a pointer to the subdriver's struct usb_driver.
* The registering driver is responsible for calling this subdriver's
* disconnect, suspend, resume, pre_reset and post_reset methods from
* its own.
*/
struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
struct usb_endpoint_descriptor *ep,
int bufsize,
int (*manage_power)(struct usb_interface *, int))
{
int rv = -EINVAL;
rv = wdm_create(intf, ep, bufsize, manage_power);
if (rv < 0)
goto err;
return &wdm_driver;
err:
return ERR_PTR(rv);
}
EXPORT_SYMBOL(usb_cdc_wdm_register);
static void wdm_disconnect(struct usb_interface *intf) static void wdm_disconnect(struct usb_interface *intf)
{ {
struct wdm_device *desc; struct wdm_device *desc;
unsigned long flags; unsigned long flags;
usb_deregister_dev(intf, &wdm_class); usb_deregister_dev(intf, &wdm_class);
desc = wdm_find_device(intf);
mutex_lock(&wdm_mutex); mutex_lock(&wdm_mutex);
desc = usb_get_intfdata(intf);
/* the spinlock makes sure no new urbs are generated in the callbacks */ /* the spinlock makes sure no new urbs are generated in the callbacks */
spin_lock_irqsave(&desc->iuspin, flags); spin_lock_irqsave(&desc->iuspin, flags);
...@@ -803,7 +901,7 @@ static void wdm_disconnect(struct usb_interface *intf) ...@@ -803,7 +901,7 @@ static void wdm_disconnect(struct usb_interface *intf)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int wdm_suspend(struct usb_interface *intf, pm_message_t message) static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
{ {
struct wdm_device *desc = usb_get_intfdata(intf); struct wdm_device *desc = wdm_find_device(intf);
int rv = 0; int rv = 0;
dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
...@@ -853,7 +951,7 @@ static int recover_from_urb_loss(struct wdm_device *desc) ...@@ -853,7 +951,7 @@ static int recover_from_urb_loss(struct wdm_device *desc)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int wdm_resume(struct usb_interface *intf) static int wdm_resume(struct usb_interface *intf)
{ {
struct wdm_device *desc = usb_get_intfdata(intf); struct wdm_device *desc = wdm_find_device(intf);
int rv; int rv;
dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
...@@ -867,11 +965,7 @@ static int wdm_resume(struct usb_interface *intf) ...@@ -867,11 +965,7 @@ static int wdm_resume(struct usb_interface *intf)
static int wdm_pre_reset(struct usb_interface *intf) static int wdm_pre_reset(struct usb_interface *intf)
{ {
struct wdm_device *desc = usb_get_intfdata(intf); struct wdm_device *desc = wdm_find_device(intf);
mutex_lock(&desc->rlock);
mutex_lock(&desc->wlock);
kill_urbs(desc);
/* /*
* we notify everybody using poll of * we notify everybody using poll of
...@@ -880,17 +974,25 @@ static int wdm_pre_reset(struct usb_interface *intf) ...@@ -880,17 +974,25 @@ static int wdm_pre_reset(struct usb_interface *intf)
* message from the device is lost * message from the device is lost
*/ */
spin_lock_irq(&desc->iuspin); spin_lock_irq(&desc->iuspin);
set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */
set_bit(WDM_READ, &desc->flags); /* unblock read */
clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */
desc->rerr = -EINTR; desc->rerr = -EINTR;
spin_unlock_irq(&desc->iuspin); spin_unlock_irq(&desc->iuspin);
wake_up_all(&desc->wait); wake_up_all(&desc->wait);
mutex_lock(&desc->rlock);
mutex_lock(&desc->wlock);
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
return 0; return 0;
} }
static int wdm_post_reset(struct usb_interface *intf) static int wdm_post_reset(struct usb_interface *intf)
{ {
struct wdm_device *desc = usb_get_intfdata(intf); struct wdm_device *desc = wdm_find_device(intf);
int rv; int rv;
clear_bit(WDM_RESETTING, &desc->flags);
rv = recover_from_urb_loss(desc); rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->wlock); mutex_unlock(&desc->wlock);
mutex_unlock(&desc->rlock); mutex_unlock(&desc->rlock);
......
...@@ -958,13 +958,8 @@ void usb_rebind_intf(struct usb_interface *intf) ...@@ -958,13 +958,8 @@ void usb_rebind_intf(struct usb_interface *intf)
int rc; int rc;
/* Delayed unbind of an existing driver */ /* Delayed unbind of an existing driver */
if (intf->dev.driver) { if (intf->dev.driver)
struct usb_driver *driver = usb_forced_unbind_intf(intf);
to_usb_driver(intf->dev.driver);
dev_dbg(&intf->dev, "forced unbind\n");
usb_driver_release_interface(driver, intf);
}
/* Try to rebind the interface */ /* Try to rebind the interface */
if (!intf->dev.power.is_prepared) { if (!intf->dev.power.is_prepared) {
...@@ -977,15 +972,13 @@ void usb_rebind_intf(struct usb_interface *intf) ...@@ -977,15 +972,13 @@ void usb_rebind_intf(struct usb_interface *intf)
#ifdef CONFIG_PM #ifdef CONFIG_PM
#define DO_UNBIND 0 /* Unbind drivers for @udev's interfaces that don't support suspend/resume
#define DO_REBIND 1 * There is no check for reset_resume here because it can be determined
* only during resume whether reset_resume is needed.
/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
* or rebind interfaces that have been unbound, according to @action.
* *
* The caller must hold @udev's device lock. * The caller must hold @udev's device lock.
*/ */
static void do_unbind_rebind(struct usb_device *udev, int action) static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
{ {
struct usb_host_config *config; struct usb_host_config *config;
int i; int i;
...@@ -996,19 +989,49 @@ static void do_unbind_rebind(struct usb_device *udev, int action) ...@@ -996,19 +989,49 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
if (config) { if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) { for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i]; intf = config->interface[i];
switch (action) {
case DO_UNBIND:
if (intf->dev.driver) { if (intf->dev.driver) {
drv = to_usb_driver(intf->dev.driver); drv = to_usb_driver(intf->dev.driver);
if (!drv->suspend || !drv->resume) if (!drv->suspend || !drv->resume)
usb_forced_unbind_intf(intf); usb_forced_unbind_intf(intf);
} }
break; }
case DO_REBIND: }
}
/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
* These interfaces have the needs_binding flag set by usb_resume_interface().
*
* The caller must hold @udev's device lock.
*/
static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
{
struct usb_host_config *config;
int i;
struct usb_interface *intf;
config = udev->actconfig;
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i];
if (intf->dev.driver && intf->needs_binding)
usb_forced_unbind_intf(intf);
}
}
}
static void do_rebind_interfaces(struct usb_device *udev)
{
struct usb_host_config *config;
int i;
struct usb_interface *intf;
config = udev->actconfig;
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i];
if (intf->needs_binding) if (intf->needs_binding)
usb_rebind_intf(intf); usb_rebind_intf(intf);
break;
}
} }
} }
} }
...@@ -1302,35 +1325,48 @@ int usb_suspend(struct device *dev, pm_message_t msg) ...@@ -1302,35 +1325,48 @@ int usb_suspend(struct device *dev, pm_message_t msg)
{ {
struct usb_device *udev = to_usb_device(dev); struct usb_device *udev = to_usb_device(dev);
do_unbind_rebind(udev, DO_UNBIND); unbind_no_pm_drivers_interfaces(udev);
/* From now on we are sure all drivers support suspend/resume
* but not necessarily reset_resume()
* so we may still need to unbind and rebind upon resume
*/
choose_wakeup(udev, msg); choose_wakeup(udev, msg);
return usb_suspend_both(udev, msg); return usb_suspend_both(udev, msg);
} }
/* The device lock is held by the PM core */ /* The device lock is held by the PM core */
int usb_resume(struct device *dev, pm_message_t msg) int usb_resume_complete(struct device *dev)
{ {
struct usb_device *udev = to_usb_device(dev); struct usb_device *udev = to_usb_device(dev);
int status;
/* For PM complete calls, all we do is rebind interfaces */ /* For PM complete calls, all we do is rebind interfaces
if (msg.event == PM_EVENT_ON) { * whose needs_binding flag is set
*/
if (udev->state != USB_STATE_NOTATTACHED) if (udev->state != USB_STATE_NOTATTACHED)
do_unbind_rebind(udev, DO_REBIND); do_rebind_interfaces(udev);
status = 0; return 0;
}
/* The device lock is held by the PM core */
int usb_resume(struct device *dev, pm_message_t msg)
{
struct usb_device *udev = to_usb_device(dev);
int status;
/* For all other calls, take the device back to full power and /* For all calls, take the device back to full power and
* tell the PM core in case it was autosuspended previously. * tell the PM core in case it was autosuspended previously.
* Unbind the interfaces that will need rebinding later. * Unbind the interfaces that will need rebinding later,
* because they fail to support reset_resume.
* (This can't be done in usb_resume_interface()
* above because it doesn't own the right set of locks.)
*/ */
} else {
status = usb_resume_both(udev, msg); status = usb_resume_both(udev, msg);
if (status == 0) { if (status == 0) {
pm_runtime_disable(dev); pm_runtime_disable(dev);
pm_runtime_set_active(dev); pm_runtime_set_active(dev);
pm_runtime_enable(dev); pm_runtime_enable(dev);
do_unbind_rebind(udev, DO_REBIND); unbind_no_reset_resume_drivers_interfaces(udev);
}
} }
/* Avoid PM error messages for devices disconnected while suspended /* Avoid PM error messages for devices disconnected while suspended
......
...@@ -380,6 +380,7 @@ static int check_root_hub_suspended(struct device *dev) ...@@ -380,6 +380,7 @@ static int check_root_hub_suspended(struct device *dev)
return 0; return 0;
} }
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
static int suspend_common(struct device *dev, bool do_wakeup) static int suspend_common(struct device *dev, bool do_wakeup)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
...@@ -471,6 +472,7 @@ static int resume_common(struct device *dev, int event) ...@@ -471,6 +472,7 @@ static int resume_common(struct device *dev, int event)
} }
return retval; return retval;
} }
#endif /* SLEEP || RUNTIME */
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
......
...@@ -2352,7 +2352,7 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd, ...@@ -2352,7 +2352,7 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
"io mem" : "io base", "io mem" : "io base",
(unsigned long long)hcd->rsrc_start); (unsigned long long)hcd->rsrc_start);
} else { } else {
hcd->irq = -1; hcd->irq = 0;
if (hcd->rsrc_start) if (hcd->rsrc_start)
dev_info(hcd->self.controller, "%s 0x%08llx\n", dev_info(hcd->self.controller, "%s 0x%08llx\n",
(hcd->driver->flags & HCD_MEMORY) ? (hcd->driver->flags & HCD_MEMORY) ?
...@@ -2508,7 +2508,7 @@ int usb_add_hcd(struct usb_hcd *hcd, ...@@ -2508,7 +2508,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
del_timer_sync(&hcd->rh_timer); del_timer_sync(&hcd->rh_timer);
err_hcd_driver_start: err_hcd_driver_start:
if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0) if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
free_irq(irqnum, hcd); free_irq(irqnum, hcd);
err_request_irq: err_request_irq:
err_hcd_driver_setup: err_hcd_driver_setup:
...@@ -2573,7 +2573,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) ...@@ -2573,7 +2573,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
del_timer_sync(&hcd->rh_timer); del_timer_sync(&hcd->rh_timer);
if (usb_hcd_is_primary_hcd(hcd)) { if (usb_hcd_is_primary_hcd(hcd)) {
if (hcd->irq >= 0) if (hcd->irq > 0)
free_irq(hcd->irq, hcd); free_irq(hcd->irq, hcd);
} }
......
...@@ -62,6 +62,8 @@ struct usb_hub { ...@@ -62,6 +62,8 @@ struct usb_hub {
resumed */ resumed */
unsigned long removed_bits[1]; /* ports with a "removed" unsigned long removed_bits[1]; /* ports with a "removed"
device present */ device present */
unsigned long wakeup_bits[1]; /* ports that have signaled
remote wakeup */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short! #error event_bits[] is too short!
#endif #endif
...@@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev) ...@@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev)
kick_khubd(hub); kick_khubd(hub);
} }
/*
* Let the USB core know that a USB 3.0 device has sent a Function Wake Device
* Notification, which indicates it had initiated remote wakeup.
*
* USB 3.0 hubs do not report the port link state change from U3 to U0 when the
* device initiates resume, so the USB core will not receive notice of the
* resume through the normal hub interrupt URB.
*/
void usb_wakeup_notification(struct usb_device *hdev,
unsigned int portnum)
{
struct usb_hub *hub;
if (!hdev)
return;
hub = hdev_to_hub(hdev);
if (hub) {
set_bit(portnum, hub->wakeup_bits);
kick_khubd(hub);
}
}
EXPORT_SYMBOL_GPL(usb_wakeup_notification);
/* completion function, fires on port status changes and various faults */ /* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb) static void hub_irq(struct urb *urb)
...@@ -823,12 +848,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) ...@@ -823,12 +848,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
clear_port_feature(hub->hdev, port1, clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE); USB_PORT_FEAT_C_ENABLE);
} }
if (portchange & USB_PORT_STAT_C_LINK_STATE) {
need_debounce_delay = true;
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
}
if ((portchange & USB_PORT_STAT_C_BH_RESET) && if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
hub_is_superspeed(hub->hdev)) { hub_is_superspeed(hub->hdev)) {
need_debounce_delay = true; need_debounce_delay = true;
...@@ -850,12 +869,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) ...@@ -850,12 +869,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
set_bit(port1, hub->change_bits); set_bit(port1, hub->change_bits);
} else if (portstatus & USB_PORT_STAT_ENABLE) { } else if (portstatus & USB_PORT_STAT_ENABLE) {
bool port_resumed = (portstatus &
USB_PORT_STAT_LINK_STATE) ==
USB_SS_PORT_LS_U0;
/* The power session apparently survived the resume. /* The power session apparently survived the resume.
* If there was an overcurrent or suspend change * If there was an overcurrent or suspend change
* (i.e., remote wakeup request), have khubd * (i.e., remote wakeup request), have khubd
* take care of it. * take care of it. Look at the port link state
* for USB 3.0 hubs, since they don't have a suspend
* change bit, and they don't set the port link change
* bit on device-initiated resume.
*/ */
if (portchange) if (portchange || (hub_is_superspeed(hub->hdev) &&
port_resumed))
set_bit(port1, hub->change_bits); set_bit(port1, hub->change_bits);
} else if (udev->persist_enabled) { } else if (udev->persist_enabled) {
...@@ -1293,13 +1319,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -1293,13 +1319,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = intf->cur_altsetting; desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf); hdev = interface_to_usbdev(intf);
/* Hubs have proper suspend/resume support. USB 3.0 device suspend is /* Hubs have proper suspend/resume support. */
* different from USB 2.0/1.1 device suspend, and unfortunately we
* don't support it yet. So leave autosuspend disabled for USB 3.0
* external hubs for now. Enable autosuspend for USB 3.0 roothubs,
* since that isn't a "real" hub.
*/
if (!hub_is_superspeed(hdev) || !hdev->parent)
usb_enable_autosuspend(hdev); usb_enable_autosuspend(hdev);
if (hdev->level == MAX_TOPO_LEVEL) { if (hdev->level == MAX_TOPO_LEVEL) {
...@@ -1842,6 +1862,37 @@ static int usb_enumerate_device(struct usb_device *udev) ...@@ -1842,6 +1862,37 @@ static int usb_enumerate_device(struct usb_device *udev)
return err; return err;
} }
static void set_usb_port_removable(struct usb_device *udev)
{
struct usb_device *hdev = udev->parent;
struct usb_hub *hub;
u8 port = udev->portnum;
u16 wHubCharacteristics;
bool removable = true;
if (!hdev)
return;
hub = hdev_to_hub(udev->parent);
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
return;
if (hub_is_superspeed(hdev)) {
if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
removable = false;
} else {
if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
removable = false;
}
if (removable)
udev->removable = USB_DEVICE_REMOVABLE;
else
udev->removable = USB_DEVICE_FIXED;
}
/** /**
* usb_new_device - perform initial device setup (usbcore-internal) * usb_new_device - perform initial device setup (usbcore-internal)
...@@ -1900,6 +1951,15 @@ int usb_new_device(struct usb_device *udev) ...@@ -1900,6 +1951,15 @@ int usb_new_device(struct usb_device *udev)
announce_device(udev); announce_device(udev);
device_enable_async_suspend(&udev->dev); device_enable_async_suspend(&udev->dev);
/*
* check whether the hub marks this port as non-removable. Do it
* now so that platform-specific data can override it in
* device_add()
*/
if (udev->parent)
set_usb_port_removable(udev);
/* Register the device. The device driver is responsible /* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device * for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others). * notifier chain (used by usbfs and possibly others).
...@@ -2385,11 +2445,27 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) ...@@ -2385,11 +2445,27 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
* we don't explicitly enable it here. * we don't explicitly enable it here.
*/ */
if (udev->do_remote_wakeup) { if (udev->do_remote_wakeup) {
if (!hub_is_superspeed(hub->hdev)) {
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP, 0, USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT); USB_CTRL_SET_TIMEOUT);
} else {
/* Assume there's only one function on the USB 3.0
* device and enable remote wake for the first
* interface. FIXME if the interface association
* descriptor shows there's more than one function.
*/
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE,
USB_RECIP_INTERFACE,
USB_INTRF_FUNC_SUSPEND,
USB_INTRF_FUNC_SUSPEND_RW |
USB_INTRF_FUNC_SUSPEND_LP,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
if (status) { if (status) {
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
status); status);
...@@ -2679,6 +2755,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) ...@@ -2679,6 +2755,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
struct usb_hub *hub = usb_get_intfdata (intf); struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev = hub->hdev; struct usb_device *hdev = hub->hdev;
unsigned port1; unsigned port1;
int status;
/* Warn if children aren't already suspended */ /* Warn if children aren't already suspended */
for (port1 = 1; port1 <= hdev->maxchild; port1++) { for (port1 = 1; port1 <= hdev->maxchild; port1++) {
...@@ -2691,6 +2768,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) ...@@ -2691,6 +2768,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
return -EBUSY; return -EBUSY;
} }
} }
if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
/* Enable hub to send remote wakeup for all ports. */
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
status = set_port_feature(hdev,
port1 |
USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
USB_PORT_FEAT_REMOTE_WAKE_MASK);
}
}
dev_dbg(&intf->dev, "%s\n", __func__); dev_dbg(&intf->dev, "%s\n", __func__);
...@@ -3424,6 +3512,46 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, ...@@ -3424,6 +3512,46 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
hcd->driver->relinquish_port(hcd, port1); hcd->driver->relinquish_port(hcd, port1);
} }
/* Returns 1 if there was a remote wakeup and a connect status change. */
static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
u16 portstatus, u16 portchange)
{
struct usb_device *hdev;
struct usb_device *udev;
int connect_change = 0;
int ret;
hdev = hub->hdev;
udev = hdev->children[port-1];
if (!hub_is_superspeed(hdev)) {
if (!(portchange & USB_PORT_STAT_C_SUSPEND))
return 0;
clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
} else {
if (!udev || udev->state != USB_STATE_SUSPENDED ||
(portstatus & USB_PORT_STAT_LINK_STATE) !=
USB_SS_PORT_LS_U0)
return 0;
}
if (udev) {
/* TRSMRCY = 10 msec */
msleep(10);
usb_lock_device(udev);
ret = usb_remote_wakeup(udev);
usb_unlock_device(udev);
if (ret < 0)
connect_change = 1;
} else {
ret = -ENODEV;
hub_port_disable(hub, port, 1);
}
dev_dbg(hub->intfdev, "resume on port %d, status %d\n",
port, ret);
return connect_change;
}
static void hub_events(void) static void hub_events(void)
{ {
struct list_head *tmp; struct list_head *tmp;
...@@ -3436,7 +3564,7 @@ static void hub_events(void) ...@@ -3436,7 +3564,7 @@ static void hub_events(void)
u16 portstatus; u16 portstatus;
u16 portchange; u16 portchange;
int i, ret; int i, ret;
int connect_change; int connect_change, wakeup_change;
/* /*
* We restart the list every time to avoid a deadlock with * We restart the list every time to avoid a deadlock with
...@@ -3515,8 +3643,9 @@ static void hub_events(void) ...@@ -3515,8 +3643,9 @@ static void hub_events(void)
if (test_bit(i, hub->busy_bits)) if (test_bit(i, hub->busy_bits))
continue; continue;
connect_change = test_bit(i, hub->change_bits); connect_change = test_bit(i, hub->change_bits);
wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
if (!test_and_clear_bit(i, hub->event_bits) && if (!test_and_clear_bit(i, hub->event_bits) &&
!connect_change) !connect_change && !wakeup_change)
continue; continue;
ret = hub_port_status(hub, i, ret = hub_port_status(hub, i,
...@@ -3557,30 +3686,9 @@ static void hub_events(void) ...@@ -3557,30 +3686,9 @@ static void hub_events(void)
} }
} }
if (portchange & USB_PORT_STAT_C_SUSPEND) { if (hub_handle_remote_wakeup(hub, i,
struct usb_device *udev; portstatus, portchange))
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_SUSPEND);
udev = hdev->children[i-1];
if (udev) {
/* TRSMRCY = 10 msec */
msleep(10);
usb_lock_device(udev);
ret = usb_remote_wakeup(hdev->
children[i-1]);
usb_unlock_device(udev);
if (ret < 0)
connect_change = 1; connect_change = 1;
} else {
ret = -ENODEV;
hub_port_disable(hub, i, 1);
}
dev_dbg (hub_dev,
"resume on port %d, status %d\n",
i, ret);
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) { if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
u16 status = 0; u16 status = 0;
......
...@@ -230,6 +230,28 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -230,6 +230,28 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf)
} }
static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
static ssize_t
show_removable(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev;
char *state;
udev = to_usb_device(dev);
switch (udev->removable) {
case USB_DEVICE_REMOVABLE:
state = "removable";
break;
case USB_DEVICE_FIXED:
state = "fixed";
break;
default:
state = "unknown";
}
return sprintf(buf, "%s\n", state);
}
static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL);
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -626,6 +648,7 @@ static struct attribute *dev_attrs[] = { ...@@ -626,6 +648,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_avoid_reset_quirk.attr, &dev_attr_avoid_reset_quirk.attr,
&dev_attr_authorized.attr, &dev_attr_authorized.attr,
&dev_attr_remove.attr, &dev_attr_remove.attr,
&dev_attr_removable.attr,
NULL, NULL,
}; };
static struct attribute_group dev_attr_grp = { static struct attribute_group dev_attr_grp = {
......
...@@ -403,20 +403,17 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) ...@@ -403,20 +403,17 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
* cause problems in HCDs if they get it wrong. * cause problems in HCDs if they get it wrong.
*/ */
{ {
unsigned int orig_flags = urb->transfer_flags;
unsigned int allowed; unsigned int allowed;
static int pipetypes[4] = { static int pipetypes[4] = {
PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
}; };
/* Check that the pipe's type matches the endpoint's type */ /* Check that the pipe's type matches the endpoint's type */
if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) { if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
dev_err(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
usb_pipetype(urb->pipe), pipetypes[xfertype]); usb_pipetype(urb->pipe), pipetypes[xfertype]);
return -EPIPE; /* The most suitable error code :-) */
}
/* enforce simple/standard policy */ /* Check against a simple/standard policy */
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK | allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
URB_FREE_BUFFER); URB_FREE_BUFFER);
switch (xfertype) { switch (xfertype) {
...@@ -435,14 +432,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) ...@@ -435,14 +432,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
allowed |= URB_ISO_ASAP; allowed |= URB_ISO_ASAP;
break; break;
} }
urb->transfer_flags &= allowed; allowed &= urb->transfer_flags;
/* fail if submitter gave bogus flags */ /* warn if submitter gave bogus flags */
if (urb->transfer_flags != orig_flags) { if (allowed != urb->transfer_flags)
dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n", dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
orig_flags, urb->transfer_flags); urb->transfer_flags, allowed);
return -EINVAL;
}
} }
#endif #endif
/* /*
...@@ -532,10 +527,13 @@ EXPORT_SYMBOL_GPL(usb_submit_urb); ...@@ -532,10 +527,13 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);
* a driver's I/O routines to insure that all URB-related activity has * a driver's I/O routines to insure that all URB-related activity has
* completed before it returns. * completed before it returns.
* *
* This request is always asynchronous. Success is indicated by * This request is asynchronous, however the HCD might call the ->complete()
* returning -EINPROGRESS, at which time the URB will probably not yet * callback during unlink. Therefore when drivers call usb_unlink_urb(), they
* have been given back to the device driver. When it is eventually * must not hold any locks that may be taken by the completion function.
* called, the completion function will see @urb->status == -ECONNRESET. * Success is indicated by returning -EINPROGRESS, at which time the URB will
* probably not yet have been given back to the device driver. When it is
* eventually called, the completion function will see @urb->status ==
* -ECONNRESET.
* Failure is indicated by usb_unlink_urb() returning any other value. * Failure is indicated by usb_unlink_urb() returning any other value.
* Unlinking will fail when @urb is not currently "linked" (i.e., it was * Unlinking will fail when @urb is not currently "linked" (i.e., it was
* never submitted, or it was unlinked before, or the hardware is already * never submitted, or it was unlinked before, or the hardware is already
......
...@@ -274,7 +274,7 @@ static int usb_dev_prepare(struct device *dev) ...@@ -274,7 +274,7 @@ static int usb_dev_prepare(struct device *dev)
static void usb_dev_complete(struct device *dev) static void usb_dev_complete(struct device *dev)
{ {
/* Currently used only for rebinding interfaces */ /* Currently used only for rebinding interfaces */
usb_resume(dev, PMSG_ON); /* FIXME: change to PMSG_COMPLETE */ usb_resume_complete(dev);
} }
static int usb_dev_suspend(struct device *dev) static int usb_dev_suspend(struct device *dev)
......
...@@ -56,6 +56,7 @@ extern void usb_major_cleanup(void); ...@@ -56,6 +56,7 @@ extern void usb_major_cleanup(void);
extern int usb_suspend(struct device *dev, pm_message_t msg); extern int usb_suspend(struct device *dev, pm_message_t msg);
extern int usb_resume(struct device *dev, pm_message_t msg); extern int usb_resume(struct device *dev, pm_message_t msg);
extern int usb_resume_complete(struct device *dev);
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg); extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg); extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
......
...@@ -28,6 +28,19 @@ endif ...@@ -28,6 +28,19 @@ endif
obj-$(CONFIG_USB_DWC3) += dwc3-omap.o obj-$(CONFIG_USB_DWC3) += dwc3-omap.o
##
# REVISIT Samsung Exynos platform needs the clk API which isn't
# defined on all architectures. If we allow dwc3-exynos.c compile
# always we will fail the linking phase on those architectures
# which don't provide clk api implementation and that's unnaceptable.
#
# When Samsung's platform start supporting pm_runtime, this check
# for HAVE_CLK should be removed.
##
ifneq ($(CONFIG_HAVE_CLK),)
obj-$(CONFIG_USB_DWC3) += dwc3-exynos.o
endif
ifneq ($(CONFIG_PCI),) ifneq ($(CONFIG_PCI),)
obj-$(CONFIG_USB_DWC3) += dwc3-pci.o obj-$(CONFIG_USB_DWC3) += dwc3-pci.o
endif endif
......
...@@ -48,10 +48,10 @@ ...@@ -48,10 +48,10 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/module.h>
#include "core.h" #include "core.h"
#include "gadget.h" #include "gadget.h"
...@@ -86,7 +86,7 @@ int dwc3_get_device_id(void) ...@@ -86,7 +86,7 @@ int dwc3_get_device_id(void)
id = -ENOMEM; id = -ENOMEM;
} }
return 0; return id;
} }
EXPORT_SYMBOL_GPL(dwc3_get_device_id); EXPORT_SYMBOL_GPL(dwc3_get_device_id);
...@@ -167,11 +167,11 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc, ...@@ -167,11 +167,11 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
} }
/** /**
* dwc3_alloc_one_event_buffer - Allocated one event buffer structure * dwc3_alloc_one_event_buffer - Allocates one event buffer structure
* @dwc: Pointer to our controller context structure * @dwc: Pointer to our controller context structure
* @length: size of the event buffer * @length: size of the event buffer
* *
* Returns a pointer to the allocated event buffer structure on succes * Returns a pointer to the allocated event buffer structure on success
* otherwise ERR_PTR(errno). * otherwise ERR_PTR(errno).
*/ */
static struct dwc3_event_buffer *__devinit static struct dwc3_event_buffer *__devinit
...@@ -215,10 +215,10 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) ...@@ -215,10 +215,10 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
/** /**
* dwc3_alloc_event_buffers - Allocates @num event buffers of size @length * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
* @dwc: Pointer to out controller context structure * @dwc: pointer to our controller context structure
* @length: size of event buffer * @length: size of event buffer
* *
* Returns 0 on success otherwise negative errno. In error the case, dwc * Returns 0 on success otherwise negative errno. In the error case, dwc
* may contain some buffers allocated but not all which were requested. * may contain some buffers allocated but not all which were requested.
*/ */
static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
...@@ -251,7 +251,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) ...@@ -251,7 +251,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
/** /**
* dwc3_event_buffers_setup - setup our allocated event buffers * dwc3_event_buffers_setup - setup our allocated event buffers
* @dwc: Pointer to out controller context structure * @dwc: pointer to our controller context structure
* *
* Returns 0 on success otherwise negative errno. * Returns 0 on success otherwise negative errno.
*/ */
...@@ -350,7 +350,7 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) ...@@ -350,7 +350,7 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
dwc3_cache_hwparams(dwc); dwc3_cache_hwparams(dwc);
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN(3); reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
reg &= ~DWC3_GCTL_DISSCRAMBLE; reg &= ~DWC3_GCTL_DISSCRAMBLE;
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
...@@ -363,9 +363,9 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) ...@@ -363,9 +363,9 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
/* /*
* WORKAROUND: DWC3 revisions <1.90a have a bug * WORKAROUND: DWC3 revisions <1.90a have a bug
* when The device fails to connect at SuperSpeed * where the device can fail to connect at SuperSpeed
* and falls back to high-speed mode which causes * and falls back to high-speed mode which causes
* the device to enter in a Connect/Disconnect loop * the device to enter a Connect/Disconnect loop
*/ */
if (dwc->revision < DWC3_REVISION_190A) if (dwc->revision < DWC3_REVISION_190A)
reg |= DWC3_GCTL_U2RSTECN; reg |= DWC3_GCTL_U2RSTECN;
...@@ -404,8 +404,10 @@ static void dwc3_core_exit(struct dwc3 *dwc) ...@@ -404,8 +404,10 @@ static void dwc3_core_exit(struct dwc3 *dwc)
static int __devinit dwc3_probe(struct platform_device *pdev) static int __devinit dwc3_probe(struct platform_device *pdev)
{ {
struct device_node *node = pdev->dev.of_node;
struct resource *res; struct resource *res;
struct dwc3 *dwc; struct dwc3 *dwc;
struct device *dev = &pdev->dev;
int ret = -ENOMEM; int ret = -ENOMEM;
int irq; int irq;
...@@ -415,39 +417,39 @@ static int __devinit dwc3_probe(struct platform_device *pdev) ...@@ -415,39 +417,39 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
u8 mode; u8 mode;
mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
if (!mem) { if (!mem) {
dev_err(&pdev->dev, "not enough memory\n"); dev_err(dev, "not enough memory\n");
goto err0; return -ENOMEM;
} }
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
dwc->mem = mem; dwc->mem = mem;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) { if (!res) {
dev_err(&pdev->dev, "missing resource\n"); dev_err(dev, "missing resource\n");
goto err1; return -ENODEV;
} }
dwc->res = res; dwc->res = res;
res = request_mem_region(res->start, resource_size(res), res = devm_request_mem_region(dev, res->start, resource_size(res),
dev_name(&pdev->dev)); dev_name(dev));
if (!res) { if (!res) {
dev_err(&pdev->dev, "can't request mem region\n"); dev_err(dev, "can't request mem region\n");
goto err1; return -ENOMEM;
} }
regs = ioremap(res->start, resource_size(res)); regs = devm_ioremap(dev, res->start, resource_size(res));
if (!regs) { if (!regs) {
dev_err(&pdev->dev, "ioremap failed\n"); dev_err(dev, "ioremap failed\n");
goto err2; return -ENOMEM;
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
dev_err(&pdev->dev, "missing IRQ\n"); dev_err(dev, "missing IRQ\n");
goto err3; return -ENODEV;
} }
spin_lock_init(&dwc->lock); spin_lock_init(&dwc->lock);
...@@ -455,7 +457,7 @@ static int __devinit dwc3_probe(struct platform_device *pdev) ...@@ -455,7 +457,7 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
dwc->regs = regs; dwc->regs = regs;
dwc->regs_size = resource_size(res); dwc->regs_size = resource_size(res);
dwc->dev = &pdev->dev; dwc->dev = dev;
dwc->irq = irq; dwc->irq = irq;
if (!strncmp("super", maximum_speed, 5)) if (!strncmp("super", maximum_speed, 5))
...@@ -469,14 +471,17 @@ static int __devinit dwc3_probe(struct platform_device *pdev) ...@@ -469,14 +471,17 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
else else
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
pm_runtime_enable(&pdev->dev); if (of_get_property(node, "tx-fifo-resize", NULL))
pm_runtime_get_sync(&pdev->dev); dwc->needs_fifo_resize = true;
pm_runtime_forbid(&pdev->dev);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
pm_runtime_forbid(dev);
ret = dwc3_core_init(dwc); ret = dwc3_core_init(dwc);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to initialize core\n"); dev_err(dev, "failed to initialize core\n");
goto err3; return ret;
} }
mode = DWC3_MODE(dwc->hwparams.hwparams0); mode = DWC3_MODE(dwc->hwparams.hwparams0);
...@@ -486,49 +491,49 @@ static int __devinit dwc3_probe(struct platform_device *pdev) ...@@ -486,49 +491,49 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc); ret = dwc3_gadget_init(dwc);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to initialize gadget\n"); dev_err(dev, "failed to initialize gadget\n");
goto err4; goto err1;
} }
break; break;
case DWC3_MODE_HOST: case DWC3_MODE_HOST:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc); ret = dwc3_host_init(dwc);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to initialize host\n"); dev_err(dev, "failed to initialize host\n");
goto err4; goto err1;
} }
break; break;
case DWC3_MODE_DRD: case DWC3_MODE_DRD:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc); ret = dwc3_host_init(dwc);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to initialize host\n"); dev_err(dev, "failed to initialize host\n");
goto err4; goto err1;
} }
ret = dwc3_gadget_init(dwc); ret = dwc3_gadget_init(dwc);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to initialize gadget\n"); dev_err(dev, "failed to initialize gadget\n");
goto err4; goto err1;
} }
break; break;
default: default:
dev_err(&pdev->dev, "Unsupported mode of operation %d\n", mode); dev_err(dev, "Unsupported mode of operation %d\n", mode);
goto err4; goto err1;
} }
dwc->mode = mode; dwc->mode = mode;
ret = dwc3_debugfs_init(dwc); ret = dwc3_debugfs_init(dwc);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to initialize debugfs\n"); dev_err(dev, "failed to initialize debugfs\n");
goto err5; goto err2;
} }
pm_runtime_allow(&pdev->dev); pm_runtime_allow(dev);
return 0; return 0;
err5: err2:
switch (mode) { switch (mode) {
case DWC3_MODE_DEVICE: case DWC3_MODE_DEVICE:
dwc3_gadget_exit(dwc); dwc3_gadget_exit(dwc);
...@@ -545,19 +550,9 @@ static int __devinit dwc3_probe(struct platform_device *pdev) ...@@ -545,19 +550,9 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
break; break;
} }
err4:
dwc3_core_exit(dwc);
err3:
iounmap(regs);
err2:
release_mem_region(res->start, resource_size(res));
err1: err1:
kfree(dwc->mem); dwc3_core_exit(dwc);
err0:
return ret; return ret;
} }
...@@ -590,9 +585,6 @@ static int __devexit dwc3_remove(struct platform_device *pdev) ...@@ -590,9 +585,6 @@ static int __devexit dwc3_remove(struct platform_device *pdev)
} }
dwc3_core_exit(dwc); dwc3_core_exit(dwc);
release_mem_region(res->start, resource_size(res));
iounmap(dwc->regs);
kfree(dwc->mem);
return 0; return 0;
} }
...@@ -605,19 +597,9 @@ static struct platform_driver dwc3_driver = { ...@@ -605,19 +597,9 @@ static struct platform_driver dwc3_driver = {
}, },
}; };
module_platform_driver(dwc3_driver);
MODULE_ALIAS("platform:dwc3"); MODULE_ALIAS("platform:dwc3");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL"); MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
static int __devinit dwc3_init(void)
{
return platform_driver_register(&dwc3_driver);
}
module_init(dwc3_init);
static void __exit dwc3_exit(void)
{
platform_driver_unregister(&dwc3_driver);
}
module_exit(dwc3_exit);
...@@ -145,22 +145,23 @@ ...@@ -145,22 +145,23 @@
/* Bit fields */ /* Bit fields */
/* Global Configuration Register */ /* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) (n << 19) #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_U2RSTECN (1 << 16) #define DWC3_GCTL_U2RSTECN (1 << 16)
#define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6) #define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0) #define DWC3_GCTL_CLK_BUS (0)
#define DWC3_GCTL_CLK_PIPE (1) #define DWC3_GCTL_CLK_PIPE (1)
#define DWC3_GCTL_CLK_PIPEHALF (2) #define DWC3_GCTL_CLK_PIPEHALF (2)
#define DWC3_GCTL_CLK_MASK (3) #define DWC3_GCTL_CLK_MASK (3)
#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) #define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12)
#define DWC3_GCTL_PRTCAPDIR(n) (n << 12) #define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12)
#define DWC3_GCTL_PRTCAP_HOST 1 #define DWC3_GCTL_PRTCAP_HOST 1
#define DWC3_GCTL_PRTCAP_DEVICE 2 #define DWC3_GCTL_PRTCAP_DEVICE 2
#define DWC3_GCTL_PRTCAP_OTG 3 #define DWC3_GCTL_PRTCAP_OTG 3
#define DWC3_GCTL_CORESOFTRESET (1 << 11) #define DWC3_GCTL_CORESOFTRESET (1 << 11)
#define DWC3_GCTL_SCALEDOWN(n) (n << 4) #define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
#define DWC3_GCTL_DISSCRAMBLE (1 << 3) #define DWC3_GCTL_DISSCRAMBLE (1 << 3)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) #define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
...@@ -172,8 +173,12 @@ ...@@ -172,8 +173,12 @@
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) #define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
/* Global TX Fifo Size Register */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
/* Global HWPARAMS1 Register */ /* Global HWPARAMS1 Register */
#define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
...@@ -198,6 +203,15 @@ ...@@ -198,6 +203,15 @@
#define DWC3_DCTL_APPL1RES (1 << 23) #define DWC3_DCTL_APPL1RES (1 << 23)
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
#define DWC3_DCTL_INITU2ENA (1 << 12) #define DWC3_DCTL_INITU2ENA (1 << 12)
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11) #define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
#define DWC3_DCTL_INITU1ENA (1 << 10) #define DWC3_DCTL_INITU1ENA (1 << 10)
...@@ -260,10 +274,10 @@ ...@@ -260,10 +274,10 @@
/* Device Endpoint Command Register */ /* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16 #define DWC3_DEPCMD_PARAM_SHIFT 16
#define DWC3_DEPCMD_PARAM(x) (x << DWC3_DEPCMD_PARAM_SHIFT) #define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
#define DWC3_DEPCMD_GET_RSC_IDX(x) ((x >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) #define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12) #define DWC3_DEPCMD_STATUS_MASK (0x0f << 12)
#define DWC3_DEPCMD_STATUS(x) ((x & DWC3_DEPCMD_STATUS_MASK) >> 12) #define DWC3_DEPCMD_STATUS(x) (((x) & DWC3_DEPCMD_STATUS_MASK) >> 12)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) #define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10) #define DWC3_DEPCMD_CMDACT (1 << 10)
#define DWC3_DEPCMD_CMDIOC (1 << 8) #define DWC3_DEPCMD_CMDIOC (1 << 8)
...@@ -288,7 +302,7 @@ ...@@ -288,7 +302,7 @@
/* Structures */ /* Structures */
struct dwc3_trb_hw; struct dwc3_trb;
/** /**
* struct dwc3_event_buffer - Software event buffer representation * struct dwc3_event_buffer - Software event buffer representation
...@@ -343,7 +357,7 @@ struct dwc3_ep { ...@@ -343,7 +357,7 @@ struct dwc3_ep {
struct list_head request_list; struct list_head request_list;
struct list_head req_queued; struct list_head req_queued;
struct dwc3_trb_hw *trb_pool; struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma; dma_addr_t trb_pool_dma;
u32 free_slot; u32 free_slot;
u32 busy_slot; u32 busy_slot;
...@@ -418,102 +432,49 @@ enum dwc3_device_state { ...@@ -418,102 +432,49 @@ enum dwc3_device_state {
DWC3_CONFIGURED_STATE, DWC3_CONFIGURED_STATE,
}; };
/** /* TRB Length, PCM and Status */
* struct dwc3_trb - transfer request block #define DWC3_TRB_SIZE_MASK (0x00ffffff)
* @bpl: lower 32bit of the buffer #define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
* @bph: higher 32bit of the buffer #define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24)
* @length: buffer size (up to 16mb - 1) #define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28) >> 28))
* @pcm1: packet count m1
* @trbsts: trb status #define DWC3_TRBSTS_OK 0
* 0 = ok #define DWC3_TRBSTS_MISSED_ISOC 1
* 1 = missed isoc #define DWC3_TRBSTS_SETUP_PENDING 2
* 2 = setup pending
* @hwo: hardware owner of descriptor /* TRB Control */
* @lst: last trb #define DWC3_TRB_CTRL_HWO (1 << 0)
* @chn: chain buffers #define DWC3_TRB_CTRL_LST (1 << 1)
* @csp: continue on short packets (only supported on isoc eps) #define DWC3_TRB_CTRL_CHN (1 << 2)
* @trbctl: trb control #define DWC3_TRB_CTRL_CSP (1 << 3)
* 1 = normal #define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4)
* 2 = control-setup #define DWC3_TRB_CTRL_ISP_IMI (1 << 10)
* 3 = control-status-2 #define DWC3_TRB_CTRL_IOC (1 << 11)
* 4 = control-status-3 #define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
* 5 = control-data (first trb of data stage)
* 6 = isochronous-first (first trb of service interval) #define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
* 7 = isochronous #define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2)
* 8 = link trb #define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3)
* others = reserved #define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4)
* @isp_imi: interrupt on short packet / interrupt on missed isoc #define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5)
* @ioc: interrupt on complete #define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6)
* @sid_sofn: Stream ID / SOF Number #define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7)
*/ #define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8)
struct dwc3_trb {
u64 bplh;
union {
struct {
u32 length:24;
u32 pcm1:2;
u32 reserved27_26:2;
u32 trbsts:4;
#define DWC3_TRB_STS_OKAY 0
#define DWC3_TRB_STS_MISSED_ISOC 1
#define DWC3_TRB_STS_SETUP_PENDING 2
};
u32 len_pcm;
};
union {
struct {
u32 hwo:1;
u32 lst:1;
u32 chn:1;
u32 csp:1;
u32 trbctl:6;
u32 isp_imi:1;
u32 ioc:1;
u32 reserved13_12:2;
u32 sid_sofn:16;
u32 reserved31_30:2;
};
u32 control;
};
} __packed;
/** /**
* struct dwc3_trb_hw - transfer request block (hw format) * struct dwc3_trb - transfer request block (hw format)
* @bpl: DW0-3 * @bpl: DW0-3
* @bph: DW4-7 * @bph: DW4-7
* @size: DW8-B * @size: DW8-B
* @trl: DWC-F * @trl: DWC-F
*/ */
struct dwc3_trb_hw { struct dwc3_trb {
__le32 bpl; u32 bpl;
__le32 bph; u32 bph;
__le32 size; u32 size;
__le32 ctrl; u32 ctrl;
} __packed; } __packed;
static inline void dwc3_trb_to_hw(struct dwc3_trb *nat, struct dwc3_trb_hw *hw)
{
hw->bpl = cpu_to_le32(lower_32_bits(nat->bplh));
hw->bph = cpu_to_le32(upper_32_bits(nat->bplh));
hw->size = cpu_to_le32p(&nat->len_pcm);
/* HWO is written last */
hw->ctrl = cpu_to_le32p(&nat->control);
}
static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat)
{
u64 bplh;
bplh = le32_to_cpup(&hw->bpl);
bplh |= (u64) le32_to_cpup(&hw->bph) << 32;
nat->bplh = bplh;
nat->len_pcm = le32_to_cpup(&hw->size);
nat->control = le32_to_cpup(&hw->ctrl);
}
/** /**
* dwc3_hwparams - copy of HWPARAMS registers * dwc3_hwparams - copy of HWPARAMS registers
* @hwparams0 - GHWPARAMS0 * @hwparams0 - GHWPARAMS0
...@@ -546,16 +507,21 @@ struct dwc3_hwparams { ...@@ -546,16 +507,21 @@ struct dwc3_hwparams {
#define DWC3_MODE_DRD 2 #define DWC3_MODE_DRD 2
#define DWC3_MODE_HUB 3 #define DWC3_MODE_HUB 3
#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8)
/* HWPARAMS1 */ /* HWPARAMS1 */
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
/* HWPARAMS7 */
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
struct dwc3_request { struct dwc3_request {
struct usb_request request; struct usb_request request;
struct list_head list; struct list_head list;
struct dwc3_ep *dep; struct dwc3_ep *dep;
u8 epnum; u8 epnum;
struct dwc3_trb_hw *trb; struct dwc3_trb *trb;
dma_addr_t trb_dma; dma_addr_t trb_dma;
unsigned direction:1; unsigned direction:1;
...@@ -572,7 +538,6 @@ struct dwc3_request { ...@@ -572,7 +538,6 @@ struct dwc3_request {
* @ctrl_req_addr: dma address of ctrl_req * @ctrl_req_addr: dma address of ctrl_req
* @ep0_trb: dma address of ep0_trb * @ep0_trb: dma address of ep0_trb
* @ep0_usb_req: dummy req used while handling STD USB requests * @ep0_usb_req: dummy req used while handling STD USB requests
* @setup_buf_addr: dma address of setup_buf
* @ep0_bounce_addr: dma address of ep0_bounce * @ep0_bounce_addr: dma address of ep0_bounce
* @lock: for synchronizing * @lock: for synchronizing
* @dev: pointer to our struct device * @dev: pointer to our struct device
...@@ -594,6 +559,8 @@ struct dwc3_request { ...@@ -594,6 +559,8 @@ struct dwc3_request {
* @ep0_expect_in: true when we expect a DATA IN transfer * @ep0_expect_in: true when we expect a DATA IN transfer
* @start_config_issued: true when StartConfig command has been issued * @start_config_issued: true when StartConfig command has been issued
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @ep0_next_event: hold the next expected event * @ep0_next_event: hold the next expected event
* @ep0state: state of endpoint zero * @ep0state: state of endpoint zero
* @link_state: link state * @link_state: link state
...@@ -604,12 +571,11 @@ struct dwc3_request { ...@@ -604,12 +571,11 @@ struct dwc3_request {
*/ */
struct dwc3 { struct dwc3 {
struct usb_ctrlrequest *ctrl_req; struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb_hw *ep0_trb; struct dwc3_trb *ep0_trb;
void *ep0_bounce; void *ep0_bounce;
u8 *setup_buf; u8 *setup_buf;
dma_addr_t ctrl_req_addr; dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr; dma_addr_t ep0_trb_addr;
dma_addr_t setup_buf_addr;
dma_addr_t ep0_bounce_addr; dma_addr_t ep0_bounce_addr;
struct dwc3_request ep0_usb_req; struct dwc3_request ep0_usb_req;
/* device lock */ /* device lock */
...@@ -651,6 +617,8 @@ struct dwc3 { ...@@ -651,6 +617,8 @@ struct dwc3 {
unsigned start_config_issued:1; unsigned start_config_issued:1;
unsigned setup_packet_pending:1; unsigned setup_packet_pending:1;
unsigned delayed_status:1; unsigned delayed_status:1;
unsigned needs_fifo_resize:1;
unsigned resize_fifos:1;
enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state; enum dwc3_ep0_state ep0state;
...@@ -662,23 +630,13 @@ struct dwc3 { ...@@ -662,23 +630,13 @@ struct dwc3 {
struct dwc3_hwparams hwparams; struct dwc3_hwparams hwparams;
struct dentry *root; struct dentry *root;
u8 test_mode;
u8 test_mode_nr;
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
#define DWC3_TRBSTS_OK 0
#define DWC3_TRBSTS_MISSED_ISOC 1
#define DWC3_TRBSTS_SETUP_PENDING 2
#define DWC3_TRBCTL_NORMAL 1
#define DWC3_TRBCTL_CONTROL_SETUP 2
#define DWC3_TRBCTL_CONTROL_STATUS2 3
#define DWC3_TRBCTL_CONTROL_STATUS3 4
#define DWC3_TRBCTL_CONTROL_DATA 5
#define DWC3_TRBCTL_ISOCHRONOUS_FIRST 6
#define DWC3_TRBCTL_ISOCHRONOUS 7
#define DWC3_TRBCTL_LINK_TRB 8
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
struct dwc3_event_type { struct dwc3_event_type {
...@@ -719,6 +677,11 @@ struct dwc3_event_depevt { ...@@ -719,6 +677,11 @@ struct dwc3_event_depevt {
u32 endpoint_event:4; u32 endpoint_event:4;
u32 reserved11_10:2; u32 reserved11_10:2;
u32 status:4; u32 status:4;
/* Within XferNotReady */
#define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3)
/* Within XferComplete */
#define DEPEVT_STATUS_BUSERR (1 << 0) #define DEPEVT_STATUS_BUSERR (1 << 0)
#define DEPEVT_STATUS_SHORT (1 << 1) #define DEPEVT_STATUS_SHORT (1 << 1)
#define DEPEVT_STATUS_IOC (1 << 2) #define DEPEVT_STATUS_IOC (1 << 2)
...@@ -807,6 +770,7 @@ union dwc3_event { ...@@ -807,6 +770,7 @@ union dwc3_event {
/* prototypes */ /* prototypes */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode); void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
int dwc3_host_init(struct dwc3 *dwc); int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc);
......
...@@ -46,6 +46,8 @@ ...@@ -46,6 +46,8 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/usb/ch9.h>
#include "core.h" #include "core.h"
#include "gadget.h" #include "gadget.h"
#include "io.h" #include "io.h"
...@@ -464,6 +466,192 @@ static const struct file_operations dwc3_mode_fops = { ...@@ -464,6 +466,192 @@ static const struct file_operations dwc3_mode_fops = {
.release = single_release, .release = single_release,
}; };
static int dwc3_testmode_show(struct seq_file *s, void *unused)
{
struct dwc3 *dwc = s->private;
unsigned long flags;
u32 reg;
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= DWC3_DCTL_TSTCTRL_MASK;
reg >>= 1;
spin_unlock_irqrestore(&dwc->lock, flags);
switch (reg) {
case 0:
seq_printf(s, "no test\n");
break;
case TEST_J:
seq_printf(s, "test_j\n");
break;
case TEST_K:
seq_printf(s, "test_k\n");
break;
case TEST_SE0_NAK:
seq_printf(s, "test_se0_nak\n");
break;
case TEST_PACKET:
seq_printf(s, "test_packet\n");
break;
case TEST_FORCE_EN:
seq_printf(s, "test_force_enable\n");
break;
default:
seq_printf(s, "UNKNOWN %d\n", reg);
}
return 0;
}
static int dwc3_testmode_open(struct inode *inode, struct file *file)
{
return single_open(file, dwc3_testmode_show, inode->i_private);
}
static ssize_t dwc3_testmode_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct dwc3 *dwc = s->private;
unsigned long flags;
u32 testmode = 0;
char buf[32];
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "test_j", 6))
testmode = TEST_J;
else if (!strncmp(buf, "test_k", 6))
testmode = TEST_K;
else if (!strncmp(buf, "test_se0_nak", 12))
testmode = TEST_SE0_NAK;
else if (!strncmp(buf, "test_packet", 11))
testmode = TEST_PACKET;
else if (!strncmp(buf, "test_force_enable", 17))
testmode = TEST_FORCE_EN;
else
testmode = 0;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_set_test_mode(dwc, testmode);
spin_unlock_irqrestore(&dwc->lock, flags);
return count;
}
static const struct file_operations dwc3_testmode_fops = {
.open = dwc3_testmode_open,
.write = dwc3_testmode_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int dwc3_link_state_show(struct seq_file *s, void *unused)
{
struct dwc3 *dwc = s->private;
unsigned long flags;
enum dwc3_link_state state;
u32 reg;
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
state = DWC3_DSTS_USBLNKST(reg);
spin_unlock_irqrestore(&dwc->lock, flags);
switch (state) {
case DWC3_LINK_STATE_U0:
seq_printf(s, "U0\n");
break;
case DWC3_LINK_STATE_U1:
seq_printf(s, "U1\n");
break;
case DWC3_LINK_STATE_U2:
seq_printf(s, "U2\n");
break;
case DWC3_LINK_STATE_U3:
seq_printf(s, "U3\n");
break;
case DWC3_LINK_STATE_SS_DIS:
seq_printf(s, "SS.Disabled\n");
break;
case DWC3_LINK_STATE_RX_DET:
seq_printf(s, "Rx.Detect\n");
break;
case DWC3_LINK_STATE_SS_INACT:
seq_printf(s, "SS.Inactive\n");
break;
case DWC3_LINK_STATE_POLL:
seq_printf(s, "Poll\n");
break;
case DWC3_LINK_STATE_RECOV:
seq_printf(s, "Recovery\n");
break;
case DWC3_LINK_STATE_HRESET:
seq_printf(s, "HRESET\n");
break;
case DWC3_LINK_STATE_CMPLY:
seq_printf(s, "Compliance\n");
break;
case DWC3_LINK_STATE_LPBK:
seq_printf(s, "Loopback\n");
break;
default:
seq_printf(s, "UNKNOWN %d\n", reg);
}
return 0;
}
static int dwc3_link_state_open(struct inode *inode, struct file *file)
{
return single_open(file, dwc3_link_state_show, inode->i_private);
}
static ssize_t dwc3_link_state_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct dwc3 *dwc = s->private;
unsigned long flags;
enum dwc3_link_state state = 0;
char buf[32];
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "SS.Disabled", 11))
state = DWC3_LINK_STATE_SS_DIS;
else if (!strncmp(buf, "Rx.Detect", 9))
state = DWC3_LINK_STATE_RX_DET;
else if (!strncmp(buf, "SS.Inactive", 11))
state = DWC3_LINK_STATE_SS_INACT;
else if (!strncmp(buf, "Recovery", 8))
state = DWC3_LINK_STATE_RECOV;
else if (!strncmp(buf, "Compliance", 10))
state = DWC3_LINK_STATE_CMPLY;
else if (!strncmp(buf, "Loopback", 8))
state = DWC3_LINK_STATE_LPBK;
else
return -EINVAL;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_set_link_state(dwc, state);
spin_unlock_irqrestore(&dwc->lock, flags);
return count;
}
static const struct file_operations dwc3_link_state_fops = {
.open = dwc3_link_state_open,
.write = dwc3_link_state_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
int __devinit dwc3_debugfs_init(struct dwc3 *dwc) int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
{ {
struct dentry *root; struct dentry *root;
...@@ -471,8 +659,8 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc) ...@@ -471,8 +659,8 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
int ret; int ret;
root = debugfs_create_dir(dev_name(dwc->dev), NULL); root = debugfs_create_dir(dev_name(dwc->dev), NULL);
if (IS_ERR(root)) { if (!root) {
ret = PTR_ERR(root); ret = -ENOMEM;
goto err0; goto err0;
} }
...@@ -480,15 +668,29 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc) ...@@ -480,15 +668,29 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
file = debugfs_create_file("regdump", S_IRUGO, root, dwc, file = debugfs_create_file("regdump", S_IRUGO, root, dwc,
&dwc3_regdump_fops); &dwc3_regdump_fops);
if (IS_ERR(file)) { if (!file) {
ret = PTR_ERR(file); ret = -ENOMEM;
goto err1; goto err1;
} }
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
dwc, &dwc3_mode_fops); dwc, &dwc3_mode_fops);
if (IS_ERR(file)) { if (!file) {
ret = PTR_ERR(file); ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
dwc, &dwc3_testmode_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
dwc, &dwc3_link_state_fops);
if (!file) {
ret = -ENOMEM;
goto err1; goto err1;
} }
......
/**
* dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Anton Tikhomirov <av.tikhomirov@samsung.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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/clk.h>
#include "core.h"
struct dwc3_exynos {
struct platform_device *dwc3;
struct device *dev;
struct clk *clk;
};
static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
{
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
struct platform_device *dwc3;
struct dwc3_exynos *exynos;
struct clk *clk;
int devid;
int ret = -ENOMEM;
exynos = kzalloc(sizeof(*exynos), GFP_KERNEL);
if (!exynos) {
dev_err(&pdev->dev, "not enough memory\n");
goto err0;
}
platform_set_drvdata(pdev, exynos);
devid = dwc3_get_device_id();
if (devid < 0)
goto err1;
dwc3 = platform_device_alloc("dwc3", devid);
if (!dwc3) {
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
goto err2;
}
clk = clk_get(&pdev->dev, "usbdrd30");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "couldn't get clock\n");
ret = -EINVAL;
goto err3;
}
dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
dwc3->dev.parent = &pdev->dev;
dwc3->dev.dma_mask = pdev->dev.dma_mask;
dwc3->dev.dma_parms = pdev->dev.dma_parms;
exynos->dwc3 = dwc3;
exynos->dev = &pdev->dev;
exynos->clk = clk;
clk_enable(exynos->clk);
/* PHY initialization */
if (!pdata) {
dev_dbg(&pdev->dev, "missing platform data\n");
} else {
if (pdata->phy_init)
pdata->phy_init(pdev, pdata->phy_type);
}
ret = platform_device_add_resources(dwc3, pdev->resource,
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
goto err4;
}
ret = platform_device_add(dwc3);
if (ret) {
dev_err(&pdev->dev, "failed to register dwc3 device\n");
goto err4;
}
return 0;
err4:
if (pdata && pdata->phy_exit)
pdata->phy_exit(pdev, pdata->phy_type);
clk_disable(clk);
clk_put(clk);
err3:
platform_device_put(dwc3);
err2:
dwc3_put_device_id(devid);
err1:
kfree(exynos);
err0:
return ret;
}
static int __devexit dwc3_exynos_remove(struct platform_device *pdev)
{
struct dwc3_exynos *exynos = platform_get_drvdata(pdev);
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
platform_device_unregister(exynos->dwc3);
dwc3_put_device_id(exynos->dwc3->id);
if (pdata && pdata->phy_exit)
pdata->phy_exit(pdev, pdata->phy_type);
clk_disable(exynos->clk);
clk_put(exynos->clk);
kfree(exynos);
return 0;
}
static struct platform_driver dwc3_exynos_driver = {
.probe = dwc3_exynos_probe,
.remove = __devexit_p(dwc3_exynos_remove),
.driver = {
.name = "exynos-dwc3",
},
};
module_platform_driver(dwc3_exynos_driver);
MODULE_ALIAS("platform:exynos-dwc3");
MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/of.h>
#include "core.h" #include "core.h"
#include "io.h" #include "io.h"
...@@ -197,80 +197,87 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) ...@@ -197,80 +197,87 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
static int __devinit dwc3_omap_probe(struct platform_device *pdev) static int __devinit dwc3_omap_probe(struct platform_device *pdev)
{ {
struct dwc3_omap_data *pdata = pdev->dev.platform_data; struct dwc3_omap_data *pdata = pdev->dev.platform_data;
struct device_node *node = pdev->dev.of_node;
struct platform_device *dwc3; struct platform_device *dwc3;
struct dwc3_omap *omap; struct dwc3_omap *omap;
struct resource *res; struct resource *res;
struct device *dev = &pdev->dev;
int devid; int devid;
int size;
int ret = -ENOMEM; int ret = -ENOMEM;
int irq; int irq;
const u32 *utmi_mode;
u32 reg; u32 reg;
void __iomem *base; void __iomem *base;
void *context; void *context;
omap = kzalloc(sizeof(*omap), GFP_KERNEL); omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
if (!omap) { if (!omap) {
dev_err(&pdev->dev, "not enough memory\n"); dev_err(dev, "not enough memory\n");
goto err0; return -ENOMEM;
} }
platform_set_drvdata(pdev, omap); platform_set_drvdata(pdev, omap);
irq = platform_get_irq(pdev, 1); irq = platform_get_irq(pdev, 1);
if (irq < 0) { if (irq < 0) {
dev_err(&pdev->dev, "missing IRQ resource\n"); dev_err(dev, "missing IRQ resource\n");
ret = -EINVAL; return -EINVAL;
goto err1;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) { if (!res) {
dev_err(&pdev->dev, "missing memory base resource\n"); dev_err(dev, "missing memory base resource\n");
ret = -EINVAL; return -EINVAL;
goto err1;
} }
base = ioremap_nocache(res->start, resource_size(res)); base = devm_ioremap_nocache(dev, res->start, resource_size(res));
if (!base) { if (!base) {
dev_err(&pdev->dev, "ioremap failed\n"); dev_err(dev, "ioremap failed\n");
goto err1; return -ENOMEM;
} }
devid = dwc3_get_device_id(); devid = dwc3_get_device_id();
if (devid < 0) if (devid < 0)
goto err2; return -ENODEV;
dwc3 = platform_device_alloc("dwc3", devid); dwc3 = platform_device_alloc("dwc3", devid);
if (!dwc3) { if (!dwc3) {
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); dev_err(dev, "couldn't allocate dwc3 device\n");
goto err3; goto err1;
} }
context = kzalloc(resource_size(res), GFP_KERNEL); context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
if (!context) { if (!context) {
dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n"); dev_err(dev, "couldn't allocate dwc3 context memory\n");
goto err4; goto err2;
} }
spin_lock_init(&omap->lock); spin_lock_init(&omap->lock);
dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask); dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
dwc3->dev.parent = &pdev->dev; dwc3->dev.parent = dev;
dwc3->dev.dma_mask = pdev->dev.dma_mask; dwc3->dev.dma_mask = dev->dma_mask;
dwc3->dev.dma_parms = pdev->dev.dma_parms; dwc3->dev.dma_parms = dev->dma_parms;
omap->resource_size = resource_size(res); omap->resource_size = resource_size(res);
omap->context = context; omap->context = context;
omap->dev = &pdev->dev; omap->dev = dev;
omap->irq = irq; omap->irq = irq;
omap->base = base; omap->base = base;
omap->dwc3 = dwc3; omap->dwc3 = dwc3;
reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
utmi_mode = of_get_property(node, "utmi-mode", &size);
if (utmi_mode && size == sizeof(*utmi_mode)) {
reg |= *utmi_mode;
} else {
if (!pdata) { if (!pdata) {
dev_dbg(&pdev->dev, "missing platform data\n"); dev_dbg(dev, "missing platform data\n");
} else { } else {
switch (pdata->utmi_mode) { switch (pdata->utmi_mode) {
case DWC3_OMAP_UTMI_MODE_SW: case DWC3_OMAP_UTMI_MODE_SW:
...@@ -280,10 +287,11 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) ...@@ -280,10 +287,11 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
break; break;
default: default:
dev_dbg(&pdev->dev, "UNKNOWN utmi mode %d\n", dev_dbg(dev, "UNKNOWN utmi mode %d\n",
pdata->utmi_mode); pdata->utmi_mode);
} }
} }
}
dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg); dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
...@@ -300,12 +308,12 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) ...@@ -300,12 +308,12 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg); dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
ret = request_irq(omap->irq, dwc3_omap_interrupt, 0, ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
"dwc3-omap", omap); "dwc3-omap", omap);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n", dev_err(dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret); omap->irq, ret);
goto err5; goto err2;
} }
/* enable all IRQs */ /* enable all IRQs */
...@@ -327,37 +335,24 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) ...@@ -327,37 +335,24 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
ret = platform_device_add_resources(dwc3, pdev->resource, ret = platform_device_add_resources(dwc3, pdev->resource,
pdev->num_resources); pdev->num_resources);
if (ret) { if (ret) {
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); dev_err(dev, "couldn't add resources to dwc3 device\n");
goto err6; goto err2;
} }
ret = platform_device_add(dwc3); ret = platform_device_add(dwc3);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register dwc3 device\n"); dev_err(dev, "failed to register dwc3 device\n");
goto err6; goto err2;
} }
return 0; return 0;
err6:
free_irq(omap->irq, omap);
err5:
kfree(omap->context);
err4:
platform_device_put(dwc3);
err3:
dwc3_put_device_id(devid);
err2: err2:
iounmap(base); platform_device_put(dwc3);
err1: err1:
kfree(omap); dwc3_put_device_id(devid);
err0:
return ret; return ret;
} }
...@@ -368,11 +363,6 @@ static int __devexit dwc3_omap_remove(struct platform_device *pdev) ...@@ -368,11 +363,6 @@ static int __devexit dwc3_omap_remove(struct platform_device *pdev)
platform_device_unregister(omap->dwc3); platform_device_unregister(omap->dwc3);
dwc3_put_device_id(omap->dwc3->id); dwc3_put_device_id(omap->dwc3->id);
free_irq(omap->irq, omap);
iounmap(omap->base);
kfree(omap->context);
kfree(omap);
return 0; return 0;
} }
......
...@@ -61,32 +61,35 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, ...@@ -61,32 +61,35 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
struct dwc3_pci *glue; struct dwc3_pci *glue;
int ret = -ENOMEM; int ret = -ENOMEM;
int devid; int devid;
struct device *dev = &pci->dev;
glue = kzalloc(sizeof(*glue), GFP_KERNEL); glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
if (!glue) { if (!glue) {
dev_err(&pci->dev, "not enough memory\n"); dev_err(dev, "not enough memory\n");
goto err0; return -ENOMEM;
} }
glue->dev = &pci->dev; glue->dev = dev;
ret = pci_enable_device(pci); ret = pci_enable_device(pci);
if (ret) { if (ret) {
dev_err(&pci->dev, "failed to enable pci device\n"); dev_err(dev, "failed to enable pci device\n");
goto err1; return -ENODEV;
} }
pci_set_power_state(pci, PCI_D0); pci_set_power_state(pci, PCI_D0);
pci_set_master(pci); pci_set_master(pci);
devid = dwc3_get_device_id(); devid = dwc3_get_device_id();
if (devid < 0) if (devid < 0) {
goto err2; ret = -ENOMEM;
goto err1;
}
dwc3 = platform_device_alloc("dwc3", devid); dwc3 = platform_device_alloc("dwc3", devid);
if (!dwc3) { if (!dwc3) {
dev_err(&pci->dev, "couldn't allocate dwc3 device\n"); dev_err(dev, "couldn't allocate dwc3 device\n");
goto err3; goto err1;
} }
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
...@@ -102,41 +105,37 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, ...@@ -102,41 +105,37 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res)); ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
if (ret) { if (ret) {
dev_err(&pci->dev, "couldn't add resources to dwc3 device\n"); dev_err(dev, "couldn't add resources to dwc3 device\n");
goto err4; goto err2;
} }
pci_set_drvdata(pci, glue); pci_set_drvdata(pci, glue);
dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask); dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
dwc3->dev.dma_mask = pci->dev.dma_mask; dwc3->dev.dma_mask = dev->dma_mask;
dwc3->dev.dma_parms = pci->dev.dma_parms; dwc3->dev.dma_parms = dev->dma_parms;
dwc3->dev.parent = &pci->dev; dwc3->dev.parent = dev;
glue->dwc3 = dwc3; glue->dwc3 = dwc3;
ret = platform_device_add(dwc3); ret = platform_device_add(dwc3);
if (ret) { if (ret) {
dev_err(&pci->dev, "failed to register dwc3 device\n"); dev_err(dev, "failed to register dwc3 device\n");
goto err4; goto err3;
} }
return 0; return 0;
err4: err3:
pci_set_drvdata(pci, NULL); pci_set_drvdata(pci, NULL);
platform_device_put(dwc3); platform_device_put(dwc3);
err3:
dwc3_put_device_id(devid);
err2: err2:
pci_disable_device(pci); dwc3_put_device_id(devid);
err1: err1:
kfree(glue); pci_disable_device(pci);
err0:
return ret; return ret;
} }
...@@ -148,7 +147,6 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci) ...@@ -148,7 +147,6 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci)
platform_device_unregister(glue->dwc3); platform_device_unregister(glue->dwc3);
pci_set_drvdata(pci, NULL); pci_set_drvdata(pci, NULL);
pci_disable_device(pci); pci_disable_device(pci);
kfree(glue);
} }
static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
......
...@@ -76,8 +76,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, ...@@ -76,8 +76,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
u32 len, u32 type) u32 len, u32 type)
{ {
struct dwc3_gadget_ep_cmd_params params; struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb_hw *trb_hw; struct dwc3_trb *trb;
struct dwc3_trb trb;
struct dwc3_ep *dep; struct dwc3_ep *dep;
int ret; int ret;
...@@ -88,19 +87,17 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, ...@@ -88,19 +87,17 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
return 0; return 0;
} }
trb_hw = dwc->ep0_trb; trb = dwc->ep0_trb;
memset(&trb, 0, sizeof(trb));
trb.trbctl = type; trb->bpl = lower_32_bits(buf_dma);
trb.bplh = buf_dma; trb->bph = upper_32_bits(buf_dma);
trb.length = len; trb->size = len;
trb->ctrl = type;
trb.hwo = 1; trb->ctrl |= (DWC3_TRB_CTRL_HWO
trb.lst = 1; | DWC3_TRB_CTRL_LST
trb.ioc = 1; | DWC3_TRB_CTRL_IOC
trb.isp_imi = 1; | DWC3_TRB_CTRL_ISP_IMI);
dwc3_trb_to_hw(&trb, trb_hw);
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param0 = upper_32_bits(dwc->ep0_trb_addr);
...@@ -302,7 +299,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, ...@@ -302,7 +299,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
dep = dwc->eps[0]; dep = dwc->eps[0];
dwc->ep0_usb_req.dep = dep; dwc->ep0_usb_req.dep = dep;
dwc->ep0_usb_req.request.length = sizeof(*response_pkt); dwc->ep0_usb_req.request.length = sizeof(*response_pkt);
dwc->ep0_usb_req.request.dma = dwc->setup_buf_addr; dwc->ep0_usb_req.request.buf = dwc->setup_buf;
dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl;
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
...@@ -315,9 +312,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, ...@@ -315,9 +312,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
u32 recip; u32 recip;
u32 wValue; u32 wValue;
u32 wIndex; u32 wIndex;
u32 reg;
int ret; int ret;
u32 mode;
wValue = le16_to_cpu(ctrl->wValue); wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex); wIndex = le16_to_cpu(ctrl->wIndex);
...@@ -356,25 +351,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, ...@@ -356,25 +351,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
if (!set) if (!set)
return -EINVAL; return -EINVAL;
mode = wIndex >> 8; dwc->test_mode_nr = wIndex >> 8;
reg = dwc3_readl(dwc->regs, DWC3_DCTL); dwc->test_mode = true;
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) {
case TEST_J:
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
case TEST_FORCE_EN:
reg |= mode << 1;
break;
default:
return -EINVAL;
}
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
break;
default:
return -EINVAL;
} }
break; break;
...@@ -470,8 +448,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ...@@ -470,8 +448,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case DWC3_ADDRESS_STATE: case DWC3_ADDRESS_STATE:
ret = dwc3_ep0_delegate_req(dwc, ctrl); ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */ /* if the cfg matches and the cfg is non zero */
if (!ret && cfg) if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
dwc->dev_state = DWC3_CONFIGURED_STATE; dwc->dev_state = DWC3_CONFIGURED_STATE;
dwc->resize_fifos = true;
dev_dbg(dwc->dev, "resize fifos flag SET\n");
}
break; break;
case DWC3_CONFIGURED_STATE: case DWC3_CONFIGURED_STATE:
...@@ -560,9 +541,10 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ...@@ -560,9 +541,10 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
{ {
struct dwc3_request *r = NULL; struct dwc3_request *r = NULL;
struct usb_request *ur; struct usb_request *ur;
struct dwc3_trb trb; struct dwc3_trb *trb;
struct dwc3_ep *ep0; struct dwc3_ep *ep0;
u32 transferred; u32 transferred;
u32 length;
u8 epnum; u8 epnum;
epnum = event->endpoint_number; epnum = event->endpoint_number;
...@@ -573,16 +555,16 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ...@@ -573,16 +555,16 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
r = next_request(&ep0->request_list); r = next_request(&ep0->request_list);
ur = &r->request; ur = &r->request;
dwc3_trb_to_nat(dwc->ep0_trb, &trb); trb = dwc->ep0_trb;
length = trb->size & DWC3_TRB_SIZE_MASK;
if (dwc->ep0_bounced) { if (dwc->ep0_bounced) {
transferred = min_t(u32, ur->length, transferred = min_t(u32, ur->length,
ep0->endpoint.maxpacket - trb.length); ep0->endpoint.maxpacket - length);
memcpy(ur->buf, dwc->ep0_bounce, transferred); memcpy(ur->buf, dwc->ep0_bounce, transferred);
dwc->ep0_bounced = false; dwc->ep0_bounced = false;
} else { } else {
transferred = ur->length - trb.length; transferred = ur->length - length;
ur->actual += transferred; ur->actual += transferred;
} }
...@@ -614,6 +596,17 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc, ...@@ -614,6 +596,17 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc,
dwc3_gadget_giveback(dep, r, 0); dwc3_gadget_giveback(dep, r, 0);
} }
if (dwc->test_mode) {
int ret;
ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
if (ret < 0) {
dev_dbg(dwc->dev, "Invalid Test #%d\n",
dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc);
}
}
dwc->ep0state = EP0_SETUP_PHASE; dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc); dwc3_ep0_out_start(dwc);
} }
...@@ -624,6 +617,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, ...@@ -624,6 +617,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
dep->flags &= ~DWC3_EP_BUSY; dep->flags &= ~DWC3_EP_BUSY;
dep->res_trans_idx = 0;
dwc->setup_packet_pending = false; dwc->setup_packet_pending = false;
switch (dwc->ep0state) { switch (dwc->ep0state) {
...@@ -679,7 +673,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, ...@@ -679,7 +673,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
DWC3_TRBCTL_CONTROL_DATA); DWC3_TRBCTL_CONTROL_DATA);
} else if ((req->request.length % dep->endpoint.maxpacket) } else if ((req->request.length % dep->endpoint.maxpacket)
&& (event->endpoint_number == 0)) { && (event->endpoint_number == 0)) {
dwc3_map_buffer_to_dma(req); ret = usb_gadget_map_request(&dwc->gadget, &req->request,
event->endpoint_number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
return;
}
WARN_ON(req->request.length > dep->endpoint.maxpacket); WARN_ON(req->request.length > dep->endpoint.maxpacket);
...@@ -694,7 +693,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, ...@@ -694,7 +693,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc->ep0_bounce_addr, dep->endpoint.maxpacket, dwc->ep0_bounce_addr, dep->endpoint.maxpacket,
DWC3_TRBCTL_CONTROL_DATA); DWC3_TRBCTL_CONTROL_DATA);
} else { } else {
dwc3_map_buffer_to_dma(req); ret = usb_gadget_map_request(&dwc->gadget, &req->request,
event->endpoint_number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
return;
}
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
req->request.dma, req->request.length, req->request.dma, req->request.length,
...@@ -720,6 +724,12 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) ...@@ -720,6 +724,12 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
{ {
struct dwc3_ep *dep = dwc->eps[epnum]; struct dwc3_ep *dep = dwc->eps[epnum];
if (dwc->resize_fifos) {
dev_dbg(dwc->dev, "starting to resize fifos\n");
dwc3_gadget_resize_tx_fifos(dwc);
dwc->resize_fifos = 0;
}
WARN_ON(dwc3_ep0_start_control_status(dep)); WARN_ON(dwc3_ep0_start_control_status(dep));
} }
......
此差异已折叠。
...@@ -100,6 +100,9 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) ...@@ -100,6 +100,9 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status); int status);
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
void dwc3_ep0_interrupt(struct dwc3 *dwc, void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event); const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc); void dwc3_ep0_out_start(struct dwc3 *dwc);
...@@ -108,8 +111,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, ...@@ -108,8 +111,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
void dwc3_map_buffer_to_dma(struct dwc3_request *req);
void dwc3_unmap_buffer_from_dma(struct dwc3_request *req);
/** /**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
......
...@@ -53,7 +53,7 @@ int dwc3_host_init(struct dwc3 *dwc) ...@@ -53,7 +53,7 @@ int dwc3_host_init(struct dwc3 *dwc)
struct platform_device *xhci; struct platform_device *xhci;
int ret; int ret;
xhci = platform_device_alloc("xhci", -1); xhci = platform_device_alloc("xhci-hcd", -1);
if (!xhci) { if (!xhci) {
dev_err(dwc->dev, "couldn't allocate xHCI device\n"); dev_err(dwc->dev, "couldn't allocate xHCI device\n");
ret = -ENOMEM; ret = -ENOMEM;
......
...@@ -599,16 +599,29 @@ config USB_AUDIO ...@@ -599,16 +599,29 @@ config USB_AUDIO
depends on SND depends on SND
select SND_PCM select SND_PCM
help help
Gadget Audio is compatible with USB Audio Class specification 1.0. This Gadget Audio driver is compatible with USB Audio Class
It will include at least one AudioControl interface, zero or more specification 2.0. It implements 1 AudioControl interface,
AudioStream interface and zero or more MIDIStream interface. 1 AudioStreaming Interface each for USB-OUT and USB-IN.
Number of channels, sample rate and sample size can be
Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to specified as module parameters.
playback or capture audio stream. This driver doesn't expect any real Audio codec to be present
on the device - the audio streams are simply sinked to and
sourced from a virtual ALSA sound card created. The user-space
application may choose to do whatever it wants with the data
received from the USB Host and choose to provide whatever it
wants as audio data to the USB Host.
Say "y" to link the driver statically, or "m" to build a Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_audio". dynamically linked module called "g_audio".
config GADGET_UAC1
bool "UAC 1.0 (Legacy)"
depends on USB_AUDIO
help
If you instead want older UAC Spec-1.0 driver that also has audio
paths hardwired to the Audio codec chip on-board and doesn't work
without one.
config USB_ETH config USB_ETH
tristate "Ethernet Gadget (with CDC Ethernet support)" tristate "Ethernet Gadget (with CDC Ethernet support)"
depends on NET depends on NET
......
此差异已折叠。
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/prefetch.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <mach/hardware.h> #include <mach/hardware.h>
...@@ -558,6 +557,7 @@ static int at91_ep_disable (struct usb_ep * _ep) ...@@ -558,6 +557,7 @@ static int at91_ep_disable (struct usb_ep * _ep)
/* restore the endpoint's pristine config */ /* restore the endpoint's pristine config */
ep->desc = NULL; ep->desc = NULL;
ep->ep.desc = NULL;
ep->ep.maxpacket = ep->maxpacket; ep->ep.maxpacket = ep->maxpacket;
/* reset fifos and endpoint */ /* reset fifos and endpoint */
......
...@@ -659,6 +659,7 @@ static int usba_ep_disable(struct usb_ep *_ep) ...@@ -659,6 +659,7 @@ static int usba_ep_disable(struct usb_ep *_ep)
return -EINVAL; return -EINVAL;
} }
ep->desc = NULL; ep->desc = NULL;
ep->ep.desc = NULL;
list_splice_init(&ep->queue, &req_list); list_splice_init(&ep->queue, &req_list);
if (ep->can_dma) { if (ep->can_dma) {
......
...@@ -14,10 +14,8 @@ ...@@ -14,10 +14,8 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include "u_audio.h"
#define DRIVER_DESC "Linux USB Audio Gadget" #define DRIVER_DESC "Linux USB Audio Gadget"
#define DRIVER_VERSION "Dec 18, 2008" #define DRIVER_VERSION "Feb 2, 2012"
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -33,8 +31,36 @@ ...@@ -33,8 +31,36 @@
#include "config.c" #include "config.c"
#include "epautoconf.c" #include "epautoconf.c"
#include "u_audio.c" /* string IDs are assigned dynamically */
#include "f_audio.c"
#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,
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *audio_strings[] = {
&stringtab_dev,
NULL,
};
#ifdef CONFIG_GADGET_UAC1
#include "u_uac1.h"
#include "u_uac1.c"
#include "f_uac1.c"
#else
#include "f_uac2.c"
#endif
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -54,9 +80,15 @@ static struct usb_device_descriptor device_desc = { ...@@ -54,9 +80,15 @@ static struct usb_device_descriptor device_desc = {
.bcdUSB = __constant_cpu_to_le16(0x200), .bcdUSB = __constant_cpu_to_le16(0x200),
#ifdef CONFIG_GADGET_UAC1
.bDeviceClass = USB_CLASS_PER_INTERFACE, .bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0, .bDeviceSubClass = 0,
.bDeviceProtocol = 0, .bDeviceProtocol = 0,
#else
.bDeviceClass = USB_CLASS_MISC,
.bDeviceSubClass = 0x02,
.bDeviceProtocol = 0x01,
#endif
/* .bMaxPacketSize0 = f(hardware) */ /* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id defaults change according to what configs /* Vendor and product id defaults change according to what configs
...@@ -108,6 +140,9 @@ static struct usb_configuration audio_config_driver = { ...@@ -108,6 +140,9 @@ static struct usb_configuration audio_config_driver = {
.bConfigurationValue = 1, .bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */ /* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER, .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
#ifndef CONFIG_GADGET_UAC1
.unbind = uac2_unbind_config,
#endif
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -157,7 +192,9 @@ static int __init audio_bind(struct usb_composite_dev *cdev) ...@@ -157,7 +192,9 @@ static int __init audio_bind(struct usb_composite_dev *cdev)
static int __exit audio_unbind(struct usb_composite_dev *cdev) static int __exit audio_unbind(struct usb_composite_dev *cdev)
{ {
#ifdef CONFIG_GADGET_UAC1
gaudio_cleanup(); gaudio_cleanup();
#endif
return 0; return 0;
} }
......
...@@ -37,10 +37,10 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) ...@@ -37,10 +37,10 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
* Put the transceiver in non-driving mode. Otherwise host * Put the transceiver in non-driving mode. Otherwise host
* may not detect soft-disconnection. * may not detect soft-disconnection.
*/ */
val = otg_io_read(udc->transceiver, ULPI_FUNC_CTRL); val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL);
val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
otg_io_write(udc->transceiver, val, ULPI_FUNC_CTRL); usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
break; break;
default: default:
dev_dbg(dev, "unknown ci13xxx_udc event\n"); dev_dbg(dev, "unknown ci13xxx_udc event\n");
......
此差异已折叠。
...@@ -136,7 +136,7 @@ struct ci13xxx { ...@@ -136,7 +136,7 @@ struct ci13xxx {
struct usb_gadget_driver *driver; /* 3rd party gadget driver */ struct usb_gadget_driver *driver; /* 3rd party gadget driver */
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
int vbus_active; /* is VBUS active */ int vbus_active; /* is VBUS active */
struct otg_transceiver *transceiver; /* Transceiver struct */ struct usb_phy *transceiver; /* Transceiver struct */
}; };
/****************************************************************************** /******************************************************************************
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* f_fs.c -- user mode file system API for USB composite function controllers * f_fs.c -- user mode file system API for USB composite function controllers
* *
* Copyright (C) 2010 Samsung Electronics * Copyright (C) 2010 Samsung Electronics
* Author: Michal Nazarewicz <m.nazarewicz@samsung.com> * Author: Michal Nazarewicz <mina86@mina86.com>
* *
* Based on inode.c (GadgetFS) which was: * Based on inode.c (GadgetFS) which was:
* Copyright (C) 2003-2004 David Brownell * Copyright (C) 2003-2004 David Brownell
......
此差异已折叠。
...@@ -780,7 +780,7 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -780,7 +780,7 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
midi->out_ep->driver_data = cdev; /* claim */ midi->out_ep->driver_data = cdev; /* claim */
/* allocate temporary function list */ /* allocate temporary function list */
midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(midi_function), midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function),
GFP_KERNEL); GFP_KERNEL);
if (!midi_function) { if (!midi_function) {
status = -ENOMEM; status = -ENOMEM;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册