mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 19:51:31 -04:00
USB 3.5-rc1 pull request
Here is the big USB 3.5-rc1 pull request for the 3.5-rc1 merge window. It's touches a lot of different parts of the kernel, all USB drivers, due to some API cleanups (getting rid of the ancient err() macro) and some changes that are needed for USB 3.0 power management updates. There are also lots of new drivers, pimarily gadget, but others as well. We deleted a staging driver, which was nice, and finally dropped the obsolete usbfs code, which will make Al happy to never have to touch that again. There were some build errors in the tree that linux-next found a few days ago, but those were fixed by the most recent changes (all were due to us not building with CONFIG_PM disabled.) Signed-off-by: Greg Kroah-Hartman <[email protected]> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iEYEABECAAYFAk+7qs0ACgkQMUfUDdst+ymjOgCfeoWqWk1bsKKt6SZULvQois5i 3csAn1Uapcm8Uswwpnj2v1/2Zh4rBHLA =4jM1 -----END PGP SIGNATURE----- Merge tag 'usb-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB 3.5-rc1 changes from Greg Kroah-Hartman: "Here is the big USB 3.5-rc1 pull request for the 3.5-rc1 merge window. It's touches a lot of different parts of the kernel, all USB drivers, due to some API cleanups (getting rid of the ancient err() macro) and some changes that are needed for USB 3.0 power management updates. There are also lots of new drivers, pimarily gadget, but others as well. We deleted a staging driver, which was nice, and finally dropped the obsolete usbfs code, which will make Al happy to never have to touch that again. There were some build errors in the tree that linux-next found a few days ago, but those were fixed by the most recent changes (all were due to us not building with CONFIG_PM disabled.) Signed-off-by: Greg Kroah-Hartman <[email protected]>" * tag 'usb-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (477 commits) xhci: Fix DIV_ROUND_UP compile error. xhci: Fix compile with CONFIG_USB_SUSPEND=n USB: Fix core compile with CONFIG_USB_SUSPEND=n brcm80211: Fix compile error for .disable_hub_initiated_lpm. Revert "USB: EHCI: work around bug in the Philips ISP1562 controller" MAINTAINERS: Add myself as maintainer to the USB PHY Layer USB: EHCI: fix command register configuration lost problem USB: Remove races in devio.c USB: ehci-platform: remove update_device USB: Disable hub-initiated LPM for comms devices. xhci: Add Intel U1/U2 timeout policy. xhci: Add infrastructure for host-specific LPM policies. USB: Add macros for interrupt endpoint types. xhci: Reserve one command for USB3 LPM disable. xhci: Some Evaluate Context commands must succeed. USB: Disable USB 3.0 LPM in critical sections. USB: Add support to enable/disable USB3 link states. USB: Allow drivers to disable hub-initiated LPM. USB: Calculate USB 3.0 exit latencies for LPM. USB: Refactor code to set LPM support flag. ... Conflicts: arch/arm/mach-exynos/mach-nuri.c arch/arm/mach-exynos/mach-universal_c210.c drivers/net/wireless/ath/ath6kl/usb.c
This commit is contained in:
commit
a481991467
395 changed files with 19447 additions and 16215 deletions
|
|
@ -133,6 +133,8 @@ source "drivers/usb/host/Kconfig"
|
|||
|
||||
source "drivers/usb/musb/Kconfig"
|
||||
|
||||
source "drivers/usb/chipidea/Kconfig"
|
||||
|
||||
source "drivers/usb/renesas_usbhs/Kconfig"
|
||||
|
||||
source "drivers/usb/class/Kconfig"
|
||||
|
|
@ -177,6 +179,8 @@ source "drivers/usb/serial/Kconfig"
|
|||
|
||||
source "drivers/usb/misc/Kconfig"
|
||||
|
||||
source "drivers/usb/phy/Kconfig"
|
||||
|
||||
source "drivers/usb/atm/Kconfig"
|
||||
|
||||
source "drivers/usb/gadget/Kconfig"
|
||||
|
|
|
|||
|
|
@ -46,12 +46,14 @@ obj-$(CONFIG_USB_MICROTEK) += image/
|
|||
obj-$(CONFIG_USB_SERIAL) += serial/
|
||||
|
||||
obj-$(CONFIG_USB) += misc/
|
||||
obj-$(CONFIG_USB) += phy/
|
||||
obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
|
||||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
|
||||
|
||||
obj-$(CONFIG_USB_MUSB_HDRC) += musb/
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += chipidea/
|
||||
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
|
||||
obj-$(CONFIG_USB_GADGET) += gadget/
|
||||
|
||||
|
|
|
|||
|
|
@ -674,7 +674,7 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
|
|||
}
|
||||
|
||||
ret = offd;
|
||||
dbg("cm %#x", cm);
|
||||
usb_dbg(instance->usbatm, "cm %#x\n", cm);
|
||||
fail:
|
||||
mutex_unlock(&instance->cm_serialize);
|
||||
err:
|
||||
|
|
@ -733,7 +733,7 @@ static int cxacru_card_status(struct cxacru_data *instance)
|
|||
{
|
||||
int ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);
|
||||
if (ret < 0) { /* firmware not loaded */
|
||||
dbg("cxacru_adsl_start: CARD_GET_STATUS returned %d", ret);
|
||||
usb_dbg(instance->usbatm, "cxacru_adsl_start: CARD_GET_STATUS returned %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -758,7 +758,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
|
|||
int ret;
|
||||
int start_polling = 1;
|
||||
|
||||
dbg("cxacru_atm_start");
|
||||
dev_dbg(&intf->dev, "%s\n", __func__);
|
||||
|
||||
/* Read MAC address */
|
||||
ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0,
|
||||
|
|
@ -962,13 +962,13 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
|
|||
ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD),
|
||||
buf, offb, NULL, CMD_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
dbg("sending fw %#x failed", fw);
|
||||
dev_dbg(&usb_dev->dev, "sending fw %#x failed\n", fw);
|
||||
goto cleanup;
|
||||
}
|
||||
offb = 0;
|
||||
}
|
||||
} while (offd < size);
|
||||
dbg("sent fw %#x", fw);
|
||||
dev_dbg(&usb_dev->dev, "sent fw %#x\n", fw);
|
||||
|
||||
ret = 0;
|
||||
|
||||
|
|
@ -988,7 +988,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
|
|||
usb_dev->descriptor.idProduct };
|
||||
__le32 val;
|
||||
|
||||
dbg("cxacru_upload_firmware");
|
||||
usb_dbg(usbatm, "%s\n", __func__);
|
||||
|
||||
/* FirmwarePllFClkValue */
|
||||
val = cpu_to_le32(instance->modem_type->pll_f_clk);
|
||||
|
|
@ -1074,7 +1074,7 @@ static int cxacru_find_firmware(struct cxacru_data *instance,
|
|||
char buf[16];
|
||||
|
||||
sprintf(buf, "cxacru-%s.bin", phase);
|
||||
dbg("cxacru_find_firmware: looking for %s", buf);
|
||||
usb_dbg(usbatm, "cxacru_find_firmware: looking for %s\n", buf);
|
||||
|
||||
if (request_firmware(fw_p, buf, dev)) {
|
||||
usb_dbg(usbatm, "no stage %s firmware found\n", phase);
|
||||
|
|
@ -1115,9 +1115,9 @@ static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
|
|||
|
||||
ret = cxacru_card_status(instance);
|
||||
if (ret)
|
||||
dbg("modem initialisation failed");
|
||||
usb_dbg(usbatm_instance, "modem initialisation failed\n");
|
||||
else
|
||||
dbg("done setting up the modem");
|
||||
usb_dbg(usbatm_instance, "done setting up the modem\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1133,7 +1133,7 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
|
|||
/* instance init */
|
||||
instance = kzalloc(sizeof(*instance), GFP_KERNEL);
|
||||
if (!instance) {
|
||||
dbg("cxacru_bind: no memory for instance data");
|
||||
usb_dbg(usbatm_instance, "cxacru_bind: no memory for instance data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -1149,31 +1149,31 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
|
|||
|
||||
instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
|
||||
if (!instance->rcv_buf) {
|
||||
dbg("cxacru_bind: no memory for rcv_buf");
|
||||
usb_dbg(usbatm_instance, "cxacru_bind: no memory for rcv_buf\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
instance->snd_buf = (u8 *) __get_free_page(GFP_KERNEL);
|
||||
if (!instance->snd_buf) {
|
||||
dbg("cxacru_bind: no memory for snd_buf");
|
||||
usb_dbg(usbatm_instance, "cxacru_bind: no memory for snd_buf\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
instance->rcv_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!instance->rcv_urb) {
|
||||
dbg("cxacru_bind: no memory for rcv_urb");
|
||||
usb_dbg(usbatm_instance, "cxacru_bind: no memory for rcv_urb\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
instance->snd_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!instance->snd_urb) {
|
||||
dbg("cxacru_bind: no memory for snd_urb");
|
||||
usb_dbg(usbatm_instance, "cxacru_bind: no memory for snd_urb\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!cmd_ep) {
|
||||
dbg("cxacru_bind: no command endpoint");
|
||||
usb_dbg(usbatm_instance, "cxacru_bind: no command endpoint\n");
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -1227,10 +1227,10 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
|
|||
struct cxacru_data *instance = usbatm_instance->driver_data;
|
||||
int is_polling = 1;
|
||||
|
||||
dbg("cxacru_unbind entered");
|
||||
usb_dbg(usbatm_instance, "cxacru_unbind entered\n");
|
||||
|
||||
if (!instance) {
|
||||
dbg("cxacru_unbind: NULL instance!");
|
||||
usb_dbg(usbatm_instance, "cxacru_unbind: NULL instance!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int sta
|
|||
"%sabling SW buffering: usb_control_msg returned %d\n",
|
||||
state ? "En" : "Dis", ret);
|
||||
else
|
||||
dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
|
||||
usb_dbg(usbatm, "speedtch_set_swbuff: %sbled SW buffering\n", state ? "En" : "Dis");
|
||||
}
|
||||
|
||||
static void speedtch_test_sequence(struct speedtch_instance_data *instance)
|
||||
|
|
|
|||
|
|
@ -1357,10 +1357,8 @@ static int uea_stat_e1(struct uea_softc *sc)
|
|||
/* release the dsp firmware as it is not needed until
|
||||
* the next failure
|
||||
*/
|
||||
if (sc->dsp_firm) {
|
||||
release_firmware(sc->dsp_firm);
|
||||
sc->dsp_firm = NULL;
|
||||
}
|
||||
release_firmware(sc->dsp_firm);
|
||||
sc->dsp_firm = NULL;
|
||||
}
|
||||
|
||||
/* always update it as atm layer could not be init when we switch to
|
||||
|
|
@ -1496,10 +1494,8 @@ static int uea_stat_e4(struct uea_softc *sc)
|
|||
/* release the dsp firmware as it is not needed until
|
||||
* the next failure
|
||||
*/
|
||||
if (sc->dsp_firm) {
|
||||
release_firmware(sc->dsp_firm);
|
||||
sc->dsp_firm = NULL;
|
||||
}
|
||||
release_firmware(sc->dsp_firm);
|
||||
sc->dsp_firm = NULL;
|
||||
}
|
||||
|
||||
/* always update it as atm layer could not be init when we switch to
|
||||
|
|
@ -2240,8 +2236,7 @@ static void uea_stop(struct uea_softc *sc)
|
|||
/* flush the work item, when no one can schedule it */
|
||||
flush_work_sync(&sc->task);
|
||||
|
||||
if (sc->dsp_firm)
|
||||
release_firmware(sc->dsp_firm);
|
||||
release_firmware(sc->dsp_firm);
|
||||
uea_leaves(INS_TO_USBDEV(sc));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
#ifdef VERBOSE_DEBUG
|
||||
static int usbatm_print_packet(const unsigned char *data, int len);
|
||||
#define PACKETDEBUG(arg...) usbatm_print_packet(arg)
|
||||
#define vdbg(arg...) dbg(arg)
|
||||
#define vdbg(arg...) dev_dbg(arg)
|
||||
#else
|
||||
#define PACKETDEBUG(arg...)
|
||||
#define vdbg(arg...)
|
||||
|
|
@ -714,7 +714,7 @@ static void usbatm_destroy_instance(struct kref *kref)
|
|||
{
|
||||
struct usbatm_data *instance = container_of(kref, struct usbatm_data, refcount);
|
||||
|
||||
dbg("%s", __func__);
|
||||
usb_dbg(instance, "%s\n", __func__);
|
||||
|
||||
tasklet_kill(&instance->rx_channel.tasklet);
|
||||
tasklet_kill(&instance->tx_channel.tasklet);
|
||||
|
|
@ -724,14 +724,14 @@ static void usbatm_destroy_instance(struct kref *kref)
|
|||
|
||||
static void usbatm_get_instance(struct usbatm_data *instance)
|
||||
{
|
||||
dbg("%s", __func__);
|
||||
usb_dbg(instance, "%s\n", __func__);
|
||||
|
||||
kref_get(&instance->refcount);
|
||||
}
|
||||
|
||||
static void usbatm_put_instance(struct usbatm_data *instance)
|
||||
{
|
||||
dbg("%s", __func__);
|
||||
usb_dbg(instance, "%s\n", __func__);
|
||||
|
||||
kref_put(&instance->refcount, usbatm_destroy_instance);
|
||||
}
|
||||
|
|
@ -745,11 +745,10 @@ static void usbatm_atm_dev_close(struct atm_dev *atm_dev)
|
|||
{
|
||||
struct usbatm_data *instance = atm_dev->dev_data;
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
if (!instance)
|
||||
return;
|
||||
|
||||
usb_dbg(instance, "%s\n", __func__);
|
||||
atm_dev->dev_data = NULL; /* catch bugs */
|
||||
usbatm_put_instance(instance); /* taken in usbatm_atm_init */
|
||||
}
|
||||
|
|
@ -759,10 +758,8 @@ static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *pag
|
|||
struct usbatm_data *instance = atm_dev->dev_data;
|
||||
int left = *pos;
|
||||
|
||||
if (!instance) {
|
||||
dbg("%s: NULL instance!", __func__);
|
||||
if (!instance)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!left--)
|
||||
return sprintf(page, "%s\n", instance->description);
|
||||
|
|
@ -804,10 +801,8 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
|
|||
int vci = vcc->vci;
|
||||
short vpi = vcc->vpi;
|
||||
|
||||
if (!instance) {
|
||||
dbg("%s: NULL data!", __func__);
|
||||
if (!instance)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
atm_dbg(instance, "%s: vpi %hd, vci %d\n", __func__, vpi, vci);
|
||||
|
||||
|
|
@ -884,10 +879,8 @@ static void usbatm_atm_close(struct atm_vcc *vcc)
|
|||
struct usbatm_data *instance = vcc->dev->dev_data;
|
||||
struct usbatm_vcc_data *vcc_data = vcc->dev_data;
|
||||
|
||||
if (!instance || !vcc_data) {
|
||||
dbg("%s: NULL data!", __func__);
|
||||
if (!instance || !vcc_data)
|
||||
return;
|
||||
}
|
||||
|
||||
atm_dbg(instance, "%s entered\n", __func__);
|
||||
|
||||
|
|
@ -929,10 +922,8 @@ static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd,
|
|||
{
|
||||
struct usbatm_data *instance = atm_dev->dev_data;
|
||||
|
||||
if (!instance || instance->disconnected) {
|
||||
dbg("%s: %s!", __func__, instance ? "disconnected" : "NULL instance");
|
||||
if (!instance || instance->disconnected)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case ATM_QUERYLOOP:
|
||||
|
|
@ -1336,8 +1327,6 @@ EXPORT_SYMBOL_GPL(usbatm_usb_disconnect);
|
|||
|
||||
static int __init usbatm_usb_init(void)
|
||||
{
|
||||
dbg("%s: driver version %s", __func__, DRIVER_VERSION);
|
||||
|
||||
if (sizeof(struct usbatm_control) > FIELD_SIZEOF(struct sk_buff, cb)) {
|
||||
printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name);
|
||||
return -EIO;
|
||||
|
|
@ -1357,7 +1346,6 @@ module_init(usbatm_usb_init);
|
|||
|
||||
static void __exit usbatm_usb_exit(void)
|
||||
{
|
||||
dbg("%s", __func__);
|
||||
}
|
||||
module_exit(usbatm_usb_exit);
|
||||
|
||||
|
|
|
|||
|
|
@ -187,8 +187,6 @@ static int __init xusbatm_init(void)
|
|||
{
|
||||
int i;
|
||||
|
||||
dbg("xusbatm_init");
|
||||
|
||||
if (!num_vendor ||
|
||||
num_vendor != num_product ||
|
||||
num_vendor != num_rx_endpoint ||
|
||||
|
|
@ -221,8 +219,6 @@ module_init(xusbatm_init);
|
|||
|
||||
static void __exit xusbatm_exit(void)
|
||||
{
|
||||
dbg("xusbatm_exit entered");
|
||||
|
||||
usb_deregister(&xusbatm_usb_driver);
|
||||
}
|
||||
module_exit(xusbatm_exit);
|
||||
|
|
|
|||
32
drivers/usb/chipidea/Kconfig
Normal file
32
drivers/usb/chipidea/Kconfig
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
config USB_CHIPIDEA
|
||||
tristate "ChipIdea Highspeed Dual Role Controller"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if your system has a dual role high speed USB
|
||||
controller based on ChipIdea silicon IP. Currently, only the
|
||||
peripheral mode is supported.
|
||||
|
||||
When compiled dynamically, the module will be called ci-hdrc.ko.
|
||||
|
||||
if USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_UDC
|
||||
bool "ChipIdea device controller"
|
||||
depends on USB_GADGET
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Say Y here to enable device controller functionality of the
|
||||
ChipIdea driver.
|
||||
|
||||
config USB_CHIPIDEA_HOST
|
||||
bool "ChipIdea host controller"
|
||||
help
|
||||
Say Y here to enable host controller functionality of the
|
||||
ChipIdea driver.
|
||||
|
||||
config USB_CHIPIDEA_DEBUG
|
||||
bool "ChipIdea driver debug"
|
||||
help
|
||||
Say Y here to enable debugging output of the ChipIdea driver.
|
||||
|
||||
endif
|
||||
14
drivers/usb/chipidea/Makefile
Normal file
14
drivers/usb/chipidea/Makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
|
||||
|
||||
ci_hdrc-y := core.o
|
||||
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
|
||||
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
|
||||
ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
|
||||
|
||||
ifneq ($(CONFIG_PCI),)
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_ARCH_MSM),)
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_msm.o
|
||||
endif
|
||||
90
drivers/usb/chipidea/bits.h
Normal file
90
drivers/usb/chipidea/bits.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* bits.h - register bits of the ChipIdea USB IP core
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_CHIPIDEA_BITS_H
|
||||
#define __DRIVERS_USB_CHIPIDEA_BITS_H
|
||||
|
||||
#include <linux/usb/ehci_def.h>
|
||||
|
||||
/* HCCPARAMS */
|
||||
#define HCCPARAMS_LEN BIT(17)
|
||||
|
||||
/* DCCPARAMS */
|
||||
#define DCCPARAMS_DEN (0x1F << 0)
|
||||
#define DCCPARAMS_DC BIT(7)
|
||||
#define DCCPARAMS_HC BIT(8)
|
||||
|
||||
/* TESTMODE */
|
||||
#define TESTMODE_FORCE BIT(0)
|
||||
|
||||
/* USBCMD */
|
||||
#define USBCMD_RS BIT(0)
|
||||
#define USBCMD_RST BIT(1)
|
||||
#define USBCMD_SUTW BIT(13)
|
||||
#define USBCMD_ATDTW BIT(14)
|
||||
|
||||
/* USBSTS & USBINTR */
|
||||
#define USBi_UI BIT(0)
|
||||
#define USBi_UEI BIT(1)
|
||||
#define USBi_PCI BIT(2)
|
||||
#define USBi_URI BIT(6)
|
||||
#define USBi_SLI BIT(8)
|
||||
|
||||
/* DEVICEADDR */
|
||||
#define DEVICEADDR_USBADRA BIT(24)
|
||||
#define DEVICEADDR_USBADR (0x7FUL << 25)
|
||||
|
||||
/* PORTSC */
|
||||
#define PORTSC_FPR BIT(6)
|
||||
#define PORTSC_SUSP BIT(7)
|
||||
#define PORTSC_HSP BIT(9)
|
||||
#define PORTSC_PTC (0x0FUL << 16)
|
||||
|
||||
/* DEVLC */
|
||||
#define DEVLC_PSPD (0x03UL << 25)
|
||||
#define DEVLC_PSPD_HS (0x02UL << 25)
|
||||
|
||||
/* OTGSC */
|
||||
#define OTGSC_IDPU BIT(5)
|
||||
#define OTGSC_ID BIT(8)
|
||||
#define OTGSC_AVV BIT(9)
|
||||
#define OTGSC_ASV BIT(10)
|
||||
#define OTGSC_BSV BIT(11)
|
||||
#define OTGSC_BSE BIT(12)
|
||||
#define OTGSC_IDIS BIT(16)
|
||||
#define OTGSC_AVVIS BIT(17)
|
||||
#define OTGSC_ASVIS BIT(18)
|
||||
#define OTGSC_BSVIS BIT(19)
|
||||
#define OTGSC_BSEIS BIT(20)
|
||||
#define OTGSC_IDIE BIT(24)
|
||||
#define OTGSC_AVVIE BIT(25)
|
||||
#define OTGSC_ASVIE BIT(26)
|
||||
#define OTGSC_BSVIE BIT(27)
|
||||
#define OTGSC_BSEIE BIT(28)
|
||||
|
||||
/* USBMODE */
|
||||
#define USBMODE_CM (0x03UL << 0)
|
||||
#define USBMODE_CM_DC (0x02UL << 0)
|
||||
#define USBMODE_SLOM BIT(3)
|
||||
#define USBMODE_CI_SDIS BIT(4)
|
||||
|
||||
/* ENDPTCTRL */
|
||||
#define ENDPTCTRL_RXS BIT(0)
|
||||
#define ENDPTCTRL_RXT (0x03UL << 2)
|
||||
#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */
|
||||
#define ENDPTCTRL_RXE BIT(7)
|
||||
#define ENDPTCTRL_TXS BIT(16)
|
||||
#define ENDPTCTRL_TXT (0x03UL << 18)
|
||||
#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */
|
||||
#define ENDPTCTRL_TXE BIT(23)
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_BITS_H */
|
||||
313
drivers/usb/chipidea/ci.h
Normal file
313
drivers/usb/chipidea/ci.h
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* ci.h - common structures, functions, and macros of the ChipIdea driver
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_CHIPIDEA_CI_H
|
||||
#define __DRIVERS_USB_CHIPIDEA_CI_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/******************************************************************************
|
||||
* DEFINE
|
||||
*****************************************************************************/
|
||||
#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
|
||||
#define ENDPT_MAX 32
|
||||
|
||||
/******************************************************************************
|
||||
* STRUCTURES
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* struct ci13xxx_ep - endpoint representation
|
||||
* @ep: endpoint structure for gadget drivers
|
||||
* @dir: endpoint direction (TX/RX)
|
||||
* @num: endpoint number
|
||||
* @type: endpoint type
|
||||
* @name: string description of the endpoint
|
||||
* @qh: queue head for this endpoint
|
||||
* @wedge: is the endpoint wedged
|
||||
* @udc: pointer to the controller
|
||||
* @lock: pointer to controller's spinlock
|
||||
* @td_pool: pointer to controller's TD pool
|
||||
*/
|
||||
struct ci13xxx_ep {
|
||||
struct usb_ep ep;
|
||||
u8 dir;
|
||||
u8 num;
|
||||
u8 type;
|
||||
char name[16];
|
||||
struct {
|
||||
struct list_head queue;
|
||||
struct ci13xxx_qh *ptr;
|
||||
dma_addr_t dma;
|
||||
} qh;
|
||||
int wedge;
|
||||
|
||||
/* global resources */
|
||||
struct ci13xxx *udc;
|
||||
spinlock_t *lock;
|
||||
struct dma_pool *td_pool;
|
||||
};
|
||||
|
||||
enum ci_role {
|
||||
CI_ROLE_HOST = 0,
|
||||
CI_ROLE_GADGET,
|
||||
CI_ROLE_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ci_role_driver - host/gadget role driver
|
||||
* start: start this role
|
||||
* stop: stop this role
|
||||
* irq: irq handler for this role
|
||||
* name: role name string (host/gadget)
|
||||
*/
|
||||
struct ci_role_driver {
|
||||
int (*start)(struct ci13xxx *);
|
||||
void (*stop)(struct ci13xxx *);
|
||||
irqreturn_t (*irq)(struct ci13xxx *);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hw_bank - hardware register mapping representation
|
||||
* @lpm: set if the device is LPM capable
|
||||
* @phys: physical address of the controller's registers
|
||||
* @abs: absolute address of the beginning of register window
|
||||
* @cap: capability registers
|
||||
* @op: operational registers
|
||||
* @size: size of the register window
|
||||
* @regmap: register lookup table
|
||||
*/
|
||||
struct hw_bank {
|
||||
unsigned lpm;
|
||||
resource_size_t phys;
|
||||
void __iomem *abs;
|
||||
void __iomem *cap;
|
||||
void __iomem *op;
|
||||
size_t size;
|
||||
void __iomem **regmap;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ci13xxx - chipidea device representation
|
||||
* @dev: pointer to parent device
|
||||
* @lock: access synchronization
|
||||
* @hw_bank: hardware register mapping
|
||||
* @irq: IRQ number
|
||||
* @roles: array of supported roles for this controller
|
||||
* @role: current role
|
||||
* @is_otg: if the device is otg-capable
|
||||
* @work: work for role changing
|
||||
* @wq: workqueue thread
|
||||
* @qh_pool: allocation pool for queue heads
|
||||
* @td_pool: allocation pool for transfer descriptors
|
||||
* @gadget: device side representation for peripheral controller
|
||||
* @driver: gadget driver
|
||||
* @hw_ep_max: total number of endpoints supported by hardware
|
||||
* @ci13xxx_ep: array of endpoints
|
||||
* @ep0_dir: ep0 direction
|
||||
* @ep0out: pointer to ep0 OUT endpoint
|
||||
* @ep0in: pointer to ep0 IN endpoint
|
||||
* @status: ep0 status request
|
||||
* @setaddr: if we should set the address on status completion
|
||||
* @address: usb address received from the host
|
||||
* @remote_wakeup: host-enabled remote wakeup
|
||||
* @suspended: suspended by host
|
||||
* @test_mode: the selected test mode
|
||||
* @udc_driver: platform specific information supplied by parent device
|
||||
* @vbus_active: is VBUS active
|
||||
* @transceiver: pointer to USB PHY, if any
|
||||
* @hcd: pointer to usb_hcd for ehci host driver
|
||||
*/
|
||||
struct ci13xxx {
|
||||
struct device *dev;
|
||||
spinlock_t lock;
|
||||
struct hw_bank hw_bank;
|
||||
int irq;
|
||||
struct ci_role_driver *roles[CI_ROLE_END];
|
||||
enum ci_role role;
|
||||
bool is_otg;
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
struct dma_pool *qh_pool;
|
||||
struct dma_pool *td_pool;
|
||||
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
unsigned hw_ep_max;
|
||||
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX];
|
||||
u32 ep0_dir;
|
||||
struct ci13xxx_ep *ep0out, *ep0in;
|
||||
|
||||
struct usb_request *status;
|
||||
bool setaddr;
|
||||
u8 address;
|
||||
u8 remote_wakeup;
|
||||
u8 suspended;
|
||||
u8 test_mode;
|
||||
|
||||
struct ci13xxx_udc_driver *udc_driver;
|
||||
int vbus_active;
|
||||
struct usb_phy *transceiver;
|
||||
struct usb_hcd *hcd;
|
||||
};
|
||||
|
||||
static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
|
||||
{
|
||||
BUG_ON(ci->role >= CI_ROLE_END || !ci->roles[ci->role]);
|
||||
return ci->roles[ci->role];
|
||||
}
|
||||
|
||||
static inline int ci_role_start(struct ci13xxx *ci, enum ci_role role)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (role >= CI_ROLE_END)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ci->roles[role])
|
||||
return -ENXIO;
|
||||
|
||||
ret = ci->roles[role]->start(ci);
|
||||
if (!ret)
|
||||
ci->role = role;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void ci_role_stop(struct ci13xxx *ci)
|
||||
{
|
||||
enum ci_role role = ci->role;
|
||||
|
||||
if (role == CI_ROLE_END)
|
||||
return;
|
||||
|
||||
ci->role = CI_ROLE_END;
|
||||
|
||||
ci->roles[role]->stop(ci);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* REGISTERS
|
||||
*****************************************************************************/
|
||||
/* register size */
|
||||
#define REG_BITS (32)
|
||||
|
||||
/* register indices */
|
||||
enum ci13xxx_regs {
|
||||
CAP_CAPLENGTH,
|
||||
CAP_HCCPARAMS,
|
||||
CAP_DCCPARAMS,
|
||||
CAP_TESTMODE,
|
||||
CAP_LAST = CAP_TESTMODE,
|
||||
OP_USBCMD,
|
||||
OP_USBSTS,
|
||||
OP_USBINTR,
|
||||
OP_DEVICEADDR,
|
||||
OP_ENDPTLISTADDR,
|
||||
OP_PORTSC,
|
||||
OP_DEVLC,
|
||||
OP_OTGSC,
|
||||
OP_USBMODE,
|
||||
OP_ENDPTSETUPSTAT,
|
||||
OP_ENDPTPRIME,
|
||||
OP_ENDPTFLUSH,
|
||||
OP_ENDPTSTAT,
|
||||
OP_ENDPTCOMPLETE,
|
||||
OP_ENDPTCTRL,
|
||||
/* endptctrl1..15 follow */
|
||||
OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* ffs_nr: find first (least significant) bit set
|
||||
* @x: the word to search
|
||||
*
|
||||
* This function returns bit number (instead of position)
|
||||
*/
|
||||
static inline int ffs_nr(u32 x)
|
||||
{
|
||||
int n = ffs(x);
|
||||
|
||||
return n ? n-1 : 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read: reads from a hw register
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
*
|
||||
* This function returns register contents
|
||||
*/
|
||||
static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask)
|
||||
{
|
||||
return ioread32(udc->hw_bank.regmap[reg]) & mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_write: writes to a hw register
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
* @data: new value
|
||||
*/
|
||||
static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
|
||||
u32 mask, u32 data)
|
||||
{
|
||||
if (~mask)
|
||||
data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask)
|
||||
| (data & mask);
|
||||
|
||||
iowrite32(data, udc->hw_bank.regmap[reg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_test_and_clear: tests & clears a hw register
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
*
|
||||
* This function returns register contents
|
||||
*/
|
||||
static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg,
|
||||
u32 mask)
|
||||
{
|
||||
u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask;
|
||||
|
||||
iowrite32(val, udc->hw_bank.regmap[reg]);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_test_and_write: tests & writes a hw register
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
* @data: new value
|
||||
*
|
||||
* This function returns register contents
|
||||
*/
|
||||
static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
|
||||
u32 mask, u32 data)
|
||||
{
|
||||
u32 val = hw_read(udc, reg, ~0);
|
||||
|
||||
hw_write(udc, reg, mask, data);
|
||||
return (val & mask) >> ffs_nr(mask);
|
||||
}
|
||||
|
||||
int hw_device_reset(struct ci13xxx *ci, u32 mode);
|
||||
|
||||
int hw_port_test_set(struct ci13xxx *ci, u8 mode);
|
||||
|
||||
u8 hw_port_test_get(struct ci13xxx *ci);
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */
|
||||
|
|
@ -10,15 +10,12 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/msm_hsusb_hw.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
#include "ci13xxx_udc.c"
|
||||
#include "ci.h"
|
||||
|
||||
#define MSM_USB_BASE (udc->regs)
|
||||
|
||||
static irqreturn_t msm_udc_irq(int irq, void *data)
|
||||
{
|
||||
return udc_irq();
|
||||
}
|
||||
#define MSM_USB_BASE (udc->hw_bank.abs)
|
||||
|
||||
static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
|
||||
{
|
||||
|
|
@ -60,53 +57,40 @@ static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
|
|||
|
||||
static int ci13xxx_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
struct platform_device *plat_ci;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get platform resource mem\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
regs = ioremap(res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
plat_ci = platform_device_alloc("ci_hdrc", -1);
|
||||
if (!plat_ci) {
|
||||
dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "udc_probe failed\n");
|
||||
goto iounmap;
|
||||
ret = platform_device_add_resources(plat_ci, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't add resources to platform device\n");
|
||||
goto put_platform;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "IRQ not found\n");
|
||||
ret = -ENXIO;
|
||||
goto udc_remove;
|
||||
}
|
||||
ret = platform_device_add_data(plat_ci, &ci13xxx_msm_udc_driver,
|
||||
sizeof(ci13xxx_msm_udc_driver));
|
||||
if (ret)
|
||||
goto put_platform;
|
||||
|
||||
ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request_irq failed\n");
|
||||
goto udc_remove;
|
||||
}
|
||||
ret = platform_device_add(plat_ci);
|
||||
if (ret)
|
||||
goto put_platform;
|
||||
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
udc_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
iounmap(regs);
|
||||
put_platform:
|
||||
platform_device_put(plat_ci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -10,10 +10,12 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "ci13xxx_udc.c"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
/* driver name */
|
||||
#define UDC_DRIVER_NAME "ci13xxx_pci"
|
||||
|
|
@ -21,25 +23,20 @@
|
|||
/******************************************************************************
|
||||
* PCI block
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* ci13xxx_pci_irq: interrut handler
|
||||
* @irq: irq number
|
||||
* @pdev: USB Device Controller interrupt source
|
||||
*
|
||||
* This function returns IRQ_HANDLED if the IRQ has been handled
|
||||
* This is an ISR don't trace, use attribute interface instead
|
||||
*/
|
||||
static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
|
||||
{
|
||||
if (irq == 0) {
|
||||
dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return udc_irq();
|
||||
}
|
||||
|
||||
static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = {
|
||||
struct ci13xxx_udc_driver pci_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
};
|
||||
|
||||
struct ci13xxx_udc_driver langwell_pci_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.capoffset = 0,
|
||||
};
|
||||
|
||||
struct ci13xxx_udc_driver penwell_pci_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.capoffset = 0,
|
||||
.power_budget = 200,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -54,11 +51,15 @@ static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = {
|
|||
static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *regs = NULL;
|
||||
int retval = 0;
|
||||
struct ci13xxx_udc_driver *driver = (void *)id->driver_data;
|
||||
struct platform_device *plat_ci;
|
||||
struct resource res[3];
|
||||
int retval = 0, nres = 2;
|
||||
|
||||
if (id == NULL)
|
||||
return -EINVAL;
|
||||
if (!driver) {
|
||||
dev_err(&pdev->dev, "device doesn't provide driver data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
retval = pci_enable_device(pdev);
|
||||
if (retval)
|
||||
|
|
@ -70,41 +71,50 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
|
|||
goto disable_device;
|
||||
}
|
||||
|
||||
retval = pci_request_regions(pdev, UDC_DRIVER_NAME);
|
||||
if (retval)
|
||||
goto disable_device;
|
||||
|
||||
/* BAR 0 holds all the registers */
|
||||
regs = pci_iomap(pdev, 0, 0);
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "Error mapping memory!");
|
||||
retval = -EFAULT;
|
||||
goto release_regions;
|
||||
}
|
||||
pci_set_drvdata(pdev, (__force void *)regs);
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
retval = udc_probe(&ci13xxx_pci_udc_driver, &pdev->dev, regs);
|
||||
if (retval)
|
||||
goto iounmap;
|
||||
plat_ci = platform_device_alloc("ci_hdrc", -1);
|
||||
if (!plat_ci) {
|
||||
dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n");
|
||||
retval = -ENOMEM;
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
/* our device does not have MSI capability */
|
||||
memset(res, 0, sizeof(res));
|
||||
res[0].start = pci_resource_start(pdev, 0);
|
||||
res[0].end = pci_resource_end(pdev, 0);
|
||||
res[0].flags = IORESOURCE_MEM;
|
||||
res[1].start = pdev->irq;
|
||||
res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED,
|
||||
UDC_DRIVER_NAME, pdev);
|
||||
retval = platform_device_add_resources(plat_ci, res, nres);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "can't add resources to platform device\n");
|
||||
goto put_platform;
|
||||
}
|
||||
|
||||
retval = platform_device_add_data(plat_ci, driver, sizeof(*driver));
|
||||
if (retval)
|
||||
goto gadget_remove;
|
||||
goto put_platform;
|
||||
|
||||
dma_set_coherent_mask(&plat_ci->dev, pdev->dev.coherent_dma_mask);
|
||||
plat_ci->dev.dma_mask = pdev->dev.dma_mask;
|
||||
plat_ci->dev.dma_parms = pdev->dev.dma_parms;
|
||||
plat_ci->dev.parent = &pdev->dev;
|
||||
|
||||
pci_set_drvdata(pdev, plat_ci);
|
||||
|
||||
retval = platform_device_add(plat_ci);
|
||||
if (retval)
|
||||
goto put_platform;
|
||||
|
||||
return 0;
|
||||
|
||||
gadget_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
pci_iounmap(pdev, regs);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
put_platform:
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
platform_device_put(plat_ci);
|
||||
disable_device:
|
||||
pci_disable_device(pdev);
|
||||
done:
|
||||
|
|
@ -121,10 +131,10 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
|
|||
*/
|
||||
static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
free_irq(pdev->irq, pdev);
|
||||
udc_remove();
|
||||
pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev));
|
||||
pci_release_regions(pdev);
|
||||
struct platform_device *plat_ci = pci_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(plat_ci);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
|
|
@ -135,8 +145,22 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
|
|||
* Check "pci.h" for details
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
|
||||
{ PCI_DEVICE(0x153F, 0x1004) },
|
||||
{ PCI_DEVICE(0x153F, 0x1006) },
|
||||
{
|
||||
PCI_DEVICE(0x153F, 0x1004),
|
||||
.driver_data = (kernel_ulong_t)&pci_driver,
|
||||
},
|
||||
{
|
||||
PCI_DEVICE(0x153F, 0x1006),
|
||||
.driver_data = (kernel_ulong_t)&pci_driver,
|
||||
},
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811),
|
||||
.driver_data = (kernel_ulong_t)&langwell_pci_driver,
|
||||
},
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829),
|
||||
.driver_data = (kernel_ulong_t)&penwell_pci_driver,
|
||||
},
|
||||
{ 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table);
|
||||
|
|
@ -148,27 +172,7 @@ static struct pci_driver ci13xxx_pci_driver = {
|
|||
.remove = __devexit_p(ci13xxx_pci_remove),
|
||||
};
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_init: module init
|
||||
*
|
||||
* Driver load
|
||||
*/
|
||||
static int __init ci13xxx_pci_init(void)
|
||||
{
|
||||
return pci_register_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_init(ci13xxx_pci_init);
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_exit: module exit
|
||||
*
|
||||
* Driver unload
|
||||
*/
|
||||
static void __exit ci13xxx_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_exit(ci13xxx_pci_exit);
|
||||
module_pci_driver(ci13xxx_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("MIPS - David Lopo <[email protected]>");
|
||||
MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
|
||||
474
drivers/usb/chipidea/core.c
Normal file
474
drivers/usb/chipidea/core.c
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* core.c - ChipIdea USB IP core family device controller
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Description: ChipIdea USB IP core family device controller
|
||||
*
|
||||
* This driver is composed of several blocks:
|
||||
* - HW: hardware interface
|
||||
* - DBG: debug facilities (optional)
|
||||
* - UTIL: utilities
|
||||
* - ISR: interrupts handling
|
||||
* - ENDPT: endpoint operations (Gadget API)
|
||||
* - GADGET: gadget operations (Gadget API)
|
||||
* - BUS: bus glue code, bus abstraction layer
|
||||
*
|
||||
* Compile Options
|
||||
* - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
|
||||
* - STALL_IN: non-empty bulk-in pipes cannot be halted
|
||||
* if defined mass storage compliance succeeds but with warnings
|
||||
* => case 4: Hi > Dn
|
||||
* => case 5: Hi > Di
|
||||
* => case 8: Hi <> Do
|
||||
* if undefined usbtest 13 fails
|
||||
* - TRACE: enable function tracing (depends on DEBUG)
|
||||
*
|
||||
* Main Features
|
||||
* - Chapter 9 & Mass Storage Compliance with Gadget File Storage
|
||||
* - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
|
||||
* - Normal & LPM support
|
||||
*
|
||||
* USBTEST Report
|
||||
* - OK: 0-12, 13 (STALL_IN defined) & 14
|
||||
* - Not Supported: 15 & 16 (ISO)
|
||||
*
|
||||
* TODO List
|
||||
* - OTG
|
||||
* - Isochronous & Interrupt Traffic
|
||||
* - Handle requests which spawns into several TDs
|
||||
* - GET_STATUS(device) - always reports 0
|
||||
* - Gadget API (majority of optional features)
|
||||
* - Suspend & Remote Wakeup
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
#include "ci.h"
|
||||
#include "udc.h"
|
||||
#include "bits.h"
|
||||
#include "host.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Controller register map */
|
||||
static uintptr_t ci_regs_nolpm[] = {
|
||||
[CAP_CAPLENGTH] = 0x000UL,
|
||||
[CAP_HCCPARAMS] = 0x008UL,
|
||||
[CAP_DCCPARAMS] = 0x024UL,
|
||||
[CAP_TESTMODE] = 0x038UL,
|
||||
[OP_USBCMD] = 0x000UL,
|
||||
[OP_USBSTS] = 0x004UL,
|
||||
[OP_USBINTR] = 0x008UL,
|
||||
[OP_DEVICEADDR] = 0x014UL,
|
||||
[OP_ENDPTLISTADDR] = 0x018UL,
|
||||
[OP_PORTSC] = 0x044UL,
|
||||
[OP_DEVLC] = 0x084UL,
|
||||
[OP_OTGSC] = 0x064UL,
|
||||
[OP_USBMODE] = 0x068UL,
|
||||
[OP_ENDPTSETUPSTAT] = 0x06CUL,
|
||||
[OP_ENDPTPRIME] = 0x070UL,
|
||||
[OP_ENDPTFLUSH] = 0x074UL,
|
||||
[OP_ENDPTSTAT] = 0x078UL,
|
||||
[OP_ENDPTCOMPLETE] = 0x07CUL,
|
||||
[OP_ENDPTCTRL] = 0x080UL,
|
||||
};
|
||||
|
||||
static uintptr_t ci_regs_lpm[] = {
|
||||
[CAP_CAPLENGTH] = 0x000UL,
|
||||
[CAP_HCCPARAMS] = 0x008UL,
|
||||
[CAP_DCCPARAMS] = 0x024UL,
|
||||
[CAP_TESTMODE] = 0x0FCUL,
|
||||
[OP_USBCMD] = 0x000UL,
|
||||
[OP_USBSTS] = 0x004UL,
|
||||
[OP_USBINTR] = 0x008UL,
|
||||
[OP_DEVICEADDR] = 0x014UL,
|
||||
[OP_ENDPTLISTADDR] = 0x018UL,
|
||||
[OP_PORTSC] = 0x044UL,
|
||||
[OP_DEVLC] = 0x084UL,
|
||||
[OP_OTGSC] = 0x0C4UL,
|
||||
[OP_USBMODE] = 0x0C8UL,
|
||||
[OP_ENDPTSETUPSTAT] = 0x0D8UL,
|
||||
[OP_ENDPTPRIME] = 0x0DCUL,
|
||||
[OP_ENDPTFLUSH] = 0x0E0UL,
|
||||
[OP_ENDPTSTAT] = 0x0E4UL,
|
||||
[OP_ENDPTCOMPLETE] = 0x0E8UL,
|
||||
[OP_ENDPTCTRL] = 0x0ECUL,
|
||||
};
|
||||
|
||||
static int hw_alloc_regmap(struct ci13xxx *ci, bool is_lpm)
|
||||
{
|
||||
int i;
|
||||
|
||||
kfree(ci->hw_bank.regmap);
|
||||
|
||||
ci->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!ci->hw_bank.regmap)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < OP_ENDPTCTRL; i++)
|
||||
ci->hw_bank.regmap[i] =
|
||||
(i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) +
|
||||
(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);
|
||||
|
||||
for (; i <= OP_LAST; i++)
|
||||
ci->hw_bank.regmap[i] = ci->hw_bank.op +
|
||||
4 * (i - OP_ENDPTCTRL) +
|
||||
(is_lpm
|
||||
? ci_regs_lpm[OP_ENDPTCTRL]
|
||||
: ci_regs_nolpm[OP_ENDPTCTRL]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_port_test_set: writes port test mode (execute without interruption)
|
||||
* @mode: new value
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
int hw_port_test_set(struct ci13xxx *ci, u8 mode)
|
||||
{
|
||||
const u8 TEST_MODE_MAX = 7;
|
||||
|
||||
if (mode > TEST_MODE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_port_test_get: reads port test mode value
|
||||
*
|
||||
* This function returns port test mode value
|
||||
*/
|
||||
u8 hw_port_test_get(struct ci13xxx *ci)
|
||||
{
|
||||
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC);
|
||||
}
|
||||
|
||||
static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* bank is a module variable */
|
||||
ci->hw_bank.abs = base;
|
||||
|
||||
ci->hw_bank.cap = ci->hw_bank.abs;
|
||||
ci->hw_bank.cap += ci->udc_driver->capoffset;
|
||||
ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap);
|
||||
|
||||
hw_alloc_regmap(ci, false);
|
||||
reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >>
|
||||
ffs_nr(HCCPARAMS_LEN);
|
||||
ci->hw_bank.lpm = reg;
|
||||
hw_alloc_regmap(ci, !!reg);
|
||||
ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs;
|
||||
ci->hw_bank.size += OP_LAST;
|
||||
ci->hw_bank.size /= sizeof(u32);
|
||||
|
||||
reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >>
|
||||
ffs_nr(DCCPARAMS_DEN);
|
||||
ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */
|
||||
|
||||
if (ci->hw_ep_max > ENDPT_MAX)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n",
|
||||
ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
|
||||
|
||||
/* setup lock mode ? */
|
||||
|
||||
/* ENDPTSETUPSTAT is '0' by default */
|
||||
|
||||
/* HCSPARAMS.bf.ppc SHOULD BE zero for device */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_device_reset: resets chip (execute without interruption)
|
||||
* @ci: the controller
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
int hw_device_reset(struct ci13xxx *ci, u32 mode)
|
||||
{
|
||||
/* should flush & stop before reset */
|
||||
hw_write(ci, OP_ENDPTFLUSH, ~0, ~0);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
|
||||
while (hw_read(ci, OP_USBCMD, USBCMD_RST))
|
||||
udelay(10); /* not RTOS friendly */
|
||||
|
||||
|
||||
if (ci->udc_driver->notify_event)
|
||||
ci->udc_driver->notify_event(ci,
|
||||
CI13XXX_CONTROLLER_RESET_EVENT);
|
||||
|
||||
if (ci->udc_driver->flags & CI13XXX_DISABLE_STREAMING)
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
|
||||
|
||||
/* USBMODE should be configured step by step */
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CM, mode);
|
||||
/* HW >= 2.3 */
|
||||
hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
|
||||
|
||||
if (hw_read(ci, OP_USBMODE, USBMODE_CM) != mode) {
|
||||
pr_err("cannot enter in %s mode", ci_role(ci)->name);
|
||||
pr_err("lpm = %i", ci->hw_bank.lpm);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ci_otg_role - pick role based on ID pin state
|
||||
* @ci: the controller
|
||||
*/
|
||||
static enum ci_role ci_otg_role(struct ci13xxx *ci)
|
||||
{
|
||||
u32 sts = hw_read(ci, OP_OTGSC, ~0);
|
||||
enum ci_role role = sts & OTGSC_ID
|
||||
? CI_ROLE_GADGET
|
||||
: CI_ROLE_HOST;
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* ci_role_work - perform role changing based on ID pin
|
||||
* @work: work struct
|
||||
*/
|
||||
static void ci_role_work(struct work_struct *work)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(work, struct ci13xxx, work);
|
||||
enum ci_role role = ci_otg_role(ci);
|
||||
|
||||
hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
|
||||
|
||||
if (role != ci->role) {
|
||||
dev_dbg(ci->dev, "switching from %s to %s\n",
|
||||
ci_role(ci)->name, ci->roles[role]->name);
|
||||
|
||||
ci_role_stop(ci);
|
||||
ci_role_start(ci, role);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t show_role(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *ci = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", ci_role(ci)->name);
|
||||
}
|
||||
|
||||
static ssize_t store_role(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci13xxx *ci = dev_get_drvdata(dev);
|
||||
enum ci_role role;
|
||||
int ret;
|
||||
|
||||
for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
|
||||
if (ci->roles[role] && !strcmp(buf, ci->roles[role]->name))
|
||||
break;
|
||||
|
||||
if (role == CI_ROLE_END || role == ci->role)
|
||||
return -EINVAL;
|
||||
|
||||
ci_role_stop(ci);
|
||||
ret = ci_role_start(ci, role);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(role, S_IRUSR | S_IWUSR, show_role, store_role);
|
||||
|
||||
static irqreturn_t ci_irq(int irq, void *data)
|
||||
{
|
||||
struct ci13xxx *ci = data;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
if (ci->is_otg) {
|
||||
u32 sts = hw_read(ci, OP_OTGSC, ~0);
|
||||
|
||||
if (sts & OTGSC_IDIS) {
|
||||
queue_work(ci->wq, &ci->work);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci);
|
||||
}
|
||||
|
||||
static int __devinit ci_hdrc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ci13xxx *ci;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
if (!dev->platform_data) {
|
||||
dev_err(dev, "platform data missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
base = devm_request_and_ioremap(dev, res);
|
||||
if (!res) {
|
||||
dev_err(dev, "can't request and ioremap resource\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
|
||||
if (!ci) {
|
||||
dev_err(dev, "can't allocate device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ci->dev = dev;
|
||||
ci->udc_driver = dev->platform_data;
|
||||
|
||||
ret = hw_device_init(ci, base);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't initialize hardware\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ci->hw_bank.phys = res->start;
|
||||
|
||||
ci->irq = platform_get_irq(pdev, 0);
|
||||
if (ci->irq < 0) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
INIT_WORK(&ci->work, ci_role_work);
|
||||
ci->wq = create_singlethread_workqueue("ci_otg");
|
||||
if (!ci->wq) {
|
||||
dev_err(dev, "can't create workqueue\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* initialize role(s) before the interrupt is requested */
|
||||
ret = ci_hdrc_host_init(ci);
|
||||
if (ret)
|
||||
dev_info(dev, "doesn't support host\n");
|
||||
|
||||
ret = ci_hdrc_gadget_init(ci);
|
||||
if (ret)
|
||||
dev_info(dev, "doesn't support gadget\n");
|
||||
|
||||
if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
|
||||
dev_err(dev, "no supported roles\n");
|
||||
ret = -ENODEV;
|
||||
goto rm_wq;
|
||||
}
|
||||
|
||||
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
|
||||
ci->is_otg = true;
|
||||
ci->role = ci_otg_role(ci);
|
||||
} else {
|
||||
ci->role = ci->roles[CI_ROLE_HOST]
|
||||
? CI_ROLE_HOST
|
||||
: CI_ROLE_GADGET;
|
||||
}
|
||||
|
||||
ret = ci_role_start(ci, ci->role);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't start %s role\n", ci_role(ci)->name);
|
||||
ret = -ENODEV;
|
||||
goto rm_wq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ci);
|
||||
ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->udc_driver->name,
|
||||
ci);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_role);
|
||||
if (ret)
|
||||
goto rm_attr;
|
||||
|
||||
if (ci->is_otg)
|
||||
hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE);
|
||||
|
||||
return ret;
|
||||
|
||||
rm_attr:
|
||||
device_remove_file(dev, &dev_attr_role);
|
||||
stop:
|
||||
ci_role_stop(ci);
|
||||
rm_wq:
|
||||
flush_workqueue(ci->wq);
|
||||
destroy_workqueue(ci->wq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ci_hdrc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ci13xxx *ci = platform_get_drvdata(pdev);
|
||||
|
||||
flush_workqueue(ci->wq);
|
||||
destroy_workqueue(ci->wq);
|
||||
device_remove_file(ci->dev, &dev_attr_role);
|
||||
free_irq(ci->irq, ci);
|
||||
ci_role_stop(ci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ci_hdrc_driver = {
|
||||
.probe = ci_hdrc_probe,
|
||||
.remove = __devexit_p(ci_hdrc_remove),
|
||||
.driver = {
|
||||
.name = "ci_hdrc",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ci_hdrc_driver);
|
||||
|
||||
MODULE_ALIAS("platform:ci_hdrc");
|
||||
MODULE_ALIAS("platform:ci13xxx");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("David Lopo <[email protected]>");
|
||||
MODULE_DESCRIPTION("ChipIdea HDRC Driver");
|
||||
804
drivers/usb/chipidea/debug.c
Normal file
804
drivers/usb/chipidea/debug.c
Normal file
|
|
@ -0,0 +1,804 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
#include "ci.h"
|
||||
#include "udc.h"
|
||||
#include "bits.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Interrupt statistics */
|
||||
#define ISR_MASK 0x1F
|
||||
static struct isr_statistics {
|
||||
u32 test;
|
||||
u32 ui;
|
||||
u32 uei;
|
||||
u32 pci;
|
||||
u32 uri;
|
||||
u32 sli;
|
||||
u32 none;
|
||||
struct {
|
||||
u32 cnt;
|
||||
u32 buf[ISR_MASK+1];
|
||||
u32 idx;
|
||||
} hndl;
|
||||
} isr_statistics;
|
||||
|
||||
void dbg_interrupt(u32 intmask)
|
||||
{
|
||||
if (!intmask) {
|
||||
isr_statistics.none++;
|
||||
return;
|
||||
}
|
||||
|
||||
isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intmask;
|
||||
isr_statistics.hndl.idx &= ISR_MASK;
|
||||
isr_statistics.hndl.cnt++;
|
||||
|
||||
if (USBi_URI & intmask)
|
||||
isr_statistics.uri++;
|
||||
if (USBi_PCI & intmask)
|
||||
isr_statistics.pci++;
|
||||
if (USBi_UEI & intmask)
|
||||
isr_statistics.uei++;
|
||||
if (USBi_UI & intmask)
|
||||
isr_statistics.ui++;
|
||||
if (USBi_SLI & intmask)
|
||||
isr_statistics.sli++;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_register_read: reads all device registers (execute without interruption)
|
||||
* @buf: destination buffer
|
||||
* @size: buffer size
|
||||
*
|
||||
* This function returns number of registers read
|
||||
*/
|
||||
static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (size > udc->hw_bank.size)
|
||||
size = udc->hw_bank.size;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
buf[i] = hw_read(udc, i * sizeof(u32), ~0);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_register_write: writes to register
|
||||
* @addr: register address
|
||||
* @data: register value
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data)
|
||||
{
|
||||
/* align */
|
||||
addr /= sizeof(u32);
|
||||
|
||||
if (addr >= udc->hw_bank.size)
|
||||
return -EINVAL;
|
||||
|
||||
/* align */
|
||||
addr *= sizeof(u32);
|
||||
|
||||
hw_write(udc, addr, ~0, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_intr_clear: disables interrupt & clears interrupt status (execute without
|
||||
* interruption)
|
||||
* @n: interrupt bit
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_intr_clear(struct ci13xxx *udc, int n)
|
||||
{
|
||||
if (n >= REG_BITS)
|
||||
return -EINVAL;
|
||||
|
||||
hw_write(udc, OP_USBINTR, BIT(n), 0);
|
||||
hw_write(udc, OP_USBSTS, BIT(n), BIT(n));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_intr_force: enables interrupt & forces interrupt status (execute without
|
||||
* interruption)
|
||||
* @n: interrupt bit
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_intr_force(struct ci13xxx *udc, int n)
|
||||
{
|
||||
if (n >= REG_BITS)
|
||||
return -EINVAL;
|
||||
|
||||
hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
|
||||
hw_write(udc, OP_USBINTR, BIT(n), BIT(n));
|
||||
hw_write(udc, OP_USBSTS, BIT(n), BIT(n));
|
||||
hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* show_device: prints information about device capabilities and status
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_device(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct usb_gadget *gadget = &udc->gadget;
|
||||
int n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n",
|
||||
gadget->speed);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n",
|
||||
gadget->max_speed);
|
||||
/* TODO: Scheduled for removal in 3.8. */
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n",
|
||||
gadget_is_dualspeed(gadget));
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n",
|
||||
gadget->is_otg);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n",
|
||||
gadget->is_a_peripheral);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n",
|
||||
gadget->b_hnp_enable);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n",
|
||||
gadget->a_hnp_support);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n",
|
||||
gadget->a_alt_hnp_support);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n",
|
||||
(gadget->name ? gadget->name : ""));
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
|
||||
|
||||
/**
|
||||
* show_driver: prints information about attached gadget (if any)
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct usb_gadget_driver *driver = udc->driver;
|
||||
int n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (driver == NULL)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"There is no gadget attached!\n");
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n",
|
||||
(driver->function ? driver->function : ""));
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n",
|
||||
driver->max_speed);
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL);
|
||||
|
||||
/* Maximum event message length */
|
||||
#define DBG_DATA_MSG 64UL
|
||||
|
||||
/* Maximum event messages */
|
||||
#define DBG_DATA_MAX 128UL
|
||||
|
||||
/* Event buffer descriptor */
|
||||
static struct {
|
||||
char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */
|
||||
unsigned idx; /* index */
|
||||
unsigned tty; /* print to console? */
|
||||
rwlock_t lck; /* lock */
|
||||
} dbg_data = {
|
||||
.idx = 0,
|
||||
.tty = 0,
|
||||
.lck = __RW_LOCK_UNLOCKED(lck)
|
||||
};
|
||||
|
||||
/**
|
||||
* dbg_dec: decrements debug event index
|
||||
* @idx: buffer index
|
||||
*/
|
||||
static void dbg_dec(unsigned *idx)
|
||||
{
|
||||
*idx = (*idx - 1) & (DBG_DATA_MAX-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_inc: increments debug event index
|
||||
* @idx: buffer index
|
||||
*/
|
||||
static void dbg_inc(unsigned *idx)
|
||||
{
|
||||
*idx = (*idx + 1) & (DBG_DATA_MAX-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_print: prints the common part of the event
|
||||
* @addr: endpoint address
|
||||
* @name: event name
|
||||
* @status: status
|
||||
* @extra: extra information
|
||||
*/
|
||||
static void dbg_print(u8 addr, const char *name, int status, const char *extra)
|
||||
{
|
||||
struct timeval tval;
|
||||
unsigned int stamp;
|
||||
unsigned long flags;
|
||||
|
||||
write_lock_irqsave(&dbg_data.lck, flags);
|
||||
|
||||
do_gettimeofday(&tval);
|
||||
stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */
|
||||
stamp = stamp * 1000000 + tval.tv_usec;
|
||||
|
||||
scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
|
||||
"%04X\t? %02X %-7.7s %4i ?\t%s\n",
|
||||
stamp, addr, name, status, extra);
|
||||
|
||||
dbg_inc(&dbg_data.idx);
|
||||
|
||||
write_unlock_irqrestore(&dbg_data.lck, flags);
|
||||
|
||||
if (dbg_data.tty != 0)
|
||||
pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n",
|
||||
stamp, addr, name, status, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_done: prints a DONE event
|
||||
* @addr: endpoint address
|
||||
* @td: transfer descriptor
|
||||
* @status: status
|
||||
*/
|
||||
void dbg_done(u8 addr, const u32 token, int status)
|
||||
{
|
||||
char msg[DBG_DATA_MSG];
|
||||
|
||||
scnprintf(msg, sizeof(msg), "%d %02X",
|
||||
(int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES),
|
||||
(int)(token & TD_STATUS) >> ffs_nr(TD_STATUS));
|
||||
dbg_print(addr, "DONE", status, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_event: prints a generic event
|
||||
* @addr: endpoint address
|
||||
* @name: event name
|
||||
* @status: status
|
||||
*/
|
||||
void dbg_event(u8 addr, const char *name, int status)
|
||||
{
|
||||
if (name != NULL)
|
||||
dbg_print(addr, name, status, "");
|
||||
}
|
||||
|
||||
/*
|
||||
* dbg_queue: prints a QUEUE event
|
||||
* @addr: endpoint address
|
||||
* @req: USB request
|
||||
* @status: status
|
||||
*/
|
||||
void dbg_queue(u8 addr, const struct usb_request *req, int status)
|
||||
{
|
||||
char msg[DBG_DATA_MSG];
|
||||
|
||||
if (req != NULL) {
|
||||
scnprintf(msg, sizeof(msg),
|
||||
"%d %d", !req->no_interrupt, req->length);
|
||||
dbg_print(addr, "QUEUE", status, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_setup: prints a SETUP event
|
||||
* @addr: endpoint address
|
||||
* @req: setup request
|
||||
*/
|
||||
void dbg_setup(u8 addr, const struct usb_ctrlrequest *req)
|
||||
{
|
||||
char msg[DBG_DATA_MSG];
|
||||
|
||||
if (req != NULL) {
|
||||
scnprintf(msg, sizeof(msg),
|
||||
"%02X %02X %04X %04X %d", req->bRequestType,
|
||||
req->bRequest, le16_to_cpu(req->wValue),
|
||||
le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength));
|
||||
dbg_print(addr, "SETUP", 0, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show_events: displays the event buffer
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_events(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned i, j, n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(dev->parent, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
read_lock_irqsave(&dbg_data.lck, flags);
|
||||
|
||||
i = dbg_data.idx;
|
||||
for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) {
|
||||
n += strlen(dbg_data.buf[i]);
|
||||
if (n >= PAGE_SIZE) {
|
||||
n -= strlen(dbg_data.buf[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i))
|
||||
j += scnprintf(buf + j, PAGE_SIZE - j,
|
||||
"%s", dbg_data.buf[i]);
|
||||
|
||||
read_unlock_irqrestore(&dbg_data.lck, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* store_events: configure if events are going to be also printed to console
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t store_events(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned tty;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(dev, "[%s] EINVAL\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%u", &tty) != 1 || tty > 1) {
|
||||
dev_err(dev, "<1|0>: enable|disable console log\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbg_data.tty = tty;
|
||||
dev_info(dev, "tty = %u", dbg_data.tty);
|
||||
|
||||
done:
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events);
|
||||
|
||||
/**
|
||||
* show_inters: interrupt status, enable status and historic
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
u32 intr;
|
||||
unsigned i, j, n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
/*n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"status = %08x\n", hw_read_intr_status(udc));
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"enable = %08x\n", hw_read_intr_enable(udc));*/
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
|
||||
isr_statistics.test);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n",
|
||||
isr_statistics.ui);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n",
|
||||
isr_statistics.uei);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n",
|
||||
isr_statistics.pci);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n",
|
||||
isr_statistics.uri);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n",
|
||||
isr_statistics.sli);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n",
|
||||
isr_statistics.none);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n",
|
||||
isr_statistics.hndl.cnt);
|
||||
|
||||
for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) {
|
||||
i &= ISR_MASK;
|
||||
intr = isr_statistics.hndl.buf[i];
|
||||
|
||||
if (USBi_UI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "ui ");
|
||||
intr &= ~USBi_UI;
|
||||
if (USBi_UEI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "uei ");
|
||||
intr &= ~USBi_UEI;
|
||||
if (USBi_PCI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "pci ");
|
||||
intr &= ~USBi_PCI;
|
||||
if (USBi_URI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "uri ");
|
||||
intr &= ~USBi_URI;
|
||||
if (USBi_SLI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "sli ");
|
||||
intr &= ~USBi_SLI;
|
||||
if (intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "??? ");
|
||||
if (isr_statistics.hndl.buf[i])
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "\n");
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* store_inters: enable & force or disable an individual interrutps
|
||||
* (to be used for test purposes only)
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t store_inters(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
unsigned en, bit;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "EINVAL\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) {
|
||||
dev_err(udc->dev, "<1|0> <bit>: enable|disable interrupt\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (en) {
|
||||
if (hw_intr_force(udc, bit))
|
||||
dev_err(dev, "invalid bit number\n");
|
||||
else
|
||||
isr_statistics.test++;
|
||||
} else {
|
||||
if (hw_intr_clear(udc, bit))
|
||||
dev_err(dev, "invalid bit number\n");
|
||||
}
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
done:
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters);
|
||||
|
||||
/**
|
||||
* show_port_test: reads port test mode
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_port_test(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
unsigned mode;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "EINVAL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
mode = hw_port_test_get(udc);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* store_port_test: writes port test mode
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t store_port_test(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
unsigned mode;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%u", &mode) != 1) {
|
||||
dev_err(udc->dev, "<mode>: set port test mode");
|
||||
goto done;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (hw_port_test_set(udc, mode))
|
||||
dev_err(udc->dev, "invalid mode\n");
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
done:
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR,
|
||||
show_port_test, store_port_test);
|
||||
|
||||
/**
|
||||
* show_qheads: DMA contents of all queue heads
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
unsigned i, j, n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
for (i = 0; i < udc->hw_ep_max/2; i++) {
|
||||
struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i];
|
||||
struct ci13xxx_ep *mEpTx =
|
||||
&udc->ci13xxx_ep[i + udc->hw_ep_max/2];
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"EP=%02i: RX=%08X TX=%08X\n",
|
||||
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
|
||||
for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) {
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
" %04X: %08X %08X\n", j,
|
||||
*((u32 *)mEpRx->qh.ptr + j),
|
||||
*((u32 *)mEpTx->qh.ptr + j));
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
|
||||
|
||||
/**
|
||||
* show_registers: dumps all registers
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
#define DUMP_ENTRIES 512
|
||||
static ssize_t show_registers(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
u32 *dump;
|
||||
unsigned i, k, n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
|
||||
if (!dump) {
|
||||
dev_err(udc->dev, "%s: out of memory\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
k = hw_register_read(udc, dump, DUMP_ENTRIES);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
for (i = 0; i < k; i++) {
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"reg[0x%04X] = 0x%08X\n",
|
||||
i * (unsigned)sizeof(u32), dump[i]);
|
||||
}
|
||||
kfree(dump);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* store_registers: writes value to register address
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t store_registers(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long addr, data, flags;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%li %li", &addr, &data) != 2) {
|
||||
dev_err(udc->dev,
|
||||
"<addr> <data>: write data to register address\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (hw_register_write(udc, addr, data))
|
||||
dev_err(udc->dev, "invalid address range\n");
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
done:
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR,
|
||||
show_registers, store_registers);
|
||||
|
||||
/**
|
||||
* show_requests: DMA contents of all requests currently queued (all endpts)
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
struct list_head *ptr = NULL;
|
||||
struct ci13xxx_req *req = NULL;
|
||||
unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
for (i = 0; i < udc->hw_ep_max; i++)
|
||||
list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue)
|
||||
{
|
||||
req = list_entry(ptr, struct ci13xxx_req, queue);
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"EP=%02i: TD=%08X %s\n",
|
||||
i % udc->hw_ep_max/2, (u32)req->dma,
|
||||
((i < udc->hw_ep_max/2) ? "RX" : "TX"));
|
||||
|
||||
for (j = 0; j < qSize; j++)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
" %04X: %08X\n", j,
|
||||
*((u32 *)req->ptr + j));
|
||||
}
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
|
||||
|
||||
/**
|
||||
* dbg_create_files: initializes the attribute interface
|
||||
* @dev: device
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
int dbg_create_files(struct device *dev)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (dev == NULL)
|
||||
return -EINVAL;
|
||||
retval = device_create_file(dev, &dev_attr_device);
|
||||
if (retval)
|
||||
goto done;
|
||||
retval = device_create_file(dev, &dev_attr_driver);
|
||||
if (retval)
|
||||
goto rm_device;
|
||||
retval = device_create_file(dev, &dev_attr_events);
|
||||
if (retval)
|
||||
goto rm_driver;
|
||||
retval = device_create_file(dev, &dev_attr_inters);
|
||||
if (retval)
|
||||
goto rm_events;
|
||||
retval = device_create_file(dev, &dev_attr_port_test);
|
||||
if (retval)
|
||||
goto rm_inters;
|
||||
retval = device_create_file(dev, &dev_attr_qheads);
|
||||
if (retval)
|
||||
goto rm_port_test;
|
||||
retval = device_create_file(dev, &dev_attr_registers);
|
||||
if (retval)
|
||||
goto rm_qheads;
|
||||
retval = device_create_file(dev, &dev_attr_requests);
|
||||
if (retval)
|
||||
goto rm_registers;
|
||||
return 0;
|
||||
|
||||
rm_registers:
|
||||
device_remove_file(dev, &dev_attr_registers);
|
||||
rm_qheads:
|
||||
device_remove_file(dev, &dev_attr_qheads);
|
||||
rm_port_test:
|
||||
device_remove_file(dev, &dev_attr_port_test);
|
||||
rm_inters:
|
||||
device_remove_file(dev, &dev_attr_inters);
|
||||
rm_events:
|
||||
device_remove_file(dev, &dev_attr_events);
|
||||
rm_driver:
|
||||
device_remove_file(dev, &dev_attr_driver);
|
||||
rm_device:
|
||||
device_remove_file(dev, &dev_attr_device);
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_remove_files: destroys the attribute interface
|
||||
* @dev: device
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
int dbg_remove_files(struct device *dev)
|
||||
{
|
||||
if (dev == NULL)
|
||||
return -EINVAL;
|
||||
device_remove_file(dev, &dev_attr_requests);
|
||||
device_remove_file(dev, &dev_attr_registers);
|
||||
device_remove_file(dev, &dev_attr_qheads);
|
||||
device_remove_file(dev, &dev_attr_port_test);
|
||||
device_remove_file(dev, &dev_attr_inters);
|
||||
device_remove_file(dev, &dev_attr_events);
|
||||
device_remove_file(dev, &dev_attr_driver);
|
||||
device_remove_file(dev, &dev_attr_device);
|
||||
return 0;
|
||||
}
|
||||
56
drivers/usb/chipidea/debug.h
Normal file
56
drivers/usb/chipidea/debug.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* debug.h - ChipIdea USB driver debug interfaces
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_CHIPIDEA_DEBUG_H
|
||||
#define __DRIVERS_USB_CHIPIDEA_DEBUG_H
|
||||
|
||||
#ifdef CONFIG_USB_CHIPIDEA_DEBUG
|
||||
void dbg_interrupt(u32 intmask);
|
||||
void dbg_done(u8 addr, const u32 token, int status);
|
||||
void dbg_event(u8 addr, const char *name, int status);
|
||||
void dbg_queue(u8 addr, const struct usb_request *req, int status);
|
||||
void dbg_setup(u8 addr, const struct usb_ctrlrequest *req);
|
||||
int dbg_create_files(struct device *dev);
|
||||
int dbg_remove_files(struct device *dev);
|
||||
#else
|
||||
static inline void dbg_interrupt(u32 intmask)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_done(u8 addr, const u32 token, int status)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_event(u8 addr, const char *name, int status)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_queue(u8 addr, const struct usb_request *req, int status)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_setup(u8 addr, const struct usb_ctrlrequest *req)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int dbg_create_files(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int dbg_remove_files(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_DEBUG_H */
|
||||
160
drivers/usb/chipidea/host.c
Normal file
160
drivers/usb/chipidea/host.c
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* host.c - ChipIdea USB host controller driver
|
||||
*
|
||||
* Copyright (c) 2012 Intel Corporation
|
||||
*
|
||||
* Author: Alexander Shishkin
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
#define CHIPIDEA_EHCI
|
||||
#include "../host/ehci-hcd.c"
|
||||
|
||||
#include "ci.h"
|
||||
#include "bits.h"
|
||||
#include "host.h"
|
||||
|
||||
static int ci_ehci_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int ret;
|
||||
|
||||
hcd->has_tt = 1;
|
||||
|
||||
ret = ehci_setup(hcd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hc_driver ci_ehci_hc_driver = {
|
||||
.description = "ehci_hcd",
|
||||
.product_desc = "ChipIdea HDRC EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ci_ehci_setup,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static irqreturn_t host_irq(struct ci13xxx *ci)
|
||||
{
|
||||
return usb_hcd_irq(ci->irq, ci->hcd);
|
||||
}
|
||||
|
||||
static int host_start(struct ci13xxx *ci)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
int ret;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev));
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(ci->dev, ci);
|
||||
hcd->rsrc_start = ci->hw_bank.phys;
|
||||
hcd->rsrc_len = ci->hw_bank.size;
|
||||
hcd->regs = ci->hw_bank.abs;
|
||||
hcd->has_tt = 1;
|
||||
|
||||
hcd->power_budget = ci->udc_driver->power_budget;
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = ci->hw_bank.cap;
|
||||
ehci->has_hostpc = ci->hw_bank.lpm;
|
||||
|
||||
ret = usb_add_hcd(hcd, 0, 0);
|
||||
if (ret)
|
||||
usb_put_hcd(hcd);
|
||||
else
|
||||
ci->hcd = hcd;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void host_stop(struct ci13xxx *ci)
|
||||
{
|
||||
struct usb_hcd *hcd = ci->hcd;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
int ci_hdrc_host_init(struct ci13xxx *ci)
|
||||
{
|
||||
struct ci_role_driver *rdrv;
|
||||
|
||||
if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC))
|
||||
return -ENXIO;
|
||||
|
||||
rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
|
||||
if (!rdrv)
|
||||
return -ENOMEM;
|
||||
|
||||
rdrv->start = host_start;
|
||||
rdrv->stop = host_stop;
|
||||
rdrv->irq = host_irq;
|
||||
rdrv->name = "host";
|
||||
ci->roles[CI_ROLE_HOST] = rdrv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
17
drivers/usb/chipidea/host.h
Normal file
17
drivers/usb/chipidea/host.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef __DRIVERS_USB_CHIPIDEA_HOST_H
|
||||
#define __DRIVERS_USB_CHIPIDEA_HOST_H
|
||||
|
||||
#ifdef CONFIG_USB_CHIPIDEA_HOST
|
||||
|
||||
int ci_hdrc_host_init(struct ci13xxx *ci);
|
||||
|
||||
#else
|
||||
|
||||
static inline int ci_hdrc_host_init(struct ci13xxx *ci)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */
|
||||
1809
drivers/usb/chipidea/udc.c
Normal file
1809
drivers/usb/chipidea/udc.c
Normal file
File diff suppressed because it is too large
Load diff
93
drivers/usb/chipidea/udc.h
Normal file
93
drivers/usb/chipidea/udc.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* udc.h - ChipIdea UDC structures
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_CHIPIDEA_UDC_H
|
||||
#define __DRIVERS_USB_CHIPIDEA_UDC_H
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
#define CTRL_PAYLOAD_MAX 64
|
||||
#define RX 0 /* similar to USB_DIR_OUT but can be used as an index */
|
||||
#define TX 1 /* similar to USB_DIR_IN but can be used as an index */
|
||||
|
||||
/* DMA layout of transfer descriptors */
|
||||
struct ci13xxx_td {
|
||||
/* 0 */
|
||||
u32 next;
|
||||
#define TD_TERMINATE BIT(0)
|
||||
#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
|
||||
/* 1 */
|
||||
u32 token;
|
||||
#define TD_STATUS (0x00FFUL << 0)
|
||||
#define TD_STATUS_TR_ERR BIT(3)
|
||||
#define TD_STATUS_DT_ERR BIT(5)
|
||||
#define TD_STATUS_HALTED BIT(6)
|
||||
#define TD_STATUS_ACTIVE BIT(7)
|
||||
#define TD_MULTO (0x0003UL << 10)
|
||||
#define TD_IOC BIT(15)
|
||||
#define TD_TOTAL_BYTES (0x7FFFUL << 16)
|
||||
/* 2 */
|
||||
u32 page[5];
|
||||
#define TD_CURR_OFFSET (0x0FFFUL << 0)
|
||||
#define TD_FRAME_NUM (0x07FFUL << 0)
|
||||
#define TD_RESERVED_MASK (0x0FFFUL << 0)
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* DMA layout of queue heads */
|
||||
struct ci13xxx_qh {
|
||||
/* 0 */
|
||||
u32 cap;
|
||||
#define QH_IOS BIT(15)
|
||||
#define QH_MAX_PKT (0x07FFUL << 16)
|
||||
#define QH_ZLT BIT(29)
|
||||
#define QH_MULT (0x0003UL << 30)
|
||||
/* 1 */
|
||||
u32 curr;
|
||||
/* 2 - 8 */
|
||||
struct ci13xxx_td td;
|
||||
/* 9 */
|
||||
u32 RESERVED;
|
||||
struct usb_ctrlrequest setup;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* struct ci13xxx_req - usb request representation
|
||||
* @req: request structure for gadget drivers
|
||||
* @queue: link to QH list
|
||||
* @ptr: transfer descriptor for this request
|
||||
* @dma: dma address for the transfer descriptor
|
||||
* @zptr: transfer descriptor for the zero packet
|
||||
* @zdma: dma address of the zero packet's transfer descriptor
|
||||
*/
|
||||
struct ci13xxx_req {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
struct ci13xxx_td *ptr;
|
||||
dma_addr_t dma;
|
||||
struct ci13xxx_td *zptr;
|
||||
dma_addr_t zdma;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_CHIPIDEA_UDC
|
||||
|
||||
int ci_hdrc_gadget_init(struct ci13xxx *ci);
|
||||
|
||||
#else
|
||||
|
||||
static inline int ci_hdrc_gadget_init(struct ci13xxx *ci)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */
|
||||
|
|
@ -1664,6 +1664,7 @@ static struct usb_driver acm_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.supports_autosuspend = 1,
|
||||
#endif
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -309,9 +309,6 @@ static void free_urbs(struct wdm_device *desc)
|
|||
|
||||
static void cleanup(struct wdm_device *desc)
|
||||
{
|
||||
spin_lock(&wdm_device_list_lock);
|
||||
list_del(&desc->device_list);
|
||||
spin_unlock(&wdm_device_list_lock);
|
||||
kfree(desc->sbuf);
|
||||
kfree(desc->inbuf);
|
||||
kfree(desc->orq);
|
||||
|
|
@ -369,6 +366,7 @@ static ssize_t wdm_write
|
|||
r = usb_autopm_get_interface(desc->intf);
|
||||
if (r < 0) {
|
||||
kfree(buf);
|
||||
rv = usb_translate_errors(r);
|
||||
goto outnp;
|
||||
}
|
||||
|
||||
|
|
@ -384,6 +382,7 @@ static ssize_t wdm_write
|
|||
|
||||
if (r < 0) {
|
||||
kfree(buf);
|
||||
rv = r;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -415,6 +414,7 @@ static ssize_t wdm_write
|
|||
desc->outbuf = NULL;
|
||||
clear_bit(WDM_IN_USE, &desc->flags);
|
||||
dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
|
||||
rv = usb_translate_errors(rv);
|
||||
} else {
|
||||
dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
|
||||
req->wIndex);
|
||||
|
|
@ -530,11 +530,13 @@ static int wdm_flush(struct file *file, fl_owner_t id)
|
|||
struct wdm_device *desc = file->private_data;
|
||||
|
||||
wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
|
||||
if (desc->werr < 0)
|
||||
|
||||
/* cannot dereference desc->intf if WDM_DISCONNECTING */
|
||||
if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags))
|
||||
dev_err(&desc->intf->dev, "Error in flush path: %d\n",
|
||||
desc->werr);
|
||||
|
||||
return desc->werr;
|
||||
return usb_translate_errors(desc->werr);
|
||||
}
|
||||
|
||||
static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
|
||||
|
|
@ -545,7 +547,7 @@ static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
|
|||
|
||||
spin_lock_irqsave(&desc->iuspin, flags);
|
||||
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
|
||||
mask = POLLERR;
|
||||
mask = POLLHUP | POLLERR;
|
||||
spin_unlock_irqrestore(&desc->iuspin, flags);
|
||||
goto desc_out;
|
||||
}
|
||||
|
|
@ -596,6 +598,7 @@ static int wdm_open(struct inode *inode, struct file *file)
|
|||
desc->count--;
|
||||
dev_err(&desc->intf->dev,
|
||||
"Error submitting int urb - %d\n", rv);
|
||||
rv = usb_translate_errors(rv);
|
||||
}
|
||||
} else {
|
||||
rv = 0;
|
||||
|
|
@ -621,10 +624,15 @@ static int wdm_release(struct inode *inode, struct file *file)
|
|||
mutex_unlock(&desc->wlock);
|
||||
|
||||
if (!desc->count) {
|
||||
dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
|
||||
kill_urbs(desc);
|
||||
if (!test_bit(WDM_DISCONNECTING, &desc->flags))
|
||||
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
|
||||
dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
|
||||
kill_urbs(desc);
|
||||
desc->manage_power(desc->intf, 0);
|
||||
} else {
|
||||
/* must avoid dev_printk here as desc->intf is invalid */
|
||||
pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__);
|
||||
cleanup(desc);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&wdm_mutex);
|
||||
return 0;
|
||||
|
|
@ -771,6 +779,9 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
|
|||
out:
|
||||
return rv;
|
||||
err:
|
||||
spin_lock(&wdm_device_list_lock);
|
||||
list_del(&desc->device_list);
|
||||
spin_unlock(&wdm_device_list_lock);
|
||||
cleanup(desc);
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -896,8 +907,16 @@ static void wdm_disconnect(struct usb_interface *intf)
|
|||
cancel_work_sync(&desc->rxwork);
|
||||
mutex_unlock(&desc->wlock);
|
||||
mutex_unlock(&desc->rlock);
|
||||
|
||||
/* the desc->intf pointer used as list key is now invalid */
|
||||
spin_lock(&wdm_device_list_lock);
|
||||
list_del(&desc->device_list);
|
||||
spin_unlock(&wdm_device_list_lock);
|
||||
|
||||
if (!desc->count)
|
||||
cleanup(desc);
|
||||
else
|
||||
dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count);
|
||||
mutex_unlock(&wdm_mutex);
|
||||
}
|
||||
|
||||
|
|
@ -1015,6 +1034,7 @@ static struct usb_driver wdm_driver = {
|
|||
.post_reset = wdm_post_reset,
|
||||
.id_table = wdm_ids,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(wdm_driver);
|
||||
|
|
|
|||
|
|
@ -172,27 +172,31 @@ struct usblp {
|
|||
#ifdef DEBUG
|
||||
static void usblp_dump(struct usblp *usblp)
|
||||
{
|
||||
struct device *dev = &usblp->intf->dev;
|
||||
int p;
|
||||
|
||||
dbg("usblp=0x%p", usblp);
|
||||
dbg("dev=0x%p", usblp->dev);
|
||||
dbg("present=%d", usblp->present);
|
||||
dbg("readbuf=0x%p", usblp->readbuf);
|
||||
dbg("readcount=%d", usblp->readcount);
|
||||
dbg("ifnum=%d", usblp->ifnum);
|
||||
for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) {
|
||||
dbg("protocol[%d].alt_setting=%d", p, usblp->protocol[p].alt_setting);
|
||||
dbg("protocol[%d].epwrite=%p", p, usblp->protocol[p].epwrite);
|
||||
dbg("protocol[%d].epread=%p", p, usblp->protocol[p].epread);
|
||||
}
|
||||
dbg("current_protocol=%d", usblp->current_protocol);
|
||||
dbg("minor=%d", usblp->minor);
|
||||
dbg("wstatus=%d", usblp->wstatus);
|
||||
dbg("rstatus=%d", usblp->rstatus);
|
||||
dbg("quirks=%d", usblp->quirks);
|
||||
dbg("used=%d", usblp->used);
|
||||
dbg("bidir=%d", usblp->bidir);
|
||||
dbg("device_id_string=\"%s\"",
|
||||
dev_dbg(dev, "usblp=0x%p\n", usblp);
|
||||
dev_dbg(dev, "dev=0x%p\n", usblp->dev);
|
||||
dev_dbg(dev, "present=%d\n", usblp->present);
|
||||
dev_dbg(dev, "readbuf=0x%p\n", usblp->readbuf);
|
||||
dev_dbg(dev, "readcount=%d\n", usblp->readcount);
|
||||
dev_dbg(dev, "ifnum=%d\n", usblp->ifnum);
|
||||
for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) {
|
||||
dev_dbg(dev, "protocol[%d].alt_setting=%d\n", p,
|
||||
usblp->protocol[p].alt_setting);
|
||||
dev_dbg(dev, "protocol[%d].epwrite=%p\n", p,
|
||||
usblp->protocol[p].epwrite);
|
||||
dev_dbg(dev, "protocol[%d].epread=%p\n", p,
|
||||
usblp->protocol[p].epread);
|
||||
}
|
||||
dev_dbg(dev, "current_protocol=%d\n", usblp->current_protocol);
|
||||
dev_dbg(dev, "minor=%d\n", usblp->minor);
|
||||
dev_dbg(dev, "wstatus=%d\n", usblp->wstatus);
|
||||
dev_dbg(dev, "rstatus=%d\n", usblp->rstatus);
|
||||
dev_dbg(dev, "quirks=%d\n", usblp->quirks);
|
||||
dev_dbg(dev, "used=%d\n", usblp->used);
|
||||
dev_dbg(dev, "bidir=%d\n", usblp->bidir);
|
||||
dev_dbg(dev, "device_id_string=\"%s\"\n",
|
||||
usblp->device_id_string ?
|
||||
usblp->device_id_string + 2 :
|
||||
(unsigned char *)"(null)");
|
||||
|
|
@ -262,7 +266,8 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
|
|||
retval = usb_control_msg(usblp->dev,
|
||||
dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
|
||||
request, type | dir | recip, value, index, buf, len, USBLP_CTL_TIMEOUT);
|
||||
dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d",
|
||||
dev_dbg(&usblp->intf->dev,
|
||||
"usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d\n",
|
||||
request, !!dir, recip, value, index, len, retval);
|
||||
return retval < 0 ? retval : 0;
|
||||
}
|
||||
|
|
@ -500,8 +505,9 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd),
|
||||
_IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
|
||||
dev_dbg(&usblp->intf->dev,
|
||||
"usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)\n", cmd,
|
||||
_IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
|
||||
|
||||
if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */
|
||||
|
||||
|
|
@ -594,7 +600,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp%d requested/got HP channel %ld/%d",
|
||||
dev_dbg(&usblp->intf->dev,
|
||||
"usblp%d requested/got HP channel %ld/%d\n",
|
||||
usblp->minor, arg, newChannel);
|
||||
break;
|
||||
|
||||
|
|
@ -614,7 +621,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp%d is bus=%d, device=%d",
|
||||
dev_dbg(&usblp->intf->dev,
|
||||
"usblp%d is bus=%d, device=%d\n",
|
||||
usblp->minor, twoints[0], twoints[1]);
|
||||
break;
|
||||
|
||||
|
|
@ -634,7 +642,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
|
||||
dev_dbg(&usblp->intf->dev,
|
||||
"usblp%d is VID=0x%4.4X, PID=0x%4.4X\n",
|
||||
usblp->minor, twoints[0], twoints[1]);
|
||||
break;
|
||||
|
||||
|
|
@ -987,7 +996,7 @@ static int usblp_submit_read(struct usblp *usblp)
|
|||
usblp->rcomplete = 0;
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
|
||||
dbg("error submitting urb (%d)", rc);
|
||||
dev_dbg(&usblp->intf->dev, "error submitting urb (%d)\n", rc);
|
||||
spin_lock_irqsave(&usblp->lock, flags);
|
||||
usblp->rstatus = rc;
|
||||
usblp->rcomplete = 1;
|
||||
|
|
@ -1129,7 +1138,8 @@ static int usblp_probe(struct usb_interface *intf,
|
|||
/* Analyze and pick initial alternate settings and endpoints. */
|
||||
protocol = usblp_select_alts(usblp);
|
||||
if (protocol < 0) {
|
||||
dbg("incompatible printer-class device 0x%4.4X/0x%4.4X",
|
||||
dev_dbg(&intf->dev,
|
||||
"incompatible printer-class device 0x%4.4X/0x%4.4X\n",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
retval = -ENODEV;
|
||||
|
|
@ -1158,14 +1168,14 @@ static int usblp_probe(struct usb_interface *intf,
|
|||
|
||||
retval = usb_register_dev(intf, &usblp_class);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "usblp: Not able to get a minor"
|
||||
" (base %u, slice default): %d\n",
|
||||
USBLP_MINOR_BASE, retval);
|
||||
dev_err(&intf->dev,
|
||||
"usblp: Not able to get a minor (base %u, slice default): %d\n",
|
||||
USBLP_MINOR_BASE, retval);
|
||||
goto abort_intfdata;
|
||||
}
|
||||
usblp->minor = intf->minor;
|
||||
printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d "
|
||||
"if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n",
|
||||
dev_info(&intf->dev,
|
||||
"usblp%d: USB %sdirectional printer dev %d if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n",
|
||||
usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,
|
||||
usblp->ifnum,
|
||||
usblp->protocol[usblp->current_protocol].alt_setting,
|
||||
|
|
@ -1302,7 +1312,8 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol)
|
|||
|
||||
usblp->bidir = (usblp->protocol[protocol].epread != NULL);
|
||||
usblp->current_protocol = protocol;
|
||||
dbg("usblp%d set protocol %d", usblp->minor, protocol);
|
||||
dev_dbg(&usblp->intf->dev, "usblp%d set protocol %d\n",
|
||||
usblp->minor, protocol);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1315,7 +1326,8 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
|
|||
|
||||
err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1);
|
||||
if (err < 0) {
|
||||
dbg("usblp%d: error = %d reading IEEE-1284 Device ID string",
|
||||
dev_dbg(&usblp->intf->dev,
|
||||
"usblp%d: error = %d reading IEEE-1284 Device ID string\n",
|
||||
usblp->minor, err);
|
||||
usblp->device_id_string[0] = usblp->device_id_string[1] = '\0';
|
||||
return -EIO;
|
||||
|
|
@ -1331,7 +1343,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
|
|||
length = USBLP_DEVICE_ID_SIZE - 1;
|
||||
usblp->device_id_string[length] = '\0';
|
||||
|
||||
dbg("usblp%d Device ID string [len=%d]=\"%s\"",
|
||||
dev_dbg(&usblp->intf->dev, "usblp%d Device ID string [len=%d]=\"%s\"\n",
|
||||
usblp->minor, length, &usblp->device_id_string[2]);
|
||||
|
||||
return length;
|
||||
|
|
|
|||
|
|
@ -27,58 +27,6 @@ config USB_ANNOUNCE_NEW_DEVICES
|
|||
comment "Miscellaneous USB options"
|
||||
depends on USB
|
||||
|
||||
config USB_DEVICEFS
|
||||
bool "USB device filesystem (DEPRECATED)"
|
||||
depends on USB
|
||||
---help---
|
||||
If you say Y here (and to "/proc file system support" in the "File
|
||||
systems" section, above), you will get a file /proc/bus/usb/devices
|
||||
which lists the devices currently connected to your USB bus or
|
||||
busses, and for every connected device a file named
|
||||
"/proc/bus/usb/xxx/yyy", where xxx is the bus number and yyy the
|
||||
device number; the latter files can be used by user space programs
|
||||
to talk directly to the device. These files are "virtual", meaning
|
||||
they are generated on the fly and not stored on the hard drive.
|
||||
|
||||
You may need to mount the usbfs file system to see the files, use
|
||||
mount -t usbfs none /proc/bus/usb
|
||||
|
||||
For the format of the various /proc/bus/usb/ files, please read
|
||||
<file:Documentation/usb/proc_usb_info.txt>.
|
||||
|
||||
Modern Linux systems do not use this.
|
||||
|
||||
Usbfs entries are files and not character devices; usbfs can't
|
||||
handle Access Control Lists (ACL) which are the default way to
|
||||
grant access to USB devices for untrusted users of a desktop
|
||||
system.
|
||||
|
||||
The usbfs functionality is replaced by real device-nodes managed by
|
||||
udev. These nodes lived in /dev/bus/usb and are used by libusb.
|
||||
|
||||
config USB_DEVICE_CLASS
|
||||
bool "USB device class-devices (DEPRECATED)"
|
||||
depends on USB
|
||||
default y
|
||||
---help---
|
||||
Userspace access to USB devices is granted by device-nodes exported
|
||||
directly from the usbdev in sysfs. Old versions of the driver
|
||||
core and udev needed additional class devices to export device nodes.
|
||||
|
||||
These additional devices are difficult to handle in userspace, if
|
||||
information about USB interfaces must be available. One device
|
||||
contains the device node, the other device contains the interface
|
||||
data. Both devices are at the same level in sysfs (siblings) and one
|
||||
can't access the other. The device node created directly by the
|
||||
usb device is the parent device of the interface and therefore
|
||||
easily accessible from the interface event.
|
||||
|
||||
This option provides backward compatibility for libusb device
|
||||
nodes (lsusb) when usbfs is not used, and the following udev rule
|
||||
doesn't exist:
|
||||
SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", \
|
||||
NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0644"
|
||||
|
||||
config USB_DYNAMIC_MINORS
|
||||
bool "Dynamic USB minor allocation"
|
||||
depends on USB
|
||||
|
|
@ -125,7 +73,6 @@ config USB_OTG_WHITELIST
|
|||
bool "Rely on OTG Targeted Peripherals List"
|
||||
depends on USB_OTG || EXPERT
|
||||
default y if USB_OTG
|
||||
default n if EXPERT
|
||||
help
|
||||
If you say Y here, the "otg_whitelist.h" file will be used as a
|
||||
product whitelist, so USB peripherals not listed there will be
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
|
|||
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
|
||||
|
||||
usbcore-$(CONFIG_PCI) += hcd-pci.o
|
||||
usbcore-$(CONFIG_USB_DEVICEFS) += inode.o
|
||||
usbcore-$(CONFIG_ACPI) += usb-acpi.o
|
||||
|
||||
obj-$(CONFIG_USB) += usbcore.o
|
||||
|
|
|
|||
|
|
@ -333,17 +333,14 @@ static struct async *async_getcompleted(struct dev_state *ps)
|
|||
static struct async *async_getpending(struct dev_state *ps,
|
||||
void __user *userurb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct async *as;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
list_for_each_entry(as, &ps->async_pending, asynclist)
|
||||
if (as->userurb == userurb) {
|
||||
list_del_init(&as->asynclist);
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
return as;
|
||||
}
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -398,6 +395,7 @@ static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
|
|||
__releases(ps->lock)
|
||||
__acquires(ps->lock)
|
||||
{
|
||||
struct urb *urb;
|
||||
struct async *as;
|
||||
|
||||
/* Mark all the pending URBs that match bulk_addr, up to but not
|
||||
|
|
@ -420,8 +418,11 @@ __acquires(ps->lock)
|
|||
list_for_each_entry(as, &ps->async_pending, asynclist) {
|
||||
if (as->bulk_status == AS_UNLINK) {
|
||||
as->bulk_status = 0; /* Only once */
|
||||
urb = as->urb;
|
||||
usb_get_urb(urb);
|
||||
spin_unlock(&ps->lock); /* Allow completions */
|
||||
usb_unlink_urb(as->urb);
|
||||
usb_unlink_urb(urb);
|
||||
usb_put_urb(urb);
|
||||
spin_lock(&ps->lock);
|
||||
goto rescan;
|
||||
}
|
||||
|
|
@ -472,6 +473,7 @@ static void async_completed(struct urb *urb)
|
|||
|
||||
static void destroy_async(struct dev_state *ps, struct list_head *list)
|
||||
{
|
||||
struct urb *urb;
|
||||
struct async *as;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -479,10 +481,13 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
|
|||
while (!list_empty(list)) {
|
||||
as = list_entry(list->next, struct async, asynclist);
|
||||
list_del_init(&as->asynclist);
|
||||
urb = as->urb;
|
||||
usb_get_urb(urb);
|
||||
|
||||
/* drop the spinlock so the completion handler can run */
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
usb_kill_urb(as->urb);
|
||||
usb_kill_urb(urb);
|
||||
usb_put_urb(urb);
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
|
|
@ -727,17 +732,6 @@ static int usbdev_open(struct inode *inode, struct file *file)
|
|||
if (imajor(inode) == USB_DEVICE_MAJOR)
|
||||
dev = usbdev_lookup_by_devt(inode->i_rdev);
|
||||
|
||||
#ifdef CONFIG_USB_DEVICEFS
|
||||
/* procfs file */
|
||||
if (!dev) {
|
||||
dev = inode->i_private;
|
||||
if (dev && dev->usbfs_dentry &&
|
||||
dev->usbfs_dentry->d_inode == inode)
|
||||
usb_get_dev(dev);
|
||||
else
|
||||
dev = NULL;
|
||||
}
|
||||
#endif
|
||||
mutex_unlock(&usbfs_mutex);
|
||||
|
||||
if (!dev)
|
||||
|
|
@ -1410,12 +1404,24 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)
|
|||
|
||||
static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
|
||||
{
|
||||
struct urb *urb;
|
||||
struct async *as;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
as = async_getpending(ps, arg);
|
||||
if (!as)
|
||||
if (!as) {
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
return -EINVAL;
|
||||
usb_kill_urb(as->urb);
|
||||
}
|
||||
|
||||
urb = as->urb;
|
||||
usb_get_urb(urb);
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
|
||||
usb_kill_urb(urb);
|
||||
usb_put_urb(urb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2062,44 +2068,13 @@ static void usbdev_remove(struct usb_device *udev)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_DEVICE_CLASS
|
||||
static struct class *usb_classdev_class;
|
||||
|
||||
static int usb_classdev_add(struct usb_device *dev)
|
||||
{
|
||||
struct device *cldev;
|
||||
|
||||
cldev = device_create(usb_classdev_class, &dev->dev, dev->dev.devt,
|
||||
NULL, "usbdev%d.%d", dev->bus->busnum,
|
||||
dev->devnum);
|
||||
if (IS_ERR(cldev))
|
||||
return PTR_ERR(cldev);
|
||||
dev->usb_classdev = cldev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_classdev_remove(struct usb_device *dev)
|
||||
{
|
||||
if (dev->usb_classdev)
|
||||
device_unregister(dev->usb_classdev);
|
||||
}
|
||||
|
||||
#else
|
||||
#define usb_classdev_add(dev) 0
|
||||
#define usb_classdev_remove(dev) do {} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
static int usbdev_notify(struct notifier_block *self,
|
||||
unsigned long action, void *dev)
|
||||
{
|
||||
switch (action) {
|
||||
case USB_DEVICE_ADD:
|
||||
if (usb_classdev_add(dev))
|
||||
return NOTIFY_BAD;
|
||||
break;
|
||||
case USB_DEVICE_REMOVE:
|
||||
usb_classdev_remove(dev);
|
||||
usbdev_remove(dev);
|
||||
break;
|
||||
}
|
||||
|
|
@ -2129,21 +2104,6 @@ int __init usb_devio_init(void)
|
|||
USB_DEVICE_MAJOR);
|
||||
goto error_cdev;
|
||||
}
|
||||
#ifdef CONFIG_USB_DEVICE_CLASS
|
||||
usb_classdev_class = class_create(THIS_MODULE, "usb_device");
|
||||
if (IS_ERR(usb_classdev_class)) {
|
||||
printk(KERN_ERR "Unable to register usb_device class\n");
|
||||
retval = PTR_ERR(usb_classdev_class);
|
||||
cdev_del(&usb_device_cdev);
|
||||
usb_classdev_class = NULL;
|
||||
goto out;
|
||||
}
|
||||
/* devices of this class shadow the major:minor of their parent
|
||||
* device, so clear ->dev_kobj to prevent adding duplicate entries
|
||||
* to /sys/dev
|
||||
*/
|
||||
usb_classdev_class->dev_kobj = NULL;
|
||||
#endif
|
||||
usb_register_notify(&usbdev_nb);
|
||||
out:
|
||||
return retval;
|
||||
|
|
@ -2156,9 +2116,6 @@ error_cdev:
|
|||
void usb_devio_cleanup(void)
|
||||
{
|
||||
usb_unregister_notify(&usbdev_nb);
|
||||
#ifdef CONFIG_USB_DEVICE_CLASS
|
||||
class_destroy(usb_classdev_class);
|
||||
#endif
|
||||
cdev_del(&usb_device_cdev);
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,30 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_store_new_id);
|
||||
|
||||
ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf)
|
||||
{
|
||||
struct usb_dynid *dynid;
|
||||
size_t count = 0;
|
||||
|
||||
list_for_each_entry(dynid, &dynids->list, node)
|
||||
if (dynid->id.bInterfaceClass != 0)
|
||||
count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x %02x\n",
|
||||
dynid->id.idVendor, dynid->id.idProduct,
|
||||
dynid->id.bInterfaceClass);
|
||||
else
|
||||
count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x\n",
|
||||
dynid->id.idVendor, dynid->id.idProduct);
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_show_dynids);
|
||||
|
||||
static ssize_t show_dynids(struct device_driver *driver, char *buf)
|
||||
{
|
||||
struct usb_driver *usb_drv = to_usb_driver(driver);
|
||||
|
||||
return usb_show_dynids(&usb_drv->dynids, buf);
|
||||
}
|
||||
|
||||
static ssize_t store_new_id(struct device_driver *driver,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
|
|
@ -86,7 +110,7 @@ static ssize_t store_new_id(struct device_driver *driver,
|
|||
|
||||
return usb_store_new_id(&usb_drv->dynids, driver, buf, count);
|
||||
}
|
||||
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
|
||||
static DRIVER_ATTR(new_id, S_IRUGO | S_IWUSR, show_dynids, store_new_id);
|
||||
|
||||
/**
|
||||
* store_remove_id - remove a USB device ID from this driver
|
||||
|
|
@ -127,7 +151,7 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
|
|||
return retval;
|
||||
return count;
|
||||
}
|
||||
static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
|
||||
static DRIVER_ATTR(remove_id, S_IRUGO | S_IWUSR, show_dynids, store_remove_id);
|
||||
|
||||
static int usb_create_newid_files(struct usb_driver *usb_drv)
|
||||
{
|
||||
|
|
@ -264,6 +288,7 @@ static int usb_probe_interface(struct device *dev)
|
|||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
const struct usb_device_id *id;
|
||||
int error = -ENODEV;
|
||||
int lpm_disable_error;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
|
|
@ -300,6 +325,25 @@ static int usb_probe_interface(struct device *dev)
|
|||
if (driver->supports_autosuspend)
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* If the new driver doesn't allow hub-initiated LPM, and we can't
|
||||
* disable hub-initiated LPM, then fail the probe.
|
||||
*
|
||||
* Otherwise, leaving LPM enabled should be harmless, because the
|
||||
* endpoint intervals should remain the same, and the U1/U2 timeouts
|
||||
* should remain the same.
|
||||
*
|
||||
* If we need to install alt setting 0 before probe, or another alt
|
||||
* setting during probe, that should also be fine. usb_set_interface()
|
||||
* will attempt to disable LPM, and fail if it can't disable it.
|
||||
*/
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
||||
dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
error = lpm_disable_error;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Carry out a deferred switch to altsetting 0 */
|
||||
if (intf->needs_altsetting0) {
|
||||
error = usb_set_interface(udev, intf->altsetting[0].
|
||||
|
|
@ -314,6 +358,11 @@ static int usb_probe_interface(struct device *dev)
|
|||
goto err;
|
||||
|
||||
intf->condition = USB_INTERFACE_BOUND;
|
||||
|
||||
/* If the LPM disable succeeded, balance the ref counts. */
|
||||
if (!lpm_disable_error)
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
usb_autosuspend_device(udev);
|
||||
return error;
|
||||
|
||||
|
|
@ -337,7 +386,7 @@ static int usb_unbind_interface(struct device *dev)
|
|||
struct usb_driver *driver = to_usb_driver(dev->driver);
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_device *udev;
|
||||
int error, r;
|
||||
int error, r, lpm_disable_error;
|
||||
|
||||
intf->condition = USB_INTERFACE_UNBINDING;
|
||||
|
||||
|
|
@ -345,6 +394,13 @@ static int usb_unbind_interface(struct device *dev)
|
|||
udev = interface_to_usbdev(intf);
|
||||
error = usb_autoresume_device(udev);
|
||||
|
||||
/* Hub-initiated LPM policy may change, so attempt to disable LPM until
|
||||
* the driver is unbound. If LPM isn't disabled, that's fine because it
|
||||
* wouldn't be enabled unless all the bound interfaces supported
|
||||
* hub-initiated LPM.
|
||||
*/
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
|
||||
/* Terminate all URBs for this interface unless the driver
|
||||
* supports "soft" unbinding.
|
||||
*/
|
||||
|
|
@ -378,6 +434,10 @@ static int usb_unbind_interface(struct device *dev)
|
|||
intf->condition = USB_INTERFACE_UNBOUND;
|
||||
intf->needs_remote_wakeup = 0;
|
||||
|
||||
/* Attempt to re-enable USB3 LPM, if the disable succeeded. */
|
||||
if (!lpm_disable_error)
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
|
||||
if (driver->supports_autosuspend)
|
||||
pm_runtime_disable(dev);
|
||||
|
|
@ -418,17 +478,29 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
struct usb_interface *iface, void *priv)
|
||||
{
|
||||
struct device *dev = &iface->dev;
|
||||
struct usb_device *udev;
|
||||
int retval = 0;
|
||||
int lpm_disable_error;
|
||||
|
||||
if (dev->driver)
|
||||
return -EBUSY;
|
||||
|
||||
udev = interface_to_usbdev(iface);
|
||||
|
||||
dev->driver = &driver->drvwrap.driver;
|
||||
usb_set_intfdata(iface, priv);
|
||||
iface->needs_binding = 0;
|
||||
|
||||
iface->condition = USB_INTERFACE_BOUND;
|
||||
|
||||
/* Disable LPM until this driver is bound. */
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Claimed interfaces are initially inactive (suspended) and
|
||||
* runtime-PM-enabled, but only if the driver has autosuspend
|
||||
* support. Otherwise they are marked active, to prevent the
|
||||
|
|
@ -447,6 +519,10 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
if (device_is_registered(dev))
|
||||
retval = device_bind_driver(dev);
|
||||
|
||||
/* Attempt to re-enable USB3 LPM, if the disable was successful. */
|
||||
if (!lpm_disable_error)
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_driver_claim_interface);
|
||||
|
|
@ -726,16 +802,6 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_DEVICEFS
|
||||
/* If this is available, userspace programs can directly read
|
||||
* all the device descriptors we don't tell them about. Or
|
||||
* act as usermode drivers.
|
||||
*/
|
||||
if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d",
|
||||
usb_dev->bus->busnum, usb_dev->devnum))
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
|
||||
/* per-device configurations are common */
|
||||
if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
|
||||
le16_to_cpu(usb_dev->descriptor.idVendor),
|
||||
|
|
@ -788,15 +854,13 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
|
|||
|
||||
retval = driver_register(&new_udriver->drvwrap.driver);
|
||||
|
||||
if (!retval) {
|
||||
if (!retval)
|
||||
pr_info("%s: registered new device driver %s\n",
|
||||
usbcore_name, new_udriver->name);
|
||||
usbfs_update_special();
|
||||
} else {
|
||||
else
|
||||
printk(KERN_ERR "%s: error %d registering device "
|
||||
" driver %s\n",
|
||||
usbcore_name, retval, new_udriver->name);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -815,7 +879,6 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver)
|
|||
usbcore_name, udriver->name);
|
||||
|
||||
driver_unregister(&udriver->drvwrap.driver);
|
||||
usbfs_update_special();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
|
||||
|
||||
|
|
@ -856,8 +919,6 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
|
|||
if (retval)
|
||||
goto out;
|
||||
|
||||
usbfs_update_special();
|
||||
|
||||
retval = usb_create_newid_files(new_driver);
|
||||
if (retval)
|
||||
goto out_newid;
|
||||
|
|
@ -897,8 +958,6 @@ void usb_deregister(struct usb_driver *driver)
|
|||
usb_remove_newid_files(driver);
|
||||
driver_unregister(&driver->drvwrap.driver);
|
||||
usb_free_dynids(driver);
|
||||
|
||||
usbfs_update_special();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_deregister);
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ int usb_register_dev(struct usb_interface *intf,
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
dev_dbg(&intf->dev, "looking for a minor, starting at %d", minor_base);
|
||||
dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);
|
||||
|
||||
down_write(&minor_rwsem);
|
||||
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
|
||||
|
|
@ -239,7 +239,7 @@ void usb_deregister_dev(struct usb_interface *intf,
|
|||
if (intf->minor == -1)
|
||||
return;
|
||||
|
||||
dbg ("removing %d minor", intf->minor);
|
||||
dev_dbg(&intf->dev, "removing %d minor\n", intf->minor);
|
||||
|
||||
down_write(&minor_rwsem);
|
||||
usb_minors[intf->minor] = NULL;
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ static const u8 usb3_rh_dev_descriptor[18] = {
|
|||
0x03, /* __u8 bDeviceProtocol; USB 3.0 hub */
|
||||
0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */
|
||||
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
||||
0x03, 0x00, /* __le16 idProduct; device 0x0003 */
|
||||
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
|
|||
0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */
|
||||
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
|
||||
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
||||
0x02, 0x00, /* __le16 idProduct; device 0x0002 */
|
||||
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
|
|||
0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
|
||||
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
|
||||
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
||||
0x01, 0x00, /* __le16 idProduct; device 0x0001 */
|
||||
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
||||
|
||||
|
|
@ -997,6 +997,15 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
dev_name(&usb_dev->dev), retval);
|
||||
return (retval < 0) ? retval : -EMSGSIZE;
|
||||
}
|
||||
if (usb_dev->speed == USB_SPEED_SUPER) {
|
||||
retval = usb_get_bos_descriptor(usb_dev);
|
||||
if (retval < 0) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
dev_dbg(parent_dev, "can't read %s bos descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = usb_new_device (usb_dev);
|
||||
if (retval) {
|
||||
|
|
|
|||
|
|
@ -177,6 +177,228 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
|
|||
return usb_get_intfdata(hdev->actconfig->interface[0]);
|
||||
}
|
||||
|
||||
static int usb_device_supports_lpm(struct usb_device *udev)
|
||||
{
|
||||
/* USB 2.1 (and greater) devices indicate LPM support through
|
||||
* their USB 2.0 Extended Capabilities BOS descriptor.
|
||||
*/
|
||||
if (udev->speed == USB_SPEED_HIGH) {
|
||||
if (udev->bos->ext_cap &&
|
||||
(USB_LPM_SUPPORT &
|
||||
le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* All USB 3.0 must support LPM, but we need their max exit latency
|
||||
* information from the SuperSpeed Extended Capabilities BOS descriptor.
|
||||
*/
|
||||
if (!udev->bos->ss_cap) {
|
||||
dev_warn(&udev->dev, "No LPM exit latency info found. "
|
||||
"Power management will be impacted.\n");
|
||||
return 0;
|
||||
}
|
||||
if (udev->parent->lpm_capable)
|
||||
return 1;
|
||||
|
||||
dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. "
|
||||
"Power management will be impacted.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the Maximum Exit Latency (MEL) for the host to initiate a transition from
|
||||
* either U1 or U2.
|
||||
*/
|
||||
static void usb_set_lpm_mel(struct usb_device *udev,
|
||||
struct usb3_lpm_parameters *udev_lpm_params,
|
||||
unsigned int udev_exit_latency,
|
||||
struct usb_hub *hub,
|
||||
struct usb3_lpm_parameters *hub_lpm_params,
|
||||
unsigned int hub_exit_latency)
|
||||
{
|
||||
unsigned int total_mel;
|
||||
unsigned int device_mel;
|
||||
unsigned int hub_mel;
|
||||
|
||||
/*
|
||||
* Calculate the time it takes to transition all links from the roothub
|
||||
* to the parent hub into U0. The parent hub must then decode the
|
||||
* packet (hub header decode latency) to figure out which port it was
|
||||
* bound for.
|
||||
*
|
||||
* The Hub Header decode latency is expressed in 0.1us intervals (0x1
|
||||
* means 0.1us). Multiply that by 100 to get nanoseconds.
|
||||
*/
|
||||
total_mel = hub_lpm_params->mel +
|
||||
(hub->descriptor->u.ss.bHubHdrDecLat * 100);
|
||||
|
||||
/*
|
||||
* How long will it take to transition the downstream hub's port into
|
||||
* U0? The greater of either the hub exit latency or the device exit
|
||||
* latency.
|
||||
*
|
||||
* The BOS U1/U2 exit latencies are expressed in 1us intervals.
|
||||
* Multiply that by 1000 to get nanoseconds.
|
||||
*/
|
||||
device_mel = udev_exit_latency * 1000;
|
||||
hub_mel = hub_exit_latency * 1000;
|
||||
if (device_mel > hub_mel)
|
||||
total_mel += device_mel;
|
||||
else
|
||||
total_mel += hub_mel;
|
||||
|
||||
udev_lpm_params->mel = total_mel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the maximum Device to Host Exit Latency (PEL) for the device to initiate
|
||||
* a transition from either U1 or U2.
|
||||
*/
|
||||
static void usb_set_lpm_pel(struct usb_device *udev,
|
||||
struct usb3_lpm_parameters *udev_lpm_params,
|
||||
unsigned int udev_exit_latency,
|
||||
struct usb_hub *hub,
|
||||
struct usb3_lpm_parameters *hub_lpm_params,
|
||||
unsigned int hub_exit_latency,
|
||||
unsigned int port_to_port_exit_latency)
|
||||
{
|
||||
unsigned int first_link_pel;
|
||||
unsigned int hub_pel;
|
||||
|
||||
/*
|
||||
* First, the device sends an LFPS to transition the link between the
|
||||
* device and the parent hub into U0. The exit latency is the bigger of
|
||||
* the device exit latency or the hub exit latency.
|
||||
*/
|
||||
if (udev_exit_latency > hub_exit_latency)
|
||||
first_link_pel = udev_exit_latency * 1000;
|
||||
else
|
||||
first_link_pel = hub_exit_latency * 1000;
|
||||
|
||||
/*
|
||||
* When the hub starts to receive the LFPS, there is a slight delay for
|
||||
* it to figure out that one of the ports is sending an LFPS. Then it
|
||||
* will forward the LFPS to its upstream link. The exit latency is the
|
||||
* delay, plus the PEL that we calculated for this hub.
|
||||
*/
|
||||
hub_pel = port_to_port_exit_latency * 1000 + hub_lpm_params->pel;
|
||||
|
||||
/*
|
||||
* According to figure C-7 in the USB 3.0 spec, the PEL for this device
|
||||
* is the greater of the two exit latencies.
|
||||
*/
|
||||
if (first_link_pel > hub_pel)
|
||||
udev_lpm_params->pel = first_link_pel;
|
||||
else
|
||||
udev_lpm_params->pel = hub_pel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the System Exit Latency (SEL) to indicate the total worst-case time from
|
||||
* when a device initiates a transition to U0, until when it will receive the
|
||||
* first packet from the host controller.
|
||||
*
|
||||
* Section C.1.5.1 describes the four components to this:
|
||||
* - t1: device PEL
|
||||
* - t2: time for the ERDY to make it from the device to the host.
|
||||
* - t3: a host-specific delay to process the ERDY.
|
||||
* - t4: time for the packet to make it from the host to the device.
|
||||
*
|
||||
* t3 is specific to both the xHCI host and the platform the host is integrated
|
||||
* into. The Intel HW folks have said it's negligible, FIXME if a different
|
||||
* vendor says otherwise.
|
||||
*/
|
||||
static void usb_set_lpm_sel(struct usb_device *udev,
|
||||
struct usb3_lpm_parameters *udev_lpm_params)
|
||||
{
|
||||
struct usb_device *parent;
|
||||
unsigned int num_hubs;
|
||||
unsigned int total_sel;
|
||||
|
||||
/* t1 = device PEL */
|
||||
total_sel = udev_lpm_params->pel;
|
||||
/* How many external hubs are in between the device & the root port. */
|
||||
for (parent = udev->parent, num_hubs = 0; parent->parent;
|
||||
parent = parent->parent)
|
||||
num_hubs++;
|
||||
/* t2 = 2.1us + 250ns * (num_hubs - 1) */
|
||||
if (num_hubs > 0)
|
||||
total_sel += 2100 + 250 * (num_hubs - 1);
|
||||
|
||||
/* t4 = 250ns * num_hubs */
|
||||
total_sel += 250 * num_hubs;
|
||||
|
||||
udev_lpm_params->sel = total_sel;
|
||||
}
|
||||
|
||||
static void usb_set_lpm_parameters(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hub *hub;
|
||||
unsigned int port_to_port_delay;
|
||||
unsigned int udev_u1_del;
|
||||
unsigned int udev_u2_del;
|
||||
unsigned int hub_u1_del;
|
||||
unsigned int hub_u2_del;
|
||||
|
||||
if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER)
|
||||
return;
|
||||
|
||||
hub = hdev_to_hub(udev->parent);
|
||||
/* It doesn't take time to transition the roothub into U0, since it
|
||||
* doesn't have an upstream link.
|
||||
*/
|
||||
if (!hub)
|
||||
return;
|
||||
|
||||
udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
|
||||
udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
|
||||
hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
|
||||
hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
|
||||
|
||||
usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
|
||||
hub, &udev->parent->u1_params, hub_u1_del);
|
||||
|
||||
usb_set_lpm_mel(udev, &udev->u2_params, udev_u2_del,
|
||||
hub, &udev->parent->u2_params, hub_u2_del);
|
||||
|
||||
/*
|
||||
* Appendix C, section C.2.2.2, says that there is a slight delay from
|
||||
* when the parent hub notices the downstream port is trying to
|
||||
* transition to U0 to when the hub initiates a U0 transition on its
|
||||
* upstream port. The section says the delays are tPort2PortU1EL and
|
||||
* tPort2PortU2EL, but it doesn't define what they are.
|
||||
*
|
||||
* The hub chapter, sections 10.4.2.4 and 10.4.2.5 seem to be talking
|
||||
* about the same delays. Use the maximum delay calculations from those
|
||||
* sections. For U1, it's tHubPort2PortExitLat, which is 1us max. For
|
||||
* U2, it's tHubPort2PortExitLat + U2DevExitLat - U1DevExitLat. I
|
||||
* assume the device exit latencies they are talking about are the hub
|
||||
* exit latencies.
|
||||
*
|
||||
* What do we do if the U2 exit latency is less than the U1 exit
|
||||
* latency? It's possible, although not likely...
|
||||
*/
|
||||
port_to_port_delay = 1;
|
||||
|
||||
usb_set_lpm_pel(udev, &udev->u1_params, udev_u1_del,
|
||||
hub, &udev->parent->u1_params, hub_u1_del,
|
||||
port_to_port_delay);
|
||||
|
||||
if (hub_u2_del > hub_u1_del)
|
||||
port_to_port_delay = 1 + hub_u2_del - hub_u1_del;
|
||||
else
|
||||
port_to_port_delay = 1 + hub_u1_del;
|
||||
|
||||
usb_set_lpm_pel(udev, &udev->u2_params, udev_u2_del,
|
||||
hub, &udev->parent->u2_params, hub_u2_del,
|
||||
port_to_port_delay);
|
||||
|
||||
/* Now that we've got PEL, calculate SEL. */
|
||||
usb_set_lpm_sel(udev, &udev->u1_params);
|
||||
usb_set_lpm_sel(udev, &udev->u2_params);
|
||||
}
|
||||
|
||||
/* USB 2.0 spec Section 11.24.4.5 */
|
||||
static int get_hub_descriptor(struct usb_device *hdev, void *data)
|
||||
{
|
||||
|
|
@ -2480,6 +2702,12 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
if (udev->usb2_hw_lpm_enabled == 1)
|
||||
usb_set_usb2_hardware_lpm(udev, 0);
|
||||
|
||||
if (usb_unlocked_disable_lpm(udev)) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* see 7.1.7.6 */
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
status = set_port_feature(hub->hdev,
|
||||
|
|
@ -2499,6 +2727,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
/* Try to enable USB2 hardware LPM again */
|
||||
if (udev->usb2_hw_lpm_capable == 1)
|
||||
usb_set_usb2_hardware_lpm(udev, 1);
|
||||
|
||||
/* Try to enable USB3 LPM again */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
/* System sleep transitions should never fail */
|
||||
if (!PMSG_IS_AUTO(msg))
|
||||
status = 0;
|
||||
|
|
@ -2696,6 +2931,9 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
|||
/* Try to enable USB2 hardware LPM */
|
||||
if (udev->usb2_hw_lpm_capable == 1)
|
||||
usb_set_usb2_hardware_lpm(udev, 1);
|
||||
|
||||
/* Try to enable USB3 LPM */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
|
@ -2824,11 +3062,429 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
|
||||
|
||||
static const char * const usb3_lpm_names[] = {
|
||||
"U0",
|
||||
"U1",
|
||||
"U2",
|
||||
"U3",
|
||||
};
|
||||
|
||||
/*
|
||||
* Send a Set SEL control transfer to the device, prior to enabling
|
||||
* device-initiated U1 or U2. This lets the device know the exit latencies from
|
||||
* the time the device initiates a U1 or U2 exit, to the time it will receive a
|
||||
* packet from the host.
|
||||
*
|
||||
* This function will fail if the SEL or PEL values for udev are greater than
|
||||
* the maximum allowed values for the link state to be enabled.
|
||||
*/
|
||||
static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
|
||||
{
|
||||
struct usb_set_sel_req *sel_values;
|
||||
unsigned long long u1_sel;
|
||||
unsigned long long u1_pel;
|
||||
unsigned long long u2_sel;
|
||||
unsigned long long u2_pel;
|
||||
int ret;
|
||||
|
||||
/* Convert SEL and PEL stored in ns to us */
|
||||
u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
|
||||
u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
|
||||
u2_sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
|
||||
u2_pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
|
||||
|
||||
/*
|
||||
* Make sure that the calculated SEL and PEL values for the link
|
||||
* state we're enabling aren't bigger than the max SEL/PEL
|
||||
* value that will fit in the SET SEL control transfer.
|
||||
* Otherwise the device would get an incorrect idea of the exit
|
||||
* latency for the link state, and could start a device-initiated
|
||||
* U1/U2 when the exit latencies are too high.
|
||||
*/
|
||||
if ((state == USB3_LPM_U1 &&
|
||||
(u1_sel > USB3_LPM_MAX_U1_SEL_PEL ||
|
||||
u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) ||
|
||||
(state == USB3_LPM_U2 &&
|
||||
(u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
|
||||
u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) {
|
||||
dev_dbg(&udev->dev, "Device-initiated %s disabled due "
|
||||
"to long SEL %llu ms or PEL %llu ms\n",
|
||||
usb3_lpm_names[state], u1_sel, u1_pel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're enabling device-initiated LPM for one link state,
|
||||
* but the other link state has a too high SEL or PEL value,
|
||||
* just set those values to the max in the Set SEL request.
|
||||
*/
|
||||
if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL)
|
||||
u1_sel = USB3_LPM_MAX_U1_SEL_PEL;
|
||||
|
||||
if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL)
|
||||
u1_pel = USB3_LPM_MAX_U1_SEL_PEL;
|
||||
|
||||
if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL)
|
||||
u2_sel = USB3_LPM_MAX_U2_SEL_PEL;
|
||||
|
||||
if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL)
|
||||
u2_pel = USB3_LPM_MAX_U2_SEL_PEL;
|
||||
|
||||
/*
|
||||
* usb_enable_lpm() can be called as part of a failed device reset,
|
||||
* which may be initiated by an error path of a mass storage driver.
|
||||
* Therefore, use GFP_NOIO.
|
||||
*/
|
||||
sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
|
||||
if (!sel_values)
|
||||
return -ENOMEM;
|
||||
|
||||
sel_values->u1_sel = u1_sel;
|
||||
sel_values->u1_pel = u1_pel;
|
||||
sel_values->u2_sel = cpu_to_le16(u2_sel);
|
||||
sel_values->u2_pel = cpu_to_le16(u2_pel);
|
||||
|
||||
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_SEL,
|
||||
USB_RECIP_DEVICE,
|
||||
0, 0,
|
||||
sel_values, sizeof *(sel_values),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
kfree(sel_values);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable device-initiated U1 or U2 transitions.
|
||||
*/
|
||||
static int usb_set_device_initiated_lpm(struct usb_device *udev,
|
||||
enum usb3_link_state state, bool enable)
|
||||
{
|
||||
int ret;
|
||||
int feature;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
feature = USB_DEVICE_U1_ENABLE;
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
feature = USB_DEVICE_U2_ENABLE;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
|
||||
__func__, enable ? "enable" : "disable");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (udev->state != USB_STATE_CONFIGURED) {
|
||||
dev_dbg(&udev->dev, "%s: Can't %s %s state "
|
||||
"for unconfigured device.\n",
|
||||
__func__, enable ? "enable" : "disable",
|
||||
usb3_lpm_names[state]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
/*
|
||||
* First, let the device know about the exit latencies
|
||||
* associated with the link state we're about to enable.
|
||||
*/
|
||||
ret = usb_req_set_sel(udev, state);
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev, "Set SEL for device-initiated "
|
||||
"%s failed.\n", usb3_lpm_names[state]);
|
||||
return -EBUSY;
|
||||
}
|
||||
/*
|
||||
* Now send the control transfer to enable device-initiated LPM
|
||||
* for either U1 or U2.
|
||||
*/
|
||||
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE,
|
||||
USB_RECIP_DEVICE,
|
||||
feature,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
} else {
|
||||
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_RECIP_DEVICE,
|
||||
feature,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
|
||||
enable ? "Enable" : "Disable",
|
||||
usb3_lpm_names[state]);
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_set_lpm_timeout(struct usb_device *udev,
|
||||
enum usb3_link_state state, int timeout)
|
||||
{
|
||||
int ret;
|
||||
int feature;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
feature = USB_PORT_FEAT_U1_TIMEOUT;
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
feature = USB_PORT_FEAT_U2_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't set timeout for non-U1 or U2 state.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state == USB3_LPM_U1 && timeout > USB3_LPM_U1_MAX_TIMEOUT &&
|
||||
timeout != USB3_LPM_DEVICE_INITIATED) {
|
||||
dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, "
|
||||
"which is a reserved value.\n",
|
||||
usb3_lpm_names[state], timeout);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = set_port_feature(udev->parent,
|
||||
USB_PORT_LPM_TIMEOUT(timeout) | udev->portnum,
|
||||
feature);
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x,"
|
||||
"error code %i\n", usb3_lpm_names[state],
|
||||
timeout, ret);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (state == USB3_LPM_U1)
|
||||
udev->u1_params.timeout = timeout;
|
||||
else
|
||||
udev->u2_params.timeout = timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the hub-initiated U1/U2 idle timeouts, and enable device-initiated
|
||||
* U1/U2 entry.
|
||||
*
|
||||
* We will attempt to enable U1 or U2, but there are no guarantees that the
|
||||
* control transfers to set the hub timeout or enable device-initiated U1/U2
|
||||
* will be successful.
|
||||
*
|
||||
* If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI
|
||||
* driver know about it. If that call fails, it should be harmless, and just
|
||||
* take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
|
||||
*/
|
||||
static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
int timeout;
|
||||
|
||||
/* We allow the host controller to set the U1/U2 timeout internally
|
||||
* first, so that it can change its schedule to account for the
|
||||
* additional latency to send data to a device in a lower power
|
||||
* link state.
|
||||
*/
|
||||
timeout = hcd->driver->enable_usb3_lpm_timeout(hcd, udev, state);
|
||||
|
||||
/* xHCI host controller doesn't want to enable this LPM state. */
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout < 0) {
|
||||
dev_warn(&udev->dev, "Could not enable %s link state, "
|
||||
"xHCI error %i.\n", usb3_lpm_names[state],
|
||||
timeout);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usb_set_lpm_timeout(udev, state, timeout))
|
||||
/* If we can't set the parent hub U1/U2 timeout,
|
||||
* device-initiated LPM won't be allowed either, so let the xHCI
|
||||
* host know that this link state won't be enabled.
|
||||
*/
|
||||
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
|
||||
|
||||
/* Only a configured device will accept the Set Feature U1/U2_ENABLE */
|
||||
else if (udev->actconfig)
|
||||
usb_set_device_initiated_lpm(udev, state, true);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
|
||||
* U1/U2 entry.
|
||||
*
|
||||
* If this function returns -EBUSY, the parent hub will still allow U1/U2 entry.
|
||||
* If zero is returned, the parent will not allow the link to go into U1/U2.
|
||||
*
|
||||
* If zero is returned, device-initiated U1/U2 entry may still be enabled, but
|
||||
* it won't have an effect on the bus link state because the parent hub will
|
||||
* still disallow device-initiated U1/U2 entry.
|
||||
*
|
||||
* If zero is returned, the xHCI host controller may still think U1/U2 entry is
|
||||
* possible. The result will be slightly more bus bandwidth will be taken up
|
||||
* (to account for U1/U2 exit latency), but it should be harmless.
|
||||
*/
|
||||
static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
int feature;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
feature = USB_PORT_FEAT_U1_TIMEOUT;
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
feature = USB_PORT_FEAT_U2_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (usb_set_lpm_timeout(udev, state, 0))
|
||||
return -EBUSY;
|
||||
|
||||
usb_set_device_initiated_lpm(udev, state, false);
|
||||
|
||||
if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
|
||||
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
|
||||
"bus schedule bandwidth may be impacted.\n",
|
||||
usb3_lpm_names[state]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable hub-initiated and device-initiated U1 and U2 entry.
|
||||
* Caller must own the bandwidth_mutex.
|
||||
*
|
||||
* This will call usb_enable_lpm() on failure, which will decrement
|
||||
* lpm_disable_count, and will re-enable LPM if lpm_disable_count reaches zero.
|
||||
*/
|
||||
int usb_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable)
|
||||
return 0;
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (!hcd || !hcd->driver->disable_usb3_lpm_timeout)
|
||||
return 0;
|
||||
|
||||
udev->lpm_disable_count++;
|
||||
if ((udev->u1_params.timeout == 0 && udev->u1_params.timeout == 0))
|
||||
return 0;
|
||||
|
||||
/* If LPM is enabled, attempt to disable it. */
|
||||
if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
|
||||
goto enable_lpm;
|
||||
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
|
||||
goto enable_lpm;
|
||||
|
||||
return 0;
|
||||
|
||||
enable_lpm:
|
||||
usb_enable_lpm(udev);
|
||||
return -EBUSY;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_lpm);
|
||||
|
||||
/* Grab the bandwidth_mutex before calling usb_disable_lpm() */
|
||||
int usb_unlocked_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
int ret;
|
||||
|
||||
if (!hcd)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
ret = usb_disable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
|
||||
|
||||
/*
|
||||
* Attempt to enable device-initiated and hub-initiated U1 and U2 entry. The
|
||||
* xHCI host policy may prevent U1 or U2 from being enabled.
|
||||
*
|
||||
* Other callers may have disabled link PM, so U1 and U2 entry will be disabled
|
||||
* until the lpm_disable_count drops to zero. Caller must own the
|
||||
* bandwidth_mutex.
|
||||
*/
|
||||
void usb_enable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable)
|
||||
return;
|
||||
|
||||
udev->lpm_disable_count--;
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
/* Double check that we can both enable and disable LPM.
|
||||
* Device must be configured to accept set feature U1/U2 timeout.
|
||||
*/
|
||||
if (!hcd || !hcd->driver->enable_usb3_lpm_timeout ||
|
||||
!hcd->driver->disable_usb3_lpm_timeout)
|
||||
return;
|
||||
|
||||
if (udev->lpm_disable_count > 0)
|
||||
return;
|
||||
|
||||
usb_enable_link_state(hcd, udev, USB3_LPM_U1);
|
||||
usb_enable_link_state(hcd, udev, USB3_LPM_U2);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_enable_lpm);
|
||||
|
||||
/* Grab the bandwidth_mutex before calling usb_enable_lpm() */
|
||||
void usb_unlocked_enable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
if (!hcd)
|
||||
return;
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
|
||||
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
#define hub_suspend NULL
|
||||
#define hub_resume NULL
|
||||
#define hub_reset_resume NULL
|
||||
|
||||
int usb_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_lpm);
|
||||
|
||||
void usb_enable_lpm(struct usb_device *udev) { }
|
||||
EXPORT_SYMBOL_GPL(usb_enable_lpm);
|
||||
|
||||
int usb_unlocked_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
|
||||
|
||||
void usb_unlocked_enable_lpm(struct usb_device *udev) { }
|
||||
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -3208,9 +3864,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
|
||||
retval = usb_get_bos_descriptor(udev);
|
||||
if (!retval) {
|
||||
if (udev->bos->ext_cap && (USB_LPM_SUPPORT &
|
||||
le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
|
||||
udev->lpm_capable = 1;
|
||||
udev->lpm_capable = usb_device_supports_lpm(udev);
|
||||
usb_set_lpm_parameters(udev);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4042,11 +4697,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
goto done;
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM while we reset the device and reinstall the alt settings.
|
||||
* Device-initiated LPM settings, and system exit latency settings are
|
||||
* cleared when the device is reset, so we have to set them up again.
|
||||
*/
|
||||
ret = usb_disable_lpm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto done;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev,
|
||||
"Busted HC? Not enough HCD resources for "
|
||||
"old configuration.\n");
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
|
@ -4058,6 +4724,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
dev_err(&udev->dev,
|
||||
"can't restore configuration #%d (error=%d)\n",
|
||||
udev->actconfig->desc.bConfigurationValue, ret);
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
|
@ -4096,10 +4763,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
desc->bInterfaceNumber,
|
||||
desc->bAlternateSetting,
|
||||
ret);
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
goto re_enumerate;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the alt settings are re-installed, enable LPM. */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
done:
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,748 +0,0 @@
|
|||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* inode.c -- Inode/Dentry functions for the USB device file system.
|
||||
*
|
||||
* Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
||||
* Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* History:
|
||||
* 0.1 04.01.2000 Created
|
||||
* 0.2 10.12.2001 converted to use the vfs layer better
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "usb.h"
|
||||
|
||||
#define USBFS_DEFAULT_DEVMODE (S_IWUSR | S_IRUGO)
|
||||
#define USBFS_DEFAULT_BUSMODE (S_IXUGO | S_IRUGO)
|
||||
#define USBFS_DEFAULT_LISTMODE S_IRUGO
|
||||
|
||||
static const struct file_operations default_file_operations;
|
||||
static struct vfsmount *usbfs_mount;
|
||||
static int usbfs_mount_count; /* = 0 */
|
||||
|
||||
static struct dentry *devices_usbfs_dentry;
|
||||
static int num_buses; /* = 0 */
|
||||
|
||||
static uid_t devuid; /* = 0 */
|
||||
static uid_t busuid; /* = 0 */
|
||||
static uid_t listuid; /* = 0 */
|
||||
static gid_t devgid; /* = 0 */
|
||||
static gid_t busgid; /* = 0 */
|
||||
static gid_t listgid; /* = 0 */
|
||||
static umode_t devmode = USBFS_DEFAULT_DEVMODE;
|
||||
static umode_t busmode = USBFS_DEFAULT_BUSMODE;
|
||||
static umode_t listmode = USBFS_DEFAULT_LISTMODE;
|
||||
|
||||
static int usbfs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
{
|
||||
if (devuid != 0)
|
||||
seq_printf(seq, ",devuid=%u", devuid);
|
||||
if (devgid != 0)
|
||||
seq_printf(seq, ",devgid=%u", devgid);
|
||||
if (devmode != USBFS_DEFAULT_DEVMODE)
|
||||
seq_printf(seq, ",devmode=%o", devmode);
|
||||
if (busuid != 0)
|
||||
seq_printf(seq, ",busuid=%u", busuid);
|
||||
if (busgid != 0)
|
||||
seq_printf(seq, ",busgid=%u", busgid);
|
||||
if (busmode != USBFS_DEFAULT_BUSMODE)
|
||||
seq_printf(seq, ",busmode=%o", busmode);
|
||||
if (listuid != 0)
|
||||
seq_printf(seq, ",listuid=%u", listuid);
|
||||
if (listgid != 0)
|
||||
seq_printf(seq, ",listgid=%u", listgid);
|
||||
if (listmode != USBFS_DEFAULT_LISTMODE)
|
||||
seq_printf(seq, ",listmode=%o", listmode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
Opt_devuid, Opt_devgid, Opt_devmode,
|
||||
Opt_busuid, Opt_busgid, Opt_busmode,
|
||||
Opt_listuid, Opt_listgid, Opt_listmode,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{Opt_devuid, "devuid=%u"},
|
||||
{Opt_devgid, "devgid=%u"},
|
||||
{Opt_devmode, "devmode=%o"},
|
||||
{Opt_busuid, "busuid=%u"},
|
||||
{Opt_busgid, "busgid=%u"},
|
||||
{Opt_busmode, "busmode=%o"},
|
||||
{Opt_listuid, "listuid=%u"},
|
||||
{Opt_listgid, "listgid=%u"},
|
||||
{Opt_listmode, "listmode=%o"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
static int parse_options(struct super_block *s, char *data)
|
||||
{
|
||||
char *p;
|
||||
int option;
|
||||
|
||||
/* (re)set to defaults. */
|
||||
devuid = 0;
|
||||
busuid = 0;
|
||||
listuid = 0;
|
||||
devgid = 0;
|
||||
busgid = 0;
|
||||
listgid = 0;
|
||||
devmode = USBFS_DEFAULT_DEVMODE;
|
||||
busmode = USBFS_DEFAULT_BUSMODE;
|
||||
listmode = USBFS_DEFAULT_LISTMODE;
|
||||
|
||||
while ((p = strsep(&data, ",")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int token;
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_devuid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
devuid = option;
|
||||
break;
|
||||
case Opt_devgid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
devgid = option;
|
||||
break;
|
||||
case Opt_devmode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
devmode = option & S_IRWXUGO;
|
||||
break;
|
||||
case Opt_busuid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
busuid = option;
|
||||
break;
|
||||
case Opt_busgid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
busgid = option;
|
||||
break;
|
||||
case Opt_busmode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
busmode = option & S_IRWXUGO;
|
||||
break;
|
||||
case Opt_listuid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
listuid = option;
|
||||
break;
|
||||
case Opt_listgid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
listgid = option;
|
||||
break;
|
||||
case Opt_listmode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
listmode = option & S_IRWXUGO;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "usbfs: unrecognised mount option "
|
||||
"\"%s\" or missing value\n", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_special(struct dentry *special)
|
||||
{
|
||||
special->d_inode->i_uid = listuid;
|
||||
special->d_inode->i_gid = listgid;
|
||||
special->d_inode->i_mode = S_IFREG | listmode;
|
||||
}
|
||||
|
||||
static void update_dev(struct dentry *dev)
|
||||
{
|
||||
dev->d_inode->i_uid = devuid;
|
||||
dev->d_inode->i_gid = devgid;
|
||||
dev->d_inode->i_mode = S_IFREG | devmode;
|
||||
}
|
||||
|
||||
static void update_bus(struct dentry *bus)
|
||||
{
|
||||
struct dentry *dev = NULL;
|
||||
|
||||
bus->d_inode->i_uid = busuid;
|
||||
bus->d_inode->i_gid = busgid;
|
||||
bus->d_inode->i_mode = S_IFDIR | busmode;
|
||||
|
||||
mutex_lock(&bus->d_inode->i_mutex);
|
||||
|
||||
list_for_each_entry(dev, &bus->d_subdirs, d_u.d_child)
|
||||
if (dev->d_inode)
|
||||
update_dev(dev);
|
||||
|
||||
mutex_unlock(&bus->d_inode->i_mutex);
|
||||
}
|
||||
|
||||
static void update_sb(struct super_block *sb)
|
||||
{
|
||||
struct dentry *root = sb->s_root;
|
||||
struct dentry *bus = NULL;
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
mutex_lock_nested(&root->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
|
||||
list_for_each_entry(bus, &root->d_subdirs, d_u.d_child) {
|
||||
if (bus->d_inode) {
|
||||
switch (S_IFMT & bus->d_inode->i_mode) {
|
||||
case S_IFDIR:
|
||||
update_bus(bus);
|
||||
break;
|
||||
case S_IFREG:
|
||||
update_special(bus);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "usbfs: Unknown node %s "
|
||||
"mode %x found on remount!\n",
|
||||
bus->d_name.name, bus->d_inode->i_mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&root->d_inode->i_mutex);
|
||||
}
|
||||
|
||||
static int remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
/* If this is not a real mount,
|
||||
* i.e. it's a simple_pin_fs from create_special_files,
|
||||
* then ignore it.
|
||||
*/
|
||||
if (*flags & MS_KERNMOUNT)
|
||||
return 0;
|
||||
|
||||
if (parse_options(sb, data)) {
|
||||
printk(KERN_WARNING "usbfs: mount parameter error.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (usbfs_mount)
|
||||
update_sb(usbfs_mount->mnt_sb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct inode *usbfs_get_inode (struct super_block *sb, umode_t mode, dev_t dev)
|
||||
{
|
||||
struct inode *inode = new_inode(sb);
|
||||
|
||||
if (inode) {
|
||||
inode->i_ino = get_next_ino();
|
||||
inode_init_owner(inode, NULL, mode);
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
switch (mode & S_IFMT) {
|
||||
default:
|
||||
init_special_inode(inode, mode, dev);
|
||||
break;
|
||||
case S_IFREG:
|
||||
inode->i_fop = &default_file_operations;
|
||||
break;
|
||||
case S_IFDIR:
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
|
||||
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
||||
inc_nlink(inode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
|
||||
/* SMP-safe */
|
||||
static int usbfs_mknod (struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
dev_t dev)
|
||||
{
|
||||
struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
|
||||
int error = -EPERM;
|
||||
|
||||
if (dentry->d_inode)
|
||||
return -EEXIST;
|
||||
|
||||
if (inode) {
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
error = 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
int res;
|
||||
|
||||
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
|
||||
res = usbfs_mknod (dir, dentry, mode, 0);
|
||||
if (!res)
|
||||
inc_nlink(dir);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int usbfs_create (struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
mode = (mode & S_IALLUGO) | S_IFREG;
|
||||
return usbfs_mknod (dir, dentry, mode, 0);
|
||||
}
|
||||
|
||||
static inline int usbfs_positive (struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_inode && !d_unhashed(dentry);
|
||||
}
|
||||
|
||||
static int usbfs_empty (struct dentry *dentry)
|
||||
{
|
||||
struct list_head *list;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each(list, &dentry->d_subdirs) {
|
||||
struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
|
||||
|
||||
spin_lock_nested(&de->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (usbfs_positive(de)) {
|
||||
spin_unlock(&de->d_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&de->d_lock);
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
mutex_lock(&inode->i_mutex);
|
||||
drop_nlink(dentry->d_inode);
|
||||
dput(dentry);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
d_delete(dentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int error = -ENOTEMPTY;
|
||||
struct inode * inode = dentry->d_inode;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
dentry_unhash(dentry);
|
||||
if (usbfs_empty(dentry)) {
|
||||
dont_mount(dentry);
|
||||
drop_nlink(dentry->d_inode);
|
||||
drop_nlink(dentry->d_inode);
|
||||
dput(dentry);
|
||||
inode->i_flags |= S_DEAD;
|
||||
drop_nlink(dir);
|
||||
error = 0;
|
||||
}
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (!error)
|
||||
d_delete(dentry);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* default file operations */
|
||||
static ssize_t default_read_file (struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t default_write_file (struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
loff_t retval = -EINVAL;
|
||||
|
||||
mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
|
||||
switch(orig) {
|
||||
case 0:
|
||||
if (offset > 0) {
|
||||
file->f_pos = offset;
|
||||
retval = file->f_pos;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if ((offset + file->f_pos) > 0) {
|
||||
file->f_pos += offset;
|
||||
retval = file->f_pos;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct file_operations default_file_operations = {
|
||||
.read = default_read_file,
|
||||
.write = default_write_file,
|
||||
.open = simple_open,
|
||||
.llseek = default_file_lseek,
|
||||
};
|
||||
|
||||
static const struct super_operations usbfs_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.remount_fs = remount,
|
||||
.show_options = usbfs_show_options,
|
||||
};
|
||||
|
||||
static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = USBDEVICE_SUPER_MAGIC;
|
||||
sb->s_op = &usbfs_ops;
|
||||
sb->s_time_gran = 1;
|
||||
inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0);
|
||||
sb->s_root = d_make_root(inode);
|
||||
if (!sb->s_root) {
|
||||
dbg("%s: could not get root dentry!",__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fs_create_by_name - create a file, given a name
|
||||
* @name: name of file
|
||||
* @mode: type of file
|
||||
* @parent: dentry of directory to create it in
|
||||
* @dentry: resulting dentry of file
|
||||
*
|
||||
* This function handles both regular files and directories.
|
||||
*/
|
||||
static int fs_create_by_name (const char *name, umode_t mode,
|
||||
struct dentry *parent, struct dentry **dentry)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
/* If the parent is not specified, we create it in the root.
|
||||
* We need the root dentry to do this, which is in the super
|
||||
* block. A pointer to that is in the struct vfsmount that we
|
||||
* have around.
|
||||
*/
|
||||
if (!parent ) {
|
||||
if (usbfs_mount)
|
||||
parent = usbfs_mount->mnt_root;
|
||||
}
|
||||
|
||||
if (!parent) {
|
||||
dbg("Ah! can not find a parent!");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*dentry = NULL;
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
*dentry = lookup_one_len(name, parent, strlen(name));
|
||||
if (!IS_ERR(*dentry)) {
|
||||
if (S_ISDIR(mode))
|
||||
error = usbfs_mkdir (parent->d_inode, *dentry, mode);
|
||||
else
|
||||
error = usbfs_create (parent->d_inode, *dentry, mode);
|
||||
} else
|
||||
error = PTR_ERR(*dentry);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct dentry *fs_create_file (const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops,
|
||||
uid_t uid, gid_t gid)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int error;
|
||||
|
||||
dbg("creating file '%s'",name);
|
||||
|
||||
error = fs_create_by_name (name, mode, parent, &dentry);
|
||||
if (error) {
|
||||
dentry = NULL;
|
||||
} else {
|
||||
if (dentry->d_inode) {
|
||||
if (data)
|
||||
dentry->d_inode->i_private = data;
|
||||
if (fops)
|
||||
dentry->d_inode->i_fop = fops;
|
||||
dentry->d_inode->i_uid = uid;
|
||||
dentry->d_inode->i_gid = gid;
|
||||
}
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static void fs_remove_file (struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
|
||||
if (!parent || !parent->d_inode)
|
||||
return;
|
||||
|
||||
mutex_lock_nested(&parent->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
if (usbfs_positive(dentry)) {
|
||||
if (dentry->d_inode) {
|
||||
if (S_ISDIR(dentry->d_inode->i_mode))
|
||||
usbfs_rmdir(parent->d_inode, dentry);
|
||||
else
|
||||
usbfs_unlink(parent->d_inode, dentry);
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct dentry *usb_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return mount_single(fs_type, flags, data, usbfs_fill_super);
|
||||
}
|
||||
|
||||
static struct file_system_type usb_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "usbfs",
|
||||
.mount = usb_mount,
|
||||
.kill_sb = kill_litter_super,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int create_special_files (void)
|
||||
{
|
||||
struct dentry *parent;
|
||||
int retval;
|
||||
|
||||
/* create the devices special file */
|
||||
retval = simple_pin_fs(&usb_fs_type, &usbfs_mount, &usbfs_mount_count);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "Unable to get usbfs mount\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
parent = usbfs_mount->mnt_root;
|
||||
devices_usbfs_dentry = fs_create_file ("devices",
|
||||
listmode | S_IFREG, parent,
|
||||
NULL, &usbfs_devices_fops,
|
||||
listuid, listgid);
|
||||
if (devices_usbfs_dentry == NULL) {
|
||||
printk(KERN_ERR "Unable to create devices usbfs file\n");
|
||||
retval = -ENODEV;
|
||||
goto error_clean_mounts;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
|
||||
error_clean_mounts:
|
||||
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void remove_special_files (void)
|
||||
{
|
||||
if (devices_usbfs_dentry)
|
||||
fs_remove_file (devices_usbfs_dentry);
|
||||
devices_usbfs_dentry = NULL;
|
||||
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
|
||||
}
|
||||
|
||||
void usbfs_update_special (void)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
if (devices_usbfs_dentry) {
|
||||
inode = devices_usbfs_dentry->d_inode;
|
||||
if (inode)
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbfs_add_bus(struct usb_bus *bus)
|
||||
{
|
||||
struct dentry *parent;
|
||||
char name[8];
|
||||
int retval;
|
||||
|
||||
/* create the special files if this is the first bus added */
|
||||
if (num_buses == 0) {
|
||||
retval = create_special_files();
|
||||
if (retval)
|
||||
return;
|
||||
}
|
||||
++num_buses;
|
||||
|
||||
sprintf (name, "%03d", bus->busnum);
|
||||
|
||||
parent = usbfs_mount->mnt_root;
|
||||
bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
|
||||
bus, NULL, busuid, busgid);
|
||||
if (bus->usbfs_dentry == NULL) {
|
||||
printk(KERN_ERR "Error creating usbfs bus entry\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbfs_remove_bus(struct usb_bus *bus)
|
||||
{
|
||||
if (bus->usbfs_dentry) {
|
||||
fs_remove_file (bus->usbfs_dentry);
|
||||
bus->usbfs_dentry = NULL;
|
||||
}
|
||||
|
||||
--num_buses;
|
||||
if (num_buses <= 0) {
|
||||
remove_special_files();
|
||||
num_buses = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbfs_add_device(struct usb_device *dev)
|
||||
{
|
||||
char name[8];
|
||||
int i;
|
||||
int i_size;
|
||||
|
||||
sprintf (name, "%03d", dev->devnum);
|
||||
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
|
||||
dev->bus->usbfs_dentry, dev,
|
||||
&usbdev_file_operations,
|
||||
devuid, devgid);
|
||||
if (dev->usbfs_dentry == NULL) {
|
||||
printk(KERN_ERR "Error creating usbfs device entry\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the size of the device's file to be
|
||||
* equal to the size of the device descriptors. */
|
||||
i_size = sizeof (struct usb_device_descriptor);
|
||||
for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) {
|
||||
struct usb_config_descriptor *config =
|
||||
(struct usb_config_descriptor *)dev->rawdescriptors[i];
|
||||
i_size += le16_to_cpu(config->wTotalLength);
|
||||
}
|
||||
if (dev->usbfs_dentry->d_inode)
|
||||
dev->usbfs_dentry->d_inode->i_size = i_size;
|
||||
}
|
||||
|
||||
static void usbfs_remove_device(struct usb_device *dev)
|
||||
{
|
||||
if (dev->usbfs_dentry) {
|
||||
fs_remove_file (dev->usbfs_dentry);
|
||||
dev->usbfs_dentry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)
|
||||
{
|
||||
switch (action) {
|
||||
case USB_DEVICE_ADD:
|
||||
usbfs_add_device(dev);
|
||||
break;
|
||||
case USB_DEVICE_REMOVE:
|
||||
usbfs_remove_device(dev);
|
||||
break;
|
||||
case USB_BUS_ADD:
|
||||
usbfs_add_bus(dev);
|
||||
break;
|
||||
case USB_BUS_REMOVE:
|
||||
usbfs_remove_bus(dev);
|
||||
}
|
||||
|
||||
usbfs_update_special();
|
||||
usbfs_conn_disc_event();
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block usbfs_nb = {
|
||||
.notifier_call = usbfs_notify,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct proc_dir_entry *usbdir = NULL;
|
||||
|
||||
int __init usbfs_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = register_filesystem(&usb_fs_type);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
usb_register_notify(&usbfs_nb);
|
||||
|
||||
/* create mount point for usbfs */
|
||||
usbdir = proc_mkdir("bus/usb", NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usbfs_cleanup(void)
|
||||
{
|
||||
usb_unregister_notify(&usbfs_nb);
|
||||
unregister_filesystem(&usb_fs_type);
|
||||
if (usbdir)
|
||||
remove_proc_entry("bus/usb", NULL);
|
||||
}
|
||||
|
||||
|
|
@ -1308,10 +1308,19 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
* Remove the current alt setting and add the new alt setting.
|
||||
*/
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM, and re-enable it once the new alt setting is installed,
|
||||
* so that the xHCI driver can recalculate the U1/U2 timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
|
||||
if (ret < 0) {
|
||||
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
|
||||
alternate);
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1334,6 +1343,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
} else if (ret < 0) {
|
||||
/* Re-instate the old alt setting */
|
||||
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1354,6 +1364,9 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
|
||||
iface->cur_altsetting = alt;
|
||||
|
||||
/* Now that the interface is installed, re-enable LPM. */
|
||||
usb_unlocked_enable_lpm(dev);
|
||||
|
||||
/* If the interface only has one altsetting and the device didn't
|
||||
* accept the request, we attempt to carry out the equivalent action
|
||||
* by manually clearing the HALT feature for each endpoint in the
|
||||
|
|
@ -1437,6 +1450,14 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
config = dev->actconfig;
|
||||
retval = 0;
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM, and re-enable it once the configuration is reset, so
|
||||
* that the xHCI driver can recalculate the U1/U2 timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Make sure we have enough bandwidth for each alternate setting 0 */
|
||||
for (i = 0; i < config->desc.bNumInterfaces; i++) {
|
||||
struct usb_interface *intf = config->interface[i];
|
||||
|
|
@ -1465,6 +1486,7 @@ reset_old_alts:
|
|||
usb_hcd_alloc_bandwidth(dev, NULL,
|
||||
alt, intf->cur_altsetting);
|
||||
}
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1502,6 +1524,8 @@ reset_old_alts:
|
|||
create_intf_ep_devs(intf);
|
||||
}
|
||||
}
|
||||
/* Now that the interfaces are installed, re-enable LPM. */
|
||||
usb_unlocked_enable_lpm(dev);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_reset_configuration);
|
||||
|
|
@ -1763,8 +1787,18 @@ free_interfaces:
|
|||
* this call fails, the device state is unchanged.
|
||||
*/
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM, and re-enable it once the new configuration is
|
||||
* installed, so that the xHCI driver can recalculate the U1/U2
|
||||
* timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
|
|
@ -1784,6 +1818,7 @@ free_interfaces:
|
|||
if (!cp) {
|
||||
usb_set_device_state(dev, USB_STATE_ADDRESS);
|
||||
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
|
|
@ -1838,6 +1873,9 @@ free_interfaces:
|
|||
!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
|
||||
cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
|
||||
|
||||
/* Now that the interfaces are installed, re-enable LPM. */
|
||||
usb_unlocked_enable_lpm(dev);
|
||||
|
||||
/* Now that all the interfaces are set up, register them
|
||||
* to trigger binding of drivers to interfaces. probe()
|
||||
* routines may install different altsettings and may
|
||||
|
|
|
|||
|
|
@ -123,6 +123,9 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|||
/* Guillemot Webcam Hercules Dualpix Exchange*/
|
||||
{ USB_DEVICE(0x06f8, 0x3005), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Midiman M-Audio Keystation 88es */
|
||||
{ USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* M-Systems Flash Disk Pioneers */
|
||||
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
|
|
|
|||
|
|
@ -680,6 +680,27 @@ void usb_unpoison_urb(struct urb *urb)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unpoison_urb);
|
||||
|
||||
/**
|
||||
* usb_block_urb - reliably prevent further use of an URB
|
||||
* @urb: pointer to URB to be blocked, may be NULL
|
||||
*
|
||||
* After the routine has run, attempts to resubmit the URB will fail
|
||||
* with error -EPERM. Thus even if the URB's completion handler always
|
||||
* tries to resubmit, it will not succeed and the URB will become idle.
|
||||
*
|
||||
* The URB must not be deallocated while this routine is running. In
|
||||
* particular, when a driver calls this routine, it must insure that the
|
||||
* completion handler cannot deallocate the URB.
|
||||
*/
|
||||
void usb_block_urb(struct urb *urb)
|
||||
{
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
atomic_inc(&urb->reject);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_block_urb);
|
||||
|
||||
/**
|
||||
* usb_kill_anchored_urbs - cancel transfer requests en masse
|
||||
* @anchor: anchor the requests are bound to
|
||||
|
|
|
|||
117
drivers/usb/core/usb-acpi.c
Normal file
117
drivers/usb/core/usb-acpi.c
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* USB-ACPI glue code
|
||||
*
|
||||
* Copyright 2012 Red Hat <mjg@redhat.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, version 2.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *upc;
|
||||
int ret = 0;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
upc = buffer.pointer;
|
||||
|
||||
if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
|
||||
|| upc->package.count != 4) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (upc->package.elements[0].integer.value)
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
|
||||
out:
|
||||
kfree(upc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_pld pld;
|
||||
|
||||
status = acpi_get_physical_device_location(handle, &pld);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
if (pld.user_visible)
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct device *parent;
|
||||
acpi_handle *parent_handle;
|
||||
|
||||
if (!is_usb_device(dev))
|
||||
return -ENODEV;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
parent = dev->parent;
|
||||
parent_handle = DEVICE_ACPI_HANDLE(parent);
|
||||
|
||||
if (!parent_handle)
|
||||
return -ENODEV;
|
||||
|
||||
*handle = acpi_get_child(parent_handle, udev->portnum);
|
||||
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* PLD will tell us whether a port is removable to the user or
|
||||
* not. If we don't get an answer from PLD (it's not present
|
||||
* or it's malformed) then try to infer it from UPC. If a
|
||||
* device isn't connectable then it's probably not removable.
|
||||
*/
|
||||
if (usb_acpi_check_pld(udev, *handle) != 0)
|
||||
usb_acpi_check_upc(udev, *handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_bus_type usb_acpi_bus = {
|
||||
.bus = &usb_bus_type,
|
||||
.find_bridge = NULL,
|
||||
.find_device = usb_acpi_find_device,
|
||||
};
|
||||
|
||||
int usb_acpi_register(void)
|
||||
{
|
||||
return register_acpi_bus_type(&usb_acpi_bus);
|
||||
}
|
||||
|
||||
void usb_acpi_unregister(void)
|
||||
{
|
||||
unregister_acpi_bus_type(&usb_acpi_bus);
|
||||
}
|
||||
|
|
@ -1015,6 +1015,7 @@ static int __init usb_init(void)
|
|||
if (retval)
|
||||
goto out;
|
||||
|
||||
usb_acpi_register();
|
||||
retval = bus_register(&usb_bus_type);
|
||||
if (retval)
|
||||
goto bus_register_failed;
|
||||
|
|
@ -1030,9 +1031,6 @@ static int __init usb_init(void)
|
|||
retval = usb_devio_init();
|
||||
if (retval)
|
||||
goto usb_devio_init_failed;
|
||||
retval = usbfs_init();
|
||||
if (retval)
|
||||
goto fs_init_failed;
|
||||
retval = usb_hub_init();
|
||||
if (retval)
|
||||
goto hub_init_failed;
|
||||
|
|
@ -1042,8 +1040,6 @@ static int __init usb_init(void)
|
|||
|
||||
usb_hub_cleanup();
|
||||
hub_init_failed:
|
||||
usbfs_cleanup();
|
||||
fs_init_failed:
|
||||
usb_devio_cleanup();
|
||||
usb_devio_init_failed:
|
||||
usb_deregister(&usbfs_driver);
|
||||
|
|
@ -1054,6 +1050,7 @@ major_init_failed:
|
|||
bus_notifier_failed:
|
||||
bus_unregister(&usb_bus_type);
|
||||
bus_register_failed:
|
||||
usb_acpi_unregister();
|
||||
usb_debugfs_cleanup();
|
||||
out:
|
||||
return retval;
|
||||
|
|
@ -1070,12 +1067,12 @@ static void __exit usb_exit(void)
|
|||
|
||||
usb_deregister_device_driver(&usb_generic_driver);
|
||||
usb_major_cleanup();
|
||||
usbfs_cleanup();
|
||||
usb_deregister(&usbfs_driver);
|
||||
usb_devio_cleanup();
|
||||
usb_hub_cleanup();
|
||||
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
|
||||
bus_unregister(&usb_bus_type);
|
||||
usb_acpi_unregister();
|
||||
usb_debugfs_cleanup();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,3 +156,10 @@ extern void usb_notify_remove_device(struct usb_device *udev);
|
|||
extern void usb_notify_add_bus(struct usb_bus *ubus);
|
||||
extern void usb_notify_remove_bus(struct usb_bus *ubus);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int usb_acpi_register(void);
|
||||
extern void usb_acpi_unregister(void);
|
||||
#else
|
||||
static inline int usb_acpi_register(void) { return 0; };
|
||||
static inline void usb_acpi_unregister(void) { };
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ config USB_DWC3
|
|||
select USB_OTG_UTILS
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SUPERSPEED
|
||||
select USB_XHCI_PLATFORM
|
||||
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
|
||||
help
|
||||
Say Y or M here if your system has a Dual Role SuperSpeed
|
||||
USB controller based on the DesignWare USB3 IP Core.
|
||||
|
|
|
|||
|
|
@ -410,7 +410,6 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
|
||||
void __iomem *regs;
|
||||
void *mem;
|
||||
|
|
@ -425,15 +424,28 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
|||
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
|
||||
dwc->mem = mem;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing resource\n");
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dwc->xhci_resources[1] = *res;
|
||||
|
||||
dwc->res = res;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dwc->xhci_resources[0] = *res;
|
||||
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
|
||||
DWC3_XHCI_REGS_END;
|
||||
|
||||
res = devm_request_mem_region(dev, res->start, resource_size(res),
|
||||
/*
|
||||
* Request memory region but exclude xHCI regs,
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
*/
|
||||
res = devm_request_mem_region(dev, res->start + DWC3_GLOBALS_REGS_START,
|
||||
resource_size(res) - DWC3_GLOBALS_REGS_START,
|
||||
dev_name(dev));
|
||||
if (!res) {
|
||||
dev_err(dev, "can't request mem region\n");
|
||||
|
|
@ -446,19 +458,12 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
dwc->dev = dev;
|
||||
dwc->irq = irq;
|
||||
|
||||
if (!strncmp("super", maximum_speed, 5))
|
||||
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@
|
|||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* Global constants */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
|
||||
#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
|
||||
#define DWC3_EVENT_TYPE_MASK 0xfe
|
||||
|
|
@ -75,6 +77,16 @@
|
|||
#define DWC3_GSNPSID_MASK 0xffff0000
|
||||
#define DWC3_GSNPSREV_MASK 0xffff
|
||||
|
||||
/* DWC3 registers memory space boundries */
|
||||
#define DWC3_XHCI_REGS_START 0x0
|
||||
#define DWC3_XHCI_REGS_END 0x7fff
|
||||
#define DWC3_GLOBALS_REGS_START 0xc100
|
||||
#define DWC3_GLOBALS_REGS_END 0xc6ff
|
||||
#define DWC3_DEVICE_REGS_START 0xc700
|
||||
#define DWC3_DEVICE_REGS_END 0xcbff
|
||||
#define DWC3_OTG_REGS_START 0xcc00
|
||||
#define DWC3_OTG_REGS_END 0xccff
|
||||
|
||||
/* Global Registers */
|
||||
#define DWC3_GSBUSCFG0 0xc100
|
||||
#define DWC3_GSBUSCFG1 0xc104
|
||||
|
|
@ -183,6 +195,7 @@
|
|||
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
|
||||
|
||||
/* Device Configuration Register */
|
||||
#define DWC3_DCFG_LPM_CAP (1 << 22)
|
||||
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
|
||||
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
|
||||
|
||||
|
|
@ -272,12 +285,14 @@
|
|||
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
|
||||
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
|
||||
|
||||
#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
|
||||
#define DWC3_DGCMD_CMDACT (1 << 10)
|
||||
|
||||
/* Device Endpoint Command Register */
|
||||
#define DWC3_DEPCMD_PARAM_SHIFT 16
|
||||
#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_STATUS_MASK (0x0f << 12)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) & DWC3_DEPCMD_STATUS_MASK) >> 12)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
|
||||
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
|
||||
#define DWC3_DEPCMD_CMDACT (1 << 10)
|
||||
#define DWC3_DEPCMD_CMDIOC (1 << 8)
|
||||
|
|
@ -361,7 +376,6 @@ struct dwc3_ep {
|
|||
dma_addr_t trb_pool_dma;
|
||||
u32 free_slot;
|
||||
u32 busy_slot;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
|
|
@ -561,6 +575,11 @@ struct dwc3_request {
|
|||
* @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.
|
||||
* @isoch_delay: wValue from Set Isochronous Delay request;
|
||||
* @u2sel: parameter from Set SEL request.
|
||||
* @u2pel: parameter from Set SEL request.
|
||||
* @u1sel: parameter from Set SEL request.
|
||||
* @u1pel: parameter from Set SEL request.
|
||||
* @ep0_next_event: hold the next expected event
|
||||
* @ep0state: state of endpoint zero
|
||||
* @link_state: link state
|
||||
|
|
@ -583,7 +602,7 @@ struct dwc3 {
|
|||
struct device *dev;
|
||||
|
||||
struct platform_device *xhci;
|
||||
struct resource *res;
|
||||
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
|
||||
|
||||
struct dwc3_event_buffer **ev_buffs;
|
||||
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
|
||||
|
|
@ -594,8 +613,6 @@ struct dwc3 {
|
|||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
int irq;
|
||||
|
||||
u32 num_event_buffers;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
|
|
@ -609,6 +626,10 @@ struct dwc3 {
|
|||
#define DWC3_REVISION_185A 0x5533185a
|
||||
#define DWC3_REVISION_188A 0x5533188a
|
||||
#define DWC3_REVISION_190A 0x5533190a
|
||||
#define DWC3_REVISION_200A 0x5533200a
|
||||
#define DWC3_REVISION_202A 0x5533202a
|
||||
#define DWC3_REVISION_210A 0x5533210a
|
||||
#define DWC3_REVISION_220A 0x5533220a
|
||||
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned three_stage_setup:1;
|
||||
|
|
@ -625,7 +646,14 @@ struct dwc3 {
|
|||
enum dwc3_link_state link_state;
|
||||
enum dwc3_device_state dev_state;
|
||||
|
||||
u16 isoch_delay;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
u8 u1sel;
|
||||
u8 u1pel;
|
||||
|
||||
u8 speed;
|
||||
|
||||
void *mem;
|
||||
|
||||
struct dwc3_hwparams hwparams;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@
|
|||
#include <linux/of.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "io.h"
|
||||
|
||||
/*
|
||||
* All these registers belong to OMAP's Wrapper around the
|
||||
|
|
@ -143,6 +142,17 @@ struct dwc3_omap {
|
|||
u32 dma_status:1;
|
||||
};
|
||||
|
||||
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
|
|
@ -150,7 +160,7 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
|||
|
||||
spin_lock(&omap->lock);
|
||||
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_1);
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
|
||||
dev_dbg(omap->dev, "DMA Disable was Cleared\n");
|
||||
|
|
@ -184,10 +194,10 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
|||
if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
|
||||
dev_dbg(omap->dev, "IDPULLUP Fall\n");
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
|
||||
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0);
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
|
||||
|
||||
spin_unlock(&omap->lock);
|
||||
|
||||
|
|
@ -270,7 +280,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
omap->base = base;
|
||||
omap->dwc3 = dwc3;
|
||||
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
|
||||
utmi_mode = of_get_property(node, "utmi-mode", &size);
|
||||
if (utmi_mode && size == sizeof(*utmi_mode)) {
|
||||
|
|
@ -293,10 +303,10 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
|
||||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
|
||||
|
||||
/* Set No-Idle and No-Standby */
|
||||
|
|
@ -306,7 +316,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
|
||||
| USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
|
|
@ -318,7 +328,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
|
||||
/* enable all IRQs */
|
||||
reg = USBOTGSS_IRQO_COREIRQ_ST;
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
|
||||
|
||||
reg = (USBOTGSS_IRQ1_OEVT |
|
||||
USBOTGSS_IRQ1_DRVVBUS_RISE |
|
||||
|
|
@ -330,7 +340,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
|
||||
USBOTGSS_IRQ1_IDPULLUP_FALL);
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
|
||||
|
||||
ret = platform_device_add_resources(dwc3, pdev->resource,
|
||||
pdev->num_resources);
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!dep->desc) {
|
||||
if (!dep->endpoint.desc) {
|
||||
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
|
||||
request, dep->name);
|
||||
ret = -ESHUTDOWN;
|
||||
|
|
@ -261,6 +261,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
|||
{
|
||||
struct dwc3_ep *dep;
|
||||
u32 recip;
|
||||
u32 reg;
|
||||
u16 usb_status = 0;
|
||||
__le16 *response_pkt;
|
||||
|
||||
|
|
@ -268,10 +269,18 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
|||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
/*
|
||||
* We are self-powered. U1/U2/LTM will be set later
|
||||
* once we handle this states. RemoteWakeup is 0 on SS
|
||||
* LTM will be set once we know how to set this in HW.
|
||||
*/
|
||||
usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
|
||||
|
||||
if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
|
||||
if (reg & DWC3_DCTL_INITU2ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case USB_RECIP_INTERFACE:
|
||||
|
|
@ -312,6 +321,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
u32 recip;
|
||||
u32 wValue;
|
||||
u32 wIndex;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
|
|
@ -320,29 +330,43 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
/*
|
||||
* 9.4.1 says only only for SS, in AddressState only for
|
||||
* default control pipe
|
||||
*/
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* XXX add U[12] & LTM */
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (set)
|
||||
reg |= DWC3_DCTL_INITU1ENA;
|
||||
else
|
||||
reg &= ~DWC3_DCTL_INITU1ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
break;
|
||||
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (set)
|
||||
reg |= DWC3_DCTL_INITU2ENA;
|
||||
else
|
||||
reg &= ~DWC3_DCTL_INITU2ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
break;
|
||||
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
|
|
@ -469,6 +493,107 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct dwc3_ep *dep = to_dwc3_ep(ep);
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
u32 param = 0;
|
||||
u32 reg;
|
||||
|
||||
struct timing {
|
||||
u8 u1sel;
|
||||
u8 u1pel;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
} __packed timing;
|
||||
|
||||
int ret;
|
||||
|
||||
memcpy(&timing, req->buf, sizeof(timing));
|
||||
|
||||
dwc->u1sel = timing.u1sel;
|
||||
dwc->u1pel = timing.u1pel;
|
||||
dwc->u2sel = timing.u2sel;
|
||||
dwc->u2pel = timing.u2pel;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU2ENA)
|
||||
param = dwc->u2pel;
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
param = dwc->u1pel;
|
||||
|
||||
/*
|
||||
* According to Synopsys Databook, if parameter is
|
||||
* greater than 125, a value of zero should be
|
||||
* programmed in the register.
|
||||
*/
|
||||
if (param > 125)
|
||||
param = 0;
|
||||
|
||||
/* now that we have the time, issue DGCMD Set Sel */
|
||||
ret = dwc3_send_gadget_generic_command(dwc,
|
||||
DWC3_DGCMD_SET_PERIODIC_PAR, param);
|
||||
WARN_ON(ret < 0);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
u16 wLength;
|
||||
u16 wValue;
|
||||
|
||||
if (dwc->dev_state == DWC3_DEFAULT_STATE)
|
||||
return -EINVAL;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wLength = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
if (wLength != 6) {
|
||||
dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n",
|
||||
wLength);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* To handle Set SEL we need to receive 6 bytes from Host. So let's
|
||||
* queue a usb_request for 6 bytes.
|
||||
*
|
||||
* Remember, though, this controller can't handle non-wMaxPacketSize
|
||||
* aligned transfers on the OUT direction, so we queue a request for
|
||||
* wMaxPacketSize instead.
|
||||
*/
|
||||
dep = dwc->eps[0];
|
||||
dwc->ep0_usb_req.dep = dep;
|
||||
dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
|
||||
dwc->ep0_usb_req.request.buf = dwc->setup_buf;
|
||||
dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
|
||||
|
||||
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
u16 wLength;
|
||||
u16 wValue;
|
||||
u16 wIndex;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wLength = le16_to_cpu(ctrl->wLength);
|
||||
wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
|
||||
if (wIndex || wLength)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* REVISIT It's unclear from Databook what to do with this
|
||||
* value. For now, just cache it.
|
||||
*/
|
||||
dwc->isoch_delay = wValue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -494,6 +619,14 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
|
||||
ret = dwc3_ep0_set_config(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n");
|
||||
ret = dwc3_ep0_set_sel(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
|
||||
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
|
||||
break;
|
||||
default:
|
||||
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
|
|
|
|||
|
|
@ -178,8 +178,8 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
|
|||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->desc)
|
||||
|| usb_endpoint_xfer_isoc(dep->desc))
|
||||
if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
|
||||
|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
mult = 3;
|
||||
|
||||
/*
|
||||
|
|
@ -229,7 +229,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
* completed (not the LINK TRB).
|
||||
*/
|
||||
if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->desc))
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
dep->busy_slot++;
|
||||
}
|
||||
list_del(&req->list);
|
||||
|
|
@ -276,6 +276,33 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
|
|||
}
|
||||
}
|
||||
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
|
||||
{
|
||||
u32 timeout = 500;
|
||||
u32 reg;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
|
||||
|
||||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
|
||||
if (!(reg & DWC3_DGCMD_CMDACT)) {
|
||||
dev_vdbg(dwc->dev, "Command Complete --> %d\n",
|
||||
DWC3_DGCMD_STATUS(reg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't sleep here, because it's also called from
|
||||
* interrupt context.
|
||||
*/
|
||||
timeout--;
|
||||
if (!timeout)
|
||||
return -ETIMEDOUT;
|
||||
udelay(1);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
{
|
||||
|
|
@ -470,7 +497,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
dep->desc = desc;
|
||||
dep->endpoint.desc = desc;
|
||||
dep->comp_desc = comp_desc;
|
||||
dep->type = usb_endpoint_type(desc);
|
||||
dep->flags |= DWC3_EP_ENABLED;
|
||||
|
|
@ -533,7 +560,6 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
|||
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
||||
|
||||
dep->stream_capable = false;
|
||||
dep->desc = NULL;
|
||||
dep->endpoint.desc = NULL;
|
||||
dep->comp_desc = NULL;
|
||||
dep->type = 0;
|
||||
|
|
@ -694,7 +720,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
|
||||
/* Skip the LINK-TRB on ISOC */
|
||||
if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->desc))
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
return;
|
||||
|
||||
if (!req->trb) {
|
||||
|
|
@ -707,7 +733,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
trb->bpl = lower_32_bits(dma);
|
||||
trb->bph = upper_32_bits(dma);
|
||||
|
||||
switch (usb_endpoint_type(dep->desc)) {
|
||||
switch (usb_endpoint_type(dep->endpoint.desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
|
||||
break;
|
||||
|
|
@ -732,7 +758,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
BUG();
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
} else {
|
||||
|
|
@ -743,7 +769,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
trb->ctrl |= DWC3_TRB_CTRL_LST;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
|
||||
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
|
||||
|
||||
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
||||
|
|
@ -771,7 +797,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
|||
trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
|
||||
|
||||
/* Can't wrap around on a non-isoc EP since there's no link TRB */
|
||||
if (!usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
|
||||
if (trbs_left > max)
|
||||
trbs_left = max;
|
||||
|
|
@ -797,7 +823,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
|||
* processed from the first TRB until the last one. Since we
|
||||
* don't wrap around we have to start at the beginning.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dep->busy_slot = 1;
|
||||
dep->free_slot = 1;
|
||||
} else {
|
||||
|
|
@ -807,7 +833,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
|||
}
|
||||
|
||||
/* The last TRB is a link TRB, not used for xfer */
|
||||
if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc))
|
||||
if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(req, n, &dep->request_list, list) {
|
||||
|
|
@ -930,10 +956,12 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|||
}
|
||||
|
||||
dep->flags |= DWC3_EP_BUSY;
|
||||
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
|
||||
dep->number);
|
||||
|
||||
WARN_ON_ONCE(!dep->res_trans_idx);
|
||||
if (start_new) {
|
||||
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
|
||||
dep->number);
|
||||
WARN_ON_ONCE(!dep->res_trans_idx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -967,28 +995,37 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
|
||||
list_add_tail(&req->list, &dep->request_list);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY))
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
|
||||
/*
|
||||
* There is one special case: XferNotReady with
|
||||
* empty list of requests. We need to kick the
|
||||
* transfer here in that situation, otherwise
|
||||
* we will be NAKing forever.
|
||||
* There are two special cases:
|
||||
*
|
||||
* If we get XferNotReady before gadget driver
|
||||
* has a chance to queue a request, we will ACK
|
||||
* the IRQ but won't be able to receive the data
|
||||
* until the next request is queued. The following
|
||||
* code is handling exactly that.
|
||||
* 1. XferNotReady with empty list of requests. We need to kick the
|
||||
* transfer here in that situation, otherwise we will be NAKing
|
||||
* forever. If we get XferNotReady before gadget driver has a
|
||||
* chance to queue a request, we will ACK the IRQ but won't be
|
||||
* able to receive the data until the next request is queued.
|
||||
* The following code is handling exactly that.
|
||||
*
|
||||
* 2. XferInProgress on Isoc EP with an active transfer. We need to
|
||||
* kick the transfer here after queuing a request, otherwise the
|
||||
* core may not see the modified TRB(s).
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
|
||||
int ret;
|
||||
int start_trans;
|
||||
int ret;
|
||||
int start_trans = 1;
|
||||
u8 trans_idx = dep->res_trans_idx;
|
||||
|
||||
start_trans = 1;
|
||||
if (usb_endpoint_xfer_isoc(dep->desc) &&
|
||||
(dep->flags & DWC3_EP_BUSY))
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
(dep->flags & DWC3_EP_BUSY)) {
|
||||
start_trans = 0;
|
||||
WARN_ON_ONCE(!trans_idx);
|
||||
} else {
|
||||
trans_idx = 0;
|
||||
}
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans);
|
||||
ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans);
|
||||
if (ret && ret != -EBUSY) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
|
|
@ -1011,7 +1048,7 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
|
||||
int ret;
|
||||
|
||||
if (!dep->desc) {
|
||||
if (!dep->endpoint.desc) {
|
||||
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
|
||||
request, ep->name);
|
||||
return -ESHUTDOWN;
|
||||
|
|
@ -1125,7 +1162,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
|
@ -1356,7 +1393,24 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_SPEED_MASK);
|
||||
reg |= dwc->maximum_speed;
|
||||
|
||||
/**
|
||||
* WORKAROUND: DWC3 revision < 2.20a have an issue
|
||||
* which would cause metastability state on Run/Stop
|
||||
* bit if we try to force the IP to USB2-only mode.
|
||||
*
|
||||
* Because of that, we cannot configure the IP to any
|
||||
* speed other than the SuperSpeed
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000525659: Clock Domain Crossing on DCTL in
|
||||
* USB 2.0 Mode
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A)
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
else
|
||||
reg |= dwc->maximum_speed;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
dwc->start_config_issued = false;
|
||||
|
|
@ -1681,7 +1735,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
dep->res_trans_idx = 0;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
|
||||
dep->name);
|
||||
return;
|
||||
|
|
@ -1690,7 +1744,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
if (!usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n",
|
||||
dep->name);
|
||||
return;
|
||||
|
|
@ -1699,7 +1753,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dwc3_gadget_start_isoc(dwc, dep, event);
|
||||
} else {
|
||||
int ret;
|
||||
|
|
@ -1720,7 +1774,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
if (!usb_endpoint_xfer_bulk(dep->desc)) {
|
||||
if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
|
||||
dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
|
||||
dep->name);
|
||||
return;
|
||||
|
|
@ -1916,6 +1970,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc->test_mode = false;
|
||||
|
||||
|
|
@ -2263,8 +2318,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
dwc->setup_buf = kzalloc(sizeof(*dwc->setup_buf) * 2,
|
||||
GFP_KERNEL);
|
||||
dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
|
||||
if (!dwc->setup_buf) {
|
||||
dev_err(dwc->dev, "failed to allocate setup buffer\n");
|
||||
ret = -ENOMEM;
|
||||
|
|
@ -2272,7 +2326,8 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
|||
}
|
||||
|
||||
dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
|
||||
512, &dwc->ep0_bounce_addr, GFP_KERNEL);
|
||||
DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
|
||||
GFP_KERNEL);
|
||||
if (!dwc->ep0_bounce) {
|
||||
dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
|
||||
ret = -ENOMEM;
|
||||
|
|
@ -2313,6 +2368,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
|||
goto err5;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
/* Enable all but Start and End of Frame IRQs */
|
||||
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
|
||||
DWC3_DEVTEN_EVNTOVERFLOWEN |
|
||||
|
|
@ -2351,8 +2414,8 @@ err5:
|
|||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
err4:
|
||||
dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
|
||||
dwc->ep0_bounce_addr);
|
||||
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
||||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
err3:
|
||||
kfree(dwc->setup_buf);
|
||||
|
|
@ -2381,8 +2444,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
|||
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
|
||||
dwc->ep0_bounce_addr);
|
||||
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
||||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
kfree(dwc->setup_buf);
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ 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_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
|
|
|||
|
|
@ -39,15 +39,6 @@
|
|||
|
||||
#include "core.h"
|
||||
|
||||
static struct resource generic_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct platform_device *xhci;
|
||||
|
|
@ -68,14 +59,8 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||
|
||||
dwc->xhci = xhci;
|
||||
|
||||
/* setup resources */
|
||||
generic_resources[0].start = dwc->irq;
|
||||
|
||||
generic_resources[1].start = dwc->res->start;
|
||||
generic_resources[1].end = dwc->res->start + 0x7fff;
|
||||
|
||||
ret = platform_device_add_resources(xhci, generic_resources,
|
||||
ARRAY_SIZE(generic_resources));
|
||||
ret = platform_device_add_resources(xhci, dwc->xhci_resources,
|
||||
DWC3_XHCI_RESOURCES_NUM);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
|
||||
goto err1;
|
||||
|
|
|
|||
|
|
@ -41,14 +41,26 @@
|
|||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
return readl(base + (offset - DWC3_GLOBALS_REGS_START));
|
||||
}
|
||||
|
||||
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
writel(value, base + (offset - DWC3_GLOBALS_REGS_START));
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_IO_H */
|
||||
|
|
|
|||
|
|
@ -123,13 +123,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
|
|||
# - discrete ones (including all PCI-only controllers)
|
||||
# - debug/dummy gadget+hcd is last.
|
||||
#
|
||||
choice
|
||||
prompt "USB Peripheral Controller"
|
||||
help
|
||||
A USB device uses a controller to talk to its host.
|
||||
Systems should have only one such upstream link.
|
||||
Many controller drivers are platform-specific; these
|
||||
often need board-specific hooks.
|
||||
menu "USB Peripheral Controller"
|
||||
|
||||
#
|
||||
# Integrated controllers
|
||||
|
|
@ -147,6 +141,17 @@ config USB_AT91
|
|||
dynamically linked module called "at91_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_LPC32XX
|
||||
tristate "LPC32XX USB Peripheral Controller"
|
||||
depends on ARCH_LPC32XX
|
||||
select USB_ISP1301
|
||||
help
|
||||
This option selects the USB device controller in the LPC32xx SoC.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "lpc32xx_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_ATMEL_USBA
|
||||
tristate "Atmel USBA"
|
||||
select USB_GADGET_DUALSPEED
|
||||
|
|
@ -161,7 +166,7 @@ config USB_FSL_USB2
|
|||
select USB_GADGET_DUALSPEED
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
help
|
||||
Some of Freescale PowerPC processors have a High Speed
|
||||
Some of Freescale PowerPC and i.MX processors have a High Speed
|
||||
Dual-Role(DR) USB controller, which supports device mode.
|
||||
|
||||
The number of programmable endpoints is different through
|
||||
|
|
@ -373,18 +378,6 @@ config USB_FSL_QE
|
|||
Set CONFIG_USB_GADGET to "m" to build this driver as a
|
||||
dynamically linked module called "fsl_qe_udc".
|
||||
|
||||
config USB_CI13XXX_PCI
|
||||
tristate "MIPS USB CI13xxx PCI UDC"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
MIPS USB IP core family device controller
|
||||
Currently it only supports IP part number CI13412
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "ci13xxx_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_NET2272
|
||||
tristate "PLX NET2272"
|
||||
select USB_GADGET_DUALSPEED
|
||||
|
|
@ -438,22 +431,6 @@ config USB_GOKU
|
|||
dynamically linked module called "goku_udc" and to force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_LANGWELL
|
||||
tristate "Intel Langwell USB Device Controller"
|
||||
depends on PCI
|
||||
depends on !PHYS_ADDR_T_64BIT
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Intel Langwell USB Device Controller is a High-Speed USB
|
||||
On-The-Go device controller.
|
||||
|
||||
The number of programmable endpoints is different through
|
||||
controller revision.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "langwell_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_EG20T
|
||||
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
|
||||
depends on PCI
|
||||
|
|
@ -477,23 +454,6 @@ config USB_EG20T
|
|||
ML7213/ML7831 is companion chip for Intel Atom E6xx series.
|
||||
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
|
||||
|
||||
config USB_CI13XXX_MSM
|
||||
tristate "MIPS USB CI13xxx for MSM"
|
||||
depends on ARCH_MSM
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_MSM_OTG
|
||||
help
|
||||
MSM SoC has chipidea USB controller. This driver uses
|
||||
ci13xxx_udc core.
|
||||
This driver depends on OTG driver for PHY initialization,
|
||||
clock management, powering up VBUS, and power management.
|
||||
This driver is not supported on boards like trout which
|
||||
has an external PHY.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "ci13xxx_msm" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
#
|
||||
# LAST -- dummy/emulated controller
|
||||
#
|
||||
|
|
@ -525,7 +485,7 @@ config USB_DUMMY_HCD
|
|||
# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
|
||||
# first and will be selected by default.
|
||||
|
||||
endchoice
|
||||
endmenu
|
||||
|
||||
# Selected by UDC drivers that support high-speed operation.
|
||||
config USB_GADGET_DUALSPEED
|
||||
|
|
|
|||
|
|
@ -22,14 +22,12 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
|||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o
|
||||
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
|
||||
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
|
||||
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
|
||||
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
|
||||
obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
|
||||
mv_udc-y := mv_udc_core.o
|
||||
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
|
||||
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
|
|||
return -ESHUTDOWN;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, iflags);
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
|
||||
ep->halted = 0;
|
||||
|
||||
|
|
@ -442,7 +442,6 @@ static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
|
|||
u32 tmp;
|
||||
|
||||
VDBG(ep->dev, "ep-%d reset\n", ep->num);
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->ep.ops = &udc_ep_ops;
|
||||
INIT_LIST_HEAD(&ep->queue);
|
||||
|
|
@ -489,7 +488,7 @@ static int udc_ep_disable(struct usb_ep *usbep)
|
|||
return -EINVAL;
|
||||
|
||||
ep = container_of(usbep, struct udc_ep, ep);
|
||||
if (usbep->name == ep0_string || !ep->desc)
|
||||
if (usbep->name == ep0_string || !ep->ep.desc)
|
||||
return -EINVAL;
|
||||
|
||||
DBG(ep->dev, "Disable ep-%d\n", ep->num);
|
||||
|
|
@ -1066,7 +1065,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
|
|||
return -EINVAL;
|
||||
|
||||
ep = container_of(usbep, struct udc_ep, ep);
|
||||
if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
|
||||
if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
|
||||
return -EINVAL;
|
||||
|
||||
VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in);
|
||||
|
|
@ -1257,7 +1256,7 @@ static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
|
|||
unsigned long iflags;
|
||||
|
||||
ep = container_of(usbep, struct udc_ep, ep);
|
||||
if (!usbep || !usbreq || (!ep->desc && (ep->num != 0
|
||||
if (!usbep || !usbreq || (!ep->ep.desc && (ep->num != 0
|
||||
&& ep->num != UDC_EP0OUT_IX)))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -1317,7 +1316,7 @@ udc_set_halt(struct usb_ep *usbep, int halt)
|
|||
pr_debug("set_halt %s: halt=%d\n", usbep->name, halt);
|
||||
|
||||
ep = container_of(usbep, struct udc_ep, ep);
|
||||
if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
|
||||
if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
|
||||
return -EINVAL;
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
|
|
@ -1539,7 +1538,7 @@ static void udc_setup_endpoints(struct udc *dev)
|
|||
* disabling ep interrupts when ENUM interrupt occurs but ep is
|
||||
* not enabled by gadget driver
|
||||
*/
|
||||
if (!ep->desc)
|
||||
if (!ep->ep.desc)
|
||||
ep_init(dev->regs, ep);
|
||||
|
||||
if (use_dma) {
|
||||
|
|
@ -3402,19 +3401,7 @@ static struct pci_driver udc_pci_driver = {
|
|||
.remove = udc_pci_remove,
|
||||
};
|
||||
|
||||
/* Inits driver */
|
||||
static int __init init(void)
|
||||
{
|
||||
return pci_register_driver(&udc_pci_driver);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
/* Cleans driver */
|
||||
static void __exit cleanup(void)
|
||||
{
|
||||
pci_unregister_driver(&udc_pci_driver);
|
||||
}
|
||||
module_exit(cleanup);
|
||||
module_pci_driver(udc_pci_driver);
|
||||
|
||||
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
|
||||
MODULE_AUTHOR("Thomas Dahlmann");
|
||||
|
|
|
|||
|
|
@ -512,7 +512,6 @@ struct udc_ep {
|
|||
|
||||
/* queue for requests */
|
||||
struct list_head queue;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
unsigned halted;
|
||||
unsigned cancel_transfer;
|
||||
unsigned num : 5,
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
|
|||
if (udc->enabled && udc->vbus) {
|
||||
proc_ep_show(s, &udc->ep[0]);
|
||||
list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
|
||||
if (ep->desc)
|
||||
if (ep->ep.desc)
|
||||
proc_ep_show(s, ep);
|
||||
}
|
||||
}
|
||||
|
|
@ -475,7 +475,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
|
|||
unsigned long flags;
|
||||
|
||||
if (!_ep || !ep
|
||||
|| !desc || ep->desc
|
||||
|| !desc || ep->ep.desc
|
||||
|| _ep->name == ep0name
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT
|
||||
|| (maxpacket = usb_endpoint_maxp(desc)) == 0
|
||||
|
|
@ -530,7 +530,7 @@ ok:
|
|||
tmp |= AT91_UDP_EPEDS;
|
||||
__raw_writel(tmp, ep->creg);
|
||||
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->ep.maxpacket = maxpacket;
|
||||
|
||||
/*
|
||||
|
|
@ -558,7 +558,6 @@ static int at91_ep_disable (struct usb_ep * _ep)
|
|||
nuke(ep, -ESHUTDOWN);
|
||||
|
||||
/* restore the endpoint's pristine config */
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->ep.maxpacket = ep->maxpacket;
|
||||
|
||||
|
|
@ -618,7 +617,7 @@ static int at91_ep_queue(struct usb_ep *_ep,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
|
||||
if (!_ep || (!ep->ep.desc && ep->ep.name != ep0name)) {
|
||||
DBG("invalid ep\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -833,7 +832,7 @@ static void udc_reinit(struct at91_udc *udc)
|
|||
|
||||
if (i != 0)
|
||||
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 0;
|
||||
ep->fifo_bank = 0;
|
||||
ep->ep.maxpacket = ep->maxpacket;
|
||||
|
|
@ -978,18 +977,18 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int at91_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *));
|
||||
static int at91_stop(struct usb_gadget_driver *driver);
|
||||
|
||||
static int at91_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int at91_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static const struct usb_gadget_ops at91_udc_ops = {
|
||||
.get_frame = at91_get_frame,
|
||||
.wakeup = at91_wakeup,
|
||||
.set_selfpowered = at91_set_selfpowered,
|
||||
.vbus_session = at91_vbus_session,
|
||||
.pullup = at91_pullup,
|
||||
.start = at91_start,
|
||||
.stop = at91_stop,
|
||||
.udc_start = at91_start,
|
||||
.udc_stop = at91_stop,
|
||||
|
||||
/*
|
||||
* VBUS-powered devices may also also want to support bigger
|
||||
|
|
@ -1172,7 +1171,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
|
|||
| USB_REQ_GET_STATUS:
|
||||
tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
|
||||
ep = &udc->ep[tmp];
|
||||
if (tmp >= NUM_ENDPOINTS || (tmp && !ep->desc))
|
||||
if (tmp >= NUM_ENDPOINTS || (tmp && !ep->ep.desc))
|
||||
goto stall;
|
||||
|
||||
if (tmp) {
|
||||
|
|
@ -1197,7 +1196,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
|
|||
ep = &udc->ep[tmp];
|
||||
if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS)
|
||||
goto stall;
|
||||
if (!ep->desc || ep->is_iso)
|
||||
if (!ep->ep.desc || ep->is_iso)
|
||||
goto stall;
|
||||
if ((w_index & USB_DIR_IN)) {
|
||||
if (!ep->is_in)
|
||||
|
|
@ -1218,7 +1217,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
|
|||
goto stall;
|
||||
if (tmp == 0)
|
||||
goto succeed;
|
||||
if (!ep->desc || ep->is_iso)
|
||||
if (!ep->ep.desc || ep->is_iso)
|
||||
goto stall;
|
||||
if ((w_index & USB_DIR_IN)) {
|
||||
if (!ep->is_in)
|
||||
|
|
@ -1627,66 +1626,34 @@ static void at91_vbus_timer(unsigned long data)
|
|||
schedule_work(&udc->vbus_timer_work);
|
||||
}
|
||||
|
||||
static int at91_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *))
|
||||
static int at91_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct at91_udc *udc = &controller;
|
||||
int retval;
|
||||
unsigned long flags;
|
||||
|
||||
if (!driver
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->setup) {
|
||||
DBG("bad parameter.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (udc->driver) {
|
||||
DBG("UDC already has a gadget driver\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
struct at91_udc *udc;
|
||||
|
||||
udc = container_of(gadget, struct at91_udc, gadget);
|
||||
udc->driver = driver;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
dev_set_drvdata(&udc->gadget.dev, &driver->driver);
|
||||
udc->enabled = 1;
|
||||
udc->selfpowered = 1;
|
||||
|
||||
retval = bind(&udc->gadget);
|
||||
if (retval) {
|
||||
DBG("bind() returned %d\n", retval);
|
||||
udc->driver = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
dev_set_drvdata(&udc->gadget.dev, NULL);
|
||||
udc->enabled = 0;
|
||||
udc->selfpowered = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
pullup(udc, 1);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
DBG("bound to %s\n", driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_stop(struct usb_gadget_driver *driver)
|
||||
static int at91_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct at91_udc *udc = &controller;
|
||||
struct at91_udc *udc;
|
||||
unsigned long flags;
|
||||
|
||||
if (!driver || driver != udc->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
udc = container_of(gadget, struct at91_udc, gadget);
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->enabled = 0;
|
||||
at91_udp_write(udc, AT91_UDP_IDR, ~0);
|
||||
pullup(udc, 0);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
driver->unbind(&udc->gadget);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
dev_set_drvdata(&udc->gadget.dev, NULL);
|
||||
udc->driver = NULL;
|
||||
|
|
|
|||
|
|
@ -105,9 +105,6 @@ struct at91_ep {
|
|||
unsigned is_in:1;
|
||||
unsigned is_iso:1;
|
||||
unsigned fifo_bank:1;
|
||||
|
||||
const struct usb_endpoint_descriptor
|
||||
*desc;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -599,13 +599,13 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
|||
|
||||
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||
|
||||
if (ep->desc) {
|
||||
if (ep->ep.desc) {
|
||||
spin_unlock_irqrestore(&ep->udc->lock, flags);
|
||||
DBG(DBG_ERR, "ep%d already enabled\n", ep->index);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->ep.maxpacket = maxpacket;
|
||||
|
||||
usba_ep_writel(ep, CFG, ept_cfg);
|
||||
|
|
@ -647,7 +647,7 @@ static int usba_ep_disable(struct usb_ep *_ep)
|
|||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
if (!ep->desc) {
|
||||
if (!ep->ep.desc) {
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
/* REVISIT because this driver disables endpoints in
|
||||
* reset_all_endpoints() before calling disconnect(),
|
||||
|
|
@ -658,7 +658,6 @@ static int usba_ep_disable(struct usb_ep *_ep)
|
|||
ep->ep.name);
|
||||
return -EINVAL;
|
||||
}
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
|
||||
list_splice_init(&ep->queue, &req_list);
|
||||
|
|
@ -752,7 +751,7 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep,
|
|||
*/
|
||||
ret = -ESHUTDOWN;
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (ep->desc) {
|
||||
if (ep->ep.desc) {
|
||||
if (list_empty(&ep->queue))
|
||||
submit_request(ep, req);
|
||||
|
||||
|
|
@ -776,7 +775,8 @@ usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n",
|
||||
ep->ep.name, req, _req->length);
|
||||
|
||||
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || !ep->desc)
|
||||
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN ||
|
||||
!ep->ep.desc)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
req->submitted = 0;
|
||||
|
|
@ -792,7 +792,7 @@ usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
/* May have received a reset since last time we checked */
|
||||
ret = -ESHUTDOWN;
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (ep->desc) {
|
||||
if (ep->ep.desc) {
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
|
||||
if ((!ep_is_control(ep) && ep->is_in) ||
|
||||
|
|
@ -905,7 +905,7 @@ static int usba_ep_set_halt(struct usb_ep *_ep, int value)
|
|||
DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name,
|
||||
value ? "set" : "clear");
|
||||
|
||||
if (!ep->desc) {
|
||||
if (!ep->ep.desc) {
|
||||
DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n",
|
||||
ep->ep.name);
|
||||
return -ENODEV;
|
||||
|
|
@ -1008,16 +1008,16 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_usba_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *));
|
||||
static int atmel_usba_stop(struct usb_gadget_driver *driver);
|
||||
|
||||
static int atmel_usba_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static const struct usb_gadget_ops usba_udc_ops = {
|
||||
.get_frame = usba_udc_get_frame,
|
||||
.wakeup = usba_udc_wakeup,
|
||||
.set_selfpowered = usba_udc_set_selfpowered,
|
||||
.start = atmel_usba_start,
|
||||
.stop = atmel_usba_stop,
|
||||
.udc_start = atmel_usba_start,
|
||||
.udc_stop = atmel_usba_stop,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor usba_ep0_desc = {
|
||||
|
|
@ -1071,7 +1071,7 @@ static void reset_all_endpoints(struct usba_udc *udc)
|
|||
* FIXME remove this code ... and retest thoroughly.
|
||||
*/
|
||||
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
|
||||
if (ep->desc) {
|
||||
if (ep->ep.desc) {
|
||||
spin_unlock(&udc->lock);
|
||||
usba_ep_disable(&ep->ep);
|
||||
spin_lock(&udc->lock);
|
||||
|
|
@ -1089,9 +1089,9 @@ static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex)
|
|||
list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
|
||||
u8 bEndpointAddress;
|
||||
|
||||
if (!ep->desc)
|
||||
if (!ep->ep.desc)
|
||||
continue;
|
||||
bEndpointAddress = ep->desc->bEndpointAddress;
|
||||
bEndpointAddress = ep->ep.desc->bEndpointAddress;
|
||||
if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
|
||||
continue;
|
||||
if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
|
||||
|
|
@ -1727,7 +1727,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
|||
usb_speed_string(udc->gadget.speed));
|
||||
|
||||
ep0 = &usba_ep[0];
|
||||
ep0->desc = &usba_ep0_desc;
|
||||
ep0->ep.desc = &usba_ep0_desc;
|
||||
ep0->state = WAIT_FOR_SETUP;
|
||||
usba_ep_writel(ep0, CFG,
|
||||
(USBA_BF(EPT_SIZE, EP0_EPT_SIZE)
|
||||
|
|
@ -1795,21 +1795,13 @@ out:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int atmel_usba_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *))
|
||||
static int atmel_usba_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usba_udc *udc = &the_udc;
|
||||
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!udc->pdev)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (udc->driver) {
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
|
||||
udc->driver = driver;
|
||||
|
|
@ -1819,13 +1811,6 @@ static int atmel_usba_start(struct usb_gadget_driver *driver,
|
|||
clk_enable(udc->pclk);
|
||||
clk_enable(udc->hclk);
|
||||
|
||||
ret = bind(&udc->gadget);
|
||||
if (ret) {
|
||||
DBG(DBG_ERR, "Could not bind to driver %s: error %d\n",
|
||||
driver->driver.name, ret);
|
||||
goto err_driver_bind;
|
||||
}
|
||||
|
||||
DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name);
|
||||
|
||||
udc->vbus_prev = 0;
|
||||
|
|
@ -1842,23 +1827,14 @@ static int atmel_usba_start(struct usb_gadget_driver *driver,
|
|||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err_driver_bind:
|
||||
udc->driver = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_usba_stop(struct usb_gadget_driver *driver)
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usba_udc *udc = &the_udc;
|
||||
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
|
||||
unsigned long flags;
|
||||
|
||||
if (!udc->pdev)
|
||||
return -ENODEV;
|
||||
if (driver != udc->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
disable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
|
|
@ -1871,10 +1847,6 @@ static int atmel_usba_stop(struct usb_gadget_driver *driver)
|
|||
toggle_bias(0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
|
||||
if (udc->driver->disconnect)
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
|
||||
driver->unbind(&udc->gadget);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
|
||||
|
|
|
|||
|
|
@ -280,7 +280,6 @@ struct usba_ep {
|
|||
struct usba_udc *udc;
|
||||
|
||||
struct list_head queue;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
u16 fifo_size;
|
||||
u8 nr_banks;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,227 +0,0 @@
|
|||
/*
|
||||
* ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Description: MIPS USB IP core family device controller
|
||||
* Structures, registers and logging macros
|
||||
*/
|
||||
|
||||
#ifndef _CI13XXX_h_
|
||||
#define _CI13XXX_h_
|
||||
|
||||
/******************************************************************************
|
||||
* DEFINE
|
||||
*****************************************************************************/
|
||||
#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
|
||||
#define ENDPT_MAX (32)
|
||||
#define CTRL_PAYLOAD_MAX (64)
|
||||
#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */
|
||||
#define TX (1) /* similar to USB_DIR_IN but can be used as an index */
|
||||
|
||||
/******************************************************************************
|
||||
* STRUCTURES
|
||||
*****************************************************************************/
|
||||
/* DMA layout of transfer descriptors */
|
||||
struct ci13xxx_td {
|
||||
/* 0 */
|
||||
u32 next;
|
||||
#define TD_TERMINATE BIT(0)
|
||||
#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
|
||||
/* 1 */
|
||||
u32 token;
|
||||
#define TD_STATUS (0x00FFUL << 0)
|
||||
#define TD_STATUS_TR_ERR BIT(3)
|
||||
#define TD_STATUS_DT_ERR BIT(5)
|
||||
#define TD_STATUS_HALTED BIT(6)
|
||||
#define TD_STATUS_ACTIVE BIT(7)
|
||||
#define TD_MULTO (0x0003UL << 10)
|
||||
#define TD_IOC BIT(15)
|
||||
#define TD_TOTAL_BYTES (0x7FFFUL << 16)
|
||||
/* 2 */
|
||||
u32 page[5];
|
||||
#define TD_CURR_OFFSET (0x0FFFUL << 0)
|
||||
#define TD_FRAME_NUM (0x07FFUL << 0)
|
||||
#define TD_RESERVED_MASK (0x0FFFUL << 0)
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* DMA layout of queue heads */
|
||||
struct ci13xxx_qh {
|
||||
/* 0 */
|
||||
u32 cap;
|
||||
#define QH_IOS BIT(15)
|
||||
#define QH_MAX_PKT (0x07FFUL << 16)
|
||||
#define QH_ZLT BIT(29)
|
||||
#define QH_MULT (0x0003UL << 30)
|
||||
/* 1 */
|
||||
u32 curr;
|
||||
/* 2 - 8 */
|
||||
struct ci13xxx_td td;
|
||||
/* 9 */
|
||||
u32 RESERVED;
|
||||
struct usb_ctrlrequest setup;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Extension of usb_request */
|
||||
struct ci13xxx_req {
|
||||
struct usb_request req;
|
||||
unsigned map;
|
||||
struct list_head queue;
|
||||
struct ci13xxx_td *ptr;
|
||||
dma_addr_t dma;
|
||||
struct ci13xxx_td *zptr;
|
||||
dma_addr_t zdma;
|
||||
};
|
||||
|
||||
/* Extension of usb_ep */
|
||||
struct ci13xxx_ep {
|
||||
struct usb_ep ep;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
u8 dir;
|
||||
u8 num;
|
||||
u8 type;
|
||||
char name[16];
|
||||
struct {
|
||||
struct list_head queue;
|
||||
struct ci13xxx_qh *ptr;
|
||||
dma_addr_t dma;
|
||||
} qh;
|
||||
int wedge;
|
||||
|
||||
/* global resources */
|
||||
spinlock_t *lock;
|
||||
struct device *device;
|
||||
struct dma_pool *td_pool;
|
||||
};
|
||||
|
||||
struct ci13xxx;
|
||||
struct ci13xxx_udc_driver {
|
||||
const char *name;
|
||||
unsigned long flags;
|
||||
#define CI13XXX_REGS_SHARED BIT(0)
|
||||
#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
|
||||
#define CI13XXX_PULLUP_ON_VBUS BIT(2)
|
||||
#define CI13XXX_DISABLE_STREAMING BIT(3)
|
||||
|
||||
#define CI13XXX_CONTROLLER_RESET_EVENT 0
|
||||
#define CI13XXX_CONTROLLER_STOPPED_EVENT 1
|
||||
void (*notify_event) (struct ci13xxx *udc, unsigned event);
|
||||
};
|
||||
|
||||
/* CI13XXX UDC descriptor & global resources */
|
||||
struct ci13xxx {
|
||||
spinlock_t *lock; /* ctrl register bank access */
|
||||
void __iomem *regs; /* registers address space */
|
||||
|
||||
struct dma_pool *qh_pool; /* DMA pool for queue heads */
|
||||
struct dma_pool *td_pool; /* DMA pool for transfer descs */
|
||||
struct usb_request *status; /* ep0 status request */
|
||||
|
||||
struct usb_gadget gadget; /* USB slave device */
|
||||
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
|
||||
u32 ep0_dir; /* ep0 direction */
|
||||
#define ep0out ci13xxx_ep[0]
|
||||
#define ep0in ci13xxx_ep[hw_ep_max / 2]
|
||||
u8 remote_wakeup; /* Is remote wakeup feature
|
||||
enabled by the host? */
|
||||
u8 suspended; /* suspended by the host */
|
||||
u8 test_mode; /* the selected test mode */
|
||||
|
||||
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
|
||||
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
|
||||
int vbus_active; /* is VBUS active */
|
||||
struct usb_phy *transceiver; /* Transceiver struct */
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* REGISTERS
|
||||
*****************************************************************************/
|
||||
/* register size */
|
||||
#define REG_BITS (32)
|
||||
|
||||
/* HCCPARAMS */
|
||||
#define HCCPARAMS_LEN BIT(17)
|
||||
|
||||
/* DCCPARAMS */
|
||||
#define DCCPARAMS_DEN (0x1F << 0)
|
||||
#define DCCPARAMS_DC BIT(7)
|
||||
|
||||
/* TESTMODE */
|
||||
#define TESTMODE_FORCE BIT(0)
|
||||
|
||||
/* USBCMD */
|
||||
#define USBCMD_RS BIT(0)
|
||||
#define USBCMD_RST BIT(1)
|
||||
#define USBCMD_SUTW BIT(13)
|
||||
#define USBCMD_ATDTW BIT(14)
|
||||
|
||||
/* USBSTS & USBINTR */
|
||||
#define USBi_UI BIT(0)
|
||||
#define USBi_UEI BIT(1)
|
||||
#define USBi_PCI BIT(2)
|
||||
#define USBi_URI BIT(6)
|
||||
#define USBi_SLI BIT(8)
|
||||
|
||||
/* DEVICEADDR */
|
||||
#define DEVICEADDR_USBADRA BIT(24)
|
||||
#define DEVICEADDR_USBADR (0x7FUL << 25)
|
||||
|
||||
/* PORTSC */
|
||||
#define PORTSC_FPR BIT(6)
|
||||
#define PORTSC_SUSP BIT(7)
|
||||
#define PORTSC_HSP BIT(9)
|
||||
#define PORTSC_PTC (0x0FUL << 16)
|
||||
|
||||
/* DEVLC */
|
||||
#define DEVLC_PSPD (0x03UL << 25)
|
||||
#define DEVLC_PSPD_HS (0x02UL << 25)
|
||||
|
||||
/* USBMODE */
|
||||
#define USBMODE_CM (0x03UL << 0)
|
||||
#define USBMODE_CM_IDLE (0x00UL << 0)
|
||||
#define USBMODE_CM_DEVICE (0x02UL << 0)
|
||||
#define USBMODE_CM_HOST (0x03UL << 0)
|
||||
#define USBMODE_SLOM BIT(3)
|
||||
#define USBMODE_SDIS BIT(4)
|
||||
|
||||
/* ENDPTCTRL */
|
||||
#define ENDPTCTRL_RXS BIT(0)
|
||||
#define ENDPTCTRL_RXT (0x03UL << 2)
|
||||
#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */
|
||||
#define ENDPTCTRL_RXE BIT(7)
|
||||
#define ENDPTCTRL_TXS BIT(16)
|
||||
#define ENDPTCTRL_TXT (0x03UL << 18)
|
||||
#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */
|
||||
#define ENDPTCTRL_TXE BIT(23)
|
||||
|
||||
/******************************************************************************
|
||||
* LOGGING
|
||||
*****************************************************************************/
|
||||
#define ci13xxx_printk(level, format, args...) \
|
||||
do { \
|
||||
if (_udc == NULL) \
|
||||
printk(level "[%s] " format "\n", __func__, ## args); \
|
||||
else \
|
||||
dev_printk(level, _udc->gadget.dev.parent, \
|
||||
"[%s] " format "\n", __func__, ## args); \
|
||||
} while (0)
|
||||
|
||||
#define err(format, args...) ci13xxx_printk(KERN_ERR, format, ## args)
|
||||
#define warn(format, args...) ci13xxx_printk(KERN_WARNING, format, ## args)
|
||||
#define info(format, args...) ci13xxx_printk(KERN_INFO, format, ## args)
|
||||
|
||||
#ifdef TRACE
|
||||
#define trace(format, args...) ci13xxx_printk(KERN_DEBUG, format, ## args)
|
||||
#define dbg_trace(format, args...) dev_dbg(dev, format, ##args)
|
||||
#else
|
||||
#define trace(format, args...) do {} while (0)
|
||||
#define dbg_trace(format, args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _CI13XXX_h_ */
|
||||
|
|
@ -40,27 +40,27 @@ static int (*composite_gadget_bind)(struct usb_composite_dev *cdev);
|
|||
*/
|
||||
|
||||
static ushort idVendor;
|
||||
module_param(idVendor, ushort, 0);
|
||||
module_param(idVendor, ushort, 0644);
|
||||
MODULE_PARM_DESC(idVendor, "USB Vendor ID");
|
||||
|
||||
static ushort idProduct;
|
||||
module_param(idProduct, ushort, 0);
|
||||
module_param(idProduct, ushort, 0644);
|
||||
MODULE_PARM_DESC(idProduct, "USB Product ID");
|
||||
|
||||
static ushort bcdDevice;
|
||||
module_param(bcdDevice, ushort, 0);
|
||||
module_param(bcdDevice, ushort, 0644);
|
||||
MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
|
||||
|
||||
static char *iManufacturer;
|
||||
module_param(iManufacturer, charp, 0);
|
||||
module_param(iManufacturer, charp, 0644);
|
||||
MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
|
||||
|
||||
static char *iProduct;
|
||||
module_param(iProduct, charp, 0);
|
||||
module_param(iProduct, charp, 0644);
|
||||
MODULE_PARM_DESC(iProduct, "USB Product string");
|
||||
|
||||
static char *iSerialNumber;
|
||||
module_param(iSerialNumber, charp, 0);
|
||||
module_param(iSerialNumber, charp, 0644);
|
||||
MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
|
||||
|
||||
static char composite_manufacturer[50];
|
||||
|
|
@ -734,9 +734,23 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
|||
|
||||
INIT_LIST_HEAD(&config->functions);
|
||||
config->next_interface_id = 0;
|
||||
memset(config->interface, 0, sizeof(config->interface));
|
||||
|
||||
status = bind(config);
|
||||
if (status < 0) {
|
||||
while (!list_empty(&config->functions)) {
|
||||
struct usb_function *f;
|
||||
|
||||
f = list_first_entry(&config->functions,
|
||||
struct usb_function, list);
|
||||
list_del(&f->list);
|
||||
if (f->unbind) {
|
||||
DBG(cdev, "unbind function '%s'/%p\n",
|
||||
f->name, f);
|
||||
f->unbind(config, f);
|
||||
/* may free memory for "f" */
|
||||
}
|
||||
}
|
||||
list_del(&config->list);
|
||||
config->cdev = NULL;
|
||||
} else {
|
||||
|
|
@ -774,6 +788,53 @@ done:
|
|||
return status;
|
||||
}
|
||||
|
||||
static void remove_config(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *config)
|
||||
{
|
||||
while (!list_empty(&config->functions)) {
|
||||
struct usb_function *f;
|
||||
|
||||
f = list_first_entry(&config->functions,
|
||||
struct usb_function, list);
|
||||
list_del(&f->list);
|
||||
if (f->unbind) {
|
||||
DBG(cdev, "unbind function '%s'/%p\n", f->name, f);
|
||||
f->unbind(config, f);
|
||||
/* may free memory for "f" */
|
||||
}
|
||||
}
|
||||
list_del(&config->list);
|
||||
if (config->unbind) {
|
||||
DBG(cdev, "unbind config '%s'/%p\n", config->label, config);
|
||||
config->unbind(config);
|
||||
/* may free memory for "c" */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_remove_config() - remove a configuration from a device.
|
||||
* @cdev: wraps the USB gadget
|
||||
* @config: the configuration
|
||||
*
|
||||
* Drivers must call usb_gadget_disconnect before calling this function
|
||||
* to disconnect the device from the host and make sure the host will not
|
||||
* try to enumerate the device while we are changing the config list.
|
||||
*/
|
||||
void usb_remove_config(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *config)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
|
||||
if (cdev->config == config)
|
||||
reset_config(cdev);
|
||||
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
|
||||
remove_config(cdev, config);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* We support strings in multiple languages ... string descriptor zero
|
||||
|
|
@ -785,7 +846,7 @@ done:
|
|||
static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
|
||||
{
|
||||
const struct usb_gadget_strings *s;
|
||||
u16 language;
|
||||
__le16 language;
|
||||
__le16 *tmp;
|
||||
|
||||
while (*sp) {
|
||||
|
|
@ -877,7 +938,7 @@ static int get_string(struct usb_composite_dev *cdev,
|
|||
else if (cdev->product_override == id)
|
||||
str = iProduct ?: composite->iProduct;
|
||||
else if (cdev->serial_override == id)
|
||||
str = iSerialNumber;
|
||||
str = iSerialNumber ?: composite->iSerialNumber;
|
||||
else
|
||||
str = NULL;
|
||||
if (str) {
|
||||
|
|
@ -1328,28 +1389,9 @@ composite_unbind(struct usb_gadget *gadget)
|
|||
|
||||
while (!list_empty(&cdev->configs)) {
|
||||
struct usb_configuration *c;
|
||||
|
||||
c = list_first_entry(&cdev->configs,
|
||||
struct usb_configuration, list);
|
||||
while (!list_empty(&c->functions)) {
|
||||
struct usb_function *f;
|
||||
|
||||
f = list_first_entry(&c->functions,
|
||||
struct usb_function, list);
|
||||
list_del(&f->list);
|
||||
if (f->unbind) {
|
||||
DBG(cdev, "unbind function '%s'/%p\n",
|
||||
f->name, f);
|
||||
f->unbind(c, f);
|
||||
/* may free memory for "f" */
|
||||
}
|
||||
}
|
||||
list_del(&c->list);
|
||||
if (c->unbind) {
|
||||
DBG(cdev, "unbind config '%s'/%p\n", c->label, c);
|
||||
c->unbind(c);
|
||||
/* may free memory for "c" */
|
||||
}
|
||||
remove_config(cdev, c);
|
||||
}
|
||||
if (composite->unbind)
|
||||
composite->unbind(cdev);
|
||||
|
|
@ -1431,10 +1473,16 @@ static int composite_bind(struct usb_gadget *gadget)
|
|||
/* standardized runtime overrides for device ID data */
|
||||
if (idVendor)
|
||||
cdev->desc.idVendor = cpu_to_le16(idVendor);
|
||||
else
|
||||
idVendor = le16_to_cpu(cdev->desc.idVendor);
|
||||
if (idProduct)
|
||||
cdev->desc.idProduct = cpu_to_le16(idProduct);
|
||||
else
|
||||
idProduct = le16_to_cpu(cdev->desc.idProduct);
|
||||
if (bcdDevice)
|
||||
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
|
||||
else
|
||||
bcdDevice = le16_to_cpu(cdev->desc.bcdDevice);
|
||||
|
||||
/* string overrides */
|
||||
if (iManufacturer || !cdev->desc.iManufacturer) {
|
||||
|
|
@ -1455,7 +1503,8 @@ static int composite_bind(struct usb_gadget *gadget)
|
|||
cdev->product_override =
|
||||
override_id(cdev, &cdev->desc.iProduct);
|
||||
|
||||
if (iSerialNumber)
|
||||
if (iSerialNumber ||
|
||||
(!cdev->desc.iSerialNumber && composite->iSerialNumber))
|
||||
cdev->serial_override =
|
||||
override_id(cdev, &cdev->desc.iSerialNumber);
|
||||
|
||||
|
|
|
|||
|
|
@ -595,14 +595,12 @@ static struct usb_request *dummy_alloc_request(struct usb_ep *_ep,
|
|||
|
||||
static void dummy_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct dummy_ep *ep;
|
||||
struct dummy_request *req;
|
||||
|
||||
if (!_ep || !_req)
|
||||
return;
|
||||
ep = usb_ep_to_dummy_ep(_ep);
|
||||
if (!ep->desc && _ep->name != ep0name)
|
||||
if (!_ep || !_req) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
req = usb_request_to_dummy_request(_req);
|
||||
WARN_ON(!list_empty(&req->queue));
|
||||
|
|
|
|||
|
|
@ -1031,6 +1031,12 @@ struct ffs_sb_fill_data {
|
|||
struct ffs_file_perms perms;
|
||||
umode_t root_mode;
|
||||
const char *dev_name;
|
||||
union {
|
||||
/* set by ffs_fs_mount(), read by ffs_sb_fill() */
|
||||
void *private_data;
|
||||
/* set by ffs_sb_fill(), read by ffs_fs_mount */
|
||||
struct ffs_data *ffs_data;
|
||||
};
|
||||
};
|
||||
|
||||
static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
|
||||
|
|
@ -1047,8 +1053,14 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
|
|||
goto Enomem;
|
||||
|
||||
ffs->sb = sb;
|
||||
ffs->dev_name = data->dev_name;
|
||||
ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL);
|
||||
if (unlikely(!ffs->dev_name))
|
||||
goto Enomem;
|
||||
ffs->file_perms = data->perms;
|
||||
ffs->private_data = data->private_data;
|
||||
|
||||
/* used by the caller of this function */
|
||||
data->ffs_data = ffs;
|
||||
|
||||
sb->s_fs_info = ffs;
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
|
|
@ -1167,20 +1179,29 @@ ffs_fs_mount(struct file_system_type *t, int flags,
|
|||
},
|
||||
.root_mode = S_IFDIR | 0500,
|
||||
};
|
||||
struct dentry *rv;
|
||||
int ret;
|
||||
void *ffs_dev;
|
||||
|
||||
ENTER();
|
||||
|
||||
ret = functionfs_check_dev_callback(dev_name);
|
||||
if (unlikely(ret < 0))
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = ffs_fs_parse_opts(&data, opts);
|
||||
if (unlikely(ret < 0))
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ffs_dev = functionfs_acquire_dev_callback(dev_name);
|
||||
if (IS_ERR(ffs_dev))
|
||||
return ffs_dev;
|
||||
|
||||
data.dev_name = dev_name;
|
||||
return mount_single(t, flags, &data, ffs_sb_fill);
|
||||
data.private_data = ffs_dev;
|
||||
rv = mount_nodev(t, flags, &data, ffs_sb_fill);
|
||||
|
||||
/* data.ffs_data is set by ffs_sb_fill */
|
||||
if (IS_ERR(rv))
|
||||
functionfs_release_dev_callback(data.ffs_data);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1189,8 +1210,10 @@ ffs_fs_kill_sb(struct super_block *sb)
|
|||
ENTER();
|
||||
|
||||
kill_litter_super(sb);
|
||||
if (sb->s_fs_info)
|
||||
if (sb->s_fs_info) {
|
||||
functionfs_release_dev_callback(sb->s_fs_info);
|
||||
ffs_data_put(sb->s_fs_info);
|
||||
}
|
||||
}
|
||||
|
||||
static struct file_system_type ffs_fs_type = {
|
||||
|
|
@ -1256,6 +1279,7 @@ static void ffs_data_put(struct ffs_data *ffs)
|
|||
ffs_data_clear(ffs);
|
||||
BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
|
||||
waitqueue_active(&ffs->ep0req_completion.wait));
|
||||
kfree(ffs->dev_name);
|
||||
kfree(ffs);
|
||||
}
|
||||
}
|
||||
|
|
@ -1473,8 +1497,22 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev,
|
|||
|
||||
static void ffs_func_free(struct ffs_function *func)
|
||||
{
|
||||
struct ffs_ep *ep = func->eps;
|
||||
unsigned count = func->ffs->eps_count;
|
||||
unsigned long flags;
|
||||
|
||||
ENTER();
|
||||
|
||||
/* cleanup after autoconfig */
|
||||
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
||||
do {
|
||||
if (ep->ep && ep->req)
|
||||
usb_ep_free_request(ep->ep, ep->req);
|
||||
ep->req = NULL;
|
||||
++ep;
|
||||
} while (--count);
|
||||
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
||||
|
||||
ffs_data_put(func->ffs);
|
||||
|
||||
kfree(func->eps);
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ static int hidg_setup(struct usb_function *f,
|
|||
break;
|
||||
|
||||
default:
|
||||
VDBG(cdev, "Unknown decriptor request 0x%x\n",
|
||||
VDBG(cdev, "Unknown descriptor request 0x%x\n",
|
||||
value >> 8);
|
||||
goto stall;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ static void disable_loopback(struct f_loopback *loop)
|
|||
struct usb_composite_dev *cdev;
|
||||
|
||||
cdev = loop->function.config->cdev;
|
||||
disable_endpoints(cdev, loop->in_ep, loop->out_ep);
|
||||
disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
|
||||
VDBG(cdev, "%s disabled\n", loop->function.name);
|
||||
}
|
||||
|
||||
|
|
@ -329,7 +329,7 @@ fail0:
|
|||
* than 'buflen' bytes each.
|
||||
*/
|
||||
for (i = 0; i < qlen && result == 0; i++) {
|
||||
req = alloc_ep_req(ep);
|
||||
req = alloc_ep_req(ep, 0);
|
||||
if (req) {
|
||||
req->complete = loopback_complete;
|
||||
result = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
|
|
|
|||
|
|
@ -3110,13 +3110,6 @@ static int fsg_bind_config(struct usb_composite_dev *cdev,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static inline int __deprecated __maybe_unused
|
||||
fsg_add(struct usb_composite_dev *cdev, struct usb_configuration *c,
|
||||
struct fsg_common *common)
|
||||
{
|
||||
return fsg_bind_config(cdev, c, common);
|
||||
}
|
||||
|
||||
|
||||
/************************* Module parameters *************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ struct f_rndis {
|
|||
struct gether port;
|
||||
u8 ctrl_id, data_id;
|
||||
u8 ethaddr[ETH_ALEN];
|
||||
u32 vendorID;
|
||||
const char *manufacturer;
|
||||
int config;
|
||||
|
||||
struct usb_ep *notify;
|
||||
|
|
@ -768,12 +770,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_set_host_mac(rndis->config, rndis->ethaddr);
|
||||
|
||||
#if 0
|
||||
// FIXME
|
||||
if (rndis_set_param_vendor(rndis->config, vendorID,
|
||||
manufacturer))
|
||||
goto fail0;
|
||||
#endif
|
||||
if (rndis->manufacturer && rndis->vendorID &&
|
||||
rndis_set_param_vendor(rndis->config, rndis->vendorID,
|
||||
rndis->manufacturer))
|
||||
goto fail;
|
||||
|
||||
/* NOTE: all that is done without knowing or caring about
|
||||
* the network link ... which is unavailable to this code
|
||||
|
|
@ -820,6 +820,7 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
rndis_deregister(rndis->config);
|
||||
rndis_exit();
|
||||
rndis_string_defs[0].id = 0;
|
||||
|
||||
if (gadget_is_superspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
|
|
@ -840,20 +841,9 @@ static inline bool can_support_rndis(struct usb_configuration *c)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* rndis_bind_config - add RNDIS network link to a configuration
|
||||
* @c: the configuration to support the network link
|
||||
* @ethaddr: a buffer in which the ethernet address of the host side
|
||||
* side of the link was recorded
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int
|
||||
rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer)
|
||||
{
|
||||
struct f_rndis *rndis;
|
||||
int status;
|
||||
|
|
@ -898,6 +888,8 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
|||
goto fail;
|
||||
|
||||
memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
|
||||
rndis->vendorID = vendorID;
|
||||
rndis->manufacturer = manufacturer;
|
||||
|
||||
/* RNDIS activates when the host changes this filter */
|
||||
rndis->port.cdc_filter = 0;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ struct f_sourcesink {
|
|||
|
||||
struct usb_ep *in_ep;
|
||||
struct usb_ep *out_ep;
|
||||
struct usb_ep *iso_in_ep;
|
||||
struct usb_ep *iso_out_ep;
|
||||
int cur_alt;
|
||||
};
|
||||
|
||||
static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
||||
|
|
@ -59,18 +62,45 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
|||
}
|
||||
|
||||
static unsigned pattern;
|
||||
module_param(pattern, uint, 0);
|
||||
MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
|
||||
module_param(pattern, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none");
|
||||
|
||||
static unsigned isoc_interval = 4;
|
||||
module_param(isoc_interval, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_interval, "1 - 16");
|
||||
|
||||
static unsigned isoc_maxpacket = 1024;
|
||||
module_param(isoc_maxpacket, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
|
||||
|
||||
static unsigned isoc_mult;
|
||||
module_param(isoc_mult, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)");
|
||||
|
||||
static unsigned isoc_maxburst;
|
||||
module_param(isoc_maxburst, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_interface_descriptor source_sink_intf = {
|
||||
.bLength = sizeof source_sink_intf,
|
||||
static struct usb_interface_descriptor source_sink_intf_alt0 = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
/* .iInterface = DYNAMIC */
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor source_sink_intf_alt1 = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
.bAlternateSetting = 1,
|
||||
.bNumEndpoints = 4,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
/* full speed support: */
|
||||
|
|
@ -91,10 +121,36 @@ static struct usb_endpoint_descriptor fs_sink_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_iso_source_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(1023),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_iso_sink_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(1023),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *fs_source_sink_descs[] = {
|
||||
(struct usb_descriptor_header *) &source_sink_intf,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt0,
|
||||
(struct usb_descriptor_header *) &fs_sink_desc,
|
||||
(struct usb_descriptor_header *) &fs_source_desc,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt1,
|
||||
#define FS_ALT_IFC_1_OFFSET 3
|
||||
(struct usb_descriptor_header *) &fs_sink_desc,
|
||||
(struct usb_descriptor_header *) &fs_source_desc,
|
||||
(struct usb_descriptor_header *) &fs_iso_sink_desc,
|
||||
(struct usb_descriptor_header *) &fs_iso_source_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -116,10 +172,34 @@ static struct usb_endpoint_descriptor hs_sink_desc = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_iso_source_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_iso_sink_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hs_source_sink_descs[] = {
|
||||
(struct usb_descriptor_header *) &source_sink_intf,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt0,
|
||||
(struct usb_descriptor_header *) &hs_source_desc,
|
||||
(struct usb_descriptor_header *) &hs_sink_desc,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt1,
|
||||
#define HS_ALT_IFC_1_OFFSET 3
|
||||
(struct usb_descriptor_header *) &hs_source_desc,
|
||||
(struct usb_descriptor_header *) &hs_sink_desc,
|
||||
(struct usb_descriptor_header *) &hs_iso_source_desc,
|
||||
(struct usb_descriptor_header *) &hs_iso_sink_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -136,6 +216,7 @@ static struct usb_endpoint_descriptor ss_source_desc = {
|
|||
struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
|
||||
.bLength = USB_DT_SS_EP_COMP_SIZE,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
.bMaxBurst = 0,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = 0,
|
||||
|
|
@ -152,17 +233,64 @@ static struct usb_endpoint_descriptor ss_sink_desc = {
|
|||
struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
|
||||
.bLength = USB_DT_SS_EP_COMP_SIZE,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
.bMaxBurst = 0,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = 0,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_iso_source_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
|
||||
.bLength = USB_DT_SS_EP_COMP_SIZE,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
.bMaxBurst = 0,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_iso_sink_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
|
||||
.bLength = USB_DT_SS_EP_COMP_SIZE,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
.bMaxBurst = 0,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ss_source_sink_descs[] = {
|
||||
(struct usb_descriptor_header *) &source_sink_intf,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt0,
|
||||
(struct usb_descriptor_header *) &ss_source_desc,
|
||||
(struct usb_descriptor_header *) &ss_source_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_sink_desc,
|
||||
(struct usb_descriptor_header *) &ss_sink_comp_desc,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt1,
|
||||
#define SS_ALT_IFC_1_OFFSET 5
|
||||
(struct usb_descriptor_header *) &ss_source_desc,
|
||||
(struct usb_descriptor_header *) &ss_source_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_sink_desc,
|
||||
(struct usb_descriptor_header *) &ss_sink_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_iso_source_desc,
|
||||
(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_iso_sink_desc,
|
||||
(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -196,9 +324,10 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
id = usb_interface_id(c, f);
|
||||
if (id < 0)
|
||||
return id;
|
||||
source_sink_intf.bInterfaceNumber = id;
|
||||
source_sink_intf_alt0.bInterfaceNumber = id;
|
||||
source_sink_intf_alt1.bInterfaceNumber = id;
|
||||
|
||||
/* allocate endpoints */
|
||||
/* allocate bulk endpoints */
|
||||
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
|
||||
if (!ss->in_ep) {
|
||||
autoconf_fail:
|
||||
|
|
@ -213,12 +342,74 @@ autoconf_fail:
|
|||
goto autoconf_fail;
|
||||
ss->out_ep->driver_data = cdev; /* claim */
|
||||
|
||||
/* sanity check the isoc module parameters */
|
||||
if (isoc_interval < 1)
|
||||
isoc_interval = 1;
|
||||
if (isoc_interval > 16)
|
||||
isoc_interval = 16;
|
||||
if (isoc_mult > 2)
|
||||
isoc_mult = 2;
|
||||
if (isoc_maxburst > 15)
|
||||
isoc_maxburst = 15;
|
||||
|
||||
/* fill in the FS isoc descriptors from the module parameters */
|
||||
fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
|
||||
1023 : isoc_maxpacket;
|
||||
fs_iso_source_desc.bInterval = isoc_interval;
|
||||
fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
|
||||
1023 : isoc_maxpacket;
|
||||
fs_iso_sink_desc.bInterval = isoc_interval;
|
||||
|
||||
/* allocate iso endpoints */
|
||||
ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
|
||||
if (!ss->iso_in_ep)
|
||||
goto no_iso;
|
||||
ss->iso_in_ep->driver_data = cdev; /* claim */
|
||||
|
||||
ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
|
||||
if (ss->iso_out_ep) {
|
||||
ss->iso_out_ep->driver_data = cdev; /* claim */
|
||||
} else {
|
||||
ss->iso_in_ep->driver_data = NULL;
|
||||
ss->iso_in_ep = NULL;
|
||||
no_iso:
|
||||
/*
|
||||
* We still want to work even if the UDC doesn't have isoc
|
||||
* endpoints, so null out the alt interface that contains
|
||||
* them and continue.
|
||||
*/
|
||||
fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
|
||||
hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
|
||||
ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
|
||||
}
|
||||
|
||||
if (isoc_maxpacket > 1024)
|
||||
isoc_maxpacket = 1024;
|
||||
|
||||
/* support high speed hardware */
|
||||
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
||||
hs_source_desc.bEndpointAddress =
|
||||
fs_source_desc.bEndpointAddress;
|
||||
hs_sink_desc.bEndpointAddress =
|
||||
fs_sink_desc.bEndpointAddress;
|
||||
|
||||
/*
|
||||
* Fill in the HS isoc descriptors from the module parameters.
|
||||
* We assume that the user knows what they are doing and won't
|
||||
* give parameters that their UDC doesn't support.
|
||||
*/
|
||||
hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
|
||||
hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
|
||||
hs_iso_source_desc.bInterval = isoc_interval;
|
||||
hs_iso_source_desc.bEndpointAddress =
|
||||
fs_iso_source_desc.bEndpointAddress;
|
||||
|
||||
hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
|
||||
hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11;
|
||||
hs_iso_sink_desc.bInterval = isoc_interval;
|
||||
hs_iso_sink_desc.bEndpointAddress =
|
||||
fs_iso_sink_desc.bEndpointAddress;
|
||||
|
||||
f->hs_descriptors = hs_source_sink_descs;
|
||||
}
|
||||
|
||||
|
|
@ -228,13 +419,39 @@ autoconf_fail:
|
|||
fs_source_desc.bEndpointAddress;
|
||||
ss_sink_desc.bEndpointAddress =
|
||||
fs_sink_desc.bEndpointAddress;
|
||||
|
||||
/*
|
||||
* Fill in the SS isoc descriptors from the module parameters.
|
||||
* We assume that the user knows what they are doing and won't
|
||||
* give parameters that their UDC doesn't support.
|
||||
*/
|
||||
ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
|
||||
ss_iso_source_desc.bInterval = isoc_interval;
|
||||
ss_iso_source_comp_desc.bmAttributes = isoc_mult;
|
||||
ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst;
|
||||
ss_iso_source_comp_desc.wBytesPerInterval =
|
||||
isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
|
||||
ss_iso_source_desc.bEndpointAddress =
|
||||
fs_iso_source_desc.bEndpointAddress;
|
||||
|
||||
ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
|
||||
ss_iso_sink_desc.bInterval = isoc_interval;
|
||||
ss_iso_sink_comp_desc.bmAttributes = isoc_mult;
|
||||
ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst;
|
||||
ss_iso_sink_comp_desc.wBytesPerInterval =
|
||||
isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
|
||||
ss_iso_sink_desc.bEndpointAddress =
|
||||
fs_iso_sink_desc.bEndpointAddress;
|
||||
|
||||
f->ss_descriptors = ss_source_sink_descs;
|
||||
}
|
||||
|
||||
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
|
||||
DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
|
||||
(gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
|
||||
f->name, ss->in_ep->name, ss->out_ep->name);
|
||||
f->name, ss->in_ep->name, ss->out_ep->name,
|
||||
ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
|
||||
ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -251,6 +468,9 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
|
|||
u8 *buf = req->buf;
|
||||
struct usb_composite_dev *cdev = ss->function.config->cdev;
|
||||
|
||||
if (pattern == 2)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < req->actual; i++, buf++) {
|
||||
switch (pattern) {
|
||||
|
||||
|
|
@ -265,7 +485,7 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
|
|||
* each usb transfer request should be. Resync is done
|
||||
* with set_interface or set_config. (We *WANT* it to
|
||||
* get quickly out of sync if controllers or their drivers
|
||||
* stutter for any reason, including buffer duplcation...)
|
||||
* stutter for any reason, including buffer duplication...)
|
||||
*/
|
||||
case 1:
|
||||
if (*buf == (u8)(i % 63))
|
||||
|
|
@ -292,21 +512,30 @@ static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
|
|||
for (i = 0; i < req->length; i++)
|
||||
*buf++ = (u8) (i % 63);
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_sourcesink *ss = ep->driver_data;
|
||||
struct usb_composite_dev *cdev = ss->function.config->cdev;
|
||||
int status = req->status;
|
||||
struct usb_composite_dev *cdev;
|
||||
struct f_sourcesink *ss = ep->driver_data;
|
||||
int status = req->status;
|
||||
|
||||
/* driver_data will be null if ep has been disabled */
|
||||
if (!ss)
|
||||
return;
|
||||
|
||||
cdev = ss->function.config->cdev;
|
||||
|
||||
switch (status) {
|
||||
|
||||
case 0: /* normal completion? */
|
||||
if (ep == ss->out_ep) {
|
||||
check_read_data(ss, req);
|
||||
memset(req->buf, 0x55, req->length);
|
||||
if (pattern != 2)
|
||||
memset(req->buf, 0x55, req->length);
|
||||
} else
|
||||
reinit_write_data(ep, req);
|
||||
break;
|
||||
|
|
@ -344,32 +573,57 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
}
|
||||
}
|
||||
|
||||
static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in)
|
||||
static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
||||
bool is_iso, int speed)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
int status;
|
||||
int i, size, status;
|
||||
|
||||
ep = is_in ? ss->in_ep : ss->out_ep;
|
||||
req = alloc_ep_req(ep);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (is_iso) {
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
size = isoc_maxpacket * (isoc_mult + 1) *
|
||||
(isoc_maxburst + 1);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
size = isoc_maxpacket * (isoc_mult + 1);
|
||||
break;
|
||||
default:
|
||||
size = isoc_maxpacket > 1023 ?
|
||||
1023 : isoc_maxpacket;
|
||||
break;
|
||||
}
|
||||
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
||||
req = alloc_ep_req(ep, size);
|
||||
} else {
|
||||
ep = is_in ? ss->in_ep : ss->out_ep;
|
||||
req = alloc_ep_req(ep, 0);
|
||||
}
|
||||
|
||||
req->complete = source_sink_complete;
|
||||
if (is_in)
|
||||
reinit_write_data(ep, req);
|
||||
else
|
||||
memset(req->buf, 0x55, req->length);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
status = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (status) {
|
||||
struct usb_composite_dev *cdev;
|
||||
req->complete = source_sink_complete;
|
||||
if (is_in)
|
||||
reinit_write_data(ep, req);
|
||||
else if (pattern != 2)
|
||||
memset(req->buf, 0x55, req->length);
|
||||
|
||||
cdev = ss->function.config->cdev;
|
||||
ERROR(cdev, "start %s %s --> %d\n",
|
||||
is_in ? "IN" : "OUT",
|
||||
ep->name, status);
|
||||
free_ep_req(ep, req);
|
||||
status = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (status) {
|
||||
struct usb_composite_dev *cdev;
|
||||
|
||||
cdev = ss->function.config->cdev;
|
||||
ERROR(cdev, "start %s%s %s --> %d\n",
|
||||
is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
|
||||
ep->name, status);
|
||||
free_ep_req(ep, req);
|
||||
}
|
||||
|
||||
if (!is_iso)
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
|
|
@ -380,17 +634,20 @@ static void disable_source_sink(struct f_sourcesink *ss)
|
|||
struct usb_composite_dev *cdev;
|
||||
|
||||
cdev = ss->function.config->cdev;
|
||||
disable_endpoints(cdev, ss->in_ep, ss->out_ep);
|
||||
disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
|
||||
ss->iso_out_ep);
|
||||
VDBG(cdev, "%s disabled\n", ss->function.name);
|
||||
}
|
||||
|
||||
static int
|
||||
enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
|
||||
enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
|
||||
int alt)
|
||||
{
|
||||
int result = 0;
|
||||
int speed = cdev->gadget->speed;
|
||||
struct usb_ep *ep;
|
||||
|
||||
/* one endpoint writes (sources) zeroes IN (to the host) */
|
||||
/* one bulk endpoint writes (sources) zeroes IN (to the host) */
|
||||
ep = ss->in_ep;
|
||||
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
|
||||
if (result)
|
||||
|
|
@ -400,7 +657,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
|
|||
return result;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, true);
|
||||
result = source_sink_start_ep(ss, true, false, speed);
|
||||
if (result < 0) {
|
||||
fail:
|
||||
ep = ss->in_ep;
|
||||
|
|
@ -409,7 +666,7 @@ fail:
|
|||
return result;
|
||||
}
|
||||
|
||||
/* one endpoint reads (sinks) anything OUT (from the host) */
|
||||
/* one bulk endpoint reads (sinks) anything OUT (from the host) */
|
||||
ep = ss->out_ep;
|
||||
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
|
||||
if (result)
|
||||
|
|
@ -419,27 +676,82 @@ fail:
|
|||
goto fail;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, false);
|
||||
result = source_sink_start_ep(ss, false, false, speed);
|
||||
if (result < 0) {
|
||||
fail2:
|
||||
ep = ss->out_ep;
|
||||
usb_ep_disable(ep);
|
||||
ep->driver_data = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DBG(cdev, "%s enabled\n", ss->function.name);
|
||||
if (alt == 0)
|
||||
goto out;
|
||||
|
||||
/* one iso endpoint writes (sources) zeroes IN (to the host) */
|
||||
ep = ss->iso_in_ep;
|
||||
if (ep) {
|
||||
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
|
||||
if (result)
|
||||
goto fail2;
|
||||
result = usb_ep_enable(ep);
|
||||
if (result < 0)
|
||||
goto fail2;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, true, true, speed);
|
||||
if (result < 0) {
|
||||
fail3:
|
||||
ep = ss->iso_in_ep;
|
||||
if (ep) {
|
||||
usb_ep_disable(ep);
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
goto fail2;
|
||||
}
|
||||
}
|
||||
|
||||
/* one iso endpoint reads (sinks) anything OUT (from the host) */
|
||||
ep = ss->iso_out_ep;
|
||||
if (ep) {
|
||||
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
|
||||
if (result)
|
||||
goto fail3;
|
||||
result = usb_ep_enable(ep);
|
||||
if (result < 0)
|
||||
goto fail3;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, false, true, speed);
|
||||
if (result < 0) {
|
||||
usb_ep_disable(ep);
|
||||
ep->driver_data = NULL;
|
||||
goto fail3;
|
||||
}
|
||||
}
|
||||
out:
|
||||
ss->cur_alt = alt;
|
||||
|
||||
DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sourcesink_set_alt(struct usb_function *f,
|
||||
unsigned intf, unsigned alt)
|
||||
{
|
||||
struct f_sourcesink *ss = func_to_ss(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct f_sourcesink *ss = func_to_ss(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
/* we know alt is zero */
|
||||
if (ss->in_ep->driver_data)
|
||||
disable_source_sink(ss);
|
||||
return enable_source_sink(cdev, ss);
|
||||
return enable_source_sink(cdev, ss, alt);
|
||||
}
|
||||
|
||||
static int sourcesink_get_alt(struct usb_function *f, unsigned intf)
|
||||
{
|
||||
struct f_sourcesink *ss = func_to_ss(f);
|
||||
|
||||
return ss->cur_alt;
|
||||
}
|
||||
|
||||
static void sourcesink_disable(struct usb_function *f)
|
||||
|
|
@ -465,6 +777,7 @@ static int __init sourcesink_bind_config(struct usb_configuration *c)
|
|||
ss->function.bind = sourcesink_bind;
|
||||
ss->function.unbind = sourcesink_unbind;
|
||||
ss->function.set_alt = sourcesink_set_alt;
|
||||
ss->function.get_alt = sourcesink_get_alt;
|
||||
ss->function.disable = sourcesink_disable;
|
||||
|
||||
status = usb_add_function(c, &ss->function);
|
||||
|
|
@ -536,7 +849,7 @@ unknown:
|
|||
req->length = value;
|
||||
value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
if (value < 0)
|
||||
ERROR(c->cdev, "source/sinkc response, err %d\n",
|
||||
ERROR(c->cdev, "source/sink response, err %d\n",
|
||||
value);
|
||||
}
|
||||
|
||||
|
|
@ -545,12 +858,12 @@ unknown:
|
|||
}
|
||||
|
||||
static struct usb_configuration sourcesink_driver = {
|
||||
.label = "source/sink",
|
||||
.strings = sourcesink_strings,
|
||||
.setup = sourcesink_setup,
|
||||
.bConfigurationValue = 3,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.label = "source/sink",
|
||||
.strings = sourcesink_strings,
|
||||
.setup = sourcesink_setup,
|
||||
.bConfigurationValue = 3,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -567,7 +880,8 @@ int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume)
|
|||
return id;
|
||||
strings_sourcesink[0].id = id;
|
||||
|
||||
source_sink_intf.iInterface = id;
|
||||
source_sink_intf_alt0.iInterface = id;
|
||||
source_sink_intf_alt1.iInterface = id;
|
||||
sourcesink_driver.iConfiguration = id;
|
||||
|
||||
/* support autoresume for remote wakeup testing */
|
||||
|
|
|
|||
|
|
@ -71,9 +71,6 @@ static struct usb_endpoint_descriptor qe_ep0_desc = {
|
|||
.wMaxPacketSize = USB_MAX_CTRL_PAYLOAD,
|
||||
};
|
||||
|
||||
/* it is initialized in probe() */
|
||||
static struct qe_udc *udc_controller;
|
||||
|
||||
/********************************************************************
|
||||
* Internal Used Function Start
|
||||
********************************************************************/
|
||||
|
|
@ -188,8 +185,8 @@ static int qe_ep0_stall(struct qe_udc *udc)
|
|||
{
|
||||
qe_eptx_stall_change(&udc->eps[0], 1);
|
||||
qe_eprx_stall_change(&udc->eps[0], 1);
|
||||
udc_controller->ep0_state = WAIT_FOR_SETUP;
|
||||
udc_controller->ep0_dir = 0;
|
||||
udc->ep0_state = WAIT_FOR_SETUP;
|
||||
udc->ep0_dir = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -450,13 +447,13 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
|
|||
|
||||
ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer);
|
||||
if (ep->rxbuf_d == DMA_ADDR_INVALID) {
|
||||
ep->rxbuf_d = dma_map_single(udc_controller->gadget.dev.parent,
|
||||
ep->rxbuf_d = dma_map_single(ep->udc->gadget.dev.parent,
|
||||
ep->rxbuffer,
|
||||
size,
|
||||
DMA_FROM_DEVICE);
|
||||
ep->rxbufmap = 1;
|
||||
} else {
|
||||
dma_sync_single_for_device(udc_controller->gadget.dev.parent,
|
||||
dma_sync_single_for_device(ep->udc->gadget.dev.parent,
|
||||
ep->rxbuf_d, size,
|
||||
DMA_FROM_DEVICE);
|
||||
ep->rxbufmap = 0;
|
||||
|
|
@ -489,10 +486,10 @@ static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num)
|
|||
epparam = udc->ep_param[pipe_num];
|
||||
|
||||
usep = 0;
|
||||
logepnum = (ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
|
||||
logepnum = (ep->ep.desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
|
||||
usep |= (logepnum << USB_EPNUM_SHIFT);
|
||||
|
||||
switch (ep->desc->bmAttributes & 0x03) {
|
||||
switch (ep->ep.desc->bmAttributes & 0x03) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
usep |= USB_TRANS_BULK;
|
||||
break;
|
||||
|
|
@ -644,7 +641,7 @@ static int qe_ep_init(struct qe_udc *udc,
|
|||
/* initialize ep structure */
|
||||
ep->ep.maxpacket = max;
|
||||
ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->stopped = 0;
|
||||
ep->init = 1;
|
||||
|
||||
|
|
@ -698,14 +695,14 @@ en_done:
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void qe_usb_enable(void)
|
||||
static inline void qe_usb_enable(struct qe_udc *udc)
|
||||
{
|
||||
setbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
|
||||
setbits8(&udc->usb_regs->usb_usmod, USB_MODE_EN);
|
||||
}
|
||||
|
||||
static inline void qe_usb_disable(void)
|
||||
static inline void qe_usb_disable(struct qe_udc *udc)
|
||||
{
|
||||
clrbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
|
||||
clrbits8(&udc->usb_regs->usb_usmod, USB_MODE_EN);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*
|
||||
|
|
@ -1599,7 +1596,7 @@ static int qe_ep_enable(struct usb_ep *_ep,
|
|||
ep = container_of(_ep, struct qe_ep, ep);
|
||||
|
||||
/* catch various bogus parameters */
|
||||
if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] ||
|
||||
if (!_ep || !desc || ep->ep.desc || _ep->name == ep_name[0] ||
|
||||
(desc->bDescriptorType != USB_DT_ENDPOINT))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -1629,7 +1626,7 @@ static int qe_ep_disable(struct usb_ep *_ep)
|
|||
ep = container_of(_ep, struct qe_ep, ep);
|
||||
udc = ep->udc;
|
||||
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
dev_dbg(udc->dev, "%s not enabled\n", _ep ? ep->ep.name : NULL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1637,7 +1634,6 @@ static int qe_ep_disable(struct usb_ep *_ep)
|
|||
spin_lock_irqsave(&udc->lock, flags);
|
||||
/* Nuke all pending requests (does flush) */
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
ep->tx_req = NULL;
|
||||
|
|
@ -1656,13 +1652,13 @@ static int qe_ep_disable(struct usb_ep *_ep)
|
|||
if (ep->dir != USB_DIR_IN) {
|
||||
kfree(ep->rxframe);
|
||||
if (ep->rxbufmap) {
|
||||
dma_unmap_single(udc_controller->gadget.dev.parent,
|
||||
dma_unmap_single(udc->gadget.dev.parent,
|
||||
ep->rxbuf_d, size,
|
||||
DMA_FROM_DEVICE);
|
||||
ep->rxbuf_d = DMA_ADDR_INVALID;
|
||||
} else {
|
||||
dma_sync_single_for_cpu(
|
||||
udc_controller->gadget.dev.parent,
|
||||
udc->gadget.dev.parent,
|
||||
ep->rxbuf_d, size,
|
||||
DMA_FROM_DEVICE);
|
||||
}
|
||||
|
|
@ -1715,7 +1711,7 @@ static int __qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
dev_dbg(udc->dev, "bad params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!_ep || (!ep->desc && ep_index(ep))) {
|
||||
if (!_ep || (!ep->ep.desc && ep_index(ep))) {
|
||||
dev_dbg(udc->dev, "bad ep\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1826,7 +1822,7 @@ static int qe_ep_set_halt(struct usb_ep *_ep, int value)
|
|||
struct qe_udc *udc;
|
||||
|
||||
ep = container_of(_ep, struct qe_ep, ep);
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
status = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1880,9 +1876,10 @@ static struct usb_ep_ops qe_ep_ops = {
|
|||
/* Get the current frame number */
|
||||
static int qe_get_frame(struct usb_gadget *gadget)
|
||||
{
|
||||
struct qe_udc *udc = container_of(gadget, struct qe_udc, gadget);
|
||||
u16 tmp;
|
||||
|
||||
tmp = in_be16(&udc_controller->usb_param->frame_n);
|
||||
tmp = in_be16(&udc->usb_param->frame_n);
|
||||
if (tmp & 0x8000)
|
||||
tmp = tmp & 0x07ff;
|
||||
else
|
||||
|
|
@ -1891,57 +1888,16 @@ static int qe_get_frame(struct usb_gadget *gadget)
|
|||
return (int)tmp;
|
||||
}
|
||||
|
||||
/* Tries to wake up the host connected to this gadget
|
||||
*
|
||||
* Return : 0-success
|
||||
* Negative-this feature not enabled by host or not supported by device hw
|
||||
*/
|
||||
static int qe_wakeup(struct usb_gadget *gadget)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/* Notify controller that VBUS is powered, Called by whatever
|
||||
detects VBUS sessions */
|
||||
static int qe_vbus_session(struct usb_gadget *gadget, int is_active)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/* constrain controller's VBUS power usage
|
||||
* This call is used by gadget drivers during SET_CONFIGURATION calls,
|
||||
* reporting how much power the device may consume. For example, this
|
||||
* could affect how quickly batteries are recharged.
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
static int qe_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/* Change Data+ pullup status
|
||||
* this func is used by usb_gadget_connect/disconnect
|
||||
*/
|
||||
static int qe_pullup(struct usb_gadget *gadget, int is_on)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int fsl_qe_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *));
|
||||
static int fsl_qe_stop(struct usb_gadget_driver *driver);
|
||||
static int fsl_qe_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int fsl_qe_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
/* defined in usb_gadget.h */
|
||||
static struct usb_gadget_ops qe_gadget_ops = {
|
||||
.get_frame = qe_get_frame,
|
||||
.wakeup = qe_wakeup,
|
||||
/* .set_selfpowered = qe_set_selfpowered,*/ /* always selfpowered */
|
||||
.vbus_session = qe_vbus_session,
|
||||
.vbus_draw = qe_vbus_draw,
|
||||
.pullup = qe_pullup,
|
||||
.start = fsl_qe_start,
|
||||
.stop = fsl_qe_stop,
|
||||
.udc_start = fsl_qe_start,
|
||||
.udc_stop = fsl_qe_stop,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
|
|
@ -2015,7 +1971,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value,
|
|||
u16 usep;
|
||||
|
||||
/* stall if endpoint doesn't exist */
|
||||
if (!target_ep->desc)
|
||||
if (!target_ep->ep.desc)
|
||||
goto stall;
|
||||
|
||||
usep = in_be16(&udc->usb_regs->usb_usep[pipe]);
|
||||
|
|
@ -2190,7 +2146,7 @@ static int reset_irq(struct qe_udc *udc)
|
|||
if (udc->usb_state == USB_STATE_DEFAULT)
|
||||
return 0;
|
||||
|
||||
qe_usb_disable();
|
||||
qe_usb_disable(udc);
|
||||
out_8(&udc->usb_regs->usb_usadr, 0);
|
||||
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
|
||||
|
|
@ -2202,7 +2158,7 @@ static int reset_irq(struct qe_udc *udc)
|
|||
udc->usb_state = USB_STATE_DEFAULT;
|
||||
udc->ep0_state = WAIT_FOR_SETUP;
|
||||
udc->ep0_dir = USB_DIR_OUT;
|
||||
qe_usb_enable();
|
||||
qe_usb_enable(udc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2327,92 +2283,65 @@ static irqreturn_t qe_udc_irq(int irq, void *_udc)
|
|||
/*-------------------------------------------------------------------------
|
||||
Gadget driver probe and unregister.
|
||||
--------------------------------------------------------------------------*/
|
||||
static int fsl_qe_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *))
|
||||
static int fsl_qe_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
int retval;
|
||||
unsigned long flags = 0;
|
||||
|
||||
/* standard operations */
|
||||
if (!udc_controller)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind || !driver->disconnect || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
if (udc_controller->driver)
|
||||
return -EBUSY;
|
||||
struct qe_udc *udc;
|
||||
unsigned long flags;
|
||||
|
||||
udc = container_of(gadget, struct qe_udc, gadget);
|
||||
/* lock is needed but whether should use this lock or another */
|
||||
spin_lock_irqsave(&udc_controller->lock, flags);
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
driver->driver.bus = NULL;
|
||||
/* hook up the driver */
|
||||
udc_controller->driver = driver;
|
||||
udc_controller->gadget.dev.driver = &driver->driver;
|
||||
udc_controller->gadget.speed = driver->max_speed;
|
||||
spin_unlock_irqrestore(&udc_controller->lock, flags);
|
||||
|
||||
retval = bind(&udc_controller->gadget);
|
||||
if (retval) {
|
||||
dev_err(udc_controller->dev, "bind to %s --> %d",
|
||||
driver->driver.name, retval);
|
||||
udc_controller->gadget.dev.driver = NULL;
|
||||
udc_controller->driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
udc->driver = driver;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
udc->gadget.speed = driver->max_speed;
|
||||
|
||||
/* Enable IRQ reg and Set usbcmd reg EN bit */
|
||||
qe_usb_enable();
|
||||
qe_usb_enable(udc);
|
||||
|
||||
out_be16(&udc_controller->usb_regs->usb_usber, 0xffff);
|
||||
out_be16(&udc_controller->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE);
|
||||
udc_controller->usb_state = USB_STATE_ATTACHED;
|
||||
udc_controller->ep0_state = WAIT_FOR_SETUP;
|
||||
udc_controller->ep0_dir = USB_DIR_OUT;
|
||||
dev_info(udc_controller->dev, "%s bind to driver %s \n",
|
||||
udc_controller->gadget.name, driver->driver.name);
|
||||
out_be16(&udc->usb_regs->usb_usber, 0xffff);
|
||||
out_be16(&udc->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE);
|
||||
udc->usb_state = USB_STATE_ATTACHED;
|
||||
udc->ep0_state = WAIT_FOR_SETUP;
|
||||
udc->ep0_dir = USB_DIR_OUT;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
dev_info(udc->dev, "%s bind to driver %s\n", udc->gadget.name,
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_qe_stop(struct usb_gadget_driver *driver)
|
||||
static int fsl_qe_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct qe_udc *udc;
|
||||
struct qe_ep *loop_ep;
|
||||
unsigned long flags;
|
||||
|
||||
if (!udc_controller)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver != udc_controller->driver)
|
||||
return -EINVAL;
|
||||
|
||||
udc = container_of(gadget, struct qe_udc, gadget);
|
||||
/* stop usb controller, disable intr */
|
||||
qe_usb_disable();
|
||||
qe_usb_disable(udc);
|
||||
|
||||
/* in fact, no needed */
|
||||
udc_controller->usb_state = USB_STATE_ATTACHED;
|
||||
udc_controller->ep0_state = WAIT_FOR_SETUP;
|
||||
udc_controller->ep0_dir = 0;
|
||||
udc->usb_state = USB_STATE_ATTACHED;
|
||||
udc->ep0_state = WAIT_FOR_SETUP;
|
||||
udc->ep0_dir = 0;
|
||||
|
||||
/* stand operation */
|
||||
spin_lock_irqsave(&udc_controller->lock, flags);
|
||||
udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
nuke(&udc_controller->eps[0], -ESHUTDOWN);
|
||||
list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
|
||||
ep.ep_list)
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
nuke(&udc->eps[0], -ESHUTDOWN);
|
||||
list_for_each_entry(loop_ep, &udc->gadget.ep_list, ep.ep_list)
|
||||
nuke(loop_ep, -ESHUTDOWN);
|
||||
spin_unlock_irqrestore(&udc_controller->lock, flags);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
/* report disconnect; the controller is already quiesced */
|
||||
driver->disconnect(&udc_controller->gadget);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
|
||||
/* unbind gadget and unhook driver. */
|
||||
driver->unbind(&udc_controller->gadget);
|
||||
udc_controller->gadget.dev.driver = NULL;
|
||||
udc_controller->driver = NULL;
|
||||
|
||||
dev_info(udc_controller->dev, "unregistered gadget driver '%s'\r\n",
|
||||
dev_info(udc->dev, "unregistered gadget driver '%s'\r\n",
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2502,7 +2431,7 @@ static int __devinit qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
|
|||
ep->ep.ops = &qe_ep_ops;
|
||||
ep->stopped = 1;
|
||||
ep->ep.maxpacket = (unsigned short) ~0;
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->dir = 0xff;
|
||||
ep->epnum = (u8)pipe_num;
|
||||
ep->sent = 0;
|
||||
|
|
@ -2531,21 +2460,22 @@ static int __devinit qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
|
|||
*----------------------------------------------------------------------*/
|
||||
static void qe_udc_release(struct device *dev)
|
||||
{
|
||||
int i = 0;
|
||||
struct qe_udc *udc = container_of(dev, struct qe_udc, gadget.dev);
|
||||
int i;
|
||||
|
||||
complete(udc_controller->done);
|
||||
cpm_muram_free(cpm_muram_offset(udc_controller->ep_param[0]));
|
||||
complete(udc->done);
|
||||
cpm_muram_free(cpm_muram_offset(udc->ep_param[0]));
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++)
|
||||
udc_controller->ep_param[i] = NULL;
|
||||
udc->ep_param[i] = NULL;
|
||||
|
||||
kfree(udc_controller);
|
||||
udc_controller = NULL;
|
||||
kfree(udc);
|
||||
}
|
||||
|
||||
/* Driver probe functions */
|
||||
static const struct of_device_id qe_udc_match[];
|
||||
static int __devinit qe_udc_probe(struct platform_device *ofdev)
|
||||
{
|
||||
struct qe_udc *udc;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np = ofdev->dev.of_node;
|
||||
struct qe_ep *ep;
|
||||
|
|
@ -2562,44 +2492,44 @@ static int __devinit qe_udc_probe(struct platform_device *ofdev)
|
|||
return -ENODEV;
|
||||
|
||||
/* Initialize the udc structure including QH member and other member */
|
||||
udc_controller = qe_udc_config(ofdev);
|
||||
if (!udc_controller) {
|
||||
udc = qe_udc_config(ofdev);
|
||||
if (!udc) {
|
||||
dev_err(&ofdev->dev, "failed to initialize\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
udc_controller->soc_type = (unsigned long)match->data;
|
||||
udc_controller->usb_regs = of_iomap(np, 0);
|
||||
if (!udc_controller->usb_regs) {
|
||||
udc->soc_type = (unsigned long)match->data;
|
||||
udc->usb_regs = of_iomap(np, 0);
|
||||
if (!udc->usb_regs) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* initialize usb hw reg except for regs for EP,
|
||||
* leave usbintr reg untouched*/
|
||||
qe_udc_reg_init(udc_controller);
|
||||
qe_udc_reg_init(udc);
|
||||
|
||||
/* here comes the stand operations for probe
|
||||
* set the qe_udc->gadget.xxx */
|
||||
udc_controller->gadget.ops = &qe_gadget_ops;
|
||||
udc->gadget.ops = &qe_gadget_ops;
|
||||
|
||||
/* gadget.ep0 is a pointer */
|
||||
udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
|
||||
udc->gadget.ep0 = &udc->eps[0].ep;
|
||||
|
||||
INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
|
||||
INIT_LIST_HEAD(&udc->gadget.ep_list);
|
||||
|
||||
/* modify in register gadget process */
|
||||
udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
/* name: Identifies the controller hardware type. */
|
||||
udc_controller->gadget.name = driver_name;
|
||||
udc->gadget.name = driver_name;
|
||||
|
||||
device_initialize(&udc_controller->gadget.dev);
|
||||
device_initialize(&udc->gadget.dev);
|
||||
|
||||
dev_set_name(&udc_controller->gadget.dev, "gadget");
|
||||
dev_set_name(&udc->gadget.dev, "gadget");
|
||||
|
||||
udc_controller->gadget.dev.release = qe_udc_release;
|
||||
udc_controller->gadget.dev.parent = &ofdev->dev;
|
||||
udc->gadget.dev.release = qe_udc_release;
|
||||
udc->gadget.dev.parent = &ofdev->dev;
|
||||
|
||||
/* initialize qe_ep struct */
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS ; i++) {
|
||||
|
|
@ -2608,104 +2538,104 @@ static int __devinit qe_udc_probe(struct platform_device *ofdev)
|
|||
|
||||
/* setup the qe_ep struct and link ep.ep.list
|
||||
* into gadget.ep_list */
|
||||
qe_ep_config(udc_controller, (unsigned char)i);
|
||||
qe_ep_config(udc, (unsigned char)i);
|
||||
}
|
||||
|
||||
/* ep0 initialization in here */
|
||||
ret = qe_ep_init(udc_controller, 0, &qe_ep0_desc);
|
||||
ret = qe_ep_init(udc, 0, &qe_ep0_desc);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
/* create a buf for ZLP send, need to remain zeroed */
|
||||
udc_controller->nullbuf = kzalloc(256, GFP_KERNEL);
|
||||
if (udc_controller->nullbuf == NULL) {
|
||||
dev_err(udc_controller->dev, "cannot alloc nullbuf\n");
|
||||
udc->nullbuf = kzalloc(256, GFP_KERNEL);
|
||||
if (udc->nullbuf == NULL) {
|
||||
dev_err(udc->dev, "cannot alloc nullbuf\n");
|
||||
ret = -ENOMEM;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
/* buffer for data of get_status request */
|
||||
udc_controller->statusbuf = kzalloc(2, GFP_KERNEL);
|
||||
if (udc_controller->statusbuf == NULL) {
|
||||
udc->statusbuf = kzalloc(2, GFP_KERNEL);
|
||||
if (udc->statusbuf == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
udc_controller->nullp = virt_to_phys((void *)udc_controller->nullbuf);
|
||||
if (udc_controller->nullp == DMA_ADDR_INVALID) {
|
||||
udc_controller->nullp = dma_map_single(
|
||||
udc_controller->gadget.dev.parent,
|
||||
udc_controller->nullbuf,
|
||||
udc->nullp = virt_to_phys((void *)udc->nullbuf);
|
||||
if (udc->nullp == DMA_ADDR_INVALID) {
|
||||
udc->nullp = dma_map_single(
|
||||
udc->gadget.dev.parent,
|
||||
udc->nullbuf,
|
||||
256,
|
||||
DMA_TO_DEVICE);
|
||||
udc_controller->nullmap = 1;
|
||||
udc->nullmap = 1;
|
||||
} else {
|
||||
dma_sync_single_for_device(udc_controller->gadget.dev.parent,
|
||||
udc_controller->nullp, 256,
|
||||
dma_sync_single_for_device(udc->gadget.dev.parent,
|
||||
udc->nullp, 256,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
tasklet_init(&udc_controller->rx_tasklet, ep_rx_tasklet,
|
||||
(unsigned long)udc_controller);
|
||||
tasklet_init(&udc->rx_tasklet, ep_rx_tasklet,
|
||||
(unsigned long)udc);
|
||||
/* request irq and disable DR */
|
||||
udc_controller->usb_irq = irq_of_parse_and_map(np, 0);
|
||||
if (!udc_controller->usb_irq) {
|
||||
udc->usb_irq = irq_of_parse_and_map(np, 0);
|
||||
if (!udc->usb_irq) {
|
||||
ret = -EINVAL;
|
||||
goto err_noirq;
|
||||
}
|
||||
|
||||
ret = request_irq(udc_controller->usb_irq, qe_udc_irq, 0,
|
||||
driver_name, udc_controller);
|
||||
ret = request_irq(udc->usb_irq, qe_udc_irq, 0,
|
||||
driver_name, udc);
|
||||
if (ret) {
|
||||
dev_err(udc_controller->dev, "cannot request irq %d err %d \n",
|
||||
udc_controller->usb_irq, ret);
|
||||
dev_err(udc->dev, "cannot request irq %d err %d\n",
|
||||
udc->usb_irq, ret);
|
||||
goto err5;
|
||||
}
|
||||
|
||||
ret = device_add(&udc_controller->gadget.dev);
|
||||
ret = device_add(&udc->gadget.dev);
|
||||
if (ret)
|
||||
goto err6;
|
||||
|
||||
ret = usb_add_gadget_udc(&ofdev->dev, &udc_controller->gadget);
|
||||
ret = usb_add_gadget_udc(&ofdev->dev, &udc->gadget);
|
||||
if (ret)
|
||||
goto err7;
|
||||
|
||||
dev_info(udc_controller->dev,
|
||||
dev_set_drvdata(&ofdev->dev, udc);
|
||||
dev_info(udc->dev,
|
||||
"%s USB controller initialized as device\n",
|
||||
(udc_controller->soc_type == PORT_QE) ? "QE" : "CPM");
|
||||
(udc->soc_type == PORT_QE) ? "QE" : "CPM");
|
||||
return 0;
|
||||
|
||||
err7:
|
||||
device_unregister(&udc_controller->gadget.dev);
|
||||
device_unregister(&udc->gadget.dev);
|
||||
err6:
|
||||
free_irq(udc_controller->usb_irq, udc_controller);
|
||||
free_irq(udc->usb_irq, udc);
|
||||
err5:
|
||||
irq_dispose_mapping(udc_controller->usb_irq);
|
||||
irq_dispose_mapping(udc->usb_irq);
|
||||
err_noirq:
|
||||
if (udc_controller->nullmap) {
|
||||
dma_unmap_single(udc_controller->gadget.dev.parent,
|
||||
udc_controller->nullp, 256,
|
||||
if (udc->nullmap) {
|
||||
dma_unmap_single(udc->gadget.dev.parent,
|
||||
udc->nullp, 256,
|
||||
DMA_TO_DEVICE);
|
||||
udc_controller->nullp = DMA_ADDR_INVALID;
|
||||
udc->nullp = DMA_ADDR_INVALID;
|
||||
} else {
|
||||
dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
|
||||
udc_controller->nullp, 256,
|
||||
dma_sync_single_for_cpu(udc->gadget.dev.parent,
|
||||
udc->nullp, 256,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
kfree(udc_controller->statusbuf);
|
||||
kfree(udc->statusbuf);
|
||||
err4:
|
||||
kfree(udc_controller->nullbuf);
|
||||
kfree(udc->nullbuf);
|
||||
err3:
|
||||
ep = &udc_controller->eps[0];
|
||||
ep = &udc->eps[0];
|
||||
cpm_muram_free(cpm_muram_offset(ep->rxbase));
|
||||
kfree(ep->rxframe);
|
||||
kfree(ep->rxbuffer);
|
||||
kfree(ep->txframe);
|
||||
err2:
|
||||
iounmap(udc_controller->usb_regs);
|
||||
iounmap(udc->usb_regs);
|
||||
err1:
|
||||
kfree(udc_controller);
|
||||
udc_controller = NULL;
|
||||
kfree(udc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -2723,44 +2653,41 @@ static int qe_udc_resume(struct platform_device *dev)
|
|||
|
||||
static int __devexit qe_udc_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct qe_udc *udc = dev_get_drvdata(&ofdev->dev);
|
||||
struct qe_ep *ep;
|
||||
unsigned int size;
|
||||
|
||||
DECLARE_COMPLETION(done);
|
||||
|
||||
if (!udc_controller)
|
||||
return -ENODEV;
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
|
||||
usb_del_gadget_udc(&udc_controller->gadget);
|
||||
udc->done = &done;
|
||||
tasklet_disable(&udc->rx_tasklet);
|
||||
|
||||
udc_controller->done = &done;
|
||||
tasklet_disable(&udc_controller->rx_tasklet);
|
||||
|
||||
if (udc_controller->nullmap) {
|
||||
dma_unmap_single(udc_controller->gadget.dev.parent,
|
||||
udc_controller->nullp, 256,
|
||||
if (udc->nullmap) {
|
||||
dma_unmap_single(udc->gadget.dev.parent,
|
||||
udc->nullp, 256,
|
||||
DMA_TO_DEVICE);
|
||||
udc_controller->nullp = DMA_ADDR_INVALID;
|
||||
udc->nullp = DMA_ADDR_INVALID;
|
||||
} else {
|
||||
dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
|
||||
udc_controller->nullp, 256,
|
||||
dma_sync_single_for_cpu(udc->gadget.dev.parent,
|
||||
udc->nullp, 256,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
kfree(udc_controller->statusbuf);
|
||||
kfree(udc_controller->nullbuf);
|
||||
kfree(udc->statusbuf);
|
||||
kfree(udc->nullbuf);
|
||||
|
||||
ep = &udc_controller->eps[0];
|
||||
ep = &udc->eps[0];
|
||||
cpm_muram_free(cpm_muram_offset(ep->rxbase));
|
||||
size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (USB_BDRING_LEN + 1);
|
||||
|
||||
kfree(ep->rxframe);
|
||||
if (ep->rxbufmap) {
|
||||
dma_unmap_single(udc_controller->gadget.dev.parent,
|
||||
dma_unmap_single(udc->gadget.dev.parent,
|
||||
ep->rxbuf_d, size,
|
||||
DMA_FROM_DEVICE);
|
||||
ep->rxbuf_d = DMA_ADDR_INVALID;
|
||||
} else {
|
||||
dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
|
||||
dma_sync_single_for_cpu(udc->gadget.dev.parent,
|
||||
ep->rxbuf_d, size,
|
||||
DMA_FROM_DEVICE);
|
||||
}
|
||||
|
|
@ -2768,14 +2695,14 @@ static int __devexit qe_udc_remove(struct platform_device *ofdev)
|
|||
kfree(ep->rxbuffer);
|
||||
kfree(ep->txframe);
|
||||
|
||||
free_irq(udc_controller->usb_irq, udc_controller);
|
||||
irq_dispose_mapping(udc_controller->usb_irq);
|
||||
free_irq(udc->usb_irq, udc);
|
||||
irq_dispose_mapping(udc->usb_irq);
|
||||
|
||||
tasklet_kill(&udc_controller->rx_tasklet);
|
||||
tasklet_kill(&udc->rx_tasklet);
|
||||
|
||||
iounmap(udc_controller->usb_regs);
|
||||
iounmap(udc->usb_regs);
|
||||
|
||||
device_unregister(&udc_controller->gadget.dev);
|
||||
device_unregister(&udc->gadget.dev);
|
||||
/* wait for release() of gadget.dev to free udc */
|
||||
wait_for_completion(&done);
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,6 @@ struct qe_ep {
|
|||
struct usb_ep ep;
|
||||
struct list_head queue;
|
||||
struct qe_udc *udc;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
u8 state;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2007,2011 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2004-2007,2011-2012 Freescale Semiconductor, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Author: Li Yang <leoli@freescale.com>
|
||||
|
|
@ -58,9 +58,8 @@ static const char driver_name[] = "fsl-usb2-udc";
|
|||
static const char driver_desc[] = DRIVER_DESC;
|
||||
|
||||
static struct usb_dr_device *dr_regs;
|
||||
#ifndef CONFIG_ARCH_MXC
|
||||
|
||||
static struct usb_sys_interface *usb_sys_regs;
|
||||
#endif
|
||||
|
||||
/* it is initialized in probe() */
|
||||
static struct fsl_udc *udc_controller = NULL;
|
||||
|
|
@ -244,10 +243,9 @@ static int dr_controller_setup(struct fsl_udc *udc)
|
|||
{
|
||||
unsigned int tmp, portctrl, ep_num;
|
||||
unsigned int max_no_of_ep;
|
||||
#ifndef CONFIG_ARCH_MXC
|
||||
unsigned int ctrl;
|
||||
#endif
|
||||
unsigned long timeout;
|
||||
|
||||
#define FSL_UDC_RESET_TIMEOUT 1000
|
||||
|
||||
/* Config PHY interface */
|
||||
|
|
@ -255,12 +253,32 @@ static int dr_controller_setup(struct fsl_udc *udc)
|
|||
portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
|
||||
switch (udc->phy_mode) {
|
||||
case FSL_USB2_PHY_ULPI:
|
||||
if (udc->pdata->have_sysif_regs) {
|
||||
if (udc->pdata->controller_ver) {
|
||||
/* controller version 1.6 or above */
|
||||
ctrl = __raw_readl(&usb_sys_regs->control);
|
||||
ctrl &= ~USB_CTRL_UTMI_PHY_EN;
|
||||
ctrl |= USB_CTRL_USB_EN;
|
||||
__raw_writel(ctrl, &usb_sys_regs->control);
|
||||
}
|
||||
}
|
||||
portctrl |= PORTSCX_PTS_ULPI;
|
||||
break;
|
||||
case FSL_USB2_PHY_UTMI_WIDE:
|
||||
portctrl |= PORTSCX_PTW_16BIT;
|
||||
/* fall through */
|
||||
case FSL_USB2_PHY_UTMI:
|
||||
if (udc->pdata->have_sysif_regs) {
|
||||
if (udc->pdata->controller_ver) {
|
||||
/* controller version 1.6 or above */
|
||||
ctrl = __raw_readl(&usb_sys_regs->control);
|
||||
ctrl |= (USB_CTRL_UTMI_PHY_EN |
|
||||
USB_CTRL_USB_EN);
|
||||
__raw_writel(ctrl, &usb_sys_regs->control);
|
||||
mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI
|
||||
PHY CLK to become stable - 10ms*/
|
||||
}
|
||||
}
|
||||
portctrl |= PORTSCX_PTS_UTMI;
|
||||
break;
|
||||
case FSL_USB2_PHY_SERIAL:
|
||||
|
|
@ -549,7 +567,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
|
|||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
|
||||
/* catch various bogus parameters */
|
||||
if (!_ep || !desc || ep->desc
|
||||
if (!_ep || !desc || ep->ep.desc
|
||||
|| (desc->bDescriptorType != USB_DT_ENDPOINT))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -590,7 +608,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
|
|||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
ep->ep.maxpacket = max;
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->stopped = 0;
|
||||
|
||||
/* Controller related setup */
|
||||
|
|
@ -614,7 +632,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
|
|||
retval = 0;
|
||||
|
||||
VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name,
|
||||
ep->desc->bEndpointAddress & 0x0f,
|
||||
ep->ep.desc->bEndpointAddress & 0x0f,
|
||||
(desc->bEndpointAddress & USB_DIR_IN)
|
||||
? "in" : "out", max);
|
||||
en_done:
|
||||
|
|
@ -634,7 +652,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)
|
|||
int ep_num;
|
||||
|
||||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -657,7 +675,6 @@ static int fsl_ep_disable(struct usb_ep *_ep)
|
|||
/* nuke all pending requests (does flush) */
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
|
@ -736,6 +753,8 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
|
|||
lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
|
||||
lastreq->tail->next_td_ptr =
|
||||
cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK);
|
||||
/* Ensure dTD's next dtd pointer to be updated */
|
||||
wmb();
|
||||
/* Read prime bit, if 1 goto done */
|
||||
if (fsl_readl(&dr_regs->endpointprime) & bitmask)
|
||||
return;
|
||||
|
|
@ -874,11 +893,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
VDBG("%s, bad params", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (unlikely(!_ep || !ep->desc)) {
|
||||
if (unlikely(!_ep || !ep->ep.desc)) {
|
||||
VDBG("%s, bad ep", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (usb_endpoint_xfer_isoc(ep->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
|
||||
if (req->req.length > ep->ep.maxpacket)
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
|
@ -1017,12 +1036,12 @@ static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
|
|||
|
||||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
udc = ep->udc;
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
status = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(ep->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
|
||||
status = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1061,7 +1080,7 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep)
|
|||
struct ep_queue_head *qh;
|
||||
|
||||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep_index(ep) != 0))
|
||||
if (!_ep || (!ep->ep.desc && ep_index(ep) != 0))
|
||||
return -ENODEV;
|
||||
|
||||
udc = (struct fsl_udc *)ep->udc;
|
||||
|
|
@ -1094,7 +1113,7 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep)
|
|||
return;
|
||||
} else {
|
||||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
if (!ep->desc)
|
||||
if (!ep->ep.desc)
|
||||
return;
|
||||
}
|
||||
ep_num = ep_index(ep);
|
||||
|
|
@ -1349,7 +1368,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
|
|||
target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
|
||||
|
||||
/* stall if endpoint doesn't exist */
|
||||
if (!target_ep->desc)
|
||||
if (!target_ep->ep.desc)
|
||||
goto stall;
|
||||
tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
|
||||
<< USB_ENDPOINT_HALT;
|
||||
|
|
@ -2259,7 +2278,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
|
|||
}
|
||||
/* other gadget->eplist ep */
|
||||
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
|
||||
if (ep->desc) {
|
||||
if (ep->ep.desc) {
|
||||
t = scnprintf(next, size,
|
||||
"\nFor %s Maxpkt is 0x%x "
|
||||
"index is 0x%x\n",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
/*
|
||||
* Copyright (C) 2004,2012 Freescale Semiconductor, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Freescale USB device/endpoint management registers
|
||||
*/
|
||||
#ifndef __FSL_USB2_UDC_H
|
||||
|
|
@ -348,6 +356,9 @@ struct usb_sys_interface {
|
|||
/* control Register Bit Masks */
|
||||
#define USB_CTRL_IOENB 0x00000004
|
||||
#define USB_CTRL_ULPI_INT0EN 0x00000001
|
||||
#define USB_CTRL_UTMI_PHY_EN 0x00000200
|
||||
#define USB_CTRL_USB_EN 0x00000004
|
||||
#define USB_CTRL_ULPI_PHY_CLK_SEL 0x00000400
|
||||
|
||||
/* Endpoint Queue Head data struct
|
||||
* Rem: all the variables of qh are LittleEndian Mode
|
||||
|
|
@ -450,7 +461,6 @@ struct fsl_ep {
|
|||
struct list_head queue;
|
||||
struct fsl_udc *udc;
|
||||
struct ep_queue_head *qh;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
char name[14];
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ static int config_ep(struct fusb300_ep *ep,
|
|||
struct fusb300 *fusb300 = ep->fusb300;
|
||||
struct fusb300_ep_info info;
|
||||
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
|
||||
info.interval = 0;
|
||||
info.addrofs = 0;
|
||||
|
|
@ -443,7 +443,7 @@ static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
req->req.actual = 0;
|
||||
req->req.status = -EINPROGRESS;
|
||||
|
||||
if (ep->desc == NULL) /* ep0 */
|
||||
if (ep->ep.desc == NULL) /* ep0 */
|
||||
ep0_queue(ep, req);
|
||||
else if (request && !ep->stall)
|
||||
enable_fifo_int(ep);
|
||||
|
|
|
|||
|
|
@ -650,7 +650,6 @@ struct fusb300_ep {
|
|||
|
||||
unsigned char epnum;
|
||||
unsigned char type;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
};
|
||||
|
||||
struct fusb300 {
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ MODULE_LICENSE("GPL");
|
|||
#define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */
|
||||
#define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */
|
||||
|
||||
#define GFS_MAX_DEVS 10
|
||||
|
||||
struct gfs_ffs_obj {
|
||||
const char *name;
|
||||
bool mounted;
|
||||
bool desc_ready;
|
||||
struct ffs_data *ffs_data;
|
||||
};
|
||||
|
||||
static struct usb_device_descriptor gfs_dev_desc = {
|
||||
.bLength = sizeof gfs_dev_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
|
@ -78,12 +87,17 @@ static struct usb_device_descriptor gfs_dev_desc = {
|
|||
.idProduct = cpu_to_le16(GFS_PRODUCT_ID),
|
||||
};
|
||||
|
||||
static char *func_names[GFS_MAX_DEVS];
|
||||
static unsigned int func_num;
|
||||
|
||||
module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644);
|
||||
MODULE_PARM_DESC(bDeviceClass, "USB Device class");
|
||||
module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644);
|
||||
MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
|
||||
module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
|
||||
MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
|
||||
module_param_array_named(functions, func_names, charp, &func_num, 0);
|
||||
MODULE_PARM_DESC(functions, "USB Functions list");
|
||||
|
||||
static const struct usb_descriptor_header *gfs_otg_desc[] = {
|
||||
(const struct usb_descriptor_header *)
|
||||
|
|
@ -158,13 +172,34 @@ static struct usb_composite_driver gfs_driver = {
|
|||
.iProduct = DRIVER_DESC,
|
||||
};
|
||||
|
||||
static struct ffs_data *gfs_ffs_data;
|
||||
static unsigned long gfs_registered;
|
||||
static DEFINE_MUTEX(gfs_lock);
|
||||
static unsigned int missing_funcs;
|
||||
static bool gfs_ether_setup;
|
||||
static bool gfs_registered;
|
||||
static bool gfs_single_func;
|
||||
static struct gfs_ffs_obj *ffs_tab;
|
||||
|
||||
static int __init gfs_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
ENTER();
|
||||
|
||||
if (!func_num) {
|
||||
gfs_single_func = true;
|
||||
func_num = 1;
|
||||
}
|
||||
|
||||
ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL);
|
||||
if (!ffs_tab)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!gfs_single_func)
|
||||
for (i = 0; i < func_num; i++)
|
||||
ffs_tab[i].name = func_names[i];
|
||||
|
||||
missing_funcs = func_num;
|
||||
|
||||
return functionfs_init();
|
||||
}
|
||||
module_init(gfs_init);
|
||||
|
|
@ -172,63 +207,165 @@ module_init(gfs_init);
|
|||
static void __exit gfs_exit(void)
|
||||
{
|
||||
ENTER();
|
||||
mutex_lock(&gfs_lock);
|
||||
|
||||
if (test_and_clear_bit(0, &gfs_registered))
|
||||
if (gfs_registered)
|
||||
usb_composite_unregister(&gfs_driver);
|
||||
gfs_registered = false;
|
||||
|
||||
functionfs_cleanup();
|
||||
|
||||
mutex_unlock(&gfs_lock);
|
||||
kfree(ffs_tab);
|
||||
}
|
||||
module_exit(gfs_exit);
|
||||
|
||||
static int functionfs_ready_callback(struct ffs_data *ffs)
|
||||
static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ENTER();
|
||||
|
||||
if (WARN_ON(test_and_set_bit(0, &gfs_registered)))
|
||||
return -EBUSY;
|
||||
if (gfs_single_func)
|
||||
return &ffs_tab[0];
|
||||
|
||||
for (i = 0; i < func_num; i++)
|
||||
if (strcmp(ffs_tab[i].name, dev_name) == 0)
|
||||
return &ffs_tab[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int functionfs_ready_callback(struct ffs_data *ffs)
|
||||
{
|
||||
struct gfs_ffs_obj *ffs_obj;
|
||||
int ret;
|
||||
|
||||
ENTER();
|
||||
mutex_lock(&gfs_lock);
|
||||
|
||||
ffs_obj = ffs->private_data;
|
||||
if (!ffs_obj) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (WARN_ON(ffs_obj->desc_ready)) {
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
ffs_obj->desc_ready = true;
|
||||
ffs_obj->ffs_data = ffs;
|
||||
|
||||
if (--missing_funcs) {
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (gfs_registered) {
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
gfs_registered = true;
|
||||
|
||||
gfs_ffs_data = ffs;
|
||||
ret = usb_composite_probe(&gfs_driver, gfs_bind);
|
||||
if (unlikely(ret < 0))
|
||||
clear_bit(0, &gfs_registered);
|
||||
gfs_registered = false;
|
||||
|
||||
done:
|
||||
mutex_unlock(&gfs_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void functionfs_closed_callback(struct ffs_data *ffs)
|
||||
{
|
||||
struct gfs_ffs_obj *ffs_obj;
|
||||
|
||||
ENTER();
|
||||
mutex_lock(&gfs_lock);
|
||||
|
||||
if (test_and_clear_bit(0, &gfs_registered))
|
||||
ffs_obj = ffs->private_data;
|
||||
if (!ffs_obj)
|
||||
goto done;
|
||||
|
||||
ffs_obj->desc_ready = false;
|
||||
missing_funcs++;
|
||||
|
||||
if (gfs_registered)
|
||||
usb_composite_unregister(&gfs_driver);
|
||||
gfs_registered = false;
|
||||
|
||||
done:
|
||||
mutex_unlock(&gfs_lock);
|
||||
}
|
||||
|
||||
static int functionfs_check_dev_callback(const char *dev_name)
|
||||
static void *functionfs_acquire_dev_callback(const char *dev_name)
|
||||
{
|
||||
return 0;
|
||||
struct gfs_ffs_obj *ffs_dev;
|
||||
|
||||
ENTER();
|
||||
mutex_lock(&gfs_lock);
|
||||
|
||||
ffs_dev = gfs_find_dev(dev_name);
|
||||
if (!ffs_dev) {
|
||||
ffs_dev = ERR_PTR(-ENODEV);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ffs_dev->mounted) {
|
||||
ffs_dev = ERR_PTR(-EBUSY);
|
||||
goto done;
|
||||
}
|
||||
ffs_dev->mounted = true;
|
||||
|
||||
done:
|
||||
mutex_unlock(&gfs_lock);
|
||||
return ffs_dev;
|
||||
}
|
||||
|
||||
static void functionfs_release_dev_callback(struct ffs_data *ffs_data)
|
||||
{
|
||||
struct gfs_ffs_obj *ffs_dev;
|
||||
|
||||
ENTER();
|
||||
mutex_lock(&gfs_lock);
|
||||
|
||||
ffs_dev = ffs_data->private_data;
|
||||
if (ffs_dev)
|
||||
ffs_dev->mounted = false;
|
||||
|
||||
mutex_unlock(&gfs_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* It is assumed that gfs_bind is called from a context where gfs_lock is held
|
||||
*/
|
||||
static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
ENTER();
|
||||
|
||||
if (WARN_ON(!gfs_ffs_data))
|
||||
if (missing_funcs)
|
||||
return -ENODEV;
|
||||
|
||||
ret = gether_setup(cdev->gadget, gfs_hostaddr);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_quick;
|
||||
gfs_ether_setup = true;
|
||||
|
||||
ret = usb_string_ids_tab(cdev, gfs_strings);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
|
||||
ret = functionfs_bind(gfs_ffs_data, cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
for (i = func_num; --i; ) {
|
||||
ret = functionfs_bind(ffs_tab[i].ffs_data, cdev);
|
||||
if (unlikely(ret < 0)) {
|
||||
while (++i < func_num)
|
||||
functionfs_unbind(ffs_tab[i].ffs_data);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
|
||||
struct gfs_configuration *c = gfs_configurations + i;
|
||||
|
|
@ -246,16 +383,22 @@ static int gfs_bind(struct usb_composite_dev *cdev)
|
|||
return 0;
|
||||
|
||||
error_unbind:
|
||||
functionfs_unbind(gfs_ffs_data);
|
||||
for (i = 0; i < func_num; i++)
|
||||
functionfs_unbind(ffs_tab[i].ffs_data);
|
||||
error:
|
||||
gether_cleanup();
|
||||
error_quick:
|
||||
gfs_ffs_data = NULL;
|
||||
gfs_ether_setup = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is assumed that gfs_unbind is called from a context where gfs_lock is held
|
||||
*/
|
||||
static int gfs_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
ENTER();
|
||||
|
||||
/*
|
||||
|
|
@ -266,22 +409,29 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
|
|||
* from composite on orror recovery, but what you're gonna
|
||||
* do...?
|
||||
*/
|
||||
if (gfs_ffs_data) {
|
||||
if (gfs_ether_setup)
|
||||
gether_cleanup();
|
||||
functionfs_unbind(gfs_ffs_data);
|
||||
gfs_ffs_data = NULL;
|
||||
}
|
||||
gfs_ether_setup = false;
|
||||
|
||||
for (i = func_num; --i; )
|
||||
if (ffs_tab[i].ffs_data)
|
||||
functionfs_unbind(ffs_tab[i].ffs_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is assumed that gfs_do_config is called from a context where
|
||||
* gfs_lock is held
|
||||
*/
|
||||
static int gfs_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct gfs_configuration *gc =
|
||||
container_of(c, struct gfs_configuration, c);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!gfs_ffs_data))
|
||||
if (missing_funcs)
|
||||
return -ENODEV;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
|
|
@ -295,9 +445,11 @@ static int gfs_do_config(struct usb_configuration *c)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
for (i = 0; i < func_num; i++) {
|
||||
ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* After previous do_configs there may be some invalid
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ extern unsigned buflen;
|
|||
extern const struct usb_descriptor_header *otg_desc[];
|
||||
|
||||
/* common utilities */
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep);
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len);
|
||||
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
|
||||
void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out);
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out);
|
||||
|
||||
/* configuration-specific linkup */
|
||||
int sourcesink_add(struct usb_composite_dev *cdev, bool autoresume);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
|
||||
#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
|
||||
#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
|
||||
#define gadget_is_lpc32xx(g) (!strcmp("lpc32xx_udc", (g)->name))
|
||||
#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
|
||||
#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
|
||||
#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
|
||||
|
|
@ -118,6 +119,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
|||
return 0x31;
|
||||
else if (gadget_is_dwc3(gadget))
|
||||
return 0x32;
|
||||
else if (gadget_is_lpc32xx(gadget))
|
||||
return 0x33;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
|||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct goku_ep, ep);
|
||||
if (!_ep || !desc || ep->desc
|
||||
if (!_ep || !desc || ep->ep.desc
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT)
|
||||
return -EINVAL;
|
||||
dev = ep->dev;
|
||||
|
|
@ -176,7 +176,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
|||
command(ep->dev->regs, COMMAND_RESET, ep->num);
|
||||
ep->ep.maxpacket = max;
|
||||
ep->stopped = 0;
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
|
||||
DBG(dev, "enable %s %s %s maxpacket %u\n", ep->ep.name,
|
||||
|
|
@ -233,7 +233,6 @@ static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep)
|
|||
}
|
||||
|
||||
ep->ep.maxpacket = MAX_FIFO_SIZE;
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
ep->irqs = 0;
|
||||
|
|
@ -247,7 +246,7 @@ static int goku_ep_disable(struct usb_ep *_ep)
|
|||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct goku_ep, ep);
|
||||
if (!_ep || !ep->desc)
|
||||
if (!_ep || !ep->ep.desc)
|
||||
return -ENODEV;
|
||||
dev = ep->dev;
|
||||
if (dev->ep0state == EP0_SUSPEND)
|
||||
|
|
@ -722,7 +721,7 @@ goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
|| !_req->buf || !list_empty(&req->queue)))
|
||||
return -EINVAL;
|
||||
ep = container_of(_ep, struct goku_ep, ep);
|
||||
if (unlikely(!_ep || (!ep->desc && ep->num != 0)))
|
||||
if (unlikely(!_ep || (!ep->ep.desc && ep->num != 0)))
|
||||
return -EINVAL;
|
||||
dev = ep->dev;
|
||||
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
|
||||
|
|
@ -815,7 +814,7 @@ static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct goku_ep, ep);
|
||||
if (!_ep || !_req || (!ep->desc && ep->num != 0))
|
||||
if (!_ep || !_req || (!ep->ep.desc && ep->num != 0))
|
||||
return -EINVAL;
|
||||
dev = ep->dev;
|
||||
if (!dev->driver)
|
||||
|
|
@ -896,7 +895,7 @@ static int goku_set_halt(struct usb_ep *_ep, int value)
|
|||
return -EINVAL;
|
||||
|
||||
/* don't change EPxSTATUS_EP_INVALID to READY */
|
||||
} else if (!ep->desc) {
|
||||
} else if (!ep->ep.desc) {
|
||||
DBG(ep->dev, "%s %s inactive?\n", __func__, ep->ep.name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -955,7 +954,7 @@ static void goku_fifo_flush(struct usb_ep *_ep)
|
|||
VDBG(ep->dev, "%s %s\n", __func__, ep->ep.name);
|
||||
|
||||
/* don't change EPxSTATUS_EP_INVALID to READY */
|
||||
if (!ep->desc && ep->num != 0) {
|
||||
if (!ep->ep.desc && ep->num != 0) {
|
||||
DBG(ep->dev, "%s %s inactive?\n", __func__, ep->ep.name);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1152,7 +1151,7 @@ udc_proc_read(char *buffer, char **start, off_t off, int count,
|
|||
struct goku_ep *ep = &dev->ep [i];
|
||||
struct goku_request *req;
|
||||
|
||||
if (i && !ep->desc)
|
||||
if (i && !ep->ep.desc)
|
||||
continue;
|
||||
|
||||
tmp = readl(ep->reg_status);
|
||||
|
|
@ -1473,7 +1472,8 @@ static void ep0_setup(struct goku_udc *dev)
|
|||
case USB_RECIP_ENDPOINT:
|
||||
tmp = le16_to_cpu(ctrl.wIndex) & 0x0f;
|
||||
/* active endpoint */
|
||||
if (tmp > 3 || (!dev->ep[tmp].desc && tmp != 0))
|
||||
if (tmp > 3 ||
|
||||
(!dev->ep[tmp].ep.desc && tmp != 0))
|
||||
goto stall;
|
||||
if (ctrl.wIndex & cpu_to_le16(
|
||||
USB_DIR_IN)) {
|
||||
|
|
@ -1895,14 +1895,4 @@ static struct pci_driver goku_pci_driver = {
|
|||
/* FIXME add power management support */
|
||||
};
|
||||
|
||||
static int __init init (void)
|
||||
{
|
||||
return pci_register_driver (&goku_pci_driver);
|
||||
}
|
||||
module_init (init);
|
||||
|
||||
static void __exit cleanup (void)
|
||||
{
|
||||
pci_unregister_driver (&goku_pci_driver);
|
||||
}
|
||||
module_exit (cleanup);
|
||||
module_pci_driver(goku_pci_driver);
|
||||
|
|
|
|||
|
|
@ -216,7 +216,6 @@ struct goku_ep {
|
|||
|
||||
/* analogous to a host-side qh */
|
||||
struct list_head queue;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
u32 __iomem *reg_fifo;
|
||||
u32 __iomem *reg_mode;
|
||||
|
|
|
|||
|
|
@ -1237,14 +1237,15 @@ irq_handler_t intr_handler(int i)
|
|||
*******************************************************************************
|
||||
*/
|
||||
|
||||
static int imx_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *));
|
||||
static int imx_udc_stop(struct usb_gadget_driver *driver);
|
||||
static int imx_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int imx_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static const struct usb_gadget_ops imx_udc_ops = {
|
||||
.get_frame = imx_udc_get_frame,
|
||||
.wakeup = imx_udc_wakeup,
|
||||
.start = imx_udc_start,
|
||||
.stop = imx_udc_stop,
|
||||
.get_frame = imx_udc_get_frame,
|
||||
.wakeup = imx_udc_wakeup,
|
||||
.udc_start = imx_udc_start,
|
||||
.udc_stop = imx_udc_stop,
|
||||
};
|
||||
|
||||
static struct imx_udc_struct controller = {
|
||||
|
|
@ -1329,23 +1330,13 @@ static struct imx_udc_struct controller = {
|
|||
* USB gadget driver functions
|
||||
*******************************************************************************
|
||||
*/
|
||||
static int imx_udc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *))
|
||||
static int imx_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct imx_udc_struct *imx_usb = &controller;
|
||||
struct imx_udc_struct *imx_usb;
|
||||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->disconnect
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
if (!imx_usb)
|
||||
return -ENODEV;
|
||||
if (imx_usb->driver)
|
||||
return -EBUSY;
|
||||
|
||||
imx_usb = container_of(gadget, struct imx_udc_struct, gadget);
|
||||
/* first hook up the driver ... */
|
||||
imx_usb->driver = driver;
|
||||
imx_usb->gadget.dev.driver = &driver->driver;
|
||||
|
|
@ -1353,14 +1344,6 @@ static int imx_udc_start(struct usb_gadget_driver *driver,
|
|||
retval = device_add(&imx_usb->gadget.dev);
|
||||
if (retval)
|
||||
goto fail;
|
||||
retval = bind(&imx_usb->gadget);
|
||||
if (retval) {
|
||||
D_ERR(imx_usb->dev, "<%s> bind to driver %s --> error %d\n",
|
||||
__func__, driver->driver.name, retval);
|
||||
device_del(&imx_usb->gadget.dev);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n",
|
||||
__func__, driver->driver.name);
|
||||
|
|
@ -1374,20 +1357,16 @@ fail:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int imx_udc_stop(struct usb_gadget_driver *driver)
|
||||
static int imx_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct imx_udc_struct *imx_usb = &controller;
|
||||
|
||||
if (!imx_usb)
|
||||
return -ENODEV;
|
||||
if (!driver || driver != imx_usb->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
struct imx_udc_struct *imx_usb = container_of(gadget,
|
||||
struct imx_udc_struct, gadget);
|
||||
|
||||
udc_stop_activity(imx_usb, driver);
|
||||
imx_udc_disable(imx_usb);
|
||||
del_timer(&imx_usb->timer);
|
||||
|
||||
driver->unbind(&imx_usb->gadget);
|
||||
imx_usb->gadget.dev.driver = NULL;
|
||||
imx_usb->driver = NULL;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,224 +0,0 @@
|
|||
/*
|
||||
* Intel Langwell USB Device Controller driver
|
||||
* Copyright (C) 2008-2009, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/usb/langwell_udc.h>
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* driver data structures and utilities */
|
||||
|
||||
/*
|
||||
* dTD: Device Endpoint Transfer Descriptor
|
||||
* describe to the device controller the location and quantity of
|
||||
* data to be send/received for given transfer
|
||||
*/
|
||||
struct langwell_dtd {
|
||||
u32 dtd_next;
|
||||
/* bits 31:5, next transfer element pointer */
|
||||
#define DTD_NEXT(d) (((d)>>5)&0x7ffffff)
|
||||
#define DTD_NEXT_MASK (0x7ffffff << 5)
|
||||
/* terminate */
|
||||
#define DTD_TERM BIT(0)
|
||||
/* bits 7:0, execution back states */
|
||||
u32 dtd_status:8;
|
||||
#define DTD_STATUS(d) (((d)>>0)&0xff)
|
||||
#define DTD_STS_ACTIVE BIT(7) /* active */
|
||||
#define DTD_STS_HALTED BIT(6) /* halted */
|
||||
#define DTD_STS_DBE BIT(5) /* data buffer error */
|
||||
#define DTD_STS_TRE BIT(3) /* transaction error */
|
||||
/* bits 9:8 */
|
||||
u32 dtd_res0:2;
|
||||
/* bits 11:10, multipier override */
|
||||
u32 dtd_multo:2;
|
||||
#define DTD_MULTO (BIT(11) | BIT(10))
|
||||
/* bits 14:12 */
|
||||
u32 dtd_res1:3;
|
||||
/* bit 15, interrupt on complete */
|
||||
u32 dtd_ioc:1;
|
||||
#define DTD_IOC BIT(15)
|
||||
/* bits 30:16, total bytes */
|
||||
u32 dtd_total:15;
|
||||
#define DTD_TOTAL(d) (((d)>>16)&0x7fff)
|
||||
#define DTD_MAX_TRANSFER_LENGTH 0x4000
|
||||
/* bit 31 */
|
||||
u32 dtd_res2:1;
|
||||
/* dTD buffer pointer page 0 to 4 */
|
||||
u32 dtd_buf[5];
|
||||
#define DTD_OFFSET_MASK 0xfff
|
||||
/* bits 31:12, buffer pointer */
|
||||
#define DTD_BUFFER(d) (((d)>>12)&0x3ff)
|
||||
/* bits 11:0, current offset */
|
||||
#define DTD_C_OFFSET(d) (((d)>>0)&0xfff)
|
||||
/* bits 10:0, frame number */
|
||||
#define DTD_FRAME(d) (((d)>>0)&0x7ff)
|
||||
|
||||
/* driver-private parts */
|
||||
|
||||
/* dtd dma address */
|
||||
dma_addr_t dtd_dma;
|
||||
/* next dtd virtual address */
|
||||
struct langwell_dtd *next_dtd_virt;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* dQH: Device Endpoint Queue Head
|
||||
* describe where all transfers are managed
|
||||
* 48-byte data structure, aligned on 64-byte boundary
|
||||
*
|
||||
* These are associated with dTD structure
|
||||
*/
|
||||
struct langwell_dqh {
|
||||
/* endpoint capabilities and characteristics */
|
||||
u32 dqh_res0:15; /* bits 14:0 */
|
||||
u32 dqh_ios:1; /* bit 15, interrupt on setup */
|
||||
#define DQH_IOS BIT(15)
|
||||
u32 dqh_mpl:11; /* bits 26:16, maximum packet length */
|
||||
#define DQH_MPL (0x7ff << 16)
|
||||
u32 dqh_res1:2; /* bits 28:27 */
|
||||
u32 dqh_zlt:1; /* bit 29, zero length termination */
|
||||
#define DQH_ZLT BIT(29)
|
||||
u32 dqh_mult:2; /* bits 31:30 */
|
||||
#define DQH_MULT (BIT(30) | BIT(31))
|
||||
|
||||
/* current dTD pointer */
|
||||
u32 dqh_current; /* locate the transfer in progress */
|
||||
#define DQH_C_DTD(e) \
|
||||
(((e)>>5)&0x7ffffff) /* bits 31:5, current dTD pointer */
|
||||
|
||||
/* transfer overlay, hardware parts of a struct langwell_dtd */
|
||||
u32 dtd_next;
|
||||
u32 dtd_status:8; /* bits 7:0, execution back states */
|
||||
u32 dtd_res0:2; /* bits 9:8 */
|
||||
u32 dtd_multo:2; /* bits 11:10, multipier override */
|
||||
u32 dtd_res1:3; /* bits 14:12 */
|
||||
u32 dtd_ioc:1; /* bit 15, interrupt on complete */
|
||||
u32 dtd_total:15; /* bits 30:16, total bytes */
|
||||
u32 dtd_res2:1; /* bit 31 */
|
||||
u32 dtd_buf[5]; /* dTD buffer pointer page 0 to 4 */
|
||||
|
||||
u32 dqh_res2;
|
||||
struct usb_ctrlrequest dqh_setup; /* setup packet buffer */
|
||||
} __attribute__ ((aligned(64)));
|
||||
|
||||
|
||||
/* endpoint data structure */
|
||||
struct langwell_ep {
|
||||
struct usb_ep ep;
|
||||
dma_addr_t dma;
|
||||
struct langwell_udc *dev;
|
||||
unsigned long irqs;
|
||||
struct list_head queue;
|
||||
struct langwell_dqh *dqh;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
char name[14];
|
||||
unsigned stopped:1,
|
||||
ep_type:2,
|
||||
ep_num:8;
|
||||
};
|
||||
|
||||
|
||||
/* request data structure */
|
||||
struct langwell_request {
|
||||
struct usb_request req;
|
||||
struct langwell_dtd *dtd, *head, *tail;
|
||||
struct langwell_ep *ep;
|
||||
dma_addr_t dtd_dma;
|
||||
struct list_head queue;
|
||||
unsigned dtd_count;
|
||||
unsigned mapped:1;
|
||||
};
|
||||
|
||||
|
||||
/* ep0 transfer state */
|
||||
enum ep0_state {
|
||||
WAIT_FOR_SETUP,
|
||||
DATA_STATE_XMIT,
|
||||
DATA_STATE_NEED_ZLP,
|
||||
WAIT_FOR_OUT_STATUS,
|
||||
DATA_STATE_RECV,
|
||||
};
|
||||
|
||||
|
||||
/* device suspend state */
|
||||
enum lpm_state {
|
||||
LPM_L0, /* on */
|
||||
LPM_L1, /* LPM L1 sleep */
|
||||
LPM_L2, /* suspend */
|
||||
LPM_L3, /* off */
|
||||
};
|
||||
|
||||
|
||||
/* device data structure */
|
||||
struct langwell_udc {
|
||||
/* each pci device provides one gadget, several endpoints */
|
||||
struct usb_gadget gadget;
|
||||
spinlock_t lock; /* device lock */
|
||||
struct langwell_ep *ep;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct usb_phy *transceiver;
|
||||
u8 dev_addr;
|
||||
u32 usb_state;
|
||||
u32 resume_state;
|
||||
u32 bus_reset;
|
||||
enum lpm_state lpm_state;
|
||||
enum ep0_state ep0_state;
|
||||
u32 ep0_dir;
|
||||
u16 dciversion;
|
||||
unsigned ep_max;
|
||||
unsigned devcap:1,
|
||||
enabled:1,
|
||||
region:1,
|
||||
got_irq:1,
|
||||
powered:1,
|
||||
remote_wakeup:1,
|
||||
rate:1,
|
||||
is_reset:1,
|
||||
softconnected:1,
|
||||
vbus_active:1,
|
||||
suspended:1,
|
||||
stopped:1,
|
||||
lpm:1, /* LPM capability */
|
||||
has_sram:1, /* SRAM caching */
|
||||
got_sram:1;
|
||||
|
||||
/* pci state used to access those endpoints */
|
||||
struct pci_dev *pdev;
|
||||
|
||||
/* Langwell otg transceiver */
|
||||
struct langwell_otg *lotg;
|
||||
|
||||
/* control registers */
|
||||
struct langwell_cap_regs __iomem *cap_regs;
|
||||
struct langwell_op_regs __iomem *op_regs;
|
||||
|
||||
struct usb_ctrlrequest local_setup_buff;
|
||||
struct langwell_dqh *ep_dqh;
|
||||
size_t ep_dqh_size;
|
||||
dma_addr_t ep_dqh_dma;
|
||||
|
||||
/* ep0 status request */
|
||||
struct langwell_request *status_req;
|
||||
|
||||
/* dma pool */
|
||||
struct dma_pool *dtd_pool;
|
||||
|
||||
/* make sure release() is done */
|
||||
struct completion *done;
|
||||
|
||||
/* for private SRAM caching */
|
||||
unsigned int sram_addr;
|
||||
unsigned int sram_size;
|
||||
|
||||
/* device status data for get_status request */
|
||||
u16 dev_status;
|
||||
};
|
||||
|
||||
#define gadget_to_langwell(g) container_of((g), struct langwell_udc, gadget)
|
||||
|
||||
3538
drivers/usb/gadget/lpc32xx_udc.c
Normal file
3538
drivers/usb/gadget/lpc32xx_udc.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -390,7 +390,7 @@ static int alloc_pipe_config(struct m66592_ep *ep,
|
|||
int *counter;
|
||||
int ret;
|
||||
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
|
||||
BUG_ON(ep->pipenum);
|
||||
|
||||
|
|
@ -558,7 +558,7 @@ static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req)
|
|||
|
||||
static void start_packet(struct m66592_ep *ep, struct m66592_request *req)
|
||||
{
|
||||
if (ep->desc->bEndpointAddress & USB_DIR_IN)
|
||||
if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
|
||||
start_packet_write(ep, req);
|
||||
else
|
||||
start_packet_read(ep, req);
|
||||
|
|
@ -734,7 +734,7 @@ __acquires(m66592->lock)
|
|||
|
||||
if (restart) {
|
||||
req = list_entry(ep->queue.next, struct m66592_request, queue);
|
||||
if (ep->desc)
|
||||
if (ep->ep.desc)
|
||||
start_packet(ep, req);
|
||||
}
|
||||
}
|
||||
|
|
@ -917,7 +917,7 @@ static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb)
|
|||
ep = m66592->pipenum2ep[pipenum];
|
||||
req = list_entry(ep->queue.next,
|
||||
struct m66592_request, queue);
|
||||
if (ep->desc->bEndpointAddress & USB_DIR_IN)
|
||||
if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
|
||||
irq_packet_write(ep, req);
|
||||
else
|
||||
irq_packet_read(ep, req);
|
||||
|
|
@ -1377,7 +1377,7 @@ static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
req->req.actual = 0;
|
||||
req->req.status = -EINPROGRESS;
|
||||
|
||||
if (ep->desc == NULL) /* control */
|
||||
if (ep->ep.desc == NULL) /* control */
|
||||
start_ep0(ep, req);
|
||||
else {
|
||||
if (request && !ep->busy)
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ struct m66592_ep {
|
|||
unsigned use_dma:1;
|
||||
u16 pipenum;
|
||||
u16 type;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
/* register address */
|
||||
unsigned long fifoaddr;
|
||||
unsigned long fifosel;
|
||||
|
|
|
|||
|
|
@ -232,7 +232,6 @@ struct mv_ep {
|
|||
struct mv_udc *udc;
|
||||
struct list_head queue;
|
||||
struct mv_dqh *dqh;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
u32 direction;
|
||||
char name[14];
|
||||
unsigned stopped:1,
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
|
|||
ep = container_of(_ep, struct mv_ep, ep);
|
||||
udc = ep->udc;
|
||||
|
||||
if (!_ep || !desc || ep->desc
|
||||
if (!_ep || !desc || ep->ep.desc
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -528,7 +528,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
|
|||
dqh->size_ioc_int_sts = 0;
|
||||
|
||||
ep->ep.maxpacket = max;
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->stopped = 0;
|
||||
|
||||
/* Enable the endpoint for Rx or Tx and set the endpoint type */
|
||||
|
|
@ -580,7 +580,7 @@ static int mv_ep_disable(struct usb_ep *_ep)
|
|||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct mv_ep, ep);
|
||||
if ((_ep == NULL) || !ep->desc)
|
||||
if ((_ep == NULL) || !ep->ep.desc)
|
||||
return -EINVAL;
|
||||
|
||||
udc = ep->udc;
|
||||
|
|
@ -606,7 +606,6 @@ static int mv_ep_disable(struct usb_ep *_ep)
|
|||
/* nuke all pending requests (does flush) */
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
|
||||
|
|
@ -651,7 +650,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep)
|
|||
return;
|
||||
|
||||
ep = container_of(_ep, struct mv_ep, ep);
|
||||
if (!ep->desc)
|
||||
if (!ep->ep.desc)
|
||||
return;
|
||||
|
||||
udc = ep->udc;
|
||||
|
|
@ -715,11 +714,11 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
dev_err(&udc->dev->dev, "%s, bad params", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (unlikely(!_ep || !ep->desc)) {
|
||||
if (unlikely(!_ep || !ep->ep.desc)) {
|
||||
dev_err(&udc->dev->dev, "%s, bad ep", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
|
||||
if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
|
||||
if (req->req.length > ep->ep.maxpacket)
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
|
@ -925,12 +924,12 @@ static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
|
|||
|
||||
ep = container_of(_ep, struct mv_ep, ep);
|
||||
udc = ep->udc;
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
status = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
|
||||
if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
|
||||
status = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1279,7 +1278,7 @@ static int eps_init(struct mv_udc *udc)
|
|||
ep->stopped = 0;
|
||||
ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
|
||||
ep->ep_num = 0;
|
||||
ep->desc = &mv_ep0_desc;
|
||||
ep->ep.desc = &mv_ep0_desc;
|
||||
INIT_LIST_HEAD(&ep->queue);
|
||||
|
||||
ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ static int omap_ep_enable(struct usb_ep *_ep,
|
|||
u16 maxp;
|
||||
|
||||
/* catch various bogus parameters */
|
||||
if (!_ep || !desc || ep->desc
|
||||
if (!_ep || !desc || ep->ep.desc
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT
|
||||
|| ep->bEndpointAddress != desc->bEndpointAddress
|
||||
|| ep->maxpacket < usb_endpoint_maxp(desc)) {
|
||||
|
|
@ -200,7 +200,7 @@ static int omap_ep_enable(struct usb_ep *_ep,
|
|||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->irqs = 0;
|
||||
ep->stopped = 0;
|
||||
ep->ep.maxpacket = maxp;
|
||||
|
|
@ -242,14 +242,13 @@ static int omap_ep_disable(struct usb_ep *_ep)
|
|||
struct omap_ep *ep = container_of(_ep, struct omap_ep, ep);
|
||||
unsigned long flags;
|
||||
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
DBG("%s, %s not enabled\n", __func__,
|
||||
_ep ? ep->ep.name : NULL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
nuke (ep, -ESHUTDOWN);
|
||||
ep->ep.maxpacket = ep->maxpacket;
|
||||
|
|
@ -917,7 +916,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
DBG("%s, bad params\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!_ep || (!ep->desc && ep->bEndpointAddress)) {
|
||||
if (!_ep || (!ep->ep.desc && ep->bEndpointAddress)) {
|
||||
DBG("%s, bad ep\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1121,7 +1120,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value)
|
|||
status = 0;
|
||||
|
||||
/* otherwise, all active non-ISO endpoints can halt */
|
||||
} else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->desc) {
|
||||
} else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->ep.desc) {
|
||||
|
||||
/* IN endpoints must already be idle */
|
||||
if ((ep->bEndpointAddress & USB_DIR_IN)
|
||||
|
|
@ -1625,7 +1624,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
|
|||
if (w_index & USB_DIR_IN)
|
||||
ep += 16;
|
||||
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
|
||||
|| !ep->desc)
|
||||
|| !ep->ep.desc)
|
||||
goto do_stall;
|
||||
use_ep(ep, 0);
|
||||
omap_writew(udc->clr_halt, UDC_CTRL);
|
||||
|
|
@ -1653,7 +1652,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
|
|||
if (w_index & USB_DIR_IN)
|
||||
ep += 16;
|
||||
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
|
||||
|| ep == ep0 || !ep->desc)
|
||||
|| ep == ep0 || !ep->ep.desc)
|
||||
goto do_stall;
|
||||
if (use_dma && ep->has_dma) {
|
||||
/* this has rude side-effects (aborts) and
|
||||
|
|
@ -1688,7 +1687,7 @@ ep0out_status_stage:
|
|||
ep = &udc->ep[w_index & 0xf];
|
||||
if (w_index & USB_DIR_IN)
|
||||
ep += 16;
|
||||
if (!ep->desc)
|
||||
if (!ep->ep.desc)
|
||||
goto do_stall;
|
||||
|
||||
/* iso never stalls */
|
||||
|
|
@ -2509,7 +2508,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
|
|||
if (tmp & UDC_ADD) {
|
||||
list_for_each_entry (ep, &udc->gadget.ep_list,
|
||||
ep.ep_list) {
|
||||
if (ep->desc)
|
||||
if (ep->ep.desc)
|
||||
proc_ep_show(s, ep);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ struct omap_ep {
|
|||
struct list_head queue;
|
||||
unsigned long irqs;
|
||||
struct list_head iso;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
char name[14];
|
||||
u16 maxpacket;
|
||||
u8 bEndpointAddress;
|
||||
|
|
|
|||
|
|
@ -295,7 +295,6 @@ struct pch_udc_ep {
|
|||
struct pch_udc_data_dma_desc *td_data;
|
||||
struct pch_udc_dev *dev;
|
||||
unsigned long offset_addr;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct list_head queue;
|
||||
unsigned num:5,
|
||||
in:1,
|
||||
|
|
@ -1705,7 +1704,7 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep,
|
|||
if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
|
||||
return -ESHUTDOWN;
|
||||
spin_lock_irqsave(&dev->lock, iflags);
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->halted = 0;
|
||||
pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc);
|
||||
ep->ep.maxpacket = usb_endpoint_maxp(desc);
|
||||
|
|
@ -1734,7 +1733,7 @@ static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
|
|||
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if ((usbep->name == ep0_string) || !ep->desc)
|
||||
if ((usbep->name == ep0_string) || !ep->ep.desc)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&ep->dev->lock, iflags);
|
||||
|
|
@ -1742,7 +1741,6 @@ static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
|
|||
ep->halted = 1;
|
||||
pch_udc_ep_disable(ep);
|
||||
pch_udc_disable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
INIT_LIST_HEAD(&ep->queue);
|
||||
spin_unlock_irqrestore(&ep->dev->lock, iflags);
|
||||
|
|
@ -1849,7 +1847,7 @@ static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
|
|||
return -EINVAL;
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if (!ep->desc && ep->num)
|
||||
if (!ep->ep.desc && ep->num)
|
||||
return -EINVAL;
|
||||
req = container_of(usbreq, struct pch_udc_request, req);
|
||||
if (!list_empty(&req->queue))
|
||||
|
|
@ -1949,7 +1947,7 @@ static int pch_udc_pcd_dequeue(struct usb_ep *usbep,
|
|||
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if (!usbep || !usbreq || (!ep->desc && ep->num))
|
||||
if (!usbep || !usbreq || (!ep->ep.desc && ep->num))
|
||||
return ret;
|
||||
req = container_of(usbreq, struct pch_udc_request, req);
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
|
|
@ -1988,7 +1986,7 @@ static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
|
|||
return -EINVAL;
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if (!ep->desc && !ep->num)
|
||||
if (!ep->ep.desc && !ep->num)
|
||||
return -EINVAL;
|
||||
if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
|
||||
return -ESHUTDOWN;
|
||||
|
|
@ -2033,7 +2031,7 @@ static int pch_udc_pcd_set_wedge(struct usb_ep *usbep)
|
|||
return -EINVAL;
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
dev = ep->dev;
|
||||
if (!ep->desc && !ep->num)
|
||||
if (!ep->ep.desc && !ep->num)
|
||||
return -EINVAL;
|
||||
if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
|
||||
return -ESHUTDOWN;
|
||||
|
|
@ -2065,7 +2063,7 @@ static void pch_udc_pcd_fifo_flush(struct usb_ep *usbep)
|
|||
return;
|
||||
|
||||
ep = container_of(usbep, struct pch_udc_ep, ep);
|
||||
if (ep->desc || !ep->num)
|
||||
if (ep->ep.desc || !ep->num)
|
||||
pch_udc_ep_fifo_flush(ep, ep->in);
|
||||
}
|
||||
|
||||
|
|
@ -3282,7 +3280,6 @@ static DEFINE_PCI_DEVICE_TABLE(pch_udc_pcidev_id) = {
|
|||
|
||||
MODULE_DEVICE_TABLE(pci, pch_udc_pcidev_id);
|
||||
|
||||
|
||||
static struct pci_driver pch_udc_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = pch_udc_pcidev_id,
|
||||
|
|
@ -3293,17 +3290,7 @@ static struct pci_driver pch_udc_driver = {
|
|||
.shutdown = pch_udc_shutdown,
|
||||
};
|
||||
|
||||
static int __init pch_udc_pci_init(void)
|
||||
{
|
||||
return pci_register_driver(&pch_udc_driver);
|
||||
}
|
||||
module_init(pch_udc_pci_init);
|
||||
|
||||
static void __exit pch_udc_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&pch_udc_driver);
|
||||
}
|
||||
module_exit(pch_udc_pci_exit);
|
||||
module_pci_driver(pch_udc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Intel EG20T USB Device Controller");
|
||||
MODULE_AUTHOR("LAPIS Semiconductor, <[email protected]>");
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
|
@ -75,8 +76,6 @@ struct printer_dev {
|
|||
/* lock buffer lists during read/write calls */
|
||||
struct mutex lock_printer_io;
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_request *req; /* for control responses */
|
||||
u8 config;
|
||||
s8 interface;
|
||||
struct usb_ep *in_ep, *out_ep;
|
||||
|
||||
|
|
@ -100,6 +99,7 @@ struct printer_dev {
|
|||
struct device *pdev;
|
||||
u8 printer_cdev_open;
|
||||
wait_queue_head_t wait;
|
||||
struct usb_function function;
|
||||
};
|
||||
|
||||
static struct printer_dev usb_printer_gadget;
|
||||
|
|
@ -120,26 +120,6 @@ static struct printer_dev usb_printer_gadget;
|
|||
* parameters are in UTF-8 (superset of ASCII's 7 bit characters).
|
||||
*/
|
||||
|
||||
static ushort idVendor;
|
||||
module_param(idVendor, ushort, S_IRUGO);
|
||||
MODULE_PARM_DESC(idVendor, "USB Vendor ID");
|
||||
|
||||
static ushort idProduct;
|
||||
module_param(idProduct, ushort, S_IRUGO);
|
||||
MODULE_PARM_DESC(idProduct, "USB Product ID");
|
||||
|
||||
static ushort bcdDevice;
|
||||
module_param(bcdDevice, ushort, S_IRUGO);
|
||||
MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
|
||||
|
||||
static char *iManufacturer;
|
||||
module_param(iManufacturer, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
|
||||
|
||||
static char *iProduct;
|
||||
module_param(iProduct, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(iProduct, "USB Product string");
|
||||
|
||||
static char *iSerialNum;
|
||||
module_param(iSerialNum, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(iSerialNum, "1");
|
||||
|
|
@ -154,47 +134,8 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
|
|||
|
||||
#define QLEN qlen
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
#define DEVSPEED USB_SPEED_HIGH
|
||||
#else /* full speed (low speed doesn't do bulk) */
|
||||
#define DEVSPEED USB_SPEED_FULL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define xprintk(d, level, fmt, args...) \
|
||||
printk(level "%s: " fmt, DRIVER_DESC, ## args)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(dev, fmt, args...) \
|
||||
xprintk(dev, KERN_DEBUG, fmt, ## args)
|
||||
#else
|
||||
#define DBG(dev, fmt, args...) \
|
||||
do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define VDBG(dev, fmt, args...) \
|
||||
xprintk(dev, KERN_DEBUG, fmt, ## args)
|
||||
#else
|
||||
#define VDBG(dev, fmt, args...) \
|
||||
do { } while (0)
|
||||
#endif /* VERBOSE */
|
||||
|
||||
#define ERROR(dev, fmt, args...) \
|
||||
xprintk(dev, KERN_ERR, fmt, ## args)
|
||||
#define WARNING(dev, fmt, args...) \
|
||||
xprintk(dev, KERN_WARNING, fmt, ## args)
|
||||
#define INFO(dev, fmt, args...) \
|
||||
xprintk(dev, KERN_INFO, fmt, ## args)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
|
||||
* ep0 implementation: descriptors, config management, setup().
|
||||
* also optional class-specific notification interrupt transfer.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DESCRIPTORS ... most are static, but strings and (full) configuration
|
||||
* descriptors are built on demand.
|
||||
|
|
@ -227,24 +168,6 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bNumConfigurations = 1
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_desc = {
|
||||
.bLength = sizeof otg_desc,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
.bmAttributes = USB_OTG_SRP
|
||||
};
|
||||
|
||||
static struct usb_config_descriptor config_desc = {
|
||||
.bLength = sizeof config_desc,
|
||||
.bDescriptorType = USB_DT_CONFIG,
|
||||
|
||||
/* compute wTotalLength on the fly */
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = DEV_CONFIG_VALUE,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
|
||||
.bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor intf_desc = {
|
||||
.bLength = sizeof intf_desc,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
|
@ -270,16 +193,13 @@ static struct usb_endpoint_descriptor fs_ep_out_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *fs_printer_function [11] = {
|
||||
(struct usb_descriptor_header *) &otg_desc,
|
||||
static struct usb_descriptor_header *fs_printer_function[] = {
|
||||
(struct usb_descriptor_header *) &intf_desc,
|
||||
(struct usb_descriptor_header *) &fs_ep_in_desc,
|
||||
(struct usb_descriptor_header *) &fs_ep_out_desc,
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
|
||||
/*
|
||||
* usb 2.0 devices need to expose both high speed and full speed
|
||||
* descriptors, unless they only run at full speed.
|
||||
|
|
@ -307,24 +227,27 @@ static struct usb_qualifier_descriptor dev_qualifier = {
|
|||
.bNumConfigurations = 1
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *hs_printer_function [11] = {
|
||||
(struct usb_descriptor_header *) &otg_desc,
|
||||
static struct usb_descriptor_header *hs_printer_function[] = {
|
||||
(struct usb_descriptor_header *) &intf_desc,
|
||||
(struct usb_descriptor_header *) &hs_ep_in_desc,
|
||||
(struct usb_descriptor_header *) &hs_ep_out_desc,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_descriptor = {
|
||||
.bLength = sizeof otg_descriptor,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
.bmAttributes = USB_OTG_SRP,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *otg_desc[] = {
|
||||
(struct usb_descriptor_header *) &otg_descriptor,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* maxpacket and other transfer characteristics vary by speed. */
|
||||
#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs))
|
||||
|
||||
#else
|
||||
|
||||
/* if there's no high speed support, maxpacket doesn't change. */
|
||||
#define ep_desc(g, hs, fs) (((void)(g)), (fs))
|
||||
|
||||
#endif /* !CONFIG_USB_GADGET_DUALSPEED */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* descriptors that are built on-demand */
|
||||
|
|
@ -343,11 +266,16 @@ static struct usb_string strings [] = {
|
|||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab = {
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *dev_strings[] = {
|
||||
&stringtab_dev,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_request *
|
||||
|
|
@ -937,82 +865,8 @@ static void printer_reset_interface(struct printer_dev *dev)
|
|||
dev->interface = -1;
|
||||
}
|
||||
|
||||
/* change our operational config. must agree with the code
|
||||
* that returns config descriptors, and altsetting code.
|
||||
*/
|
||||
static int
|
||||
printer_set_config(struct printer_dev *dev, unsigned number)
|
||||
{
|
||||
int result = 0;
|
||||
struct usb_gadget *gadget = dev->gadget;
|
||||
|
||||
switch (number) {
|
||||
case DEV_CONFIG_VALUE:
|
||||
result = 0;
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
/* FALL THROUGH */
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
usb_gadget_vbus_draw(dev->gadget,
|
||||
dev->gadget->is_otg ? 8 : 100);
|
||||
} else {
|
||||
unsigned power;
|
||||
|
||||
power = 2 * config_desc.bMaxPower;
|
||||
usb_gadget_vbus_draw(dev->gadget, power);
|
||||
|
||||
dev->config = number;
|
||||
INFO(dev, "%s config #%d: %d mA, %s\n",
|
||||
usb_speed_string(gadget->speed),
|
||||
number, power, driver_desc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
config_buf(enum usb_device_speed speed, u8 *buf, u8 type, unsigned index,
|
||||
int is_otg)
|
||||
{
|
||||
int len;
|
||||
const struct usb_descriptor_header **function;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
int hs = (speed == USB_SPEED_HIGH);
|
||||
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
hs = !hs;
|
||||
|
||||
if (hs) {
|
||||
function = hs_printer_function;
|
||||
} else {
|
||||
function = fs_printer_function;
|
||||
}
|
||||
#else
|
||||
function = fs_printer_function;
|
||||
#endif
|
||||
|
||||
if (index >= device_desc.bNumConfigurations)
|
||||
return -EINVAL;
|
||||
|
||||
/* for now, don't advertise srp-only devices */
|
||||
if (!is_otg)
|
||||
function++;
|
||||
|
||||
len = usb_gadget_config_buf(&config_desc, buf, USB_DESC_BUFSIZE,
|
||||
function);
|
||||
if (len < 0)
|
||||
return len;
|
||||
((struct usb_config_descriptor *) buf)->bDescriptorType = type;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Change our operational Interface. */
|
||||
static int
|
||||
set_interface(struct printer_dev *dev, unsigned number)
|
||||
static int set_interface(struct printer_dev *dev, unsigned number)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
|
|
@ -1043,14 +897,6 @@ set_interface(struct printer_dev *dev, unsigned number)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void printer_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
if (req->status || req->actual != req->length)
|
||||
DBG((struct printer_dev *) ep->driver_data,
|
||||
"setup complete --> %d, %d/%d\n",
|
||||
req->status, req->actual, req->length);
|
||||
}
|
||||
|
||||
static void printer_soft_reset(struct printer_dev *dev)
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
|
@ -1107,11 +953,12 @@ static void printer_soft_reset(struct printer_dev *dev)
|
|||
* The setup() callback implements all the ep0 functionality that's not
|
||||
* handled lower down.
|
||||
*/
|
||||
static int
|
||||
printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
static int printer_func_setup(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct printer_dev *dev = get_gadget_data(gadget);
|
||||
struct usb_request *req = dev->req;
|
||||
struct printer_dev *dev = container_of(f, struct printer_dev, function);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
u16 wValue = le16_to_cpu(ctrl->wValue);
|
||||
|
|
@ -1120,102 +967,7 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
|
||||
|
||||
req->complete = printer_setup_complete;
|
||||
|
||||
switch (ctrl->bRequestType&USB_TYPE_MASK) {
|
||||
|
||||
case USB_TYPE_STANDARD:
|
||||
switch (ctrl->bRequest) {
|
||||
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
if (ctrl->bRequestType != USB_DIR_IN)
|
||||
break;
|
||||
switch (wValue >> 8) {
|
||||
|
||||
case USB_DT_DEVICE:
|
||||
device_desc.bMaxPacketSize0 =
|
||||
gadget->ep0->maxpacket;
|
||||
value = min(wLength, (u16) sizeof device_desc);
|
||||
memcpy(req->buf, &device_desc, value);
|
||||
break;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
/*
|
||||
* assumes ep0 uses the same value for both
|
||||
* speeds
|
||||
*/
|
||||
dev_qualifier.bMaxPacketSize0 =
|
||||
gadget->ep0->maxpacket;
|
||||
value = min(wLength,
|
||||
(u16) sizeof dev_qualifier);
|
||||
memcpy(req->buf, &dev_qualifier, value);
|
||||
break;
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
case USB_DT_CONFIG:
|
||||
value = config_buf(gadget->speed, req->buf,
|
||||
wValue >> 8,
|
||||
wValue & 0xff,
|
||||
gadget->is_otg);
|
||||
if (value >= 0)
|
||||
value = min(wLength, (u16) value);
|
||||
break;
|
||||
|
||||
case USB_DT_STRING:
|
||||
value = usb_gadget_get_string(&stringtab,
|
||||
wValue & 0xff, req->buf);
|
||||
if (value >= 0)
|
||||
value = min(wLength, (u16) value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
if (ctrl->bRequestType != 0)
|
||||
break;
|
||||
if (gadget->a_hnp_support)
|
||||
DBG(dev, "HNP available\n");
|
||||
else if (gadget->a_alt_hnp_support)
|
||||
DBG(dev, "HNP needs a different root port\n");
|
||||
value = printer_set_config(dev, wValue);
|
||||
if (!value)
|
||||
value = set_interface(dev, PRINTER_INTERFACE);
|
||||
break;
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
if (ctrl->bRequestType != USB_DIR_IN)
|
||||
break;
|
||||
*(u8 *)req->buf = dev->config;
|
||||
value = min(wLength, (u16) 1);
|
||||
break;
|
||||
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
if (ctrl->bRequestType != USB_RECIP_INTERFACE ||
|
||||
!dev->config)
|
||||
break;
|
||||
|
||||
value = set_interface(dev, PRINTER_INTERFACE);
|
||||
break;
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
if (ctrl->bRequestType !=
|
||||
(USB_DIR_IN|USB_RECIP_INTERFACE)
|
||||
|| !dev->config)
|
||||
break;
|
||||
|
||||
*(u8 *)req->buf = dev->interface;
|
||||
value = min(wLength, (u16) 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_TYPE_CLASS:
|
||||
switch (ctrl->bRequest) {
|
||||
case 0: /* Get the IEEE-1284 PNP String */
|
||||
|
|
@ -1261,44 +1013,50 @@ unknown:
|
|||
wValue, wIndex, wLength);
|
||||
break;
|
||||
}
|
||||
|
||||
/* respond with data transfer before status phase? */
|
||||
if (value >= 0) {
|
||||
req->length = value;
|
||||
req->zero = value < wLength;
|
||||
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
|
||||
if (value < 0) {
|
||||
DBG(dev, "ep_queue --> %d\n", value);
|
||||
req->status = 0;
|
||||
printer_setup_complete(gadget->ep0, req);
|
||||
}
|
||||
}
|
||||
|
||||
/* host either stalls (value < 0) or reports success */
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
printer_disconnect(struct usb_gadget *gadget)
|
||||
static int __init printer_func_bind(struct usb_configuration *c,
|
||||
struct usb_function *f)
|
||||
{
|
||||
struct printer_dev *dev = get_gadget_data(gadget);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void printer_func_unbind(struct usb_configuration *c,
|
||||
struct usb_function *f)
|
||||
{
|
||||
}
|
||||
|
||||
static int printer_func_set_alt(struct usb_function *f,
|
||||
unsigned intf, unsigned alt)
|
||||
{
|
||||
struct printer_dev *dev = container_of(f, struct printer_dev, function);
|
||||
int ret = -ENOTSUPP;
|
||||
|
||||
if (!alt)
|
||||
ret = set_interface(dev, PRINTER_INTERFACE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void printer_func_disable(struct usb_function *f)
|
||||
{
|
||||
struct printer_dev *dev = container_of(f, struct printer_dev, function);
|
||||
unsigned long flags;
|
||||
|
||||
DBG(dev, "%s\n", __func__);
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
printer_reset_interface(dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
printer_unbind(struct usb_gadget *gadget)
|
||||
static void printer_cfg_unbind(struct usb_configuration *c)
|
||||
{
|
||||
struct printer_dev *dev = get_gadget_data(gadget);
|
||||
struct printer_dev *dev;
|
||||
struct usb_request *req;
|
||||
|
||||
dev = &usb_printer_gadget;
|
||||
|
||||
DBG(dev, "%s\n", __func__);
|
||||
|
||||
|
|
@ -1336,18 +1094,18 @@ printer_unbind(struct usb_gadget *gadget)
|
|||
list_del(&req->list);
|
||||
printer_req_free(dev->out_ep, req);
|
||||
}
|
||||
|
||||
if (dev->req) {
|
||||
printer_req_free(gadget->ep0, dev->req);
|
||||
dev->req = NULL;
|
||||
}
|
||||
|
||||
set_gadget_data(gadget, NULL);
|
||||
}
|
||||
|
||||
static int __init
|
||||
printer_bind(struct usb_gadget *gadget)
|
||||
static struct usb_configuration printer_cfg_driver = {
|
||||
.label = "printer",
|
||||
.unbind = printer_cfg_unbind,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
static int __init printer_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
struct usb_gadget *gadget = c->cdev->gadget;
|
||||
struct printer_dev *dev;
|
||||
struct usb_ep *in_ep, *out_ep;
|
||||
int status = -ENOMEM;
|
||||
|
|
@ -1358,6 +1116,14 @@ printer_bind(struct usb_gadget *gadget)
|
|||
|
||||
dev = &usb_printer_gadget;
|
||||
|
||||
dev->function.name = shortname;
|
||||
dev->function.descriptors = fs_printer_function;
|
||||
dev->function.hs_descriptors = hs_printer_function;
|
||||
dev->function.bind = printer_func_bind;
|
||||
dev->function.setup = printer_func_setup;
|
||||
dev->function.unbind = printer_func_unbind;
|
||||
dev->function.set_alt = printer_func_set_alt;
|
||||
dev->function.disable = printer_func_disable;
|
||||
|
||||
/* Setup the sysfs files for the printer gadget. */
|
||||
dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
|
||||
|
|
@ -1393,29 +1159,6 @@ printer_bind(struct usb_gadget *gadget)
|
|||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
|
||||
device_desc.idVendor =
|
||||
cpu_to_le16(PRINTER_VENDOR_NUM);
|
||||
device_desc.idProduct =
|
||||
cpu_to_le16(PRINTER_PRODUCT_NUM);
|
||||
|
||||
/* support optional vendor/distro customization */
|
||||
if (idVendor) {
|
||||
if (!idProduct) {
|
||||
dev_err(&gadget->dev, "idVendor needs idProduct!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
device_desc.idVendor = cpu_to_le16(idVendor);
|
||||
device_desc.idProduct = cpu_to_le16(idProduct);
|
||||
if (bcdDevice)
|
||||
device_desc.bcdDevice = cpu_to_le16(bcdDevice);
|
||||
}
|
||||
|
||||
if (iManufacturer)
|
||||
strlcpy(manufacturer, iManufacturer, sizeof manufacturer);
|
||||
|
||||
if (iProduct)
|
||||
strlcpy(product_desc, iProduct, sizeof product_desc);
|
||||
|
||||
if (iSerialNum)
|
||||
strlcpy(serial_num, iSerialNum, sizeof serial_num);
|
||||
|
||||
|
|
@ -1442,17 +1185,16 @@ autoconf_fail:
|
|||
goto autoconf_fail;
|
||||
out_ep->driver_data = out_ep; /* claim */
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
/* assumes that all endpoints are dual-speed */
|
||||
hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
|
||||
hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
|
||||
#endif /* DUALSPEED */
|
||||
|
||||
usb_gadget_set_selfpowered(gadget);
|
||||
|
||||
if (gadget->is_otg) {
|
||||
otg_desc.bmAttributes |= USB_OTG_HNP,
|
||||
config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
otg_descriptor.bmAttributes |= USB_OTG_HNP;
|
||||
printer_cfg_driver.descriptors = otg_desc;
|
||||
printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
|
|
@ -1466,7 +1208,6 @@ autoconf_fail:
|
|||
init_waitqueue_head(&dev->tx_wait);
|
||||
init_waitqueue_head(&dev->tx_flush_wait);
|
||||
|
||||
dev->config = 0;
|
||||
dev->interface = -1;
|
||||
dev->printer_cdev_open = 0;
|
||||
dev->printer_status = PRINTER_NOT_ERROR;
|
||||
|
|
@ -1477,14 +1218,6 @@ autoconf_fail:
|
|||
dev->in_ep = in_ep;
|
||||
dev->out_ep = out_ep;
|
||||
|
||||
/* preallocate control message data and buffer */
|
||||
dev->req = printer_req_alloc(gadget->ep0, USB_DESC_BUFSIZE,
|
||||
GFP_KERNEL);
|
||||
if (!dev->req) {
|
||||
status = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < QLEN; i++) {
|
||||
req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
|
||||
if (!req) {
|
||||
|
|
@ -1513,45 +1246,37 @@ autoconf_fail:
|
|||
list_add(&req->list, &dev->rx_reqs);
|
||||
}
|
||||
|
||||
dev->req->complete = printer_setup_complete;
|
||||
|
||||
/* finish hookup to lower layer ... */
|
||||
dev->gadget = gadget;
|
||||
set_gadget_data(gadget, dev);
|
||||
gadget->ep0->driver_data = dev;
|
||||
|
||||
INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
|
||||
INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, out_ep->name,
|
||||
in_ep->name);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
printer_unbind(gadget);
|
||||
printer_cfg_unbind(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static int printer_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_driver printer_driver = {
|
||||
.max_speed = DEVSPEED,
|
||||
static int __init printer_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
return usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
|
||||
}
|
||||
|
||||
.function = (char *) driver_desc,
|
||||
static struct usb_composite_driver printer_driver = {
|
||||
.name = shortname,
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.unbind = printer_unbind,
|
||||
|
||||
.setup = printer_setup,
|
||||
.disconnect = printer_disconnect,
|
||||
|
||||
.driver = {
|
||||
.name = (char *) shortname,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Craig Nadler");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init
|
||||
init(void)
|
||||
{
|
||||
|
|
@ -1560,23 +1285,23 @@ init(void)
|
|||
usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
|
||||
if (IS_ERR(usb_gadget_class)) {
|
||||
status = PTR_ERR(usb_gadget_class);
|
||||
ERROR(dev, "unable to create usb_gadget class %d\n", status);
|
||||
pr_err("unable to create usb_gadget class %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = alloc_chrdev_region(&g_printer_devno, 0, 1,
|
||||
"USB printer gadget");
|
||||
if (status) {
|
||||
ERROR(dev, "alloc_chrdev_region %d\n", status);
|
||||
pr_err("alloc_chrdev_region %d\n", status);
|
||||
class_destroy(usb_gadget_class);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = usb_gadget_probe_driver(&printer_driver, printer_bind);
|
||||
status = usb_composite_probe(&printer_driver, printer_bind);
|
||||
if (status) {
|
||||
class_destroy(usb_gadget_class);
|
||||
unregister_chrdev_region(g_printer_devno, 1);
|
||||
DBG(dev, "usb_gadget_probe_driver %x\n", status);
|
||||
pr_err("usb_gadget_probe_driver %x\n", status);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
|
@ -1586,15 +1311,14 @@ module_init(init);
|
|||
static void __exit
|
||||
cleanup(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
mutex_lock(&usb_printer_gadget.lock_printer_io);
|
||||
status = usb_gadget_unregister_driver(&printer_driver);
|
||||
if (status)
|
||||
ERROR(dev, "usb_gadget_unregister_driver %x\n", status);
|
||||
|
||||
usb_composite_unregister(&printer_driver);
|
||||
unregister_chrdev_region(g_printer_devno, 1);
|
||||
class_destroy(usb_gadget_class);
|
||||
mutex_unlock(&usb_printer_gadget.lock_printer_io);
|
||||
}
|
||||
module_exit(cleanup);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Craig Nadler");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
|
|||
struct pxa25x_udc *dev;
|
||||
|
||||
ep = container_of (_ep, struct pxa25x_ep, ep);
|
||||
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|
||||
if (!_ep || !desc || ep->ep.desc || _ep->name == ep0name
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT
|
||||
|| ep->bEndpointAddress != desc->bEndpointAddress
|
||||
|| ep->fifo_size < usb_endpoint_maxp (desc)) {
|
||||
|
|
@ -249,7 +249,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
|
|||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->stopped = 0;
|
||||
ep->pio_irqs = 0;
|
||||
ep->ep.maxpacket = usb_endpoint_maxp (desc);
|
||||
|
|
@ -269,7 +269,7 @@ static int pxa25x_ep_disable (struct usb_ep *_ep)
|
|||
unsigned long flags;
|
||||
|
||||
ep = container_of (_ep, struct pxa25x_ep, ep);
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
DMSG("%s, %s not enabled\n", __func__,
|
||||
_ep ? ep->ep.name : NULL);
|
||||
return -EINVAL;
|
||||
|
|
@ -281,7 +281,6 @@ static int pxa25x_ep_disable (struct usb_ep *_ep)
|
|||
/* flush fifo (mostly for IN buffers) */
|
||||
pxa25x_ep_fifo_flush (_ep);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
|
||||
|
|
@ -390,7 +389,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
|
|||
{
|
||||
unsigned max;
|
||||
|
||||
max = usb_endpoint_maxp(ep->desc);
|
||||
max = usb_endpoint_maxp(ep->ep.desc);
|
||||
do {
|
||||
unsigned count;
|
||||
int is_last, is_short;
|
||||
|
|
@ -644,7 +643,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
}
|
||||
|
||||
ep = container_of(_ep, struct pxa25x_ep, ep);
|
||||
if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
|
||||
if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
|
||||
DMSG("%s, bad ep\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -660,7 +659,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
* we can report per-packet status. that also helps with dma.
|
||||
*/
|
||||
if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
|
||||
&& req->req.length > usb_endpoint_maxp (ep->desc)))
|
||||
&& req->req.length > usb_endpoint_maxp(ep->ep.desc)))
|
||||
return -EMSGSIZE;
|
||||
|
||||
DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
|
||||
|
|
@ -673,7 +672,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
|
||||
/* kickstart this i/o queue? */
|
||||
if (list_empty(&ep->queue) && !ep->stopped) {
|
||||
if (ep->desc == NULL/* ep0 */) {
|
||||
if (ep->ep.desc == NULL/* ep0 */) {
|
||||
unsigned length = _req->length;
|
||||
|
||||
switch (dev->ep0state) {
|
||||
|
|
@ -722,7 +721,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
req = NULL;
|
||||
}
|
||||
|
||||
if (likely (req && ep->desc))
|
||||
if (likely(req && ep->ep.desc))
|
||||
pio_irq_enable(ep->bEndpointAddress);
|
||||
}
|
||||
|
||||
|
|
@ -749,7 +748,7 @@ static void nuke(struct pxa25x_ep *ep, int status)
|
|||
queue);
|
||||
done(ep, req, status);
|
||||
}
|
||||
if (ep->desc)
|
||||
if (ep->ep.desc)
|
||||
pio_irq_disable (ep->bEndpointAddress);
|
||||
}
|
||||
|
||||
|
|
@ -792,7 +791,7 @@ static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value)
|
|||
|
||||
ep = container_of(_ep, struct pxa25x_ep, ep);
|
||||
if (unlikely (!_ep
|
||||
|| (!ep->desc && ep->ep.name != ep0name))
|
||||
|| (!ep->ep.desc && ep->ep.name != ep0name))
|
||||
|| ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
|
||||
DMSG("%s, bad ep\n", __func__);
|
||||
return -EINVAL;
|
||||
|
|
@ -820,7 +819,7 @@ static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value)
|
|||
*ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF;
|
||||
|
||||
/* ep0 needs special care */
|
||||
if (!ep->desc) {
|
||||
if (!ep->ep.desc) {
|
||||
start_watchdog(ep->dev);
|
||||
ep->dev->req_pending = 0;
|
||||
ep->dev->ep0state = EP0_STALL;
|
||||
|
|
@ -1087,7 +1086,7 @@ udc_seq_show(struct seq_file *m, void *_d)
|
|||
if (i != 0) {
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
desc = ep->desc;
|
||||
desc = ep->ep.desc;
|
||||
if (!desc)
|
||||
continue;
|
||||
tmp = *dev->ep [i].reg_udccs;
|
||||
|
|
@ -1191,7 +1190,6 @@ static void udc_reinit(struct pxa25x_udc *dev)
|
|||
if (i != 0)
|
||||
list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 0;
|
||||
INIT_LIST_HEAD (&ep->queue);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ struct pxa25x_ep {
|
|||
struct usb_ep ep;
|
||||
struct pxa25x_udc *dev;
|
||||
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct list_head queue;
|
||||
unsigned long pio_irqs;
|
||||
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
|
|||
unsigned char *counter;
|
||||
int ret;
|
||||
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
|
||||
if (ep->pipenum) /* already allocated pipe */
|
||||
return 0;
|
||||
|
|
@ -648,7 +648,7 @@ static int sudmac_alloc_channel(struct r8a66597 *r8a66597,
|
|||
/* set SUDMAC parameters */
|
||||
dma = &r8a66597->dma;
|
||||
dma->used = 1;
|
||||
if (ep->desc->bEndpointAddress & USB_DIR_IN) {
|
||||
if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) {
|
||||
dma->dir = 1;
|
||||
} else {
|
||||
dma->dir = 0;
|
||||
|
|
@ -770,7 +770,7 @@ static void start_packet_read(struct r8a66597_ep *ep,
|
|||
|
||||
static void start_packet(struct r8a66597_ep *ep, struct r8a66597_request *req)
|
||||
{
|
||||
if (ep->desc->bEndpointAddress & USB_DIR_IN)
|
||||
if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
|
||||
start_packet_write(ep, req);
|
||||
else
|
||||
start_packet_read(ep, req);
|
||||
|
|
@ -930,7 +930,7 @@ __acquires(r8a66597->lock)
|
|||
|
||||
if (restart) {
|
||||
req = get_request_from_ep(ep);
|
||||
if (ep->desc)
|
||||
if (ep->ep.desc)
|
||||
start_packet(ep, req);
|
||||
}
|
||||
}
|
||||
|
|
@ -1116,7 +1116,7 @@ static void irq_pipe_ready(struct r8a66597 *r8a66597, u16 status, u16 enb)
|
|||
r8a66597_write(r8a66597, ~check, BRDYSTS);
|
||||
ep = r8a66597->pipenum2ep[pipenum];
|
||||
req = get_request_from_ep(ep);
|
||||
if (ep->desc->bEndpointAddress & USB_DIR_IN)
|
||||
if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
|
||||
irq_packet_write(ep, req);
|
||||
else
|
||||
irq_packet_read(ep, req);
|
||||
|
|
@ -1170,7 +1170,7 @@ __acquires(r8a66597->lock)
|
|||
|
||||
switch (ctrl->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
status = 1 << USB_DEVICE_SELF_POWERED;
|
||||
status = r8a66597->device_status;
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
status = 0;
|
||||
|
|
@ -1627,7 +1627,7 @@ static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
req->req.actual = 0;
|
||||
req->req.status = -EINPROGRESS;
|
||||
|
||||
if (ep->desc == NULL) /* control */
|
||||
if (ep->ep.desc == NULL) /* control */
|
||||
start_ep0(ep, req);
|
||||
else {
|
||||
if (request && !ep->busy)
|
||||
|
|
@ -1692,7 +1692,7 @@ static int r8a66597_set_wedge(struct usb_ep *_ep)
|
|||
|
||||
ep = container_of(_ep, struct r8a66597_ep, ep);
|
||||
|
||||
if (!ep || !ep->desc)
|
||||
if (!ep || !ep->ep.desc)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&ep->r8a66597->lock, flags);
|
||||
|
|
@ -1800,11 +1800,24 @@ static int r8a66597_pullup(struct usb_gadget *gadget, int is_on)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
|
||||
|
||||
if (is_self)
|
||||
r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED;
|
||||
else
|
||||
r8a66597->device_status &= ~(1 << USB_DEVICE_SELF_POWERED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops r8a66597_gadget_ops = {
|
||||
.get_frame = r8a66597_get_frame,
|
||||
.udc_start = r8a66597_start,
|
||||
.udc_stop = r8a66597_stop,
|
||||
.pullup = r8a66597_pullup,
|
||||
.set_selfpowered = r8a66597_set_selfpowered,
|
||||
};
|
||||
|
||||
static int __exit r8a66597_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ struct r8a66597_ep {
|
|||
unsigned use_dma:1;
|
||||
u16 pipenum;
|
||||
u16 type;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
/* register address */
|
||||
unsigned char fifoaddr;
|
||||
unsigned char fifosel;
|
||||
|
|
@ -111,6 +111,7 @@ struct r8a66597 {
|
|||
u16 old_vbus;
|
||||
u16 scount;
|
||||
u16 old_dvsq;
|
||||
u16 device_status; /* for GET_STATUS */
|
||||
|
||||
/* pipe config */
|
||||
unsigned char bulk;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
377
drivers/usb/gadget/s3c-hsotg.h
Normal file
377
drivers/usb/gadget/s3c-hsotg.h
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/* drivers/usb/gadget/s3c-hsotg.h
|
||||
*
|
||||
* Copyright 2008 Openmoko, Inc.
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* USB2.0 Highspeed/OtG Synopsis DWC2 device block registers
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __REGS_USB_HSOTG_H
|
||||
#define __REGS_USB_HSOTG_H __FILE__
|
||||
|
||||
#define HSOTG_REG(x) (x)
|
||||
|
||||
#define GOTGCTL HSOTG_REG(0x000)
|
||||
#define GOTGCTL_BSESVLD (1 << 19)
|
||||
#define GOTGCTL_ASESVLD (1 << 18)
|
||||
#define GOTGCTL_DBNC_SHORT (1 << 17)
|
||||
#define GOTGCTL_CONID_B (1 << 16)
|
||||
#define GOTGCTL_DEVHNPEN (1 << 11)
|
||||
#define GOTGCTL_HSSETHNPEN (1 << 10)
|
||||
#define GOTGCTL_HNPREQ (1 << 9)
|
||||
#define GOTGCTL_HSTNEGSCS (1 << 8)
|
||||
#define GOTGCTL_SESREQ (1 << 1)
|
||||
#define GOTGCTL_SESREQSCS (1 << 0)
|
||||
|
||||
#define GOTGINT HSOTG_REG(0x004)
|
||||
#define GOTGINT_DbnceDone (1 << 19)
|
||||
#define GOTGINT_ADevTOUTChg (1 << 18)
|
||||
#define GOTGINT_HstNegDet (1 << 17)
|
||||
#define GOTGINT_HstnegSucStsChng (1 << 9)
|
||||
#define GOTGINT_SesReqSucStsChng (1 << 8)
|
||||
#define GOTGINT_SesEndDet (1 << 2)
|
||||
|
||||
#define GAHBCFG HSOTG_REG(0x008)
|
||||
#define GAHBCFG_PTxFEmpLvl (1 << 8)
|
||||
#define GAHBCFG_NPTxFEmpLvl (1 << 7)
|
||||
#define GAHBCFG_DMAEn (1 << 5)
|
||||
#define GAHBCFG_HBstLen_MASK (0xf << 1)
|
||||
#define GAHBCFG_HBstLen_SHIFT (1)
|
||||
#define GAHBCFG_HBstLen_Single (0x0 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr (0x1 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr4 (0x3 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr8 (0x5 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr16 (0x7 << 1)
|
||||
#define GAHBCFG_GlblIntrEn (1 << 0)
|
||||
|
||||
#define GUSBCFG HSOTG_REG(0x00C)
|
||||
#define GUSBCFG_PHYLPClkSel (1 << 15)
|
||||
#define GUSBCFG_HNPCap (1 << 9)
|
||||
#define GUSBCFG_SRPCap (1 << 8)
|
||||
#define GUSBCFG_PHYIf16 (1 << 3)
|
||||
#define GUSBCFG_TOutCal_MASK (0x7 << 0)
|
||||
#define GUSBCFG_TOutCal_SHIFT (0)
|
||||
#define GUSBCFG_TOutCal_LIMIT (0x7)
|
||||
#define GUSBCFG_TOutCal(_x) ((_x) << 0)
|
||||
|
||||
#define GRSTCTL HSOTG_REG(0x010)
|
||||
|
||||
#define GRSTCTL_AHBIdle (1 << 31)
|
||||
#define GRSTCTL_DMAReq (1 << 30)
|
||||
#define GRSTCTL_TxFNum_MASK (0x1f << 6)
|
||||
#define GRSTCTL_TxFNum_SHIFT (6)
|
||||
#define GRSTCTL_TxFNum_LIMIT (0x1f)
|
||||
#define GRSTCTL_TxFNum(_x) ((_x) << 6)
|
||||
#define GRSTCTL_TxFFlsh (1 << 5)
|
||||
#define GRSTCTL_RxFFlsh (1 << 4)
|
||||
#define GRSTCTL_INTknQFlsh (1 << 3)
|
||||
#define GRSTCTL_FrmCntrRst (1 << 2)
|
||||
#define GRSTCTL_HSftRst (1 << 1)
|
||||
#define GRSTCTL_CSftRst (1 << 0)
|
||||
|
||||
#define GINTSTS HSOTG_REG(0x014)
|
||||
#define GINTMSK HSOTG_REG(0x018)
|
||||
|
||||
#define GINTSTS_WkUpInt (1 << 31)
|
||||
#define GINTSTS_SessReqInt (1 << 30)
|
||||
#define GINTSTS_DisconnInt (1 << 29)
|
||||
#define GINTSTS_ConIDStsChng (1 << 28)
|
||||
#define GINTSTS_PTxFEmp (1 << 26)
|
||||
#define GINTSTS_HChInt (1 << 25)
|
||||
#define GINTSTS_PrtInt (1 << 24)
|
||||
#define GINTSTS_FetSusp (1 << 22)
|
||||
#define GINTSTS_incompIP (1 << 21)
|
||||
#define GINTSTS_IncomplSOIN (1 << 20)
|
||||
#define GINTSTS_OEPInt (1 << 19)
|
||||
#define GINTSTS_IEPInt (1 << 18)
|
||||
#define GINTSTS_EPMis (1 << 17)
|
||||
#define GINTSTS_EOPF (1 << 15)
|
||||
#define GINTSTS_ISOutDrop (1 << 14)
|
||||
#define GINTSTS_EnumDone (1 << 13)
|
||||
#define GINTSTS_USBRst (1 << 12)
|
||||
#define GINTSTS_USBSusp (1 << 11)
|
||||
#define GINTSTS_ErlySusp (1 << 10)
|
||||
#define GINTSTS_GOUTNakEff (1 << 7)
|
||||
#define GINTSTS_GINNakEff (1 << 6)
|
||||
#define GINTSTS_NPTxFEmp (1 << 5)
|
||||
#define GINTSTS_RxFLvl (1 << 4)
|
||||
#define GINTSTS_SOF (1 << 3)
|
||||
#define GINTSTS_OTGInt (1 << 2)
|
||||
#define GINTSTS_ModeMis (1 << 1)
|
||||
#define GINTSTS_CurMod_Host (1 << 0)
|
||||
|
||||
#define GRXSTSR HSOTG_REG(0x01C)
|
||||
#define GRXSTSP HSOTG_REG(0x020)
|
||||
|
||||
#define GRXSTS_FN_MASK (0x7f << 25)
|
||||
#define GRXSTS_FN_SHIFT (25)
|
||||
|
||||
#define GRXSTS_PktSts_MASK (0xf << 17)
|
||||
#define GRXSTS_PktSts_SHIFT (17)
|
||||
#define GRXSTS_PktSts_GlobalOutNAK (0x1 << 17)
|
||||
#define GRXSTS_PktSts_OutRX (0x2 << 17)
|
||||
#define GRXSTS_PktSts_OutDone (0x3 << 17)
|
||||
#define GRXSTS_PktSts_SetupDone (0x4 << 17)
|
||||
#define GRXSTS_PktSts_SetupRX (0x6 << 17)
|
||||
|
||||
#define GRXSTS_DPID_MASK (0x3 << 15)
|
||||
#define GRXSTS_DPID_SHIFT (15)
|
||||
#define GRXSTS_ByteCnt_MASK (0x7ff << 4)
|
||||
#define GRXSTS_ByteCnt_SHIFT (4)
|
||||
#define GRXSTS_EPNum_MASK (0xf << 0)
|
||||
#define GRXSTS_EPNum_SHIFT (0)
|
||||
|
||||
#define GRXFSIZ HSOTG_REG(0x024)
|
||||
|
||||
#define GNPTXFSIZ HSOTG_REG(0x028)
|
||||
|
||||
#define GNPTXFSIZ_NPTxFDep_MASK (0xffff << 16)
|
||||
#define GNPTXFSIZ_NPTxFDep_SHIFT (16)
|
||||
#define GNPTXFSIZ_NPTxFDep_LIMIT (0xffff)
|
||||
#define GNPTXFSIZ_NPTxFDep(_x) ((_x) << 16)
|
||||
#define GNPTXFSIZ_NPTxFStAddr_MASK (0xffff << 0)
|
||||
#define GNPTXFSIZ_NPTxFStAddr_SHIFT (0)
|
||||
#define GNPTXFSIZ_NPTxFStAddr_LIMIT (0xffff)
|
||||
#define GNPTXFSIZ_NPTxFStAddr(_x) ((_x) << 0)
|
||||
|
||||
#define GNPTXSTS HSOTG_REG(0x02C)
|
||||
|
||||
#define GNPTXSTS_NPtxQTop_MASK (0x7f << 24)
|
||||
#define GNPTXSTS_NPtxQTop_SHIFT (24)
|
||||
|
||||
#define GNPTXSTS_NPTxQSpcAvail_MASK (0xff << 16)
|
||||
#define GNPTXSTS_NPTxQSpcAvail_SHIFT (16)
|
||||
#define GNPTXSTS_NPTxQSpcAvail_GET(_v) (((_v) >> 16) & 0xff)
|
||||
|
||||
#define GNPTXSTS_NPTxFSpcAvail_MASK (0xffff << 0)
|
||||
#define GNPTXSTS_NPTxFSpcAvail_SHIFT (0)
|
||||
#define GNPTXSTS_NPTxFSpcAvail_GET(_v) (((_v) >> 0) & 0xffff)
|
||||
|
||||
|
||||
#define HPTXFSIZ HSOTG_REG(0x100)
|
||||
|
||||
#define DPTXFSIZn(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4))
|
||||
|
||||
#define DPTXFSIZn_DPTxFSize_MASK (0xffff << 16)
|
||||
#define DPTXFSIZn_DPTxFSize_SHIFT (16)
|
||||
#define DPTXFSIZn_DPTxFSize_GET(_v) (((_v) >> 16) & 0xffff)
|
||||
#define DPTXFSIZn_DPTxFSize_LIMIT (0xffff)
|
||||
#define DPTXFSIZn_DPTxFSize(_x) ((_x) << 16)
|
||||
|
||||
#define DPTXFSIZn_DPTxFStAddr_MASK (0xffff << 0)
|
||||
#define DPTXFSIZn_DPTxFStAddr_SHIFT (0)
|
||||
|
||||
/* Device mode registers */
|
||||
#define DCFG HSOTG_REG(0x800)
|
||||
|
||||
#define DCFG_EPMisCnt_MASK (0x1f << 18)
|
||||
#define DCFG_EPMisCnt_SHIFT (18)
|
||||
#define DCFG_EPMisCnt_LIMIT (0x1f)
|
||||
#define DCFG_EPMisCnt(_x) ((_x) << 18)
|
||||
|
||||
#define DCFG_PerFrInt_MASK (0x3 << 11)
|
||||
#define DCFG_PerFrInt_SHIFT (11)
|
||||
#define DCFG_PerFrInt_LIMIT (0x3)
|
||||
#define DCFG_PerFrInt(_x) ((_x) << 11)
|
||||
|
||||
#define DCFG_DevAddr_MASK (0x7f << 4)
|
||||
#define DCFG_DevAddr_SHIFT (4)
|
||||
#define DCFG_DevAddr_LIMIT (0x7f)
|
||||
#define DCFG_DevAddr(_x) ((_x) << 4)
|
||||
|
||||
#define DCFG_NZStsOUTHShk (1 << 2)
|
||||
|
||||
#define DCFG_DevSpd_MASK (0x3 << 0)
|
||||
#define DCFG_DevSpd_SHIFT (0)
|
||||
#define DCFG_DevSpd_HS (0x0 << 0)
|
||||
#define DCFG_DevSpd_FS (0x1 << 0)
|
||||
#define DCFG_DevSpd_LS (0x2 << 0)
|
||||
#define DCFG_DevSpd_FS48 (0x3 << 0)
|
||||
|
||||
#define DCTL HSOTG_REG(0x804)
|
||||
|
||||
#define DCTL_PWROnPrgDone (1 << 11)
|
||||
#define DCTL_CGOUTNak (1 << 10)
|
||||
#define DCTL_SGOUTNak (1 << 9)
|
||||
#define DCTL_CGNPInNAK (1 << 8)
|
||||
#define DCTL_SGNPInNAK (1 << 7)
|
||||
#define DCTL_TstCtl_MASK (0x7 << 4)
|
||||
#define DCTL_TstCtl_SHIFT (4)
|
||||
#define DCTL_GOUTNakSts (1 << 3)
|
||||
#define DCTL_GNPINNakSts (1 << 2)
|
||||
#define DCTL_SftDiscon (1 << 1)
|
||||
#define DCTL_RmtWkUpSig (1 << 0)
|
||||
|
||||
#define DSTS HSOTG_REG(0x808)
|
||||
|
||||
#define DSTS_SOFFN_MASK (0x3fff << 8)
|
||||
#define DSTS_SOFFN_SHIFT (8)
|
||||
#define DSTS_SOFFN_LIMIT (0x3fff)
|
||||
#define DSTS_SOFFN(_x) ((_x) << 8)
|
||||
#define DSTS_ErraticErr (1 << 3)
|
||||
#define DSTS_EnumSpd_MASK (0x3 << 1)
|
||||
#define DSTS_EnumSpd_SHIFT (1)
|
||||
#define DSTS_EnumSpd_HS (0x0 << 1)
|
||||
#define DSTS_EnumSpd_FS (0x1 << 1)
|
||||
#define DSTS_EnumSpd_LS (0x2 << 1)
|
||||
#define DSTS_EnumSpd_FS48 (0x3 << 1)
|
||||
|
||||
#define DSTS_SuspSts (1 << 0)
|
||||
|
||||
#define DIEPMSK HSOTG_REG(0x810)
|
||||
|
||||
#define DIEPMSK_TxFIFOEmpty (1 << 7)
|
||||
#define DIEPMSK_INEPNakEffMsk (1 << 6)
|
||||
#define DIEPMSK_INTknEPMisMsk (1 << 5)
|
||||
#define DIEPMSK_INTknTXFEmpMsk (1 << 4)
|
||||
#define DIEPMSK_TimeOUTMsk (1 << 3)
|
||||
#define DIEPMSK_AHBErrMsk (1 << 2)
|
||||
#define DIEPMSK_EPDisbldMsk (1 << 1)
|
||||
#define DIEPMSK_XferComplMsk (1 << 0)
|
||||
|
||||
#define DOEPMSK HSOTG_REG(0x814)
|
||||
|
||||
#define DOEPMSK_Back2BackSetup (1 << 6)
|
||||
#define DOEPMSK_OUTTknEPdisMsk (1 << 4)
|
||||
#define DOEPMSK_SetupMsk (1 << 3)
|
||||
#define DOEPMSK_AHBErrMsk (1 << 2)
|
||||
#define DOEPMSK_EPDisbldMsk (1 << 1)
|
||||
#define DOEPMSK_XferComplMsk (1 << 0)
|
||||
|
||||
#define DAINT HSOTG_REG(0x818)
|
||||
#define DAINTMSK HSOTG_REG(0x81C)
|
||||
|
||||
#define DAINT_OutEP_SHIFT (16)
|
||||
#define DAINT_OutEP(x) (1 << ((x) + 16))
|
||||
#define DAINT_InEP(x) (1 << (x))
|
||||
|
||||
#define DTKNQR1 HSOTG_REG(0x820)
|
||||
#define DTKNQR2 HSOTG_REG(0x824)
|
||||
#define DTKNQR3 HSOTG_REG(0x830)
|
||||
#define DTKNQR4 HSOTG_REG(0x834)
|
||||
|
||||
#define DVBUSDIS HSOTG_REG(0x828)
|
||||
#define DVBUSPULSE HSOTG_REG(0x82C)
|
||||
|
||||
#define DIEPCTL0 HSOTG_REG(0x900)
|
||||
#define DOEPCTL0 HSOTG_REG(0xB00)
|
||||
#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20))
|
||||
#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20))
|
||||
|
||||
/* EP0 specialness:
|
||||
* bits[29..28] - reserved (no SetD0PID, SetD1PID)
|
||||
* bits[25..22] - should always be zero, this isn't a periodic endpoint
|
||||
* bits[10..0] - MPS setting differenct for EP0
|
||||
*/
|
||||
#define D0EPCTL_MPS_MASK (0x3 << 0)
|
||||
#define D0EPCTL_MPS_SHIFT (0)
|
||||
#define D0EPCTL_MPS_64 (0x0 << 0)
|
||||
#define D0EPCTL_MPS_32 (0x1 << 0)
|
||||
#define D0EPCTL_MPS_16 (0x2 << 0)
|
||||
#define D0EPCTL_MPS_8 (0x3 << 0)
|
||||
|
||||
#define DxEPCTL_EPEna (1 << 31)
|
||||
#define DxEPCTL_EPDis (1 << 30)
|
||||
#define DxEPCTL_SetD1PID (1 << 29)
|
||||
#define DxEPCTL_SetOddFr (1 << 29)
|
||||
#define DxEPCTL_SetD0PID (1 << 28)
|
||||
#define DxEPCTL_SetEvenFr (1 << 28)
|
||||
#define DxEPCTL_SNAK (1 << 27)
|
||||
#define DxEPCTL_CNAK (1 << 26)
|
||||
#define DxEPCTL_TxFNum_MASK (0xf << 22)
|
||||
#define DxEPCTL_TxFNum_SHIFT (22)
|
||||
#define DxEPCTL_TxFNum_LIMIT (0xf)
|
||||
#define DxEPCTL_TxFNum(_x) ((_x) << 22)
|
||||
|
||||
#define DxEPCTL_Stall (1 << 21)
|
||||
#define DxEPCTL_Snp (1 << 20)
|
||||
#define DxEPCTL_EPType_MASK (0x3 << 18)
|
||||
#define DxEPCTL_EPType_SHIFT (18)
|
||||
#define DxEPCTL_EPType_Control (0x0 << 18)
|
||||
#define DxEPCTL_EPType_Iso (0x1 << 18)
|
||||
#define DxEPCTL_EPType_Bulk (0x2 << 18)
|
||||
#define DxEPCTL_EPType_Intterupt (0x3 << 18)
|
||||
|
||||
#define DxEPCTL_NAKsts (1 << 17)
|
||||
#define DxEPCTL_DPID (1 << 16)
|
||||
#define DxEPCTL_EOFrNum (1 << 16)
|
||||
#define DxEPCTL_USBActEp (1 << 15)
|
||||
#define DxEPCTL_NextEp_MASK (0xf << 11)
|
||||
#define DxEPCTL_NextEp_SHIFT (11)
|
||||
#define DxEPCTL_NextEp_LIMIT (0xf)
|
||||
#define DxEPCTL_NextEp(_x) ((_x) << 11)
|
||||
|
||||
#define DxEPCTL_MPS_MASK (0x7ff << 0)
|
||||
#define DxEPCTL_MPS_SHIFT (0)
|
||||
#define DxEPCTL_MPS_LIMIT (0x7ff)
|
||||
#define DxEPCTL_MPS(_x) ((_x) << 0)
|
||||
|
||||
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
|
||||
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
|
||||
|
||||
#define DxEPINT_INEPNakEff (1 << 6)
|
||||
#define DxEPINT_Back2BackSetup (1 << 6)
|
||||
#define DxEPINT_INTknEPMis (1 << 5)
|
||||
#define DxEPINT_INTknTXFEmp (1 << 4)
|
||||
#define DxEPINT_OUTTknEPdis (1 << 4)
|
||||
#define DxEPINT_Timeout (1 << 3)
|
||||
#define DxEPINT_Setup (1 << 3)
|
||||
#define DxEPINT_AHBErr (1 << 2)
|
||||
#define DxEPINT_EPDisbld (1 << 1)
|
||||
#define DxEPINT_XferCompl (1 << 0)
|
||||
|
||||
#define DIEPTSIZ0 HSOTG_REG(0x910)
|
||||
|
||||
#define DIEPTSIZ0_PktCnt_MASK (0x3 << 19)
|
||||
#define DIEPTSIZ0_PktCnt_SHIFT (19)
|
||||
#define DIEPTSIZ0_PktCnt_LIMIT (0x3)
|
||||
#define DIEPTSIZ0_PktCnt(_x) ((_x) << 19)
|
||||
|
||||
#define DIEPTSIZ0_XferSize_MASK (0x7f << 0)
|
||||
#define DIEPTSIZ0_XferSize_SHIFT (0)
|
||||
#define DIEPTSIZ0_XferSize_LIMIT (0x7f)
|
||||
#define DIEPTSIZ0_XferSize(_x) ((_x) << 0)
|
||||
|
||||
#define DOEPTSIZ0 HSOTG_REG(0xB10)
|
||||
#define DOEPTSIZ0_SUPCnt_MASK (0x3 << 29)
|
||||
#define DOEPTSIZ0_SUPCnt_SHIFT (29)
|
||||
#define DOEPTSIZ0_SUPCnt_LIMIT (0x3)
|
||||
#define DOEPTSIZ0_SUPCnt(_x) ((_x) << 29)
|
||||
|
||||
#define DOEPTSIZ0_PktCnt (1 << 19)
|
||||
#define DOEPTSIZ0_XferSize_MASK (0x7f << 0)
|
||||
#define DOEPTSIZ0_XferSize_SHIFT (0)
|
||||
|
||||
#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20))
|
||||
#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20))
|
||||
|
||||
#define DxEPTSIZ_MC_MASK (0x3 << 29)
|
||||
#define DxEPTSIZ_MC_SHIFT (29)
|
||||
#define DxEPTSIZ_MC_LIMIT (0x3)
|
||||
#define DxEPTSIZ_MC(_x) ((_x) << 29)
|
||||
|
||||
#define DxEPTSIZ_PktCnt_MASK (0x3ff << 19)
|
||||
#define DxEPTSIZ_PktCnt_SHIFT (19)
|
||||
#define DxEPTSIZ_PktCnt_GET(_v) (((_v) >> 19) & 0x3ff)
|
||||
#define DxEPTSIZ_PktCnt_LIMIT (0x3ff)
|
||||
#define DxEPTSIZ_PktCnt(_x) ((_x) << 19)
|
||||
|
||||
#define DxEPTSIZ_XferSize_MASK (0x7ffff << 0)
|
||||
#define DxEPTSIZ_XferSize_SHIFT (0)
|
||||
#define DxEPTSIZ_XferSize_GET(_v) (((_v) >> 0) & 0x7ffff)
|
||||
#define DxEPTSIZ_XferSize_LIMIT (0x7ffff)
|
||||
#define DxEPTSIZ_XferSize(_x) ((_x) << 0)
|
||||
|
||||
#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20))
|
||||
#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20))
|
||||
#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20))
|
||||
|
||||
#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000))
|
||||
|
||||
#endif /* __REGS_USB_HSOTG_H */
|
||||
|
|
@ -110,7 +110,6 @@ struct s3c_hsudc_ep {
|
|||
struct usb_ep ep;
|
||||
char name[20];
|
||||
struct s3c_hsudc *dev;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct list_head queue;
|
||||
u8 stopped;
|
||||
u8 wedge;
|
||||
|
|
@ -761,7 +760,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
|
|||
u32 ecr = 0;
|
||||
|
||||
hsep = our_ep(_ep);
|
||||
if (!_ep || !desc || hsep->desc || _ep->name == ep0name
|
||||
if (!_ep || !desc || hsep->ep.desc || _ep->name == ep0name
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT
|
||||
|| hsep->bEndpointAddress != desc->bEndpointAddress
|
||||
|| ep_maxpacket(hsep) < usb_endpoint_maxp(desc))
|
||||
|
|
@ -783,7 +782,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
|
|||
writel(ecr, hsudc->regs + S3C_ECR);
|
||||
|
||||
hsep->stopped = hsep->wedge = 0;
|
||||
hsep->desc = desc;
|
||||
hsep->ep.desc = desc;
|
||||
hsep->ep.maxpacket = usb_endpoint_maxp(desc);
|
||||
|
||||
s3c_hsudc_set_halt(_ep, 0);
|
||||
|
|
@ -806,7 +805,7 @@ static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
|
|||
struct s3c_hsudc *hsudc = hsep->dev;
|
||||
unsigned long flags;
|
||||
|
||||
if (!_ep || !hsep->desc)
|
||||
if (!_ep || !hsep->ep.desc)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&hsudc->lock, flags);
|
||||
|
|
@ -816,7 +815,6 @@ static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
|
|||
|
||||
s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
|
||||
|
||||
hsep->desc = 0;
|
||||
hsep->ep.desc = NULL;
|
||||
hsep->stopped = 1;
|
||||
|
||||
|
|
@ -1006,7 +1004,6 @@ static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
|
|||
hsep->ep.maxpacket = epnum ? 512 : 64;
|
||||
hsep->ep.ops = &s3c_hsudc_ep_ops;
|
||||
hsep->fifo = hsudc->regs + S3C_BR(epnum);
|
||||
hsep->desc = 0;
|
||||
hsep->ep.desc = NULL;
|
||||
hsep->stopped = 0;
|
||||
hsep->wedge = 0;
|
||||
|
|
|
|||
|
|
@ -1062,7 +1062,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
|
|||
|
||||
ep = to_s3c2410_ep(_ep);
|
||||
|
||||
if (!_ep || !desc || ep->desc
|
||||
if (!_ep || !desc || ep->ep.desc
|
||||
|| _ep->name == ep0name
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT)
|
||||
return -EINVAL;
|
||||
|
|
@ -1075,7 +1075,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
|
|||
|
||||
local_irq_save (flags);
|
||||
_ep->maxpacket = max & 0x7ff;
|
||||
ep->desc = desc;
|
||||
ep->ep.desc = desc;
|
||||
ep->halted = 0;
|
||||
ep->bEndpointAddress = desc->bEndpointAddress;
|
||||
|
||||
|
|
@ -1136,7 +1136,7 @@ static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
|
|||
unsigned long flags;
|
||||
u32 int_en_reg;
|
||||
|
||||
if (!_ep || !ep->desc) {
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
dprintk(DEBUG_NORMAL, "%s not enabled\n",
|
||||
_ep ? ep->ep.name : NULL);
|
||||
return -EINVAL;
|
||||
|
|
@ -1146,7 +1146,6 @@ static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
|
|||
|
||||
dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->halted = 1;
|
||||
|
||||
|
|
@ -1195,7 +1194,7 @@ s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
|||
|
||||
dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
|
||||
|
||||
if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
|
||||
if (!ep || !_req || (!ep->ep.desc && _ep->name != ep0name))
|
||||
return;
|
||||
|
||||
WARN_ON (!list_empty (&req->queue));
|
||||
|
|
@ -1215,7 +1214,7 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
int fifo_count = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
|
||||
if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
|
||||
dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1363,7 +1362,7 @@ static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
|
|||
unsigned long flags;
|
||||
u32 idx;
|
||||
|
||||
if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
|
||||
if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
|
||||
dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1629,7 +1628,6 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev)
|
|||
list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
|
||||
|
||||
ep->dev = dev;
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->halted = 0;
|
||||
INIT_LIST_HEAD (&ep->queue);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ struct s3c2410_ep {
|
|||
unsigned long last_io; /* jiffies timestamp */
|
||||
struct usb_gadget *gadget;
|
||||
struct s3c2410_udc *dev;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct usb_ep ep;
|
||||
u8 num;
|
||||
|
||||
|
|
|
|||
|
|
@ -744,10 +744,11 @@ static struct device_type gadget_type = {
|
|||
};
|
||||
|
||||
/**
|
||||
* gether_setup - initialize one ethernet-over-usb link
|
||||
* gether_setup_name - initialize one ethernet-over-usb link
|
||||
* @g: gadget to associated with these links
|
||||
* @ethaddr: NULL, or a buffer in which the ethernet address of the
|
||||
* host side of the link is recorded
|
||||
* @netname: name for network device (for example, "usb")
|
||||
* Context: may sleep
|
||||
*
|
||||
* This sets up the single network link that may be exported by a
|
||||
|
|
@ -756,7 +757,8 @@ static struct device_type gadget_type = {
|
|||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
||||
int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
const char *netname)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
struct net_device *net;
|
||||
|
|
@ -780,7 +782,7 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
|||
|
||||
/* network device setup */
|
||||
dev->net = net;
|
||||
strcpy(net->name, "usb%d");
|
||||
snprintf(net->name, sizeof(net->name), "%s%%d", netname);
|
||||
|
||||
if (get_ether_addr(dev_addr, net->dev_addr))
|
||||
dev_warn(&g->dev,
|
||||
|
|
|
|||
|
|
@ -69,9 +69,28 @@ struct gether {
|
|||
|USB_CDC_PACKET_TYPE_PROMISCUOUS \
|
||||
|USB_CDC_PACKET_TYPE_DIRECTED)
|
||||
|
||||
/* variant of gether_setup that allows customizing network device name */
|
||||
int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
const char *netname);
|
||||
|
||||
/* netdev setup/teardown as directed by the gadget driver */
|
||||
int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
|
||||
/* gether_setup - initialize one ethernet-over-usb link
|
||||
* @g: gadget to associated with these links
|
||||
* @ethaddr: NULL, or a buffer in which the ethernet address of the
|
||||
* host side of the link is recorded
|
||||
* Context: may sleep
|
||||
*
|
||||
* This sets up the single network link that may be exported by a
|
||||
* gadget driver using this framework. The link layer addresses are
|
||||
* set up using module parameters.
|
||||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
static inline int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
return gether_setup_name(g, ethaddr, "usb");
|
||||
}
|
||||
|
||||
void gether_cleanup(void);
|
||||
|
||||
/* connect/disconnect is handled by individual functions */
|
||||
|
|
@ -99,16 +118,37 @@ int eem_bind_config(struct usb_configuration *c);
|
|||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
|
||||
int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer);
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* rndis_bind_config - add RNDIS network link to a configuration
|
||||
* @c: the configuration to support the network link
|
||||
* @ethaddr: a buffer in which the ethernet address of the host side
|
||||
* side of the link was recorded
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
static inline int rndis_bind_config(struct usb_configuration *c,
|
||||
u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
return rndis_bind_config_vendor(c, ethaddr, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
#endif /* __U_ETHER_H */
|
||||
|
|
|
|||
|
|
@ -1025,7 +1025,7 @@ static const struct tty_operations gs_tty_ops = {
|
|||
|
||||
static struct tty_driver *gs_tty_driver;
|
||||
|
||||
static int __init
|
||||
static int
|
||||
gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
|
||||
{
|
||||
struct gs_port *port;
|
||||
|
|
@ -1071,7 +1071,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
|
|||
*
|
||||
* Returns negative errno or zero.
|
||||
*/
|
||||
int __init gserial_setup(struct usb_gadget *g, unsigned count)
|
||||
int gserial_setup(struct usb_gadget *g, unsigned count)
|
||||
{
|
||||
unsigned i;
|
||||
struct usb_cdc_line_coding coding;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
static const char longname[] = "Gadget Zero";
|
||||
|
||||
unsigned buflen = 4096;
|
||||
unsigned buflen = 4096; /* only used for bulk endpoints */
|
||||
module_param(buflen, uint, 0);
|
||||
|
||||
/*
|
||||
|
|
@ -170,14 +170,17 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep)
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len)
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
||||
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (req) {
|
||||
req->length = buflen;
|
||||
req->buf = kmalloc(buflen, GFP_ATOMIC);
|
||||
if (len)
|
||||
req->length = len;
|
||||
else
|
||||
req->length = buflen;
|
||||
req->buf = kmalloc(req->length, GFP_ATOMIC);
|
||||
if (!req->buf) {
|
||||
usb_ep_free_request(ep, req);
|
||||
req = NULL;
|
||||
|
|
@ -206,10 +209,15 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
|
|||
}
|
||||
|
||||
void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out)
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out)
|
||||
{
|
||||
disable_ep(cdev, in);
|
||||
disable_ep(cdev, out);
|
||||
if (iso_in)
|
||||
disable_ep(cdev, iso_in);
|
||||
if (iso_out)
|
||||
disable_ep(cdev, iso_out);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -311,7 +319,6 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
|
|||
device_desc.bcdDevice = cpu_to_le16(0x9999);
|
||||
}
|
||||
|
||||
|
||||
INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
|
||||
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue