summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/driver.c90
-rw-r--r--drivers/usb/core/hcd.c5
-rw-r--r--drivers/usb/core/hub.c246
-rw-r--r--drivers/usb/core/notify.c6
-rw-r--r--drivers/usb/core/otg_whitelist.h58
-rw-r--r--drivers/usb/core/usb.h14
-rw-r--r--drivers/usb/gadget/Kconfig103
-rw-r--r--drivers/usb/gadget/Makefile7
-rw-r--r--drivers/usb/gadget/android.c478
-rw-r--r--drivers/usb/gadget/composite.c258
-rw-r--r--drivers/usb/gadget/epautoconf.c25
-rw-r--r--drivers/usb/gadget/f_acm.c124
-rw-r--r--drivers/usb/gadget/f_adb.c670
-rw-r--r--drivers/usb/gadget/f_ecm.c182
-rw-r--r--drivers/usb/gadget/f_mass_storage.c157
-rw-r--r--drivers/usb/gadget/f_mtp.c1273
-rw-r--r--drivers/usb/gadget/f_rndis.c219
-rw-r--r--drivers/usb/gadget/file_storage.c39
-rw-r--r--drivers/usb/gadget/multi.c4
-rw-r--r--drivers/usb/gadget/storage_common.c12
-rw-r--r--drivers/usb/gadget/u_ether.c1
-rw-r--r--drivers/usb/gadget/u_ether.h2
-rw-r--r--drivers/usb/gadget/zero.c5
-rw-r--r--drivers/usb/musb/Kconfig12
-rw-r--r--drivers/usb/musb/Makefile5
-rw-r--r--drivers/usb/musb/musb_core.c94
-rw-r--r--drivers/usb/musb/musb_core.h28
-rw-r--r--drivers/usb/musb/musb_dma.h12
-rw-r--r--drivers/usb/musb/musb_gadget.c262
-rw-r--r--drivers/usb/musb/musb_gadget.h7
-rw-r--r--drivers/usb/musb/musb_host.c125
-rw-r--r--drivers/usb/musb/musb_virthub.c5
-rw-r--r--drivers/usb/musb/ste_config.h52
-rw-r--r--drivers/usb/musb/stm_musb.c638
-rw-r--r--drivers/usb/musb/stm_musb_dma.c722
-rw-r--r--drivers/usb/musb/stm_musb_dma.h59
36 files changed, 5730 insertions, 269 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index a6bd53ace03..b66fc04caa5 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1208,6 +1208,21 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
* and flush any outstanding URBs.
*/
} else {
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ /* According to OTG supplement Rev 2.0 section 6.3
+ * Unless an A-device enables b_hnp_enable before entering
+ * suspend it shall also continue polling while the bus is
+ * suspended.
+ *
+ * We don't have to perform HNP polling, as we are going to
+ * enable b_hnp_enable before suspending.
+ */
+ if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port)
+ cancel_delayed_work(&udev->bus->hnp_polling);
+#endif
+#endif
udev->can_submit = 0;
for (i = 0; i < 16; ++i) {
usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
@@ -1270,6 +1285,51 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
return status;
}
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+void usb_hnp_polling_work(struct work_struct *work)
+{
+ int ret;
+ struct usb_bus *bus =
+ container_of(work, struct usb_bus, hnp_polling.work);
+ struct usb_device *udev =
+ bus->root_hub->children[bus->otg_port - 1];
+ u8 *status = kmalloc(sizeof(*status), GFP_KERNEL);
+
+ if (!status) {
+ schedule_delayed_work(&bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ return;
+ }
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE,
+ 0, OTG_STATUS_SELECTOR, status, sizeof(*status),
+ USB_CTRL_GET_TIMEOUT);
+ if (ret < 0) {
+ /* Peripheral may not be supporting HNP polling */
+ dev_vdbg(&udev->dev, "HNP polling failed."
+ "status %d\n", ret);
+ goto out;
+ }
+
+ /* Spec says host must suspend the bus with in 2 sec. */
+ if (*status & (1 << HOST_REQUEST_FLAG)) {
+ do_unbind_rebind(udev, DO_UNBIND);
+ udev->do_remote_wakeup = 0;
+ ret = usb_suspend_both(udev, PMSG_USER_SUSPEND);
+ if (ret)
+ dev_info(&udev->dev, "suspend failed\n");
+ } else {
+ schedule_delayed_work(&bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ }
+out:
+ kfree(status);
+}
+#endif
+#endif
+
static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
{
int w;
@@ -1659,7 +1719,7 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume);
/* Internal routine to check whether we may autosuspend a device. */
static int autosuspend_check(struct usb_device *udev)
{
- int w, i;
+ int w, i, audio_class;
struct usb_interface *intf;
unsigned long suspend_time, j;
@@ -1667,6 +1727,7 @@ static int autosuspend_check(struct usb_device *udev)
* any interface drivers require remote wakeup but it isn't available.
*/
w = 0;
+ audio_class = 0;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
@@ -1694,13 +1755,32 @@ static int autosuspend_check(struct usb_device *udev)
intf->needs_remote_wakeup)
return -EOPNOTSUPP;
}
+ /* Check for audio interface class */
+ if (intf->cur_altsetting->desc.bInterfaceClass
+ == USB_CLASS_AUDIO) {
+ dev_dbg(&udev->dev,
+ "audio interface class present\n");
+ audio_class = 1;
+ }
}
}
- if (w && !device_can_wakeup(&udev->dev)) {
- dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
- return -EOPNOTSUPP;
+
+ /* To work with usb audio device having HID interface
+ * without remote wakeup support.
+ */
+ if (audio_class) {
+ /* Audio class is enabled, disable remote wakeup */
+ dev_dbg(&udev->dev,
+ "disabling remote wakeup for audio class\n");
+ udev->do_remote_wakeup = 0;
+ } else {
+ if (w && !device_can_wakeup(&udev->dev)) {
+ dev_dbg(&udev->dev,
+ "remote wakeup needed for autosuspend\n");
+ return -EOPNOTSUPP;
+ }
+ udev->do_remote_wakeup = w;
}
- udev->do_remote_wakeup = w;
/* If everything is okay but the device hasn't been idle for long
* enough, queue a delayed autosuspend request.
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 12742f152f4..834684afbea 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -870,6 +870,11 @@ static void usb_bus_init (struct usb_bus *bus)
bus->bandwidth_isoc_reqs = 0;
INIT_LIST_HEAD (&bus->bus_list);
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ INIT_DELAYED_WORK(&bus->hnp_polling, usb_hnp_polling_work);
+#endif
+#endif
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ffc80e3241e..b0b470e4a24 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -31,6 +31,12 @@
#include "usb.h"
+#ifdef CONFIG_ARCH_U8500
+#define MAX_TOPO_LEVEL_U8500 2
+#define MAX_USB_DEVICE_U8500 8
+int usb_device_count;
+#endif
+
/* if we are in debug mode, always announce new devices */
#ifdef DEBUG
#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
@@ -128,12 +134,19 @@ MODULE_PARM_DESC(initial_descriptor_timeout,
* otherwise the new scheme is used. If that fails and "use_both_schemes"
* is set, then the driver will make another attempt, using the other scheme.
*/
+#ifndef CONFIG_USB_OTG_13
static int old_scheme_first = 0;
+#else
+static int old_scheme_first = 1;
+#endif
module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(old_scheme_first,
"start with the old device initialization scheme");
-
+#ifndef CONFIG_USB_OTG_13
static int use_both_schemes = 1;
+#else
+static int use_both_schemes;
+#endif
module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(use_both_schemes,
"try the other device initialization scheme if the "
@@ -593,7 +606,7 @@ static int hub_hub_status(struct usb_hub *hub,
"%s failed (err = %d)\n", __func__, ret);
else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
- *change = le16_to_cpu(hub->status->hub.wHubChange);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@@ -1233,11 +1246,20 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* Hubs have proper suspend/resume support */
usb_enable_autosuspend(hdev);
+#ifdef CONFIG_ARCH_U8500
+ if (hdev->level > MAX_TOPO_LEVEL_U8500) {
+ dev_err(&intf->dev,
+ "Unsupported bus topology: > %d "
+ " hub nesting\n", MAX_TOPO_LEVEL_U8500);
+ return -E2BIG;
+ }
+#else
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
"Unsupported bus topology: hub nested too deep\n");
return -E2BIG;
}
+#endif
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
@@ -1510,12 +1532,14 @@ static void choose_address(struct usb_device *udev)
* bus->devnum_next. */
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next);
- if (devnum >= 128)
+ /* Due to Hardware bugs we need to reserve a device address
+ * for flushing of endpoints. */
+ if (devnum >= 127)
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
- bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+ bus->devnum_next = ( devnum >= 126 ? 1 : devnum + 1);
}
- if (devnum < 128) {
+ if (devnum < 127) {
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
@@ -1578,6 +1602,14 @@ void usb_disconnect(struct usb_device **pdev)
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) {
+ cancel_delayed_work_sync(&udev->bus->hnp_polling);
+ udev->bus->hnp_support = 0;
+ }
+#endif
+#endif
usb_lock_device(udev);
/* Free up all the children before we remove this device */
@@ -1681,16 +1713,42 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
? "" : "non-");
-
+#ifndef CONFIG_USB_OTG_20
/* enable HNP before suspend, it's simpler */
if (port1 == bus->otg_port)
bus->b_hnp_enable = 1;
+#else
+ /* a_alt_hnp_support is obsoleted */
+ if (port1 != bus->otg_port)
+ goto fail;
+
+ bus->hnp_support = 1;
+
+ /* a_hnp_support is not required for devices
+ * compliant to revision 2.0 or subsequent
+ * versions.
+ */
+ if (desc->bLength == sizeof(*desc) &&
+ le16_to_cpu(desc->bcdOTG) >= 0x0200)
+ goto out;
+
+ /* Legacy B-device i.e compliant to spec
+ * revision 1.3 expect A-device to set
+ * a_hnp_support or b_hnp_enable before
+ * selecting configuration. b_hnp_enable
+ * is set before suspending the port.
+ */
+#endif
err = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
+#ifndef CONFIG_USB_OTG_20
bus->b_hnp_enable
? USB_DEVICE_B_HNP_ENABLE
: USB_DEVICE_A_ALT_HNP_SUPPORT,
+#else
+ USB_DEVICE_A_HNP_SUPPORT,
+#endif
0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (err < 0) {
/* OTG MESSAGE: report errors here,
@@ -1699,24 +1757,133 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
dev_info(&udev->dev,
"can't set HNP mode: %d\n",
err);
+#ifndef CONFIG_USB_OTG_20
bus->b_hnp_enable = 0;
+#else
+ bus->hnp_support = 0;
+#endif
}
}
}
}
- if (!is_targeted(udev)) {
+#ifdef CONFIG_USB_OTG_20
+out:
+#endif
+#ifdef CONFIG_USB_OTG
+ {
+ u16 idVendor = le16_to_cpu(udev->descriptor.idVendor);
+ if (idVendor == USB_OTG_TEST_MODE_VID) {
+ u16 wValue, typeReq, wIndex;
+ u32 set_feature = 0;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ u16 idProduct = le16_to_cpu(
+ udev->descriptor.idProduct);
+ /* Convert the Test Mode Request to control request*/
+ wValue = USB_PORT_FEAT_TEST;
+ typeReq = SetPortFeature;
+ wIndex = 1;
+
+ switch (idProduct) {
+ case USB_OTG_TEST_SE0_NAK_PID:
+ wIndex |= USB_OTG_TEST_SE0_NAK << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_J_PID:
+ wIndex |= USB_OTG_TEST_J << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_K_PID:
+ wIndex |= USB_OTG_TEST_K << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_PACKET_PID:
+ wIndex |= USB_OTG_TEST_PACKET << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_HS_HOST_PORT_SUSPEND_RESUME_PID:
+ /* Sleep for 15 sec. Suspend for 15 Sec, Then Resume */
+ ssleep(15);
+
+ err = usb_port_suspend(udev, PMSG_SUSPEND);
+ if (err < 0)
+ dev_err(&udev->dev, "OTG TEST_MODE:"
+ "Suspend Fail, %d\n", err);
+ ssleep(15);
+ err = usb_autoresume_device(udev);
+ if (err < 0) {
+ dev_err(&udev->dev,
+ "can't autoresume for"
+ "OTG TEST_MODE: %d\n", err);
+ }
+ break;
+ case USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_PID:
+ /* Sleep for 15 Sec. Issue the GetDeviceDescriptor */
+ ssleep(15);
+ err = usb_get_device_descriptor(udev,
+ sizeof(udev->descriptor));
+ if (err < 0) {
+ dev_err(&udev->dev, "can't re-read"
+ "device descriptor for "
+ "OTG TEST MODE: %d\n", err);
+ }
+ break;
+ case USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_DATA_PID:
+ /* Issue GetDeviceDescriptor, Sleep for 15 Sec. */
+ err = usb_get_device_descriptor(udev,
+ sizeof(udev->descriptor));
+ if (err < 0) {
+ dev_err(&udev->dev, "can't re-read"
+ "device descriptor for "
+ "OTG TEST MODE: %d\n", err);
+ }
+ ssleep(15);
+ break;
+ default:
+ /* is_targeted() will take care for wrong PID */
+ dev_dbg(&udev->dev, "OTG TEST_MODE:Wrong PID %d\n",
+ idProduct);
+ break;
+ }
+
+ if (set_feature) {
+ err = hcd->driver->hub_control(hcd,
+ typeReq, wValue, wIndex,
+ NULL, 0);
+ }
+ }
+ }
+#endif
+
+ if (!is_targeted(udev)) {
/* Maybe it can talk to us, though we can't talk to it.
- * (Includes HNP test device.)
+ *(Includes HNP test device.)
*/
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
- err = usb_port_suspend(udev, PMSG_SUSPEND);
- if (err < 0)
- dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+#ifdef CONFIG_USB_OTG_20
+ if (udev->bus->hnp_support) {
+#endif
+ err = usb_port_suspend(udev, PMSG_SUSPEND);
+ if (err < 0)
+ dev_dbg(&udev->dev, "HNP fail, %d\n"
+ , err);
}
err = -ENOTSUPP;
+#ifndef CONFIG_USB_OTG_20
goto fail;
+#else
+ } else if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ /* HNP polling is introduced in OTG supplement Rev 2.0
+ * and older devices does not support. Work is not
+ * re-armed if device returns STALL. B-Host also
+ * performs HNP polling.
+ */
+ schedule_delayed_work(&udev->bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ }
+#endif
}
fail:
#endif
@@ -2197,6 +2364,22 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
return status;
}
}
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_B_HNP_ENABLE,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (status < 0)
+ dev_dbg(&udev->dev, "can't enable HNP on port %d, "
+ "status %d\n", port1, status);
+ else
+ udev->bus->b_hnp_enable = 1;
+ }
+#endif
+#endif
/* see 7.1.7.6 */
status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
@@ -2340,6 +2523,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
int status;
u16 portchange, portstatus;
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port)
+ udev->bus->b_hnp_enable = 0;
+#endif
+#endif
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND))
@@ -2516,7 +2706,7 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
- *
+ *
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
@@ -2694,7 +2884,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
default:
goto fail;
}
-
+
type = "";
switch (udev->speed) {
case USB_SPEED_LOW: speed = "low"; break;
@@ -2724,7 +2914,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->tt = &hub->tt;
udev->ttport = port1;
}
-
+
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
@@ -2869,7 +3059,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
-
+
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
@@ -3104,6 +3294,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
status = -ENOTCONN; /* Don't retry */
goto loop;
}
+#ifdef CONFIG_ARCH_U8500
+ if (hdev->parent == NULL)
+ usb_device_count = 1;
+
+ if (usb_device_count > MAX_USB_DEVICE_U8500) {
+
+ dev_err(&udev->dev,
+ "device connected is more than %d\n",
+ MAX_USB_DEVICE_U8500);
+
+ status = -ENOTCONN; /* Don't retry */
+ goto loop;
+ }
+#endif
}
/* reset (non-USB 3.0 devices) and get descriptor */
@@ -3145,7 +3349,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
goto loop_disable;
}
}
-
+
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
@@ -3203,7 +3407,7 @@ loop:
!(hcd->driver->port_handed_over)(hcd, port1))
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
-
+
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
@@ -3329,7 +3533,7 @@ static void hub_events(void)
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
- * Works at least with mouse driver.
+ * Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
@@ -3367,7 +3571,7 @@ static void hub_events(void)
"resume on port %d, status %d\n",
i, ret);
}
-
+
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
dev_err (hub_dev,
"over-current change on port %d\n",
@@ -3646,7 +3850,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret < 0)
goto re_enumerate;
-
+
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor)) {
dev_info(&udev->dev, "device firmware changed\n");
@@ -3719,7 +3923,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
done:
return 0;
-
+
re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index 7542dce3f5a..b76a8a8961e 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -45,11 +45,17 @@ EXPORT_SYMBOL_GPL(usb_unregister_notify);
void usb_notify_add_device(struct usb_device *udev)
{
+#ifdef CONFIG_ARCH_U8500
+ usb_device_count++;
+#endif
blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
}
void usb_notify_remove_device(struct usb_device *udev)
{
+#ifdef CONFIG_ARCH_U8500
+ usb_device_count--;
+#endif
/* Protect against simultaneous usbfs open */
mutex_lock(&usbfs_mutex);
blocking_notifier_call_chain(&usb_notifier_list,
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index e8cdce571bb..9cbac092e77 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -43,12 +43,43 @@ static struct usb_device_id whitelist_table [] = {
{ USB_DEVICE(0x0525, 0xa4a0), },
#endif
+#if defined(CONFIG_USB_OTG_20)
+{ USB_DEVICE_INFO(8, 6, 80) },/* Mass Storage Devices */
+#endif
+{ USB_DEVICE(0x4CC, 0x2323), },/* U8500 Board */
+{ USB_DEVICE(0x4D9, 0x1702), },/* HP Keyboard */
+{ USB_DEVICE(0x4E8, 0x1F06), },/* USB Hard disk */
+{ USB_DEVICE(0x46D, 0x0A0C), },/*USB Headset */
+{ USB_DEVICE(0x93A, 0x2510), },/* USB mouse */
+
{ } /* Terminating entry */
};
+/* The TEST_MODE Definition for OTG as per 6.4 of OTG Rev 2.0 */
+
+#ifdef CONFIG_USB_OTG
+#define USB_OTG_TEST_MODE_VID 0x1A0A
+#define USB_OTG_TEST_SE0_NAK_PID 0x0101
+#define USB_OTG_TEST_J_PID 0x0102
+#define USB_OTG_TEST_K_PID 0x0103
+#define USB_OTG_TEST_PACKET_PID 0x0104
+#define USB_OTG_TEST_HS_HOST_PORT_SUSPEND_RESUME_PID 0x0106
+#define USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_PID 0x0107
+#define USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_DATA_PID 0x0108
+
+#define USB_OTG_TEST_SE0_NAK 0x01
+#define USB_OTG_TEST_J 0x02
+#define USB_OTG_TEST_K 0x03
+#define USB_OTG_TEST_PACKET 0x04
+#endif
+
static int is_targeted(struct usb_device *dev)
{
struct usb_device_id *id = whitelist_table;
+#ifdef CONFIG_USB_OTG_20
+ u8 number_configs = 0;
+ u8 number_interface = 0;
+#endif
/* possible in developer configs only! */
if (!dev->bus->otg_port)
@@ -97,6 +128,33 @@ static int is_targeted(struct usb_device *dev)
}
/* add other match criteria here ... */
+#ifdef CONFIG_USB_OTG_20
+/* Checking class,subclass and protocal at interface level */
+for (number_configs = dev->descriptor.bNumConfigurations;
+ number_configs > 0; number_configs--)
+ for (number_interface = dev->config->desc.bNumInterfaces;
+ number_interface > 0; number_interface--)
+ for (id = whitelist_table; id->match_flags; id++) {
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceClass))
+ continue;
+ if ((id->match_flags &
+ USB_DEVICE_ID_MATCH_DEV_SUBCLASS)
+ && (id->bDeviceSubClass !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceSubClass))
+ continue;
+ if ((id->match_flags &
+ USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
+ && (id->bDeviceProtocol !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceProtocol))
+ continue;
+ return 1;
+ }
+#endif
/* OTG MESSAGE: report errors here, customize to match your product */
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index cd882203ad3..4e9bc71440e 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,5 +1,8 @@
#include <linux/pm.h>
+#ifdef CONFIG_ARCH_U8500
+extern int usb_device_count;
+#endif
/* Functions local to drivers/usb/core/ */
extern int usb_create_sysfs_dev_files(struct usb_device *dev);
@@ -72,6 +75,17 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
#endif
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+extern void usb_hnp_polling_work(struct work_struct *work);
+#else
+static inline void usb_hnp_polling_work(struct work_struct *work)
+{
+}
+#endif
+#endif
+
+
#ifdef CONFIG_USB_SUSPEND
extern void usb_autosuspend_device(struct usb_device *udev);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 591ae9fde19..70c5fe66931 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -836,6 +836,49 @@ config USB_G_PRINTER
For more information, see Documentation/usb/gadget_printer.txt
which includes sample code for accessing the device file.
+config USB_ANDROID
+ boolean "Android Gadget"
+ depends on SWITCH
+ help
+ The Android gadget driver supports multiple USB functions.
+ The functions can be configured via a board file and may be
+ enabled and disabled dynamically.
+
+config USB_ANDROID_ACM
+ boolean "Android gadget ACM serial function"
+ depends on USB_ANDROID
+ help
+ Provides ACM serial function for android gadget driver.
+
+config USB_ANDROID_ADB
+ boolean "Android gadget adb function"
+ depends on USB_ANDROID
+ help
+ Provides adb function for android gadget driver.
+
+config USB_ANDROID_MASS_STORAGE
+ boolean "Android gadget mass storage function"
+ depends on USB_ANDROID && SWITCH
+ help
+ Provides USB mass storage function for android gadget driver.
+
+config USB_ANDROID_MTP
+ boolean "Android MTP function"
+ depends on USB_ANDROID
+ help
+ Provides Media Transfer Protocol (MTP) support for android gadget driver.
+
+config USB_ANDROID_ETHER
+ boolean "Android gadget Ethernet function"
+ depends on USB_ANDROID
+ help
+ Provides CDC Ethernat (ECM: Ethernet Control Model) or
+ RNDIS (Remote NDIS) for android gadget driver.
+
+ ECM: The drivers are not provided by Microsoft.
+
+ RNDIS: The drivers are provided by Microsoft.
+
config USB_CDC_COMPOSITE
tristate "CDC Composite Device (Ethernet and ACM)"
depends on NET
@@ -928,4 +971,64 @@ config USB_G_WEBCAM
endchoice
+choice
+ prompt "Ether Mode"
+ depends on USB_ANDROID_ETHER
+
+config USB_ANDROID_ECM
+ boolean "Android gadget ECM ether function"
+ depends on USB_ANDROID
+ help
+ Provides CDC Ethernet (ECM) ether for android gadget driver.
+
+ ECM and RNDIS should be mutually exclusive as both classes are
+ serving the same purpose.
+
+config USB_ANDROID_RNDIS
+ boolean "Android gadget RNDIS ethernet function"
+ depends on USB_ANDROID
+ help
+ Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
+ and Microsoft provides redistributable binary RNDIS drivers for
+ older versions of Windows.
+
+ If you say "y" here, the Ethernet gadget driver will try to provide
+ a second device configuration, supporting RNDIS to talk to such
+ Microsoft USB hosts.
+
+ To make MS-Windows work with this, use Documentation/usb/linux.inf
+ as the "driver info file". For versions of MS-Windows older than
+ XP, you'll need to download drivers from Microsoft's website; a URL
+ is given in comments found in that info file.
+
+config USB_ANDROID_RNDIS_WCEIS
+ boolean "Use Windows Internet Sharing Class/SubClass/Protocol"
+ depends on USB_ANDROID_RNDIS
+ help
+ Causes the driver to look like a Windows-compatible Internet
+ Sharing device, so Windows auto-detects it.
+
+ If you enable this option, the device is no longer CDC ethernet
+ compatible.
+
+endchoice
+
+config USB_OTG_13
+ bool "OTG 1.3 USB SUPPORT"
+ select PM_RUNTIME
+ select USB_OTG_WHITELIST
+ select USB_SUSPEND
+ help
+ Enabling the whitelist (Target Peripheral List-TPL) and runtime power
+ management at system level and usb level for OTG 1.3.
+
+config USB_OTG_20
+ bool "OTG 2.0 USB SUPPORT"
+ select PM_RUNTIME
+ select USB_OTG_WHITELIST
+ select USB_SUSPEND
+ help
+ Enabling the whitelist (Target Peripheral List-TPL) and runtime power
+ management at system level and usb level for OTG 2.0.
+
endif # USB_GADGET
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9bcde110feb..84a72fb0773 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -63,4 +63,11 @@ obj-$(CONFIG_USB_G_HID) += g_hid.o
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
+obj-$(CONFIG_USB_ANDROID) += android.o
+obj-$(CONFIG_USB_ANDROID_ACM) += f_acm.o u_serial.o
+obj-$(CONFIG_USB_ANDROID_ADB) += f_adb.o
+obj-$(CONFIG_USB_ANDROID_ECM) += f_ecm.o u_ether.o
+obj-$(CONFIG_USB_ANDROID_MASS_STORAGE) += f_mass_storage.o
+obj-$(CONFIG_USB_ANDROID_MTP) += f_mtp.o
+obj-$(CONFIG_USB_ANDROID_RNDIS) += f_rndis.o u_ether.o
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
new file mode 100644
index 00000000000..1b316f092bf
--- /dev/null
+++ b/drivers/usb/gadget/android.c
@@ -0,0 +1,478 @@
+/*
+ * Gadget Driver for Android
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb/android_composite.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+
+#include "gadget_chips.h"
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module. So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+#include "composite.c"
+
+MODULE_AUTHOR("Mike Lockwood");
+MODULE_DESCRIPTION("Android Composite USB Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static const char longname[] = "Gadget Android";
+
+/* Default vendor and product IDs, overridden by platform data */
+#define VENDOR_ID 0x18D1
+#define PRODUCT_ID 0x0001
+
+struct android_dev {
+ struct usb_composite_dev *cdev;
+ struct usb_configuration *config;
+ int num_products;
+ struct android_usb_product *products;
+ int num_functions;
+ char **functions;
+ u8 bound;
+
+ int product_id;
+ int version;
+};
+
+static struct android_dev *_android_dev;
+
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+#define STRING_SERIAL_IDX 2
+
+/* String Table */
+static struct usb_string strings_dev[] = {
+ /* These dummy values should be overridden by platform data */
+ [STRING_MANUFACTURER_IDX].s = "Android",
+ [STRING_PRODUCT_IDX].s = "Android",
+ [STRING_SERIAL_IDX].s = "0123456789ABCDEF",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof(device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = __constant_cpu_to_le16(VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(PRODUCT_ID),
+ .bcdDevice = __constant_cpu_to_le16(0xffff),
+ .bNumConfigurations = 1,
+};
+
+#if defined(CONFIG_USB_OTG_20) || defined(CONFIG_USB_OTG_13)
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+#endif
+
+static struct list_head _functions = LIST_HEAD_INIT(_functions);
+static int _registered_function_count = 0;
+
+static struct android_usb_function *get_function(const char *name)
+{
+ struct android_usb_function *f;
+ list_for_each_entry(f, &_functions, list) {
+ if (!strcmp(name, f->name))
+ return f;
+ }
+ return 0;
+}
+
+static void bind_functions(struct android_dev *dev)
+{
+ struct android_usb_function *f;
+ char **functions = dev->functions;
+ int i;
+
+ for (i = 0; i < dev->num_functions; i++) {
+ char *name = *functions++;
+ f = get_function(name);
+ if (f)
+ f->bind_config(dev->config);
+ else
+ printk(KERN_ERR "function %s not found in bind_functions\n", name);
+ }
+ dev->bound = 1;
+}
+
+static void rebind_functions(struct android_dev *dev)
+
+{
+ struct android_usb_function *android_f;
+ struct usb_function *f;
+
+ dev->config->next_interface_id = 0;
+ list_for_each_entry(f, &dev->config->functions, list) {
+ if (f->disabled)
+ continue;
+
+ /* rebind our functions if they are enabled */
+ android_f = get_function(f->name);
+ if (android_f && android_f->rebind_config)
+ android_f->rebind_config(dev->config, f);
+ else
+ printk(KERN_ERR "function %s not found in rebind_functions\n",
+ f->name);
+ }
+ dev->config->interface[dev->config->next_interface_id] = NULL;
+}
+
+static int android_bind_config(struct usb_configuration *c)
+{
+ struct android_dev *dev = _android_dev;
+
+ printk(KERN_DEBUG "android_bind_config\n");
+#if defined(CONFIG_USB_OTG_20) || defined(CONFIG_USB_OTG_13)
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+#endif
+ dev->config = c;
+
+ /* bind our functions if they have all registered */
+ if (_registered_function_count == dev->num_functions)
+ bind_functions(dev);
+
+ return 0;
+}
+
+static int android_setup_config(struct usb_configuration *c,
+ const struct usb_ctrlrequest *ctrl);
+
+static struct usb_configuration android_config_driver = {
+ .label = "android",
+ .bind = android_bind_config,
+ .setup = android_setup_config,
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
+};
+
+static int android_setup_config(struct usb_configuration *c,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int i;
+ int ret = -EOPNOTSUPP;
+
+ for (i = 0; i < android_config_driver.next_interface_id; i++) {
+ if (android_config_driver.interface[i]->setup) {
+ ret = android_config_driver.interface[i]->setup(
+ android_config_driver.interface[i], ctrl);
+ if (ret >= 0)
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int product_has_function(struct android_usb_product *p,
+ struct usb_function *f)
+{
+ char **functions = p->functions;
+ int count = p->num_functions;
+ const char *name = f->name;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!strcmp(name, *functions++))
+ return 1;
+ }
+ return 0;
+}
+
+static int product_matches_functions(struct android_usb_product *p)
+{
+ struct usb_function *f;
+ list_for_each_entry(f, &android_config_driver.functions, list) {
+ if (product_has_function(p, f) == !!f->disabled)
+ return 0;
+ }
+ return 1;
+}
+
+static int get_product_id(struct android_dev *dev)
+{
+ struct android_usb_product *p = dev->products;
+ int count = dev->num_products;
+ int i;
+
+ if (p) {
+ for (i = 0; i < count; i++, p++) {
+ if (product_matches_functions(p))
+ return p->product_id;
+ }
+ }
+ /* use default product ID */
+ return dev->product_id;
+}
+
+static int android_bind(struct usb_composite_dev *cdev)
+{
+ struct android_dev *dev = _android_dev;
+ struct usb_gadget *gadget = cdev->gadget;
+ int gcnum, id, product_id, ret;
+
+ printk(KERN_INFO "android_bind\n");
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_MANUFACTURER_IDX].id = id;
+ device_desc.iManufacturer = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_PRODUCT_IDX].id = id;
+ device_desc.iProduct = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_SERIAL_IDX].id = id;
+ device_desc.iSerialNumber = id;
+
+
+ /*
+ * As per USB compliance update, a device that is actively drawing
+ * more than 100mA from USB must report itself as bus-powered in
+ * the GetStatus(DEVICE) call.
+ */
+ if (android_config_driver.bMaxPower <=
+ (USB_SELF_POWER_VBUS_MAX_DRAW / 2)) {
+ android_config_driver.bmAttributes =
+ USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
+ usb_gadget_set_selfpowered(gadget);
+ } else
+ android_config_driver.bmAttributes = USB_CONFIG_ATT_ONE;
+
+ /* register our configuration */
+ ret = usb_add_config(cdev, &android_config_driver);
+ if (ret) {
+ printk(KERN_ERR "usb_add_config failed\n");
+ return ret;
+ }
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+ device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+ else {
+ /* gadget zero is so simple (for now, no altsettings) that
+ * it SHOULD NOT have problems with bulk-capable hardware.
+ * so just warn about unrcognized controllers -- don't panic.
+ *
+ * things like configuration and altsetting numbering
+ * can need hardware-specific attention though.
+ */
+ pr_warning("%s: controller '%s' not recognized\n",
+ longname, gadget->name);
+ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+ }
+
+ dev->cdev = cdev;
+ product_id = get_product_id(dev);
+ device_desc.idProduct = __constant_cpu_to_le16(product_id);
+ cdev->desc.idProduct = device_desc.idProduct;
+
+ return 0;
+}
+
+static struct usb_composite_driver android_usb_driver = {
+ .name = "android_usb",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .bind = android_bind,
+ .enable_function = android_enable_function,
+};
+
+void android_register_function(struct android_usb_function *f)
+{
+ struct android_dev *dev = _android_dev;
+
+ printk(KERN_INFO "android_register_function %s\n", f->name);
+ list_add_tail(&f->list, &_functions);
+ _registered_function_count++;
+
+ /* bind our functions if they have all registered
+ * and the main driver has bound.
+ */
+ if (dev && dev->config && _registered_function_count == dev->num_functions)
+ bind_functions(dev);
+}
+
+void android_enable_function(struct usb_function *f, int enable)
+{
+ struct android_dev *dev = _android_dev;
+ int disable = !enable;
+ int product_id;
+
+ if (!!f->disabled != disable) {
+ usb_function_set_enabled(f, !disable);
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ if (!strcmp(f->name, "rndis")) {
+ struct usb_function *func;
+
+ /* We need to specify the COMM class in the device descriptor
+ * if we are using RNDIS.
+ */
+ if (enable)
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+ dev->cdev->desc.bDeviceClass = USB_CLASS_WIRELESS_CONTROLLER;
+#else
+ dev->cdev->desc.bDeviceClass = USB_CLASS_COMM;
+#endif
+ else
+ dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE;
+
+ /* Windows does not support other interfaces when RNDIS is enabled,
+ * so we disable UMS and MTP when RNDIS is on.
+ */
+ list_for_each_entry(func, &android_config_driver.functions, list) {
+ if (!strcmp(func->name, "usb_mass_storage")
+ || !strcmp(func->name, "mtp")
+ || !strcmp(func->name, "adb")
+ || !strcmp(func->name, "acm")) {
+ usb_function_set_enabled(func, !enable);
+ }
+ }
+ }
+#endif
+
+ product_id = get_product_id(dev);
+ device_desc.idProduct = __constant_cpu_to_le16(product_id);
+ if (dev->cdev)
+ dev->cdev->desc.idProduct = device_desc.idProduct;
+ if (dev->bound)
+ rebind_functions(dev);
+ usb_composite_force_reset(dev->cdev);
+ }
+}
+
+static int android_probe(struct platform_device *pdev)
+{
+ struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+ struct android_dev *dev = _android_dev;
+
+ printk(KERN_INFO "android_probe pdata: %p\n", pdata);
+
+ if (pdata) {
+ dev->products = pdata->products;
+ dev->num_products = pdata->num_products;
+ dev->functions = pdata->functions;
+ dev->num_functions = pdata->num_functions;
+ dev->bound = 0;
+ if (pdata->vendor_id)
+ device_desc.idVendor =
+ __constant_cpu_to_le16(pdata->vendor_id);
+ if (pdata->product_id) {
+ dev->product_id = pdata->product_id;
+ device_desc.idProduct =
+ __constant_cpu_to_le16(pdata->product_id);
+ }
+ if (pdata->version)
+ dev->version = pdata->version;
+
+ if (pdata->product_name)
+ strings_dev[STRING_PRODUCT_IDX].s = pdata->product_name;
+ if (pdata->manufacturer_name)
+ strings_dev[STRING_MANUFACTURER_IDX].s =
+ pdata->manufacturer_name;
+ if (pdata->serial_number)
+ strings_dev[STRING_SERIAL_IDX].s = pdata->serial_number;
+ }
+
+ return usb_composite_register(&android_usb_driver);
+}
+
+static struct platform_driver android_platform_driver = {
+ .driver = { .name = "android_usb", },
+ .probe = android_probe,
+};
+
+static int __init init(void)
+{
+ struct android_dev *dev;
+
+ printk(KERN_INFO "android init\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* set default values, which should be overridden by platform data */
+ dev->product_id = PRODUCT_ID;
+ _android_dev = dev;
+
+ return platform_driver_register(&android_platform_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ usb_composite_unregister(&android_usb_driver);
+ platform_driver_unregister(&android_platform_driver);
+ kfree(_android_dev);
+ _android_dev = NULL;
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 391d169f8d0..5b20f6ba268 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -24,9 +24,10 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
-
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
#include <linux/usb/composite.h>
-
+#include <linux/switch.h>
/*
* The code in this file is utility code, used to build a gadget driver
@@ -71,6 +72,56 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
/*-------------------------------------------------------------------------*/
+static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_function *f = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", !f->disabled);
+}
+
+static ssize_t enable_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct usb_function *f = dev_get_drvdata(dev);
+ struct usb_composite_driver *driver = f->config->cdev->driver;
+ int value;
+
+ sscanf(buf, "%d", &value);
+ if (driver->enable_function)
+ driver->enable_function(f, value);
+ else
+ usb_function_set_enabled(f, value);
+
+ return size;
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+
+void usb_function_set_enabled(struct usb_function *f, int enabled)
+{
+ f->disabled = !enabled;
+ kobject_uevent(&f->dev->kobj, KOBJ_CHANGE);
+}
+
+
+void usb_composite_force_reset(struct usb_composite_dev *cdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ /* force reenumeration */
+ if (cdev && cdev->gadget && cdev->gadget->speed != USB_SPEED_UNKNOWN) {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ usb_gadget_disconnect(cdev->gadget);
+ msleep(10);
+ usb_gadget_connect(cdev->gadget);
+ } else {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ }
+}
+
/**
* usb_add_function() - add a function to a configuration
* @config: the configuration
@@ -88,15 +139,30 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
int usb_add_function(struct usb_configuration *config,
struct usb_function *function)
{
+ struct usb_composite_dev *cdev = config->cdev;
int value = -EINVAL;
+ int index;
- DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
+ DBG(cdev, "adding '%s'/%p to config '%s'/%p\n",
function->name, function,
config->label, config);
if (!function->set_alt || !function->disable)
goto done;
+ index = atomic_inc_return(&cdev->driver->function_count);
+ function->dev = device_create(cdev->driver->class, NULL,
+ MKDEV(0, index), NULL, function->name);
+ if (IS_ERR(function->dev))
+ return PTR_ERR(function->dev);
+
+ value = device_create_file(function->dev, &dev_attr_enable);
+ if (value < 0) {
+ device_destroy(cdev->driver->class, MKDEV(0, index));
+ return value;
+ }
+ dev_set_drvdata(function->dev, function);
+
function->config = config;
list_add_tail(&function->list, &config->functions);
@@ -122,7 +188,7 @@ int usb_add_function(struct usb_configuration *config,
done:
if (value)
- DBG(config->cdev, "adding '%s'/%p --> %d\n",
+ DBG(cdev, "adding '%s'/%p --> %d\n",
function->name, function, value);
return value;
}
@@ -232,17 +298,19 @@ static int config_buf(struct usb_configuration *config,
enum usb_device_speed speed, void *buf, u8 type)
{
struct usb_config_descriptor *c = buf;
+ struct usb_interface_descriptor *intf;
void *next = buf + USB_DT_CONFIG_SIZE;
int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
struct usb_function *f;
int status;
+ int interfaceCount = 0;
+ u8 *dest;
/* write the config descriptor */
c = buf;
c->bLength = USB_DT_CONFIG_SIZE;
c->bDescriptorType = type;
- /* wTotalLength is written later */
- c->bNumInterfaces = config->next_interface_id;
+ /* wTotalLength and bNumInterfaces are written later */
c->bConfigurationValue = config->bConfigurationValue;
c->iConfiguration = config->iConfiguration;
c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
@@ -261,23 +329,40 @@ static int config_buf(struct usb_configuration *config,
/* add each function's descriptors */
list_for_each_entry(f, &config->functions, list) {
struct usb_descriptor_header **descriptors;
+ struct usb_descriptor_header *descriptor;
if (speed == USB_SPEED_HIGH)
descriptors = f->hs_descriptors;
else
descriptors = f->descriptors;
- if (!descriptors)
+ if (f->disabled || !descriptors || descriptors[0] == NULL)
continue;
status = usb_descriptor_fillbuf(next, len,
(const struct usb_descriptor_header **) descriptors);
if (status < 0)
return status;
+
+ /* set interface numbers dynamically */
+ dest = next;
+ while ((descriptor = *descriptors++) != NULL) {
+ intf = (struct usb_interface_descriptor *)dest;
+ if (intf->bDescriptorType == USB_DT_INTERFACE) {
+ /* don't increment bInterfaceNumber for alternate settings */
+ if (intf->bAlternateSetting == 0)
+ intf->bInterfaceNumber = interfaceCount++;
+ else
+ intf->bInterfaceNumber = interfaceCount - 1;
+ }
+ dest += intf->bLength;
+ }
+
len -= status;
next += status;
}
len = next - buf;
c->wTotalLength = cpu_to_le16(len);
+ c->bNumInterfaces = interfaceCount;
return len;
}
@@ -424,6 +509,8 @@ static int set_config(struct usb_composite_dev *cdev,
if (!f)
break;
+ if (f->disabled)
+ continue;
/*
* Record which endpoints are used by the function. This is used
@@ -463,6 +550,8 @@ static int set_config(struct usb_composite_dev *cdev,
power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
done:
usb_gadget_vbus_draw(gadget, power);
+
+ schedule_work(&cdev->switch_work);
return result;
}
@@ -717,6 +806,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
struct usb_function *f = NULL;
u8 endp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (!cdev->connected) {
+ cdev->connected = 1;
+ schedule_work(&cdev->switch_work);
+ }
+ spin_unlock_irqrestore(&cdev->lock, flags);
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
* when we delegate to it.
@@ -759,6 +856,21 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_DT_STRING:
value = get_string(cdev, req->buf,
w_index, w_value & 0xff);
+
+ /* Allow functions to handle USB_DT_STRING.
+ * This is required for MTP.
+ */
+ if (value < 0) {
+ struct usb_configuration *cfg;
+ list_for_each_entry(cfg, &cdev->configs, list) {
+ if (cfg && cfg->setup) {
+ value = cfg->setup(cfg, ctrl);
+ if (value >= 0)
+ break;
+ }
+ }
+ }
+
if (value >= 0)
value = min(w_length, (u16) value);
break;
@@ -784,11 +896,11 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
- if (cdev->config)
+ if (cdev->config) {
*(u8 *)req->buf = cdev->config->bConfigurationValue;
- else
+ value = min(w_length, (u16) 1);
+ } else
*(u8 *)req->buf = 0;
- value = min(w_length, (u16) 1);
break;
/* function drivers must handle get/set altsetting; if there's
@@ -838,6 +950,8 @@ unknown:
*/
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
f = cdev->config->interface[intf];
break;
@@ -862,6 +976,25 @@ unknown:
value = c->setup(c, ctrl);
}
+ /* If the vendor request is not processed (value < 0),
+ * call all device registered configure setup callbacks
+ * to process it.
+ * This is used to handle the following cases:
+ * - vendor request is for the device and arrives before
+ * setconfiguration.
+ * - Some devices are required to handle vendor request before
+ * setconfiguration such as MTP, USBNET.
+ */
+
+ if (value < 0) {
+ struct usb_configuration *cfg;
+
+ list_for_each_entry(cfg, &cdev->configs, list) {
+ if (cfg && cfg->setup)
+ value = cfg->setup(cfg, ctrl);
+ }
+ }
+
goto done;
}
@@ -887,12 +1020,19 @@ static void composite_disconnect(struct usb_gadget *gadget)
struct usb_composite_dev *cdev = get_gadget_data(gadget);
unsigned long flags;
+#ifdef CONFIG_USB_OTG_20
+ gadget->host_request = 0;
+#endif
+
/* REVISIT: should we have config and device level
* disconnect callbacks?
*/
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config)
reset_config(cdev);
+
+ cdev->connected = 0;
+ schedule_work(&cdev->switch_work);
spin_unlock_irqrestore(&cdev->lock, flags);
}
@@ -910,6 +1050,25 @@ static ssize_t composite_show_suspended(struct device *dev,
static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL);
+#ifdef CONFIG_USB_OTG_20
+static ssize_t composite_set_host_request(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ int value;
+
+ if (sscanf(buf, "%d", &value) != 1)
+ return -EINVAL;
+
+ gadget->host_request = !!value;
+ return count;
+
+}
+
+static DEVICE_ATTR(host_request, S_IWUSR, NULL, composite_set_host_request);
+#endif
+
static void
composite_unbind(struct usb_gadget *gadget)
{
@@ -954,6 +1113,13 @@ composite_unbind(struct usb_gadget *gadget)
kfree(cdev->req->buf);
usb_ep_free_request(gadget->ep0, cdev->req);
}
+
+ switch_dev_unregister(&cdev->sw_connected);
+ switch_dev_unregister(&cdev->sw_config);
+#ifdef CONFIG_USB_OTG_20
+ device_remove_file(&gadget->dev, &dev_attr_host_request);
+#endif
+
kfree(cdev);
set_gadget_data(gadget, NULL);
device_remove_file(&gadget->dev, &dev_attr_suspended);
@@ -982,6 +1148,30 @@ string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
}
}
+static void
+composite_switch_work(struct work_struct *data)
+{
+ struct usb_composite_dev *cdev =
+ container_of(data, struct usb_composite_dev, switch_work);
+ struct usb_configuration *config = cdev->config;
+ int connected;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (cdev->connected != cdev->sw_connected.state) {
+ connected = cdev->connected;
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ switch_set_state(&cdev->sw_connected, connected);
+ } else {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+ if (config)
+ switch_set_state(&cdev->sw_config, config->bConfigurationValue);
+ else
+ switch_set_state(&cdev->sw_config, 0);
+}
+
static int composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
@@ -1009,7 +1199,13 @@ static int composite_bind(struct usb_gadget *gadget)
cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
- usb_gadget_set_selfpowered(gadget);
+ /*
+ * As per USB compliance update, a device that is actively drawing
+ * more than 100mA from USB must report itself as bus-powered in
+ * the GetStatus(DEVICE) call.
+ */
+ if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
+ usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero via kzalloc.
* we force endpoints to start unassigned; few controller
@@ -1033,6 +1229,24 @@ static int composite_bind(struct usb_gadget *gadget)
if (status < 0)
goto fail;
+
+ cdev->sw_connected.name = "usb_connected";
+ status = switch_dev_register(&cdev->sw_connected);
+ if (status < 0)
+ goto fail;
+ cdev->sw_config.name = "usb_configuration";
+ status = switch_dev_register(&cdev->sw_config);
+ if (status < 0)
+ goto fail;
+ INIT_WORK(&cdev->switch_work, composite_switch_work);
+
+#ifdef CONFIG_USB_OTG_20
+ status = device_create_file(&gadget->dev, &dev_attr_host_request);
+ if (status)
+ DBG(cdev, "unable to create host_request sysfs file\n");
+#endif
+
+
cdev->desc = *composite->dev;
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
@@ -1107,6 +1321,23 @@ composite_resume(struct usb_gadget *gadget)
cdev->suspended = 0;
}
+static int
+composite_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct usb_function *f = dev_get_drvdata(dev);
+
+ if (!f) {
+ /* this happens when the device is first created */
+ return 0;
+ }
+
+ if (add_uevent_var(env, "FUNCTION=%s", f->name))
+ return -ENOMEM;
+ if (add_uevent_var(env, "ENABLED=%d", !f->disabled))
+ return -ENOMEM;
+ return 0;
+}
+
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver composite_driver = {
@@ -1152,6 +1383,11 @@ int usb_composite_register(struct usb_composite_driver *driver)
composite_driver.driver.name = driver->name;
composite = driver;
+ driver->class = class_create(THIS_MODULE, "usb_composite");
+ if (IS_ERR(driver->class))
+ return PTR_ERR(driver->class);
+ driver->class->dev_uevent = composite_uevent;
+
return usb_gadget_register_driver(&composite_driver);
}
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 8a832488ccd..dc2bd3af7d8 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -2,6 +2,7 @@
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
+ * Copyright (C) 2009 ST Ericsson
*
* 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
@@ -79,6 +80,12 @@ ep_matches (
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
+#if defined(CONFIG_ARCH_U8500)
+ /* 512 size endpoints are scarce, so leave them for bulk type */
+ if (ep->maxpacket == 512 && USB_ENDPOINT_XFER_INT == type)
+ return 0;
+#endif
+
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
@@ -287,10 +294,22 @@ struct usb_ep *usb_ep_autoconfig (
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
- if (ep_matches (gadget, ep, desc))
- return ep;
- }
+#if (defined(CONFIG_ARCH_U8500) && !defined(CONFIG_MUSB_PIO_ONLY))
+ if (!strcmp(((gadget->dev).driver)->name, "g_file_storage")) {
+ if ((strcmp(ep->name, "ep1in") == 0) ||
+ (strcmp(ep->name, "ep1out") == 0)) {
+ if (ep_matches (gadget, ep, desc))
+ return ep;
+ }
+ } else {
+#endif
+ if (ep_matches (gadget, ep, desc))
+ return ep;
+#if (defined(CONFIG_ARCH_U8500) && !defined(CONFIG_MUSB_PIO_ONLY))
+ }
+#endif
+ }
/* Fail */
return NULL;
}
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index d47a123f15a..7cc8228bf49 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/usb/android_composite.h>
#include "u_serial.h"
#include "gadget_chips.h"
@@ -111,7 +112,7 @@ acm_iad_descriptor = {
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .iFunction = DYNAMIC */
};
@@ -405,10 +406,10 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(acm->notify);
} else {
VDBG(cdev, "init acm ctrl interface %d\n", intf);
- acm->notify_desc = ep_choose(cdev->gadget,
- acm->hs.notify,
- acm->fs.notify);
}
+ acm->notify_desc = ep_choose(cdev->gadget,
+ acm->hs.notify,
+ acm->fs.notify);
usb_ep_enable(acm->notify, acm->notify_desc);
acm->notify->driver_data = acm;
@@ -418,11 +419,11 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
gserial_disconnect(&acm->port);
} else {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
- acm->port.in_desc = ep_choose(cdev->gadget,
- acm->hs.in, acm->fs.in);
- acm->port.out_desc = ep_choose(cdev->gadget,
- acm->hs.out, acm->fs.out);
}
+ acm->port.in_desc = ep_choose(cdev->gadget,
+ acm->hs.in, acm->fs.in);
+ acm->port.out_desc = ep_choose(cdev->gadget,
+ acm->hs.out, acm->fs.out);
gserial_connect(&acm->port, acm->port_num);
} else
@@ -782,3 +783,110 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num)
kfree(acm);
return status;
}
+
+#ifdef CONFIG_USB_ANDROID_ACM
+
+int acm_function_bind_config(struct usb_configuration *c)
+{
+ int ret = acm_bind_config(c, 0);
+ if (ret == 0)
+ gserial_setup(c->cdev->gadget, 1);
+ return ret;
+}
+
+static int acm_function_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_acm *acm = func_to_acm(f);
+ int status1, status2 = 0;
+ struct usb_descriptor_header **desc = f->descriptors;
+
+ /* re-allocate instance-specific interface IDs, and patch descriptors */
+ status1 = usb_interface_id(c, f);
+ if (status1 < 0)
+ goto fail;
+ acm->ctrl_id = status1;
+
+ acm_iad_descriptor.bFirstInterface = status1;
+ acm_control_interface_desc.bInterfaceNumber = status1;
+ acm_union_desc .bMasterInterface0 = status1;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bMasterInterface0 = status1;
+
+ status2 = usb_interface_id(c, f);
+ if (status2 < 0)
+ goto fail;
+ acm->data_id = status2;
+
+ acm_data_interface_desc.bInterfaceNumber = status2;
+ acm_union_desc.bSlaveInterface0 = status2;
+ acm_call_mgmt_descriptor.bDataInterface = status2;
+
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bSlaveInterface0 = status2;
+ ((struct usb_cdc_call_mgmt_descriptor *)desc[3])
+ ->bDataInterface = status2;
+
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ desc = f->hs_descriptors;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bMasterInterface0 = status1;
+
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bSlaveInterface0 = status2;
+ ((struct usb_cdc_call_mgmt_descriptor *)desc[3])
+ ->bDataInterface = status2;
+ }
+
+ return 0;
+
+fail:
+ if (acm->notify_req)
+ gs_free_req(acm->notify, acm->notify_req);
+
+ /* we might as well release our claims on endpoints */
+ if (acm->notify)
+ acm->notify->driver_data = NULL;
+ if (acm->port.out)
+ acm->port.out->driver_data = NULL;
+ if (acm->port.in)
+ acm->port.in->driver_data = NULL;
+
+ ERROR(cdev, "%s/%p: can't re-bind, err %d %d\n",
+ f->name, f, status1, status2);
+
+ if (status2 < 0)
+ status1 = status2;
+ return status1;
+}
+
+static struct android_usb_function acm_function = {
+ .name = "acm",
+ .bind_config = acm_function_bind_config,
+ .rebind_config = acm_function_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_acm init\n");
+ android_register_function(&acm_function);
+ return 0;
+}
+module_init(init);
+
+#endif /* CONFIG_USB_ANDROID_ACM */
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
new file mode 100644
index 00000000000..1319b158d72
--- /dev/null
+++ b/drivers/usb/gadget/f_adb.c
@@ -0,0 +1,670 @@
+/*
+ * Gadget Driver for Android ADB
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/usb/android_composite.h>
+
+#define BULK_BUFFER_SIZE 4096
+
+/* number of tx requests to allocate */
+#define TX_REQ_MAX 4
+
+static const char shortname[] = "android_adb";
+
+struct adb_dev {
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ spinlock_t lock;
+
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+
+ int online;
+ int error;
+
+ atomic_t read_excl;
+ atomic_t write_excl;
+ atomic_t open_excl;
+
+ struct list_head tx_idle;
+
+ wait_queue_head_t read_wq;
+ wait_queue_head_t write_wq;
+ struct usb_request *rx_req;
+ int rx_done;
+};
+
+static struct usb_interface_descriptor adb_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xFF,
+ .bInterfaceSubClass = 0x42,
+ .bInterfaceProtocol = 1,
+};
+
+static struct usb_endpoint_descriptor adb_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor adb_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor adb_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor adb_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_adb_descs[] = {
+ (struct usb_descriptor_header *) &adb_interface_desc,
+ (struct usb_descriptor_header *) &adb_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &adb_fullspeed_out_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_adb_descs[] = {
+ (struct usb_descriptor_header *) &adb_interface_desc,
+ (struct usb_descriptor_header *) &adb_highspeed_in_desc,
+ (struct usb_descriptor_header *) &adb_highspeed_out_desc,
+ NULL,
+};
+
+
+/* temporary variable used between adb_open() and adb_gadget_bind() */
+static struct adb_dev *_adb_dev;
+
+static atomic_t adb_enable_excl;
+
+static inline struct adb_dev *func_to_dev(struct usb_function *f)
+{
+ return container_of(f, struct adb_dev, function);
+}
+
+
+static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ /* now allocate buffers for the requests */
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void adb_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static inline int _lock(atomic_t *excl)
+{
+ if (atomic_inc_return(excl) == 1) {
+ return 0;
+ } else {
+ atomic_dec(excl);
+ return -1;
+ }
+}
+
+static inline void _unlock(atomic_t *excl)
+{
+ atomic_dec(excl);
+}
+
+/* add a request to the tail of a list */
+void req_put(struct adb_dev *dev, struct list_head *head,
+ struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_add_tail(&req->list, head);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* remove a request from the head of a list */
+struct usb_request *req_get(struct adb_dev *dev, struct list_head *head)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (list_empty(head)) {
+ req = 0;
+ } else {
+ req = list_first_entry(head, struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return req;
+}
+
+static void adb_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+ struct adb_dev *dev = _adb_dev;
+
+ if (req->status != 0)
+ dev->error = 1;
+
+ req_put(dev, &dev->tx_idle, req);
+
+ wake_up(&dev->write_wq);
+}
+
+static void adb_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+ struct adb_dev *dev = _adb_dev;
+
+ dev->rx_done = 1;
+ if (req->status != 0)
+ dev->error = 1;
+
+ wake_up(&dev->read_wq);
+}
+
+static int __init create_bulk_endpoints(struct adb_dev *dev,
+ struct usb_endpoint_descriptor *in_desc,
+ struct usb_endpoint_descriptor *out_desc)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int i;
+
+ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
+
+ ep = usb_ep_autoconfig(cdev->gadget, in_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ /* now allocate requests for our endpoints */
+ req = adb_request_new(dev->ep_out, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = adb_complete_out;
+ dev->rx_req = req;
+
+ for (i = 0; i < TX_REQ_MAX; i++) {
+ req = adb_request_new(dev->ep_in, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = adb_complete_in;
+ req_put(dev, &dev->tx_idle, req);
+ }
+
+ return 0;
+
+fail:
+ printk(KERN_ERR "adb_bind() could not allocate requests\n");
+ return -1;
+}
+
+static ssize_t adb_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct adb_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ int r = count, xfer;
+ int ret;
+
+ DBG(cdev, "adb_read(%d)\n", count);
+
+ if (count > BULK_BUFFER_SIZE)
+ return -EINVAL;
+
+ if (_lock(&dev->read_excl))
+ return -EBUSY;
+
+ /* we will block until we're online */
+ while (!(dev->online || dev->error)) {
+ DBG(cdev, "adb_read: waiting for online state\n");
+ ret = wait_event_interruptible(dev->read_wq,
+ (dev->online || dev->error));
+ if (ret < 0) {
+ _unlock(&dev->read_excl);
+ return ret;
+ }
+ }
+ if (dev->error) {
+ r = -EIO;
+ goto done;
+ }
+
+requeue_req:
+ /* queue a request */
+ req = dev->rx_req;
+ req->length = count;
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);
+ if (ret < 0) {
+ DBG(cdev, "adb_read: failed to queue req %p (%d)\n", req, ret);
+ r = -EIO;
+ dev->error = 1;
+ goto done;
+ } else {
+ DBG(cdev, "rx %p queue\n", req);
+ }
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+ if (ret < 0) {
+ dev->error = 1;
+ r = ret;
+ goto done;
+ }
+ if (!dev->error) {
+ /* If we got a 0-len packet, throw it back and try again. */
+ if (req->actual == 0)
+ goto requeue_req;
+
+ DBG(cdev, "rx %p %d\n", req, req->actual);
+ xfer = (req->actual < count) ? req->actual : count;
+ if (copy_to_user(buf, req->buf, xfer))
+ r = -EFAULT;
+ } else
+ r = -EIO;
+
+done:
+ _unlock(&dev->read_excl);
+ DBG(cdev, "adb_read returning %d\n", r);
+ return r;
+}
+
+static ssize_t adb_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct adb_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ int r = count, xfer;
+ int ret;
+
+ DBG(cdev, "adb_write(%d)\n", count);
+
+ if (_lock(&dev->write_excl))
+ return -EBUSY;
+
+ while (count > 0) {
+ if (dev->error) {
+ DBG(cdev, "adb_write dev->error\n");
+ r = -EIO;
+ break;
+ }
+
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ ((req = req_get(dev, &dev->tx_idle)) || dev->error));
+
+ if (ret < 0) {
+ r = ret;
+ break;
+ }
+
+ if (req != 0) {
+ if (count > BULK_BUFFER_SIZE)
+ xfer = BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ if (copy_from_user(req->buf, buf, xfer)) {
+ r = -EFAULT;
+ break;
+ }
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC);
+ if (ret < 0) {
+ DBG(cdev, "adb_write: xfer error %d\n", ret);
+ dev->error = 1;
+ r = -EIO;
+ break;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+ }
+
+ if (req)
+ req_put(dev, &dev->tx_idle, req);
+
+ _unlock(&dev->write_excl);
+ DBG(cdev, "adb_write returning %d\n", r);
+ return r;
+}
+
+static int adb_open(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "adb_open\n");
+ if (_lock(&_adb_dev->open_excl))
+ return -EBUSY;
+
+ fp->private_data = _adb_dev;
+
+ /* clear the error latch */
+ _adb_dev->error = 0;
+
+ return 0;
+}
+
+static int adb_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "adb_release\n");
+ _unlock(&_adb_dev->open_excl);
+ return 0;
+}
+
+/* file operations for ADB device /dev/android_adb */
+static struct file_operations adb_fops = {
+ .owner = THIS_MODULE,
+ .read = adb_read,
+ .write = adb_write,
+ .open = adb_open,
+ .release = adb_release,
+};
+
+static struct miscdevice adb_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = shortname,
+ .fops = &adb_fops,
+};
+
+static int adb_enable_open(struct inode *ip, struct file *fp)
+{
+ if (atomic_inc_return(&adb_enable_excl) != 1) {
+ atomic_dec(&adb_enable_excl);
+ return -EBUSY;
+ }
+
+ printk(KERN_INFO "enabling adb\n");
+ android_enable_function(&_adb_dev->function, 1);
+
+ return 0;
+}
+
+static int adb_enable_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "disabling adb\n");
+ android_enable_function(&_adb_dev->function, 0);
+ atomic_dec(&adb_enable_excl);
+ return 0;
+}
+
+static const struct file_operations adb_enable_fops = {
+ .owner = THIS_MODULE,
+ .open = adb_enable_open,
+ .release = adb_enable_release,
+};
+
+static struct miscdevice adb_enable_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "android_adb_enable",
+ .fops = &adb_enable_fops,
+};
+
+static int
+adb_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct adb_dev *dev = func_to_dev(f);
+ int id;
+ int ret;
+
+ dev->cdev = cdev;
+ DBG(cdev, "adb_function_bind dev: %p\n", dev);
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ adb_interface_desc.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ret = create_bulk_endpoints(dev, &adb_fullspeed_in_desc,
+ &adb_fullspeed_out_desc);
+ if (ret)
+ return ret;
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ adb_highspeed_in_desc.bEndpointAddress =
+ adb_fullspeed_in_desc.bEndpointAddress;
+ adb_highspeed_out_desc.bEndpointAddress =
+ adb_fullspeed_out_desc.bEndpointAddress;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, dev->ep_in->name, dev->ep_out->name);
+ return 0;
+}
+
+static void
+adb_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct adb_dev *dev = func_to_dev(f);
+ struct usb_request *req;
+
+ spin_lock_irq(&dev->lock);
+
+ adb_request_free(dev->rx_req, dev->ep_out);
+ while ((req = req_get(dev, &dev->tx_idle)))
+ adb_request_free(req, dev->ep_in);
+
+ dev->online = 0;
+ dev->error = 1;
+ spin_unlock_irq(&dev->lock);
+
+ misc_deregister(&adb_device);
+ misc_deregister(&adb_enable_device);
+ kfree(_adb_dev);
+ _adb_dev = NULL;
+}
+
+static int adb_function_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct adb_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt);
+ ret = usb_ep_enable(dev->ep_in,
+ ep_choose(cdev->gadget,
+ &adb_highspeed_in_desc,
+ &adb_fullspeed_in_desc));
+ if (ret)
+ return ret;
+ ret = usb_ep_enable(dev->ep_out,
+ ep_choose(cdev->gadget,
+ &adb_highspeed_out_desc,
+ &adb_fullspeed_out_desc));
+ if (ret) {
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+ dev->online = 1;
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ return 0;
+}
+
+static void adb_function_disable(struct usb_function *f)
+{
+ struct adb_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ DBG(cdev, "adb_function_disable\n");
+ dev->online = 0;
+ dev->error = 1;
+ usb_ep_disable(dev->ep_in);
+ usb_ep_disable(dev->ep_out);
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+
+ VDBG(cdev, "%s disabled\n", dev->function.name);
+}
+
+static int adb_bind_config(struct usb_configuration *c)
+{
+ struct adb_dev *dev;
+ int ret;
+
+ printk(KERN_INFO "adb_bind_config\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->lock);
+
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->write_wq);
+
+ atomic_set(&dev->open_excl, 0);
+ atomic_set(&dev->read_excl, 0);
+ atomic_set(&dev->write_excl, 0);
+
+ INIT_LIST_HEAD(&dev->tx_idle);
+
+ dev->cdev = c->cdev;
+ dev->function.name = "adb";
+ dev->function.descriptors = fs_adb_descs;
+ dev->function.hs_descriptors = hs_adb_descs;
+ dev->function.bind = adb_function_bind;
+ dev->function.unbind = adb_function_unbind;
+ dev->function.set_alt = adb_function_set_alt;
+ dev->function.disable = adb_function_disable;
+
+ /* start disabled */
+ dev->function.disabled = 1;
+
+ /* _adb_dev must be set before calling usb_gadget_register_driver */
+ _adb_dev = dev;
+
+ ret = misc_register(&adb_device);
+ if (ret)
+ goto err1;
+ ret = misc_register(&adb_enable_device);
+ if (ret)
+ goto err2;
+
+ ret = usb_add_function(c, &dev->function);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ misc_deregister(&adb_enable_device);
+err2:
+ misc_deregister(&adb_device);
+err1:
+ kfree(dev);
+ printk(KERN_ERR "adb gadget driver failed to initialize\n");
+ return ret;
+}
+
+static int adb_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ int id;
+
+ /* re-allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ adb_interface_desc.bInterfaceNumber = id;
+
+ return 0;
+}
+
+static struct android_usb_function adb_function = {
+ .name = "adb",
+ .bind_config = adb_bind_config,
+ .rebind_config = adb_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_adb init\n");
+ android_register_function(&adb_function);
+ return 0;
+}
+module_init(init);
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 544257a89ed..0f038a87a91 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -26,6 +26,11 @@
#include <linux/device.h>
#include <linux/etherdevice.h>
+#ifdef CONFIG_USB_ANDROID_ECM
+#include <linux/usb/android_composite.h>
+#include <linux/platform_device.h>
+#endif
+
#include "u_ether.h"
@@ -113,6 +118,19 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g)
/* interface descriptor: */
+static struct usb_interface_assoc_descriptor
+ecm_iad_descriptor = {
+ .bLength = sizeof ecm_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ /* .bFirstInterface = DYNAMIC, */
+ .bInterfaceCount = 2, /* control + data */
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ /* .iFunction = DYNAMIC */
+};
+
static struct usb_interface_descriptor ecm_control_intf = {
.bLength = sizeof ecm_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -215,6 +233,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc = {
static struct usb_descriptor_header *ecm_fs_function[] = {
/* CDC ECM control descriptors */
+ (struct usb_descriptor_header *) &ecm_iad_descriptor,
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
@@ -260,6 +279,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc = {
static struct usb_descriptor_header *ecm_hs_function[] = {
/* CDC ECM control descriptors */
+ (struct usb_descriptor_header *) &ecm_iad_descriptor,
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
@@ -280,6 +300,7 @@ static struct usb_string ecm_string_defs[] = {
[0].s = "CDC Ethernet Control Model (ECM)",
[1].s = NULL /* DYNAMIC */,
[2].s = "CDC Ethernet Data",
+ [3].s = "CDC ECM",
{ } /* end of list */
};
@@ -293,6 +314,10 @@ static struct usb_gadget_strings *ecm_strings[] = {
NULL,
};
+#ifdef CONFIG_USB_ANDROID_ECM
+static struct usb_ether_platform_data *ecm_pdata;
+#endif
+
/*-------------------------------------------------------------------------*/
static void ecm_do_notify(struct f_ecm *ecm)
@@ -466,10 +491,10 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(ecm->notify);
} else {
VDBG(cdev, "init ecm ctrl %d\n", intf);
- ecm->notify_desc = ep_choose(cdev->gadget,
- ecm->hs.notify,
- ecm->fs.notify);
}
+ ecm->notify_desc = ep_choose(cdev->gadget,
+ ecm->hs.notify,
+ ecm->fs.notify);
usb_ep_enable(ecm->notify, ecm->notify_desc);
ecm->notify->driver_data = ecm;
@@ -485,11 +510,11 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (!ecm->port.in) {
DBG(cdev, "init ecm\n");
- ecm->port.in = ep_choose(cdev->gadget,
- ecm->hs.in, ecm->fs.in);
- ecm->port.out = ep_choose(cdev->gadget,
- ecm->hs.out, ecm->fs.out);
}
+ ecm->port.in = ep_choose(cdev->gadget,
+ ecm->hs.in, ecm->fs.in);
+ ecm->port.out = ep_choose(cdev->gadget,
+ ecm->hs.out, ecm->fs.out);
/* CDC Ethernet only sends data in non-default altsettings.
* Changing altsettings resets filters, statistics, etc.
@@ -610,6 +635,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail;
ecm->ctrl_id = status;
+ ecm_iad_descriptor.bFirstInterface = status;
ecm_control_intf.bInterfaceNumber = status;
ecm_union_desc.bMasterInterface0 = status;
@@ -795,6 +821,13 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
return status;
ecm_string_defs[1].id = status;
ecm_desc.iMACAddress = status;
+
+ /* IAD label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ ecm_string_defs[3].id = status;
+ ecm_iad_descriptor.iFunction = status;
}
/* allocate and initialize one new instance */
@@ -828,3 +861,138 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
}
return status;
}
+
+#ifdef CONFIG_USB_ANDROID_ECM
+static int __init ecm_probe(struct platform_device *pdev)
+{
+ printk(KERN_INFO "ecm_probe\n");
+ ecm_pdata = pdev->dev.platform_data;
+ return 0;
+}
+
+static struct platform_driver ecm_platform_driver = {
+ .driver = { .name = "cdc_ethernet", },
+};
+
+int ecm_function_bind_config(struct usb_configuration *c)
+{
+ int ret;
+
+ if (!ecm_pdata) {
+ printk(KERN_ERR "ecm_pdata null in ecm_function_bind_config\n");
+ return -1;
+ }
+
+ printk(KERN_INFO
+ "ecm_function_bind_config MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ ecm_pdata->ethaddr[0], ecm_pdata->ethaddr[1],
+ ecm_pdata->ethaddr[2], ecm_pdata->ethaddr[3],
+ ecm_pdata->ethaddr[4], ecm_pdata->ethaddr[5]);
+
+ ret = gether_setup(c->cdev->gadget, ecm_pdata->ethaddr);
+ if (ret == 0)
+ ret = ecm_bind_config(c, ecm_pdata->ethaddr);
+ return ret;
+}
+
+static int ecm_function_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_ecm *ecm = func_to_ecm(f);
+ int status1, status2 = 0;
+ struct usb_descriptor_header **desc = f->descriptors;
+
+ /* re-allocate instance-specific interface IDs */
+ status1 = usb_interface_id(c, f);
+ if (status1 < 0)
+ goto fail;
+ ecm->ctrl_id = status1;
+
+ ecm_iad_descriptor.bFirstInterface = status1;
+ ecm_control_intf.bInterfaceNumber = status1;
+ ecm_union_desc.bMasterInterface0 = status1;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bMasterInterface0 = status1;
+
+ status2 = usb_interface_id(c, f);
+ if (status2 < 0)
+ goto fail;
+ ecm->data_id = status2;
+
+ ecm_data_nop_intf.bInterfaceNumber = status2;
+ ecm_data_intf.bInterfaceNumber = status2;
+ ecm_union_desc.bSlaveInterface0 = status2;
+
+ ((struct usb_interface_descriptor *)desc[6])
+ ->bInterfaceNumber = status2;
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bSlaveInterface0 = status2;
+
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ desc = f->hs_descriptors;
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bMasterInterface0 = status1;
+
+ ((struct usb_interface_descriptor *)desc[6])
+ ->bInterfaceNumber = status2;
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bSlaveInterface0 = status2;
+ }
+
+ return 0;
+
+fail:
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+
+ if (ecm->notify_req) {
+ kfree(ecm->notify_req->buf);
+ usb_ep_free_request(ecm->notify, ecm->notify_req);
+ }
+
+ /* we might as well release our claims on endpoints */
+ if (ecm->notify)
+ ecm->notify->driver_data = NULL;
+ if (ecm->port.out)
+ ecm->port.out_ep->driver_data = NULL;
+ if (ecm->port.in)
+ ecm->port.in_ep->driver_data = NULL;
+
+ ERROR(cdev, "%s: can't re-bind, err %d %d\n",
+ f->name, status1, status2);
+
+ if (status2 < 0)
+ status1 = status2;
+ return status1;
+}
+
+static struct android_usb_function ecm_function = {
+ .name = "cdc_ethernet",
+ .bind_config = ecm_function_bind_config,
+ .rebind_config = ecm_function_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_ecm init\n");
+ platform_driver_probe(&ecm_platform_driver, ecm_probe);
+ android_register_function(&ecm_function);
+ return 0;
+}
+module_init(init);
+#endif /* CONFIG_USB_ANDROID_ECM */
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 4ce899c9b16..1a08f15c4b8 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -295,7 +295,12 @@
#include "gadget_chips.h"
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+#include <linux/usb/android_composite.h>
+#include <linux/platform_device.h>
+#define FUNCTION_NAME "usb_mass_storage"
+#endif
/*------------------------------------------------------------------------*/
@@ -408,6 +413,10 @@ struct fsg_config {
u16 release;
char can_stall;
+
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+ struct platform_device *pdev;
+#endif
};
@@ -454,19 +463,6 @@ static int exception_in_progress(struct fsg_common *common)
return common->state > FSG_STATE_IDLE;
}
-/* Make bulk-out requests be divisible by the maxpacket size */
-static void set_bulk_out_req_length(struct fsg_common *common,
- struct fsg_buffhd *bh, unsigned int length)
-{
- unsigned int rem;
-
- bh->bulk_out_intended_length = length;
- rem = length % common->bulk_out_maxpacket;
- if (rem > 0)
- length += common->bulk_out_maxpacket - rem;
- bh->outreq->length = length;
-}
-
/*-------------------------------------------------------------------------*/
static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
@@ -564,10 +560,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
struct fsg_buffhd *bh = req->context;
dump_msg(common, "bulk-out", req->buf, req->actual);
- if (req->status || req->actual != bh->bulk_out_intended_length)
+ if (req->status || req->actual != req->length)
DBG(common, "%s --> %d, %u/%u\n", __func__,
- req->status, req->actual,
- bh->bulk_out_intended_length);
+ req->status, req->actual, req->length);
if (req->status == -ECONNRESET) /* Request was cancelled */
usb_ep_fifo_flush(ep);
@@ -609,6 +604,7 @@ static int fsg_setup(struct usb_function *f,
/* Raise an exception to stop the current operation
* and reinitialize our state. */
DBG(fsg, "bulk reset request\n");
+ fsg->common->ep0req->length = 0;
raise_exception(fsg->common, FSG_STATE_RESET);
return DELAYED_STATUS;
@@ -939,7 +935,6 @@ static int do_write(struct fsg_common *common)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
bh->outreq->length = amount;
- bh->bulk_out_intended_length = amount;
bh->outreq->short_not_ok = 1;
START_TRANSFER_OR(common, bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state)
@@ -1343,30 +1338,7 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
limit = 65535; /* Should really be FSG_BUFLEN */
}
- /* No block descriptors */
-
- /* The mode pages, in numerical order. The only page we support
- * is the Caching page. */
- if (page_code == 0x08 || all_pages) {
- valid_page = 1;
- buf[0] = 0x08; /* Page code */
- buf[1] = 10; /* Page length */
- memset(buf+2, 0, 10); /* None of the fields are changeable */
-
- if (!changeable_values) {
- buf[2] = 0x04; /* Write cache enable, */
- /* Read cache not disabled */
- /* No cache retention priorities */
- put_unaligned_be16(0xffff, &buf[4]);
- /* Don't disable prefetch */
- /* Minimum prefetch = 0 */
- put_unaligned_be16(0xffff, &buf[8]);
- /* Maximum prefetch */
- put_unaligned_be16(0xffff, &buf[10]);
- /* Maximum prefetch ceiling */
- }
- buf += 12;
- }
+ valid_page = 1;
/* Check that a valid page was requested and the mode data length
* isn't too long. */
@@ -1602,7 +1574,6 @@ static int throw_away_data(struct fsg_common *common)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
bh->outreq->length = amount;
- bh->bulk_out_intended_length = amount;
bh->outreq->short_not_ok = 1;
START_TRANSFER_OR(common, bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state)
@@ -2253,8 +2224,8 @@ static int get_next_command(struct fsg_common *common)
}
/* Queue a request to read a Bulk-only CBW */
- set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
- bh->outreq->short_not_ok = 1;
+ bh->outreq->length = USB_BULK_CB_WRAP_LEN;
+ bh->outreq->short_not_ok = 0;
START_TRANSFER_OR(common, bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state)
/* Don't know what to do if common->fsg is NULL */
@@ -2717,7 +2688,13 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
curlun->ro = lcfg->cdrom || lcfg->ro;
curlun->removable = lcfg->removable;
curlun->dev.release = fsg_lun_release;
+
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+ /* use "usb_mass_storage" platform device as parent */
+ curlun->dev.parent = &cfg->pdev->dev;
+#else
curlun->dev.parent = &gadget->dev;
+#endif
/* curlun->dev.driver = &fsg_driver.driver; XXX */
dev_set_drvdata(&curlun->dev, &common->filesem);
dev_set_name(&curlun->dev,
@@ -3001,7 +2978,11 @@ static int fsg_add(struct usb_composite_dev *cdev,
if (unlikely(!fsg))
return -ENOMEM;
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+ fsg->function.name = FUNCTION_NAME;
+#else
fsg->function.name = FSG_DRIVER_DESC;
+#endif
fsg->function.strings = fsg_strings_array;
fsg->function.bind = fsg_bind;
fsg->function.unbind = fsg_unbind;
@@ -3118,3 +3099,91 @@ fsg_common_from_params(struct fsg_common *common,
return fsg_common_init(common, cdev, &cfg);
}
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+
+static struct fsg_config fsg_cfg;
+
+static int fsg_probe(struct platform_device *pdev)
+{
+ struct usb_mass_storage_platform_data *pdata = pdev->dev.platform_data;
+ int i, nluns;
+
+ printk(KERN_INFO "fsg_probe pdev: %p, pdata: %p\n", pdev, pdata);
+ if (!pdata)
+ return -1;
+
+ nluns = pdata->nluns;
+ if (nluns > FSG_MAX_LUNS)
+ nluns = FSG_MAX_LUNS;
+ fsg_cfg.nluns = nluns;
+ for (i = 0; i < nluns; i++)
+ fsg_cfg.luns[i].removable = 1;
+
+ fsg_cfg.vendor_name = pdata->vendor;
+ fsg_cfg.product_name = pdata->product;
+ fsg_cfg.release = pdata->release;
+ fsg_cfg.can_stall = 0;
+ fsg_cfg.pdev = pdev;
+
+ return 0;
+}
+
+static struct platform_driver fsg_platform_driver = {
+ .driver = { .name = FUNCTION_NAME, },
+ .probe = fsg_probe,
+};
+
+int mass_storage_bind_config(struct usb_configuration *c)
+{
+ struct fsg_common *common = fsg_common_init(NULL, c->cdev, &fsg_cfg);
+ if (IS_ERR(common))
+ return -1;
+ return fsg_add(c->cdev, c, common);
+}
+
+static int mass_storage_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int i;
+
+ fsg->gadget = gadget;
+
+ /* New interface */
+ i = usb_interface_id(c, f);
+ if (i < 0)
+ return i;
+ fsg_intf_desc.bInterfaceNumber = i;
+ fsg->interface_number = i;
+
+#ifndef FSG_NO_OTG
+ ((struct usb_interface_assoc_descriptor *)f->descriptors[1]
+ ->bFirstInterface = i;
+#else
+ ((struct usb_interface_assoc_descriptor *)f->descriptors[0])
+ ->bFirstInterface = i;
+#endif
+
+ return 0;
+}
+
+static struct android_usb_function mass_storage_function = {
+ .name = FUNCTION_NAME,
+ .bind_config = mass_storage_bind_config,
+ .rebind_config = mass_storage_rebind_config,
+};
+
+static int __init init(void)
+{
+ int rc;
+ printk(KERN_INFO "f_mass_storage init\n");
+ rc = platform_driver_register(&fsg_platform_driver);
+ if (rc != 0)
+ return rc;
+ android_register_function(&mass_storage_function);
+ return 0;
+}module_init(init);
+
+#endif /* CONFIG_USB_ANDROID_MASS_STORAGE */
+
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
new file mode 100644
index 00000000000..2c61a261347
--- /dev/null
+++ b/drivers/usb/gadget/f_mtp.c
@@ -0,0 +1,1273 @@
+/*
+ * Gadget Function Driver for MTP
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/usb.h>
+#include <linux/usb_usual.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/android_composite.h>
+#include <linux/usb/f_mtp.h>
+
+#define BULK_BUFFER_SIZE 16384
+#define INTR_BUFFER_SIZE 28
+
+/* String IDs */
+#define INTERFACE_STRING_INDEX 0
+
+/* values for mtp_dev.state */
+#define STATE_OFFLINE 0 /* initial state, disconnected */
+#define STATE_READY 1 /* ready for userspace calls */
+#define STATE_BUSY 2 /* processing userspace calls */
+#define STATE_CANCELED 3 /* transaction canceled by host */
+#define STATE_ERROR 4 /* error from completion routine */
+
+/* number of tx and rx requests to allocate */
+#define TX_REQ_MAX 4
+#define RX_REQ_MAX 2
+
+/* IO Thread commands */
+#define ANDROID_THREAD_QUIT 1
+#define ANDROID_THREAD_SEND_FILE 2
+#define ANDROID_THREAD_RECEIVE_FILE 3
+
+/* ID for Microsoft MTP OS String */
+#define MTP_OS_STRING_ID 0xEE
+
+/* MTP class reqeusts */
+#define MTP_REQ_CANCEL 0x64
+#define MTP_REQ_GET_EXT_EVENT_DATA 0x65
+#define MTP_REQ_RESET 0x66
+#define MTP_REQ_GET_DEVICE_STATUS 0x67
+
+/* constants for device status */
+#define MTP_RESPONSE_OK 0x2001
+#define MTP_RESPONSE_DEVICE_BUSY 0x2019
+
+static const char shortname[] = "mtp_usb";
+
+struct mtp_dev {
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ spinlock_t lock;
+
+ /* appear as MTP or PTP when enumerating */
+ int interface_mode;
+
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+ struct usb_ep *ep_intr;
+
+ int state;
+
+ /* synchronize access to our device file */
+ atomic_t open_excl;
+
+ struct list_head tx_idle;
+
+ wait_queue_head_t read_wq;
+ wait_queue_head_t write_wq;
+ wait_queue_head_t intr_wq;
+ struct usb_request *rx_req[RX_REQ_MAX];
+ struct usb_request *intr_req;
+ int rx_done;
+
+ /* synchronize access to interrupt endpoint */
+ struct mutex intr_mutex;
+ /* true if interrupt endpoint is busy */
+ int intr_busy;
+
+ /* for our file IO thread */
+ struct task_struct *thread;
+ /* current command for IO thread (or zero for none) */
+ int thread_command;
+ struct file *thread_file;
+ loff_t thread_file_offset;
+ size_t thread_file_length;
+ /* used to wait for thread to complete current command */
+ struct completion thread_wait;
+ /* result from current command */
+ int thread_result;
+};
+
+static struct usb_interface_descriptor mtp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC,
+ .bInterfaceProtocol = 0,
+};
+
+static struct usb_interface_descriptor ptp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_STILL_IMAGE,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+};
+
+static struct usb_endpoint_descriptor mtp_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor mtp_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor mtp_intr_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE),
+ .bInterval = 6,
+};
+
+static struct usb_descriptor_header *fs_mtp_descs[] = {
+ (struct usb_descriptor_header *) &mtp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_mtp_descs[] = {
+ (struct usb_descriptor_header *) &mtp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *fs_ptp_descs[] = {
+ (struct usb_descriptor_header *) &ptp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_ptp_descs[] = {
+ (struct usb_descriptor_header *) &ptp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_string mtp_string_defs[] = {
+ /* Naming interface "MTP" so libmtp will recognize us */
+ [INTERFACE_STRING_INDEX].s = "MTP",
+ { }, /* end of list */
+};
+
+static struct usb_gadget_strings mtp_string_table = {
+ .language = 0x0409, /* en-US */
+ .strings = mtp_string_defs,
+};
+
+static struct usb_gadget_strings *mtp_strings[] = {
+ &mtp_string_table,
+ NULL,
+};
+
+/* Microsoft MTP OS String */
+static u8 mtp_os_string[] = {
+ 18, /* sizeof(mtp_os_string) */
+ USB_DT_STRING,
+ /* Signature field: "MSFT100" */
+ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0,
+ /* vendor code */
+ 1,
+ /* padding */
+ 0
+};
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mtp_ext_config_desc_header {
+ __le32 dwLength;
+ __u16 bcdVersion;
+ __le16 wIndex;
+ __u8 bCount;
+ __u8 reserved[7];
+};
+
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mtp_ext_config_desc_function {
+ __u8 bFirstInterfaceNumber;
+ __u8 bInterfaceCount;
+ __u8 compatibleID[8];
+ __u8 subCompatibleID[8];
+ __u8 reserved[6];
+};
+
+/* MTP Extended Configuration Descriptor */
+struct {
+ struct mtp_ext_config_desc_header header;
+ struct mtp_ext_config_desc_function function;
+} mtp_ext_config_desc = {
+ .header = {
+ .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)),
+ .bcdVersion = __constant_cpu_to_le16(0x0100),
+ .wIndex = __constant_cpu_to_le16(4),
+ .bCount = __constant_cpu_to_le16(1),
+ },
+ .function = {
+ .bFirstInterfaceNumber = 0,
+ .bInterfaceCount = 1,
+ .compatibleID = { 'M', 'T', 'P' },
+ },
+};
+
+struct mtp_device_status {
+ __le16 wLength;
+ __le16 wCode;
+};
+
+/* temporary variable used between mtp_open() and mtp_gadget_bind() */
+static struct mtp_dev *_mtp_dev;
+
+static inline struct mtp_dev *func_to_dev(struct usb_function *f)
+{
+ return container_of(f, struct mtp_dev, function);
+}
+
+static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ /* now allocate buffers for the requests */
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void mtp_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static inline int _lock(atomic_t *excl)
+{
+ if (atomic_inc_return(excl) == 1) {
+ return 0;
+ } else {
+ atomic_dec(excl);
+ return -1;
+ }
+}
+
+static inline void _unlock(atomic_t *excl)
+{
+ atomic_dec(excl);
+}
+
+/* add a request to the tail of a list */
+static void req_put(struct mtp_dev *dev, struct list_head *head,
+ struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_add_tail(&req->list, head);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* remove a request from the head of a list */
+static struct usb_request *req_get(struct mtp_dev *dev, struct list_head *head)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (list_empty(head)) {
+ req = 0;
+ } else {
+ req = list_first_entry(head, struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return req;
+}
+
+static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ req_put(dev, &dev->tx_idle, req);
+
+ wake_up(&dev->write_wq);
+}
+
+static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ dev->rx_done = 1;
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ wake_up(&dev->read_wq);
+}
+
+static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", req->status, req->actual);
+ dev->intr_busy = 0;
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ wake_up(&dev->intr_wq);
+}
+
+static int __init create_bulk_endpoints(struct mtp_dev *dev,
+ struct usb_endpoint_descriptor *in_desc,
+ struct usb_endpoint_descriptor *out_desc,
+ struct usb_endpoint_descriptor *intr_desc)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int i;
+
+ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
+
+ ep = usb_ep_autoconfig(cdev->gadget, in_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, intr_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_intr = ep;
+
+ /* now allocate requests for our endpoints */
+ for (i = 0; i < TX_REQ_MAX; i++) {
+ req = mtp_request_new(dev->ep_in, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_in;
+ req_put(dev, &dev->tx_idle, req);
+ }
+ for (i = 0; i < RX_REQ_MAX; i++) {
+ req = mtp_request_new(dev->ep_out, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_out;
+ dev->rx_req[i] = req;
+ }
+ req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_intr;
+ dev->intr_req = req;
+
+ return 0;
+
+fail:
+ printk(KERN_ERR "mtp_bind() could not allocate requests\n");
+ return -1;
+}
+
+static ssize_t mtp_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ int r = count, xfer;
+ int ret = 0;
+
+ DBG(cdev, "mtp_read(%d)\n", count);
+
+ if (count > BULK_BUFFER_SIZE)
+ return -EINVAL;
+
+ /* we will block until we're online */
+ DBG(cdev, "mtp_read: waiting for online state\n");
+ ret = wait_event_interruptible(dev->read_wq,
+ dev->state != STATE_OFFLINE);
+ if (ret < 0) {
+ r = ret;
+ goto done;
+ }
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+requeue_req:
+ /* queue a request */
+ req = dev->rx_req[0];
+ req->length = count;
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
+ if (ret < 0) {
+ r = -EIO;
+ goto done;
+ } else {
+ DBG(cdev, "rx %p queue\n", req);
+ }
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+ if (ret < 0) {
+ r = ret;
+ goto done;
+ }
+ if (dev->state == STATE_BUSY) {
+ /* If we got a 0-len packet, throw it back and try again. */
+ if (req->actual == 0)
+ goto requeue_req;
+
+ DBG(cdev, "rx %p %d\n", req, req->actual);
+ xfer = (req->actual < count) ? req->actual : count;
+ r = xfer;
+ if (copy_to_user(buf, req->buf, xfer))
+ r = -EFAULT;
+ } else
+ r = -EIO;
+
+done:
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ r = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+
+ DBG(cdev, "mtp_read returning %d\n", r);
+ return r;
+}
+
+static ssize_t mtp_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ int r = count, xfer;
+ int ret;
+
+ DBG(cdev, "mtp_write(%d)\n", count);
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ if (dev->state == STATE_OFFLINE) {
+ spin_unlock_irq(&dev->lock);
+ return -ENODEV;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+ while (count > 0) {
+ if (dev->state != STATE_BUSY) {
+ DBG(cdev, "mtp_write dev->error\n");
+ r = -EIO;
+ break;
+ }
+
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ ((req = req_get(dev, &dev->tx_idle))
+ || dev->state != STATE_BUSY));
+ if (!req) {
+ r = ret;
+ break;
+ }
+
+ if (count > BULK_BUFFER_SIZE)
+ xfer = BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ if (copy_from_user(req->buf, buf, xfer)) {
+ r = -EFAULT;
+ break;
+ }
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
+ if (ret < 0) {
+ DBG(cdev, "mtp_write: xfer error %d\n", ret);
+ r = -EIO;
+ break;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+
+ if (req)
+ req_put(dev, &dev->tx_idle, req);
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ r = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+
+ DBG(cdev, "mtp_write returning %d\n", r);
+ return r;
+}
+
+static int mtp_send_file(struct mtp_dev *dev, struct file *filp,
+ loff_t offset, size_t count)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ int r = count, xfer, ret;
+
+ DBG(cdev, "mtp_send_file(%lld %d)\n", offset, count);
+
+ while (count > 0) {
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ (req = req_get(dev, &dev->tx_idle))
+ || dev->state != STATE_BUSY);
+ if (!req) {
+ r = ret;
+ break;
+ }
+
+ if (count > BULK_BUFFER_SIZE)
+ xfer = BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ ret = vfs_read(filp, req->buf, xfer, &offset);
+ if (ret < 0) {
+ r = ret;
+ break;
+ }
+ xfer = ret;
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
+ if (ret < 0) {
+ DBG(cdev, "mtp_write: xfer error %d\n", ret);
+ dev->state = STATE_ERROR;
+ r = -EIO;
+ break;
+ }
+
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+
+ if (req)
+ req_put(dev, &dev->tx_idle, req);
+
+ DBG(cdev, "mtp_write returning %d\n", r);
+ return r;
+}
+
+static int mtp_receive_file(struct mtp_dev *dev, struct file *filp,
+ loff_t offset, size_t count)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *read_req = NULL, *write_req = NULL;
+ int r = count;
+ int ret;
+ int cur_buf = 0;
+
+ DBG(cdev, "mtp_receive_file(%d)\n", count);
+
+ while (count > 0 || write_req) {
+ if (count > 0) {
+ /* queue a request */
+ read_req = dev->rx_req[cur_buf];
+ cur_buf = (cur_buf + 1) % RX_REQ_MAX;
+
+ read_req->length = (count > BULK_BUFFER_SIZE
+ ? BULK_BUFFER_SIZE : count);
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
+ if (ret < 0) {
+ r = -EIO;
+ dev->state = STATE_ERROR;
+ break;
+ }
+ count -= ret;
+ }
+
+ if (write_req) {
+ DBG(cdev, "rx %p %d\n", write_req, write_req->actual);
+ ret = vfs_write(filp, write_req->buf, write_req->actual,
+ &offset);
+ DBG(cdev, "vfs_write %d\n", ret);
+ if (ret != write_req->actual) {
+ r = -EIO;
+ dev->state = STATE_ERROR;
+ break;
+ }
+ write_req = NULL;
+ }
+
+ if (read_req) {
+ /* wait for our last read to complete */
+ ret = wait_event_interruptible(dev->read_wq,
+ dev->rx_done || dev->state != STATE_BUSY);
+ if (ret < 0 || dev->state != STATE_BUSY) {
+ r = ret;
+ break;
+ }
+ count -= read_req->actual;
+ write_req = read_req;
+ read_req = NULL;
+ }
+ }
+
+ DBG(cdev, "mtp_read returning %d\n", r);
+ return r;
+}
+
+/* Kernel thread for handling file IO operations */
+static int mtp_thread(void *data)
+{
+ struct mtp_dev *dev = (struct mtp_dev *)data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ int flags;
+
+ DBG(cdev, "mtp_thread started\n");
+
+ while (1) {
+ /* wait for a command */
+ while (1) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (dev->thread_command != 0)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+
+ if (dev->thread_command == ANDROID_THREAD_QUIT) {
+ DBG(cdev, "ANDROID_THREAD_QUIT\n");
+ dev->thread_result = 0;
+ goto done;
+ }
+
+ if (dev->thread_command == ANDROID_THREAD_SEND_FILE)
+ flags = O_RDONLY | O_LARGEFILE;
+ else
+ flags = O_WRONLY | O_LARGEFILE | O_CREAT;
+
+ if (dev->thread_command == ANDROID_THREAD_SEND_FILE) {
+ dev->thread_result = mtp_send_file(dev,
+ dev->thread_file,
+ dev->thread_file_offset,
+ dev->thread_file_length);
+ } else {
+ dev->thread_result = mtp_receive_file(dev,
+ dev->thread_file,
+ dev->thread_file_offset,
+ dev->thread_file_length);
+ }
+
+ if (dev->thread_file) {
+ fput(dev->thread_file);
+ dev->thread_file = NULL;
+ }
+ dev->thread_command = 0;
+ complete(&dev->thread_wait);
+ }
+
+done:
+ DBG(cdev, "android_thread done\n");
+ complete_and_exit(&dev->thread_wait, 0);
+}
+
+static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
+{
+ struct usb_request *req;
+ int ret;
+ int length = event->length;
+
+ DBG(dev->cdev, "mtp_send_event(%d)\n", event->length);
+
+ if (length < 0 || length > INTR_BUFFER_SIZE)
+ return -EINVAL;
+
+ mutex_lock(&dev->intr_mutex);
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->intr_wq, !dev->intr_busy || dev->state == STATE_OFFLINE);
+ if (ret < 0)
+ goto done;
+ if (dev->state == STATE_OFFLINE) {
+ ret = -ENODEV;
+ goto done;
+ }
+ req = dev->intr_req;
+ if (copy_from_user(req->buf, (void __user *)event->data, length)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ req->length = length;
+ dev->intr_busy = 1;
+ ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL);
+ if (ret)
+ dev->intr_busy = 0;
+
+done:
+ mutex_unlock(&dev->intr_mutex);
+ return ret;
+}
+
+static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct file *filp = NULL;
+ int ret = -EINVAL;
+
+ switch (code) {
+ case MTP_SEND_FILE:
+ case MTP_RECEIVE_FILE:
+ {
+ struct mtp_file_range mfr;
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ if (dev->state == STATE_OFFLINE) {
+ spin_unlock_irq(&dev->lock);
+ return -ENODEV;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+ if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ filp = fget(mfr.fd);
+ if (!filp) {
+ ret = -EBADF;
+ goto fail;
+ }
+
+ dev->thread_file = filp;
+ dev->thread_file_offset = mfr.offset;
+ dev->thread_file_length = mfr.length;
+
+ if (code == MTP_SEND_FILE)
+ dev->thread_command = ANDROID_THREAD_SEND_FILE;
+ else
+ dev->thread_command = ANDROID_THREAD_RECEIVE_FILE;
+
+ /* wake up the thread */
+ init_completion(&dev->thread_wait);
+ wake_up_process(dev->thread);
+
+ /* wait for the thread to complete the command */
+ wait_for_completion(&dev->thread_wait);
+ ret = dev->thread_result;
+ DBG(dev->cdev, "thread returned %d\n", ret);
+ break;
+ }
+ case MTP_SET_INTERFACE_MODE:
+ if (value == MTP_INTERFACE_MODE_MTP ||
+ value == MTP_INTERFACE_MODE_PTP) {
+ dev->interface_mode = value;
+ if (value == MTP_INTERFACE_MODE_PTP) {
+ dev->function.descriptors = fs_ptp_descs;
+ dev->function.hs_descriptors = hs_ptp_descs;
+ } else {
+ dev->function.descriptors = fs_mtp_descs;
+ dev->function.hs_descriptors = hs_mtp_descs;
+ }
+ ret = 0;
+ }
+ break;
+ case MTP_SEND_EVENT:
+ {
+ struct mtp_event event;
+ /* return here so we don't change dev->state below,
+ * which would interfere with bulk transfer state.
+ */
+ if (copy_from_user(&event, (void __user *)value, sizeof(event)))
+ return -EFAULT;
+ else
+ return mtp_send_event(dev, &event);
+ }
+ }
+
+fail:
+ if (filp)
+ fput(filp);
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ ret = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ DBG(dev->cdev, "ioctl returning %d\n", ret);
+ return ret;
+}
+
+static int mtp_open(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "mtp_open\n");
+ if (_lock(&_mtp_dev->open_excl))
+ return -EBUSY;
+
+ _mtp_dev->thread = kthread_create(mtp_thread, _mtp_dev, "f_mtp");
+ if (IS_ERR(_mtp_dev->thread))
+ return -ENOMEM;
+
+ /* clear any error condition */
+ if (_mtp_dev->state != STATE_OFFLINE)
+ _mtp_dev->state = STATE_READY;
+
+ fp->private_data = _mtp_dev;
+ return 0;
+}
+
+static int mtp_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "mtp_release\n");
+
+ /* tell the thread to quit */
+ if (_mtp_dev->thread) {
+ _mtp_dev->thread_command = ANDROID_THREAD_QUIT;
+ init_completion(&_mtp_dev->thread_wait);
+ wake_up_process(_mtp_dev->thread);
+ wait_for_completion(&_mtp_dev->thread_wait);
+ }
+
+ _unlock(&_mtp_dev->open_excl);
+ return 0;
+}
+
+/* file operations for /dev/mtp_usb */
+static const struct file_operations mtp_fops = {
+ .owner = THIS_MODULE,
+ .read = mtp_read,
+ .write = mtp_write,
+ .unlocked_ioctl = mtp_ioctl,
+ .open = mtp_open,
+ .release = mtp_release,
+};
+
+static struct miscdevice mtp_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = shortname,
+ .fops = &mtp_fops,
+};
+
+static int
+mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct mtp_dev *dev = func_to_dev(f);
+ int id;
+ int ret;
+
+ dev->cdev = cdev;
+ DBG(cdev, "mtp_function_bind dev: %p\n", dev);
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ mtp_interface_desc.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ret = create_bulk_endpoints(dev, &mtp_fullspeed_in_desc,
+ &mtp_fullspeed_out_desc, &mtp_intr_desc);
+ if (ret)
+ return ret;
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ mtp_highspeed_in_desc.bEndpointAddress =
+ mtp_fullspeed_in_desc.bEndpointAddress;
+ mtp_highspeed_out_desc.bEndpointAddress =
+ mtp_fullspeed_out_desc.bEndpointAddress;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, dev->ep_in->name, dev->ep_out->name);
+ return 0;
+}
+
+static void
+mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_request *req;
+ int i;
+
+ spin_lock_irq(&dev->lock);
+ while ((req = req_get(dev, &dev->tx_idle)))
+ mtp_request_free(req, dev->ep_in);
+ for (i = 0; i < RX_REQ_MAX; i++)
+ mtp_request_free(dev->rx_req[i], dev->ep_out);
+ mtp_request_free(dev->intr_req, dev->ep_intr);
+ dev->state = STATE_OFFLINE;
+ spin_unlock_irq(&dev->lock);
+ wake_up(&dev->intr_wq);
+
+ misc_deregister(&mtp_device);
+ kfree(_mtp_dev);
+ _mtp_dev = NULL;
+}
+
+static int mtp_function_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
+
+ /* do nothing if we are disabled */
+ if (dev->function.disabled)
+ return value;
+
+ VDBG(cdev, "mtp_function_setup "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* Handle MTP OS string */
+ if (dev->interface_mode == MTP_INTERFACE_MODE_MTP
+ && ctrl->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)
+ && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR
+ && (w_value >> 8) == USB_DT_STRING
+ && (w_value & 0xFF) == MTP_OS_STRING_ID) {
+ value = (w_length < sizeof(mtp_os_string)
+ ? w_length : sizeof(mtp_os_string));
+ memcpy(cdev->req->buf, mtp_os_string, value);
+ /* return here since composite.c will send for us */
+ return value;
+ }
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
+ /* Handle MTP OS descriptor */
+ DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (dev->interface_mode == MTP_INTERFACE_MODE_MTP
+ && ctrl->bRequest == 1
+ && (ctrl->bRequestType & USB_DIR_IN)
+ && (w_index == 4 || w_index == 5)) {
+ value = (w_length < sizeof(mtp_ext_config_desc) ?
+ w_length : sizeof(mtp_ext_config_desc));
+ memcpy(cdev->req->buf, &mtp_ext_config_desc, value);
+ }
+ }
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+ DBG(cdev, "class request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0
+ && w_value == 0) {
+ DBG(cdev, "MTP_REQ_CANCEL\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state == STATE_BUSY) {
+ dev->state = STATE_CANCELED;
+ wake_up(&dev->read_wq);
+ wake_up(&dev->write_wq);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* We need to queue a request to read the remaining
+ * bytes, but we don't actually need to look at
+ * the contents.
+ */
+ value = w_length;
+ } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
+ && w_index == 0 && w_value == 0) {
+ struct mtp_device_status *status = cdev->req->buf;
+ status->wLength =
+ __constant_cpu_to_le16(sizeof(*status));
+
+ DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ /* device status is "busy" until we report
+ * the cancelation to userspace
+ */
+ if (dev->state == STATE_BUSY
+ || dev->state == STATE_CANCELED)
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY);
+ else
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_OK);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ value = sizeof(*status);
+ }
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ int rc;
+ cdev->req->zero = value < w_length;
+ cdev->req->length = value;
+ rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ if (rc < 0)
+ ERROR(cdev, "%s setup response queue error\n", __func__);
+ }
+
+ if (value == -EOPNOTSUPP)
+ VDBG(cdev,
+ "unknown class-specific control req "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ return value;
+}
+
+static int mtp_function_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt);
+ ret = usb_ep_enable(dev->ep_in,
+ ep_choose(cdev->gadget,
+ &mtp_highspeed_in_desc,
+ &mtp_fullspeed_in_desc));
+ if (ret)
+ return ret;
+ ret = usb_ep_enable(dev->ep_out,
+ ep_choose(cdev->gadget,
+ &mtp_highspeed_out_desc,
+ &mtp_fullspeed_out_desc));
+ if (ret) {
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+ ret = usb_ep_enable(dev->ep_intr, &mtp_intr_desc);
+ if (ret) {
+ usb_ep_disable(dev->ep_out);
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+ dev->state = STATE_READY;
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ return 0;
+}
+
+static void mtp_function_disable(struct usb_function *f)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ DBG(cdev, "mtp_function_disable\n");
+ dev->state = STATE_OFFLINE;
+ usb_ep_disable(dev->ep_in);
+ usb_ep_disable(dev->ep_out);
+ usb_ep_disable(dev->ep_intr);
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ wake_up(&dev->intr_wq);
+
+ VDBG(cdev, "%s disabled\n", dev->function.name);
+}
+
+static int mtp_bind_config(struct usb_configuration *c)
+{
+ struct mtp_dev *dev;
+ int ret;
+
+ printk(KERN_INFO "mtp_bind_config\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* allocate a string ID for our interface */
+ if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) {
+ ret = usb_string_id(c->cdev);
+ if (ret < 0)
+ return ret;
+ mtp_string_defs[INTERFACE_STRING_INDEX].id = ret;
+ mtp_interface_desc.iInterface = ret;
+ }
+
+ spin_lock_init(&dev->lock);
+ init_completion(&dev->thread_wait);
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->write_wq);
+ init_waitqueue_head(&dev->intr_wq);
+ atomic_set(&dev->open_excl, 0);
+ INIT_LIST_HEAD(&dev->tx_idle);
+ mutex_init(&dev->intr_mutex);
+
+ dev->cdev = c->cdev;
+ dev->function.name = "mtp";
+ dev->function.strings = mtp_strings,
+ dev->function.descriptors = fs_mtp_descs;
+ dev->function.hs_descriptors = hs_mtp_descs;
+ dev->function.bind = mtp_function_bind;
+ dev->function.unbind = mtp_function_unbind;
+ dev->function.setup = mtp_function_setup;
+ dev->function.set_alt = mtp_function_set_alt;
+ dev->function.disable = mtp_function_disable;
+
+ /* MTP mode by default */
+ dev->interface_mode = MTP_INTERFACE_MODE_MTP;
+
+ /* _mtp_dev must be set before calling usb_gadget_register_driver */
+ _mtp_dev = dev;
+
+ ret = misc_register(&mtp_device);
+ if (ret)
+ goto err1;
+
+ ret = usb_add_function(c, &dev->function);
+ if (ret)
+ goto err2;
+
+ return 0;
+
+err2:
+ misc_deregister(&mtp_device);
+err1:
+ kfree(dev);
+ printk(KERN_ERR "mtp gadget driver failed to initialize\n");
+ return ret;
+}
+
+static struct android_usb_function mtp_function = {
+ .name = "mtp",
+ .bind_config = mtp_bind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_mtp init\n");
+ android_register_function(&mtp_function);
+ return 0;
+}
+module_init(init);
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 882484a4039..6e6489b4958 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -26,8 +26,9 @@
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/etherdevice.h>
+#include <linux/usb/android_composite.h>
#include <asm/atomic.h>
@@ -129,9 +130,16 @@ static struct usb_interface_descriptor rndis_control_intf = {
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 3,
+#else
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+#endif
/* .iInterface = DYNAMIC */
};
@@ -190,9 +198,16 @@ rndis_iad_descriptor = {
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, // control + data
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bFunctionSubClass = 1,
+ .bFunctionProtocol = 3,
+#else
.bFunctionClass = USB_CLASS_COMM,
- .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = USB_CDC_ACM_PROTO_VENDOR,
+#endif
/* .iFunction = DYNAMIC */
};
@@ -304,6 +319,10 @@ static struct usb_gadget_strings *rndis_strings[] = {
NULL,
};
+#ifdef CONFIG_USB_ANDROID_RNDIS
+static struct usb_ether_platform_data *rndis_pdata;
+#endif
+
/*-------------------------------------------------------------------------*/
static struct sk_buff *rndis_add_header(struct gether *port,
@@ -487,10 +506,10 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(rndis->notify);
} else {
VDBG(cdev, "init rndis ctrl %d\n", intf);
- rndis->notify_desc = ep_choose(cdev->gadget,
- rndis->hs.notify,
- rndis->fs.notify);
}
+ rndis->notify_desc = ep_choose(cdev->gadget,
+ rndis->hs.notify,
+ rndis->fs.notify);
usb_ep_enable(rndis->notify, rndis->notify_desc);
rndis->notify->driver_data = rndis;
@@ -504,11 +523,11 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (!rndis->port.in) {
DBG(cdev, "init rndis\n");
- rndis->port.in = ep_choose(cdev->gadget,
- rndis->hs.in, rndis->fs.in);
- rndis->port.out = ep_choose(cdev->gadget,
- rndis->hs.out, rndis->fs.out);
}
+ rndis->port.in = ep_choose(cdev->gadget,
+ rndis->hs.in, rndis->fs.in);
+ rndis->port.out = ep_choose(cdev->gadget,
+ rndis->hs.out, rndis->fs.out);
/* Avoid ZLPs; they can be troublesome. */
rndis->port.is_zlp_ok = false;
@@ -590,6 +609,33 @@ static void rndis_close(struct gether *geth)
rndis_signal_disconnect(rndis->config);
}
+static rndis_release(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_rndis *rndis = func_to_rndis(f);
+ int status;
+ struct usb_ep *ep;
+
+ if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
+ usb_free_descriptors(f->hs_descriptors);
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+
+ if (rndis->notify_req) {
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+ }
+
+ /* we might as well release our claims on endpoints */
+ if (rndis->notify)
+ rndis->notify->driver_data = NULL;
+ if (rndis->port.out)
+ rndis->port.out_ep->driver_data = NULL;
+ if (rndis->port.in)
+ rndis->port.in_ep->driver_data = NULL;
+
+}
+
/*-------------------------------------------------------------------------*/
/* ethernet function driver setup/binding */
@@ -707,11 +753,12 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_set_param_medium(rndis->config, NDIS_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;
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ if (rndis_pdata) {
+ if (rndis_set_param_vendor(rndis->config, rndis_pdata->vendorID,
+ rndis_pdata->vendorDescr))
+ goto fail;
+ }
#endif
/* NOTE: all that is done without knowing or caring about
@@ -726,23 +773,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
- usb_free_descriptors(f->hs_descriptors);
- if (f->descriptors)
- usb_free_descriptors(f->descriptors);
-
- if (rndis->notify_req) {
- kfree(rndis->notify_req->buf);
- usb_ep_free_request(rndis->notify, rndis->notify_req);
- }
-
- /* we might as well release our claims on endpoints */
- if (rndis->notify)
- rndis->notify->driver_data = NULL;
- if (rndis->port.out)
- rndis->port.out_ep->driver_data = NULL;
- if (rndis->port.in)
- rndis->port.in_ep->driver_data = NULL;
+ rndis_release(c, f);
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -850,6 +881,11 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
rndis->port.func.setup = rndis_setup;
rndis->port.func.disable = rndis_disable;
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ /* start disabled */
+ rndis->port.func.disabled = 1;
+#endif
+
status = usb_add_function(c, &rndis->port.func);
if (status) {
kfree(rndis);
@@ -858,3 +894,122 @@ fail:
}
return status;
}
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+#include "rndis.c"
+
+static int rndis_probe(struct platform_device *pdev)
+{
+ rndis_pdata = pdev->dev.platform_data;
+ return 0;
+}
+
+static struct platform_driver rndis_platform_driver = {
+ .driver = { .name = "rndis", },
+ .probe = rndis_probe,
+};
+
+int rndis_function_bind_config(struct usb_configuration *c)
+{
+ int ret;
+
+ if (!rndis_pdata) {
+ printk(KERN_ERR "rndis_pdata null in rndis_function_bind_config\n");
+ return -1;
+ }
+
+ printk(KERN_INFO
+ "rndis_function_bind_config MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ rndis_pdata->ethaddr[0], rndis_pdata->ethaddr[1],
+ rndis_pdata->ethaddr[2], rndis_pdata->ethaddr[3],
+ rndis_pdata->ethaddr[4], rndis_pdata->ethaddr[5]);
+
+ ret = gether_setup(c->cdev->gadget, rndis_pdata->ethaddr);
+ if (ret == 0)
+ ret = rndis_bind_config(c, rndis_pdata->ethaddr);
+ return ret;
+}
+
+static int rndis_function_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_rndis *rndis = func_to_rndis(f);
+ int status1, status2 = 0;
+ struct usb_descriptor_header **desc = f->descriptors;
+
+ /* re-allocate instance-specific interface IDs */
+ status1 = usb_interface_id(c, f);
+
+ if (status1 < 0)
+ goto fail;
+ rndis->ctrl_id = status1;
+
+ rndis_iad_descriptor.bFirstInterface = status1;
+ rndis_control_intf.bInterfaceNumber = status1;
+ rndis_union_desc.bMasterInterface0 = status1;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bMasterInterface0 = status1;
+
+ status2 = usb_interface_id(c, f);
+ if (status2 < 0)
+ goto fail;
+ rndis->data_id = status2;
+
+ rndis_data_intf.bInterfaceNumber = status2;
+ rndis_union_desc.bSlaveInterface0 = status2;
+
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bSlaveInterface0 = status2;
+
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ desc = f->hs_descriptors;
+ ((struct usb_interface_assoc_descriptor *)
+ desc[0])->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)
+ desc[1])->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)
+ desc[3])->bMasterInterface0 = status1;
+
+ ((struct usb_interface_descriptor *)
+ desc[7])->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)
+ desc[5])->bSlaveInterface0 = status2;
+ }
+
+ return 0;
+
+fail:
+ rndis_release(c, f);
+
+ ERROR(cdev, "%s: can't re-bind, err %d %d\n", f->name,
+ status1, status2);
+ if (status2 < 0)
+ status1 = status2;
+ return status1;
+}
+
+static struct android_usb_function rndis_function = {
+ .name = "rndis",
+ .bind_config = rndis_function_bind_config,
+ .rebind_config = rndis_function_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_rndis init\n");
+ platform_driver_register(&rndis_platform_driver);
+ android_register_function(&rndis_function);
+ return 0;
+}
+module_init(init);
+
+#endif /* CONFIG_USB_ANDROID_RNDIS */
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index b49d86e3e45..8ef770214b2 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -486,19 +486,6 @@ static int exception_in_progress(struct fsg_dev *fsg)
return (fsg->state > FSG_STATE_IDLE);
}
-/* Make bulk-out requests be divisible by the maxpacket size */
-static void set_bulk_out_req_length(struct fsg_dev *fsg,
- struct fsg_buffhd *bh, unsigned int length)
-{
- unsigned int rem;
-
- bh->bulk_out_intended_length = length;
- rem = length % fsg->bulk_out_maxpacket;
- if (rem > 0)
- length += fsg->bulk_out_maxpacket - rem;
- bh->outreq->length = length;
-}
-
static struct fsg_dev *the_fsg;
static struct usb_gadget_driver fsg_driver;
@@ -719,10 +706,10 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
struct fsg_buffhd *bh = req->context;
dump_msg(fsg, "bulk-out", req->buf, req->actual);
- if (req->status || req->actual != bh->bulk_out_intended_length)
+ if (req->status || req->actual != req->length)
DBG(fsg, "%s --> %d, %u/%u\n", __func__,
req->status, req->actual,
- bh->bulk_out_intended_length);
+ req->length);
if (req->status == -ECONNRESET) // Request was cancelled
usb_ep_fifo_flush(ep);
@@ -1337,8 +1324,7 @@ static int do_write(struct fsg_dev *fsg)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
- bh->outreq->length = bh->bulk_out_intended_length =
- amount;
+ bh->outreq->length = amount;
bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
@@ -1909,6 +1895,18 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
return -EINTR;
rc = usb_ep_set_halt(fsg->bulk_in);
}
+#ifdef CONFIG_ARCH_U8500
+ /* temporary HACK: There is a problem using mass storage with
+ * the musb driver. The problem is that the status command
+ * wrapper block gets queued in hardware before the clear-stall
+ * is executed. When the clear stall gets executed the status
+ * command wrapper block gets dropped and the USB host OS
+ * starts hanging. We are not sure where the proper place to
+ * fix this bug is. Until further, add a delay here so that
+ * the USB host gets time to execute the clear-stall request.
+ */
+ msleep_interruptible(100); /* temporary HACK */
+#endif
return rc;
}
@@ -1998,8 +1996,7 @@ static int throw_away_data(struct fsg_dev *fsg)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
- bh->outreq->length = bh->bulk_out_intended_length =
- amount;
+ bh->outreq->length = amount;
bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
@@ -2676,8 +2673,8 @@ static int get_next_command(struct fsg_dev *fsg)
}
/* Queue a request to read a Bulk-only CBW */
- set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
- bh->outreq->short_not_ok = 1;
+ bh->outreq->length = USB_BULK_CB_WRAP_LEN;
+ bh->outreq->short_not_ok = 0;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index a930d7fd7e7..fe1ed688518 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -107,7 +107,11 @@ static struct usb_otg_descriptor otg_descriptor = {
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
+#ifndef USB_OTG_20
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+#else
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP | USB_OTG_ADP,
+#endif
};
static const struct usb_descriptor_header *otg_desc[] = {
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 04c462ff0ea..744291dd5ab 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -312,12 +312,6 @@ struct fsg_buffhd {
#endif
enum fsg_buffer_state state;
struct fsg_buffhd *next;
-
- /* The NetChip 2280 is faster, and handles some protocol faults
- * better, if we don't submit any short bulk-out read requests.
- * So we will record the intended request length here. */
- unsigned int bulk_out_intended_length;
-
struct usb_request *inreq;
int inreq_busy;
struct usb_request *outreq;
@@ -750,10 +744,16 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
struct rw_semaphore *filesem = dev_get_drvdata(dev);
int rc = 0;
+
+#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
+ /* disabled in android because we need to allow closing the backing file
+ * if the media was removed
+ */
if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
LDBG(curlun, "eject attempt prevented\n");
return -EBUSY; /* "Door is locked" */
}
+#endif
/* Remove a trailing newline */
if (count > 0 && buf[count-1] == '\n')
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 1da755a1c85..8bccb6211db 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -947,7 +947,6 @@ void gether_disconnect(struct gether *link)
struct eth_dev *dev = link->ioport;
struct usb_request *req;
- WARN_ON(!dev);
if (!dev)
return;
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index 3c8c0c9f9d7..99e4aa3b137 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -105,7 +105,7 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
int eem_bind_config(struct usb_configuration *c);
-#ifdef USB_ETH_RNDIS
+#if defined(USB_ETH_RNDIS) || defined(CONFIG_USB_ANDROID_RNDIS)
int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 807280d069f..a7c60c23265 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -292,7 +292,10 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
device_desc.iSerialNumber = id;
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
-
+#ifdef CONFIG_USB_OTG_20
+ if (gadget_is_otg2(cdev->gadget))
+ otg_descriptor.bcdOTG = __constant_cpu_to_le16(0x0200);
+#endif
/* Register primary, then secondary configuration. Note that
* SH3 only allows one config...
*/
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index cfd38edfcf9..8d5caddc03e 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -41,6 +41,7 @@ config USB_MUSB_SOC
default y if ARCH_OMAP4
default y if (BF54x && !BF544)
default y if (BF52x && !BF522 && !BF523)
+ default y if ARCH_U8500
comment "DaVinci 35x and 644x USB support"
depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx
@@ -57,6 +58,17 @@ comment "OMAP 44xx high speed USB support"
comment "Blackfin high speed USB Support"
depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523))
+comment "U8500 USB support"
+ depends on USB_MUSB_HDRC && ARCH_U8500
+
+config U8500_USB_HS_OTG
+ boolean "USB HS-OTG support"
+ depends on USB_MUSB_HDRC && ARCH_U8500
+ default y
+ help
+ Say Y here if you want to enable support for the U8500 USB
+ high speed on-the-go USB interface with external ULPI transceiver.
+
config USB_TUSB6010
boolean "TUSB 6010 support"
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 9705f716386..bddb323c3f2 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -26,6 +26,10 @@ ifeq ($(CONFIG_ARCH_OMAP4),y)
musb_hdrc-objs += omap2430.o
endif
+ifeq ($(CONFIG_ARCH_U8500),y)
+ musb_hdrc-objs += stm_musb.o
+endif
+
ifeq ($(CONFIG_BF54x),y)
musb_hdrc-objs += blackfin.o
endif
@@ -67,6 +71,7 @@ ifneq ($(CONFIG_MUSB_PIO_ONLY),y)
endif
endif
endif
+ musb_hdrc-objs += stm_musb_dma.o
endif
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 3b795c56221..ee10bf1f031 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -4,6 +4,7 @@
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2009 ST Ericsson
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -106,12 +107,16 @@
#endif
#include "musb_core.h"
-
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
#ifdef CONFIG_ARCH_DAVINCI
#include "davinci.h"
#endif
+#include <mach/stm_musb.h>
+
#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
@@ -459,6 +464,29 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
int_usb);
+ /*
+ * XXX The following code has been inserted here as a temporary hack
+ * to get some USB events directly from the USB hardware. This code
+ * and callbacks should eventually be integrated into the generic
+ * USB gadget stack.
+ */
+ if (!(devctl & MUSB_DEVCTL_HM)) {
+ if (int_usb & MUSB_INTR_RESET) {
+ if (power & MUSB_POWER_HSMODE)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_HS);
+ else
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_FS);
+ }
+ if (int_usb & MUSB_INTR_RESUME)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESUME);
+ else if (int_usb & MUSB_INTR_SUSPEND)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_SUSPEND);
+ }
+
/* in host mode, the peripheral may issue remote wakeup.
* in peripheral mode, the host may resume the link.
* spurious RESUME irqs happen too, paired with SUSPEND.
@@ -492,7 +520,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies
- + msecs_to_jiffies(20);
+ + msecs_to_jiffies(30);
musb->xceiv->state = OTG_STATE_A_HOST;
musb->is_active = 1;
@@ -987,6 +1015,10 @@ void musb_start(struct musb *musb)
}
musb_platform_enable(musb);
musb_writeb(regs, MUSB_DEVCTL, devctl);
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ musb_save_context(musb);
+ clk_disable(musb->clock);
+#endif
}
@@ -995,6 +1027,9 @@ static void musb_generic_disable(struct musb *musb)
void __iomem *mbase = musb->mregs;
u16 temp;
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ clk_enable(musb->clock);
+#endif
/* disable interrupts */
musb_writeb(mbase, MUSB_INTRUSBE, 0);
musb_writew(mbase, MUSB_INTRTXE, 0);
@@ -1008,6 +1043,9 @@ static void musb_generic_disable(struct musb *musb)
temp = musb_readw(mbase, MUSB_INTRTX);
temp = musb_readw(mbase, MUSB_INTRRX);
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ clk_disable(musb->clock);
+#endif
}
/*
@@ -1067,7 +1105,11 @@ static void musb_shutdown(struct platform_device *pdev)
|| defined(CONFIG_ARCH_OMAP4)
static ushort __initdata fifo_mode = 4;
#else
+#ifndef CONFIG_ARCH_U8500
static ushort __initdata fifo_mode = 2;
+#else
+static ushort __initdata fifo_mode = 5;
+#endif
#endif
/* "modprobe ... fifo_mode=1" etc */
@@ -1150,8 +1192,8 @@ static struct musb_fifo_cfg __initdata mode_4_cfg[] = {
/* mode 5 - fits in 8KB */
static struct musb_fifo_cfg __initdata mode_5_cfg[] = {
-{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
-{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
@@ -1536,7 +1578,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || \
- defined(CONFIG_ARCH_OMAP4)
+ defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
static irqreturn_t generic_interrupt(int irq, void *__hci)
{
@@ -1591,6 +1633,14 @@ irqreturn_t musb_interrupt(struct musb *musb)
}
#endif
+ /**
+ * HACK for detecting the AX8817X series Ethernet over USB
+ * Adapters for U8500 platform
+ */
+#if (defined(CONFIG_ARCH_U8500) && defined(CONFIG_USB_NET_AX8817X))
+ mdelay(10);
+#endif
+
/* the core can interrupt us for multiple reasons; docs have
* a generic interrupt flowchart to follow
*/
@@ -2417,26 +2467,42 @@ static int musb_suspend(struct device *dev)
spin_lock_irqsave(&musb->lock, flags);
if (is_peripheral_active(musb)) {
- /* FIXME force disconnect unless we know USB will wake
+ /*
+ * FIXME force disconnect unless we know USB will wake
* the system up quickly enough to respond ...
+ * For ux500 platform if usb is connected return busy
+ * state
*/
+ if (musb->is_active == 1) {
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return -EBUSY;
+ }
} else if (is_host_active(musb)) {
- /* we know all the children are suspended; sometimes
+ /*
+ * we know all the children are suspended; sometimes
* they will even be wakeup-enabled.
+ * For ux500 platform if usb is connected return busy
+ * state
*/
+ if (musb->is_active == 1) {
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return -EBUSY;
+ }
}
+#ifndef CONFIG_ARCH_U8500
musb_save_context(musb);
if (musb->set_clock)
musb->set_clock(musb->clock, 0);
else
clk_disable(musb->clock);
+#endif
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
}
-static int musb_resume_noirq(struct device *dev)
+static int musb_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct musb *musb = dev_to_musb(&pdev->dev);
@@ -2444,12 +2510,14 @@ static int musb_resume_noirq(struct device *dev)
if (!musb->clock)
return 0;
+#ifndef CONFIG_ARCH_U8500
if (musb->set_clock)
musb->set_clock(musb->clock, 1);
else
clk_enable(musb->clock);
musb_restore_context(musb);
+#endif
/* for static cmos like DaVinci, register values were preserved
* unless for some reason the whole soc powered down or the USB
@@ -2460,7 +2528,7 @@ static int musb_resume_noirq(struct device *dev)
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
- .resume_noirq = musb_resume_noirq,
+ .resume = musb_resume,
};
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
@@ -2513,10 +2581,18 @@ static int __init musb_init(void)
return platform_driver_probe(&musb_driver, musb_probe);
}
+#ifndef CONFIG_ARCH_U8500
/* make us init after usbcore and i2c (transceivers, regulators, etc)
* and before usb gadget and host-side drivers start to register
*/
fs_initcall(musb_init);
+#else
+/* with fs_initcall the dma controller driver was loaded after mentor IP
+ * driver so when DMA is enabled, it will break as DMA controller driver is
+ * not loaded. This has been done to correct the order
+ */
+module_init(musb_init);
+#endif
static void __exit musb_cleanup(void)
{
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 91d67794e35..c037939a8a2 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -72,8 +72,6 @@ struct musb_ep;
#include <linux/usb/hcd.h>
#include "musb_host.h"
-
-
#ifdef CONFIG_USB_MUSB_OTG
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
@@ -204,6 +202,7 @@ enum musb_g_ep0_state {
#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */
#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
+#define USB_SUSP_DET_DURATION 5 /* suspend time 5ms */
/*************************** REGISTER ACCESS ********************************/
@@ -492,8 +491,10 @@ extern void musb_platform_save_context(struct musb *musb,
extern void musb_platform_restore_context(struct musb *musb,
struct musb_context_registers *musb_context);
#else
-#define musb_platform_save_context(m, x) do {} while (0)
-#define musb_platform_restore_context(m, x) do {} while (0)
+static inline void musb_platform_save_context(struct musb *musb,
+ struct musb_context_registers *musb_context) { }
+static inline void musb_platform_restore_context(struct musb *musb,
+ struct musb_context_registers *musb_context) { }
#endif
#endif
@@ -598,18 +599,31 @@ extern void musb_hnp_stop(struct musb *musb);
extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode);
+#ifdef CONFIG_PM
+void musb_save_context(struct musb *);
+extern void musb_restore_context(struct musb *musb);
+#endif
+
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
- defined(CONFIG_ARCH_OMAP4)
+ defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
#else
-#define musb_platform_try_idle(x, y) do {} while (0)
+static inline void musb_platform_try_idle(struct musb *musb, unsigned long timeout) { }
#endif
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN)
extern int musb_platform_get_vbus_status(struct musb *musb);
#else
-#define musb_platform_get_vbus_status(x) 0
+static inline int musb_platform_get_vbus_status(struct musb *musb) { return 0; }
+#endif
+
+#if defined(CONFIG_ARCH_U8500)
+extern void musb_platform_device_en(int enable);
+extern void musb_platform_session_req(void);
+#else
+static inline void musb_platform_device_en(int enable) { }
+static inline void musb_platform_session_req(void) { }
#endif
extern int __init musb_platform_init(struct musb *musb, void *board_data);
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 916065ba9e7..a114af6c330 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -36,7 +36,7 @@
#define __MUSB_DMA_H__
struct musb_hw_ep;
-
+struct musb_request;
/*
* DMA Controller Abstraction
*
@@ -91,6 +91,10 @@ struct musb_hw_ep;
# endif
#endif
+#ifdef CONFIG_USB_U8500_DMA
+#undef USE_MODE1
+#endif
+
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
@@ -155,6 +159,10 @@ dma_channel_status(struct dma_channel *c)
* @channel_release: call this to release a DMA channel
* @channel_abort: call this to abort a pending DMA transaction,
* returning it to FREE (but allocated) state
+ * @is_compatible:allow dma code to indicate incompatibility
+ * with usb request. Gadget musb driver call this api, if
+ * available, before dma mappings to avoid any unnecessary
+ * mapping operations.
*
* Controllers manage dma channels.
*/
@@ -169,6 +177,8 @@ struct dma_controller {
dma_addr_t dma_addr,
u32 length);
int (*channel_abort)(struct dma_channel *);
+ int (*is_compatible)(struct dma_channel *channel,
+ struct musb_request *);
};
/* called after channel_program(), may indicate a fault */
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6fca870e957..9ac40c4bab9 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -5,6 +5,7 @@
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
* Copyright (C) 2009 MontaVista Software, Inc. <source@mvista.com>
+ * Copyright (C) 2009 ST Ericsson
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -43,11 +44,14 @@
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
#include <linux/slab.h>
#include "musb_core.h"
-
-
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
+#define USB_ENABLE_PHY 1
/* MUSB PERIPHERAL status 3-mar-2006:
*
* - EP0 seems solid. It passes both USBCV and usbtest control cases.
@@ -91,6 +95,8 @@
*/
/* ----------------------------------------------------------------------- */
+#define is_buffer_mapped(req) (is_dma_capable() && \
+ (req->map_state != UN_MAPPED))
/*
* Immediately complete a request.
@@ -109,6 +115,7 @@ __acquires(ep->musb->lock)
struct musb_request *req;
struct musb *musb;
int busy = ep->busy;
+ int dma_len;
req = to_musb_request(request);
@@ -119,20 +126,34 @@ __acquires(ep->musb->lock)
ep->busy = 1;
spin_unlock(&musb->lock);
+#ifdef CONFIG_ARM
+ /* If DMA is enabled we need to flush all data we read. */
+ if (ep->hw_ep->musb->controller->dma_mask != 0)
+ dmac_flush_range((const u8 *)req->request.buf,
+ (const u8 *)req->request.buf + req->request.length);
+#endif
+#ifdef CONFIG_USB_U8500_DMA
+ if (!(req->request.actual < ep->packet_sz)
+ && is_buffer_mapped(req)
+ && (ep->packet_sz >= DMA_PACKET_THRESHOLD)) {
+#else
if (is_dma_capable()) {
- if (req->mapped) {
+#endif
+ dma_len = req->request.actual - (req->request.actual % 512) ;
+
+ if (req->map_state == MUSB_MAPPED) {
dma_unmap_single(musb->controller,
req->request.dma,
- req->request.length,
+ req->request.actual - dma_len,
req->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
req->request.dma = DMA_ADDR_INVALID;
- req->mapped = 0;
- } else if (req->request.dma != DMA_ADDR_INVALID)
+ req->map_state = UN_MAPPED;
+ } else if (req->map_state == PRE_MAPPED)
dma_sync_single_for_cpu(musb->controller,
req->request.dma,
- req->request.length,
+ req->request.actual - dma_len,
req->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
@@ -209,7 +230,20 @@ static void nuke(struct musb_ep *ep, const int status)
static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep)
{
+#ifdef CONFIG_USB_U8500_DMA
+ /*
+ * In case of full speed mode with double buffering, bulk split
+ * does not work as defined. Hence in case of FS, return the EP
+ * size instead of EP FIFO size.
+ */
+ u8 power;
+
+ power = musb_readb(musb->mregs, MUSB_POWER);
+
+ if (can_bulk_split(musb, ep->type) && (power & MUSB_POWER_HSMODE))
+#else
if (can_bulk_split(musb, ep->type))
+#endif
return ep->hw_ep->max_packet_sz_tx;
else
return ep->packet_sz;
@@ -267,12 +301,17 @@ static void txstate(struct musb *musb, struct musb_request *req)
int use_dma = 0;
musb_ep = req->ep;
-
+#ifdef CONFIG_USB_U8500_DMA
+ if (musb_ep->dma) {
+#endif
/* we shouldn't get here while DMA is active ... but we do ... */
if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
DBG(4, "dma pending...\n");
return;
}
+#ifdef CONFIG_USB_U8500_DMA
+ }
+#endif
/* read TXCSR before */
csr = musb_readw(epio, MUSB_TXCSR);
@@ -298,10 +337,25 @@ static void txstate(struct musb *musb, struct musb_request *req)
csr);
#ifndef CONFIG_MUSB_PIO_ONLY
+#ifdef CONFIG_USB_U8500_DMA
+ if (musb_ep->dma && is_buffer_mapped(req)) {
+#else
if (is_dma_capable() && musb_ep->dma) {
+#endif
struct dma_controller *c = musb->dma_controller;
use_dma = (request->dma != DMA_ADDR_INVALID);
+#ifdef CONFIG_USB_U8500_DMA
+ if (request->length >= DMA_PACKET_THRESHOLD) {
+ csr |= (MUSB_TXCSR_AUTOSET|
+ MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_DMAMODE
+ | MUSB_TXCSR_MODE);
+ csr &= ~MUSB_TXCSR_P_UNDERRUN;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ }
+ use_dma = use_dma && c->channel_program(musb_ep->dma, musb_ep->packet_sz, DMA_MODE_1, request->dma, request->length);
+#endif
/* MUSB_TXCSR_P_ISO is still set correctly */
@@ -422,6 +476,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_in;
void __iomem *epio = musb->endpoints[epnum].regs;
struct dma_channel *dma;
+ u16 bytes_left = 0;
musb_ep_select(mbase, epnum);
request = next_request(musb_ep);
@@ -463,13 +518,41 @@ void musb_g_tx(struct musb *musb, u8 epnum)
u8 is_dma = 0;
if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
+ int count = 0;
is_dma = 1;
+
+ /* ensure writebuffer is empty */
+ csr = musb_readw(epio, MUSB_TXCSR);
+ bytes_left = request->length
+ -musb_ep->dma->actual_len;
+ for (count = 0; count < MAX_COUNT; count++) {
+ if (!(csr&MUSB_TXCSR_FIFONOTEMPTY))
+ break;
+ csr = musb_readw(epio, MUSB_TXCSR);
+ }
+
csr |= MUSB_TXCSR_P_WZC_BITS;
+#ifdef CONFIG_ARCH_U8500
+ csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_MODE
+ | MUSB_TXCSR_AUTOSET
+ | MUSB_TXCSR_P_UNDERRUN
+ | MUSB_TXCSR_DMAMODE
+ | MUSB_TXCSR_TXPKTRDY);
+#else
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
- MUSB_TXCSR_TXPKTRDY);
+ MUSB_TXCSR_DMAMODE | MUSB_TXCSR_TXPKTRDY);
+#endif
musb_writew(epio, MUSB_TXCSR, csr);
- /* Ensure writebuffer is empty. */
- csr = musb_readw(epio, MUSB_TXCSR);
+ if (bytes_left) {
+ musb_write_fifo(musb_ep->hw_ep,
+ bytes_left,
+ (u8 *) (request->buf +
+ musb_ep->dma->actual_len));
+ musb_writew(epio, MUSB_TXCSR,
+ MUSB_TXCSR_TXPKTRDY);
+ request->actual += bytes_left;
+ }
+
request->actual += musb_ep->dma->actual_len;
DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n",
epnum, csr, musb_ep->dma->actual_len, request);
@@ -502,25 +585,33 @@ void musb_g_tx(struct musb *musb, u8 epnum)
}
/* ... or if not, then complete it. */
- musb_g_giveback(musb_ep, request, 0);
+ if (request->actual == request->length) {
+ musb_g_giveback(musb_ep, request, 0);
- /*
- * Kickstart next transfer if appropriate;
- * the packet that just completed might not
- * be transmitted for hours or days.
- * REVISIT for double buffering...
- * FIXME revisit for stalls too...
- */
- musb_ep_select(mbase, epnum);
- csr = musb_readw(epio, MUSB_TXCSR);
- if (csr & MUSB_TXCSR_FIFONOTEMPTY)
- return;
-
- request = musb_ep->desc ? next_request(musb_ep) : NULL;
- if (!request) {
- DBG(4, "%s idle now\n",
- musb_ep->end_point.name);
- return;
+ /*
+ * Kickstart next transfer if appropriate;
+ * the packet that just completed might not
+ * be transmitted for hours or days.
+ * REVISIT for double buffering...
+ * FIXME revisit for stalls too...
+ */
+ musb_ep_select(mbase, epnum);
+ csr = musb_readw(epio, MUSB_TXCSR);
+#ifdef CONFIG_ARCH_U8500
+ if ((csr & MUSB_TXCSR_FIFONOTEMPTY) &&
+ !(musb_ep->hw_ep->tx_double_buffered))
+#else
+ if (csr & MUSB_TXCSR_FIFONOTEMPTY)
+#endif
+ return;
+
+ request =
+ musb_ep->desc ? next_request(musb_ep) : NULL;
+ if (!request) {
+ DBG(4, "%s idle now\n",
+ musb_ep->end_point.name);
+ return;
+ }
}
}
@@ -616,8 +707,14 @@ static void rxstate(struct musb *musb, struct musb_request *req)
if (csr & MUSB_RXCSR_RXPKTRDY) {
len = musb_readw(epio, MUSB_RXCOUNT);
if (request->actual < request->length) {
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
+
#ifdef CONFIG_USB_INVENTRA_DMA
if (is_dma_capable() && musb_ep->dma) {
+#else
+ if (is_buffer_mapped(req) &&
+ (len >= DMA_PACKET_THRESHOLD)) {
+#endif
struct dma_controller *c;
struct dma_channel *channel;
int use_dma = 0;
@@ -646,44 +743,54 @@ static void rxstate(struct musb *musb, struct musb_request *req)
* then becomes usable as a runtime "use mode 1" hint...
*/
- csr |= MUSB_RXCSR_DMAENAB;
-#ifdef USE_MODE1
- csr |= MUSB_RXCSR_AUTOCLEAR;
- /* csr |= MUSB_RXCSR_DMAMODE; */
-
- /* this special sequence (enabling and then
- * disabling MUSB_RXCSR_DMAMODE) is required
- * to get DMAReq to activate
- */
- musb_writew(epio, MUSB_RXCSR,
- csr | MUSB_RXCSR_DMAMODE);
-#endif
- musb_writew(epio, MUSB_RXCSR, csr);
-
if (request->actual < request->length) {
int transfer_size = 0;
-#ifdef USE_MODE1
- transfer_size = min(request->length,
- channel->max_len);
-#else
- transfer_size = len;
-#endif
- if (transfer_size <= musb_ep->packet_sz)
- musb_ep->dma->desired_mode = 0;
- else
+
+ /* In case first packet is short */
+ if (len < musb_ep->packet_sz)
+ transfer_size = len;
+ else if (request->short_not_ok) {
+
+ csr &= ~MUSB_RXCSR_DMAMODE;
+ csr |= MUSB_RXCSR_AUTOCLEAR;
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
+
+ transfer_size =
+ min(request->length -
+ request->actual,
+ channel->max_len);
+
+ csr |= MUSB_RXCSR_DMAMODE;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
musb_ep->dma->desired_mode = 1;
+ } else {
+ csr &= ~MUSB_RXCSR_DMAMODE;
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
+
+ transfer_size =
+ min(request->length -
+ request->actual,
+ (unsigned)len);
+
+ musb_ep->dma->desired_mode = 0;
+ }
use_dma = c->channel_program(
- channel,
- musb_ep->packet_sz,
- channel->desired_mode,
- request->dma
- + request->actual,
- transfer_size);
- }
+ channel,
+ musb_ep->packet_sz,
+ channel->desired_mode,
+ request->dma
+ + request->actual,
+ transfer_size);
- if (use_dma)
- return;
+ if (use_dma)
+ return;
+ }
}
#endif /* Mentor's DMA */
@@ -743,6 +850,7 @@ void musb_g_rx(struct musb *musb, u8 epnum)
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out;
void __iomem *epio = musb->endpoints[epnum].regs;
struct dma_channel *dma;
+ u16 bytes_left = 0;
musb_ep_select(mbase, epnum);
@@ -791,7 +899,7 @@ void musb_g_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_DMAMODE);
musb_writew(epio, MUSB_RXCSR,
MUSB_RXCSR_P_WZC_BITS | csr);
-
+ csr = musb_readw(epio, MUSB_RXCSR);
request->actual += musb_ep->dma->actual_len;
DBG(4, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n",
@@ -799,7 +907,9 @@ void musb_g_rx(struct musb *musb, u8 epnum)
musb_readw(epio, MUSB_RXCSR),
musb_ep->dma->actual_len, request);
-#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA)
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) \
+ || defined(CONFIG_USB_U8500_DMA)
+
/* Autoclear doesn't clear RxPktRdy for short packets */
if ((dma->desired_mode == 0)
|| (dma->actual_len
@@ -963,6 +1073,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
* for some reason you run out of channels here.
*/
if (is_dma_capable() && musb->dma_controller) {
+
struct dma_controller *c = musb->dma_controller;
musb_ep->dma = c->channel_alloc(c, hw_ep,
@@ -1102,6 +1213,8 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
struct musb *musb;
int status = 0;
unsigned long lockflags;
+ int compatible = 0;
+ struct dma_controller *dma;
if (!ep || !req)
return -EINVAL;
@@ -1110,6 +1223,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
musb_ep = to_musb_ep(ep);
musb = musb_ep->musb;
+ dma = musb->dma_controller;
request = to_musb_request(req);
request->musb = musb;
@@ -1125,7 +1239,15 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->epnum = musb_ep->current_epnum;
request->tx = musb_ep->is_in;
- if (is_dma_capable() && musb_ep->dma) {
+ request->map_state = UN_MAPPED;
+
+ if (musb_ep->dma && dma) {
+ if (dma->is_compatible)
+ compatible = dma->is_compatible(musb_ep->dma, request);
+
+ }
+
+ if (is_dma_capable() && musb_ep->dma && compatible) {
if (request->request.dma == DMA_ADDR_INVALID) {
request->request.dma = dma_map_single(
musb->controller,
@@ -1134,7 +1256,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
- request->mapped = 1;
+ request->map_state = MUSB_MAPPED;
} else {
dma_sync_single_for_device(musb->controller,
request->request.dma,
@@ -1142,12 +1264,12 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
- request->mapped = 0;
+ request->map_state = PRE_MAPPED;
}
} else if (!req->buf) {
return -ENODATA;
} else
- request->mapped = 0;
+ request->map_state = UN_MAPPED;
spin_lock_irqsave(&musb->lock, lockflags);
@@ -1417,6 +1539,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
u8 power, devctl;
int retries;
+
+ musb_platform_device_en(USB_ENABLE_PHY);
+ musb_platform_session_req();
+
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
@@ -1894,6 +2020,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
void musb_g_resume(struct musb *musb)
{
musb->is_suspended = 0;
+ stm_prcmu_qos_handler(SET_OPP);
switch (musb->xceiv->state) {
case OTG_STATE_B_IDLE:
break;
@@ -1940,6 +2067,7 @@ void musb_g_suspend(struct musb *musb)
WARNING("unhandled SUSPEND transition (%s)\n",
otg_state_string(musb));
}
+ stm_prcmu_qos_handler(!SET_OPP);
}
/* Called during SRP */
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index c8b140325d8..20d6b064542 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -34,6 +34,11 @@
#ifndef __MUSB_GADGET_H
#define __MUSB_GADGET_H
+enum buffer_map_state {
+ UN_MAPPED = 0,
+ PRE_MAPPED,
+ MUSB_MAPPED
+};
struct musb_request {
struct usb_request request;
@@ -41,7 +46,7 @@ struct musb_request {
struct musb *musb;
u8 tx; /* endpoint direction */
u8 epnum;
- u8 mapped;
+ enum buffer_map_state map_state;
};
static inline struct musb_request *to_musb_request(struct usb_request *req)
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 877d20b1dff..25cbeadb8b0 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -45,6 +45,9 @@
#include "musb_core.h"
#include "musb_host.h"
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
/* MUSB HOST status 22-mar-2006
*
@@ -106,24 +109,41 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
{
void __iomem *epio = ep->regs;
+ void __iomem *regs = ep->musb->mregs;
u16 csr;
- u16 lastcsr = 0;
- int retries = 1000;
+ u8 addr;
+ int retries = 3000; /* 3ms */
+ /*
+ * NOTE: We are using a hack here because the FIFO-FLUSH
+ * bit is broken in hardware! The hack consists of changing
+ * the TXFUNCADDR to an unused device address and waiting
+ * for any pending USB packets to hit the 3-strikes and your
+ * gone rule.
+ */
+ addr = musb_readb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR));
csr = musb_readw(epio, MUSB_TXCSR);
while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
- if (csr != lastcsr)
- DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
- lastcsr = csr;
- csr |= MUSB_TXCSR_FLUSHFIFO;
- musb_writew(epio, MUSB_TXCSR, csr);
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum,
+ MUSB_TXFUNCADDR), 127);
csr = musb_readw(epio, MUSB_TXCSR);
- if (WARN(retries-- < 1,
- "Could not flush host TX%d fifo: csr: %04x\n",
- ep->epnum, csr))
- return;
- mdelay(1);
+ retries--;
+ if (retries == 0) {
+ /* can happen if the USB clocks are OFF */
+ DBG(3, "Could not flush host TX%d "
+ "fifo: csr=0x%04x\n", ep->epnum, csr);
+ break;
+ }
+ udelay(1);
}
+ /* clear any errors */
+ csr &= ~(MUSB_TXCSR_H_ERROR
+ | MUSB_TXCSR_H_RXSTALL
+ | MUSB_TXCSR_H_NAKTIMEOUT);
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ /* restore endpoint address */
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR), addr);
}
static void musb_h_ep0_flush_fifo(struct musb_hw_ep *ep)
@@ -175,9 +195,16 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
/* NOTE: no locks here; caller should lock and select EP */
txcsr = musb_readw(ep->regs, MUSB_TXCSR);
+
+#ifdef CONFIG_USB_U8500_DMA
+ txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_H_WZC_BITS;
+ txcsr |= MUSB_TXCSR_DMAMODE;
+#else
txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
if (is_cppi_enabled())
txcsr |= MUSB_TXCSR_DMAMODE;
+#endif
musb_writew(ep->regs, MUSB_TXCSR, txcsr);
}
@@ -290,8 +317,13 @@ start:
if (!hw_ep->tx_channel)
musb_h_tx_start(hw_ep);
+#ifdef CONFIG_USB_U8500_DMA
+ else
+ musb_h_tx_dma_start(hw_ep);
+#else
else if (is_cppi_enabled() || tusb_dma_omap())
musb_h_tx_dma_start(hw_ep);
+#endif
}
}
@@ -323,8 +355,15 @@ __acquires(musb->lock)
urb->actual_length, urb->transfer_buffer_length
);
+
usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
spin_unlock(&musb->lock);
+#ifdef CONFIG_USB_U8500_DMA
+ /* Make it safe to call this routine more than once */
+ urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
+ URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
+ URB_DMA_MAP_SINGLE | URB_MAP_LOCAL);
+#endif
usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status);
spin_lock(&musb->lock);
}
@@ -627,7 +666,35 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
u16 csr;
u8 mode;
-#ifdef CONFIG_USB_INVENTRA_DMA
+#ifdef CONFIG_USB_U8500_DMA
+ if (length > channel->max_len)
+ length = channel->max_len;
+
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (length > pkt_size) {
+ mode = 1;
+ csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
+ /* autoset shouldn't be set in high bandwidth */
+ if (qh->hb_mult == 1)
+ csr |= MUSB_TXCSR_AUTOSET;
+ } else {
+ mode = 0;
+ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
+ csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
+ }
+ channel->desired_mode = mode;
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ channel->actual_len = 0;
+
+ /*
+ * TX uses "RNDIS" mode automatically but needs help
+ * to identify the zero-length-final-packet case.
+ */
+ mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
+
+#else
+#ifdef CONFIG_USB_INVENTRA_DMA
if (length > channel->max_len)
length = channel->max_len;
@@ -657,6 +724,7 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
*/
mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
#endif
+#endif
qh->segsize = length;
@@ -842,7 +910,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
}
/* kick things off */
-
if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
/* candidate for DMA */
if (dma_channel) {
@@ -872,7 +939,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
csr |= MUSB_RXCSR_DMAENAB;
}
}
-
csr |= MUSB_RXCSR_H_REQPKT;
DBG(7, "RXCSR%d := %04x\n", epnum, csr);
musb_writew(hw_ep->regs, MUSB_RXCSR, csr);
@@ -1141,7 +1207,9 @@ void musb_host_tx(struct musb *musb, u8 epnum)
DBG(3, "TX 3strikes on ep=%d\n", epnum);
status = -ETIMEDOUT;
-
+ } else if (tx_csr & MUSB_TXCSR_TXPKTRDY) {
+ /* BUSY - can happen during USB transfer cancel */
+ return;
} else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) {
DBG(6, "TX end=%d device not responding\n", epnum);
@@ -1437,6 +1505,9 @@ void musb_host_rx(struct musb *musb, u8 epnum)
u32 status;
struct dma_channel *dma;
+#if defined(CONFIG_USB_U8500_DMA)
+ u16 tmp_val;
+#endif
musb_ep_select(mbase, epnum);
urb = next_urb(qh);
@@ -1527,7 +1598,6 @@ void musb_host_rx(struct musb *musb, u8 epnum)
done = true;
goto finish;
}
-
if (unlikely(dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY)) {
/* SHOULD NEVER HAPPEN ... but at least DaVinci has done it */
ERR("RX%d dma busy, csr %04x\n", epnum, rx_csr);
@@ -1541,7 +1611,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
/* FIXME this is _way_ too much in-line logic for Mentor DMA */
-#ifndef CONFIG_USB_INVENTRA_DMA
+#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_U8500_DMA)
if (rx_csr & MUSB_RXCSR_H_REQPKT) {
/* REVISIT this happened for a while on some short reads...
* the cleanup still needs investigation... looks bad...
@@ -1564,6 +1634,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
MUSB_RXCSR_H_WZC_BITS | rx_csr);
}
#endif
+
if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) {
xfer_len = dma->actual_len;
@@ -1573,7 +1644,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_RXPKTRDY);
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
@@ -1629,7 +1700,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
/* we are expecting IN packets */
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (dma) {
struct dma_controller *c;
u16 rx_count;
@@ -1713,6 +1784,14 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
val = musb_readw(epio, MUSB_RXCSR);
+
+#if defined(CONFIG_USB_U8500_DMA)
+ /* retain the original value,
+ *which will be used to reset CSR
+ */
+ tmp_val = val;
+#endif
+
val &= ~MUSB_RXCSR_H_REQPKT;
if (dma->desired_mode == 0)
@@ -1741,9 +1820,13 @@ void musb_host_rx(struct musb *musb, u8 epnum)
hw_ep->rx_channel = NULL;
dma = NULL;
/* REVISIT reset CSR */
+
+#if defined(CONFIG_USB_U8500_DMA)
+ musb_writew(epio, MUSB_RXCSR, tmp_val);
+#endif
}
}
-#endif /* Mentor DMA */
+#endif /* Mentor DMA || U8500 DMA */
if (!dma) {
done = musb_host_packet_rx(musb, urb,
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 92e85e027cf..663dda191dd 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -84,8 +84,9 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
&& musb->xceiv->host->b_hnp_enable;
if (musb->is_active)
mod_timer(&musb->otg_timer, jiffies
- + msecs_to_jiffies(
- OTG_TIME_A_AIDL_BDIS));
+ + msecs_to_jiffies((
+ OTG_TIME_A_AIDL_BDIS +
+ USB_SUSP_DET_DURATION)));
musb_platform_try_idle(musb, 0);
break;
#ifdef CONFIG_USB_MUSB_OTG
diff --git a/drivers/usb/musb/ste_config.h b/drivers/usb/musb/ste_config.h
new file mode 100644
index 00000000000..daa336369fa
--- /dev/null
+++ b/drivers/usb/musb/ste_config.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STE_CONFIG_H__
+#define __STE_CONFIG_H__
+
+#if (defined(CONFIG_ARCH_U8500) && !defined(CONFIG_MUSB_PIO_ONLY))
+#define CONFIG_USB_U8500_DMA
+#endif
+#define U8500_DMA_END_POINTS 7
+#define DMA_MODE_0 0
+#define DMA_MODE_1 1
+#define DMA_PACKET_THRESHOLD 512
+#define RX_END_POINT_OFFSET 6
+#define DELAY_IN_MICROSECONDS 10
+#define MAX_COUNT 35000
+void stm_prcmu_qos_handler(int);
+#define SET_OPP 1
+
+enum nmdk_dma_tx_rx_channel {
+ TX_CHANNEL_1 = 0,
+ TX_CHANNEL_2,
+ TX_CHANNEL_3,
+ TX_CHANNEL_4,
+ TX_CHANNEL_5,
+ TX_CHANNEL_6,
+ TX_CHANNEL_7,
+ RX_CHANNEL_1,
+ RX_CHANNEL_2,
+ RX_CHANNEL_3,
+ RX_CHANNEL_4,
+ RX_CHANNEL_5,
+ RX_CHANNEL_6,
+ RX_CHANNEL_7
+};
+#endif
+
diff --git a/drivers/usb/musb/stm_musb.c b/drivers/usb/musb/stm_musb.c
new file mode 100644
index 00000000000..5ce3c4a75dc
--- /dev/null
+++ b/drivers/usb/musb/stm_musb.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2009 STMicroelectronics
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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.
+ */
+
+/** @file stm_musb.c
+ * @brief This file contains the USB controller and Phy initialization
+ * with default as interrupt mode was implemented
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mfd/ab8500/ab8500-bm.h>
+#include <mach/stm_musb.h>
+#include <mach/musb_db8500.h>
+#include <mach/prcmu-fw-api.h>
+#include "musb_core.h"
+
+static u8 ulpi_read_register(struct musb *musb, u8 address);
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data);
+/* callback argument for AB8500 callback functions */
+static struct musb *musb_status;
+static spinlock_t musb_ulpi_spinlock;
+static unsigned musb_power;
+#ifdef CONFIG_USB_OTG_20
+static int userrequest;
+#endif
+static struct workqueue_struct *stm_usb_power_wq;
+static struct work_struct stm_prcmu_qos;
+static int musb_qos_req;
+
+#define PERI5_CLK_ENABLE 1
+#define PERI5_CLK_DISABLE 0
+
+/**
+ * musb_set_session() - Start the USB session
+ *
+ * This function is used to start the USB sessios in USB host mode
+ * once the A cable is plugged in
+ */
+void musb_set_session(void)
+{
+ u8 val;
+ void __iomem *regs;
+
+ if (musb_status == NULL) {
+ printk(KERN_ERR "Error: devctl session cannot be set\n");
+ return;
+ }
+ regs = musb_status->mregs;
+ val = musb_readb(regs, MUSB_DEVCTL);
+ musb_writeb(regs, MUSB_DEVCTL, val | MUSB_DEVCTL_SESSION);
+}
+EXPORT_SYMBOL(musb_set_session);
+
+
+void stm_prcmu_qos_handler(int value)
+{
+ if (value)
+ musb_qos_req = 100;
+ else
+ musb_qos_req = 50;
+ queue_work(stm_usb_power_wq, &stm_prcmu_qos);
+}
+EXPORT_SYMBOL(stm_prcmu_qos_handler);
+
+static void stm_prcmu_qos_work(struct work_struct *work)
+{
+ if (musb_qos_req == 100) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "musb_qos", 100);
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "musb_qos", 50);
+ prcmu_release_usb_wakeup_state();
+ }
+}
+
+void stm_set_peripheral_clock(int enable)
+{
+ if (enable) {
+ if (musb_status->set_clock)
+ musb_status->set_clock(musb_status->clock, 1);
+ else
+ clk_enable(musb_status->clock);
+ } else {
+ if (musb_status->set_clock)
+ musb_status->set_clock(musb_status->clock, 0);
+ else
+ clk_disable(musb_status->clock);
+ }
+}
+
+#ifdef CONFIG_PM
+void stm_musb_context(int enable)
+{
+ void __iomem *regs;
+
+ if (enable) {
+ stm_set_peripheral_clock(PERI5_CLK_ENABLE);
+ musb_restore_context(musb_status);
+ regs = musb_status->mregs;
+ } else
+ stm_set_peripheral_clock(PERI5_CLK_DISABLE);
+}
+EXPORT_SYMBOL(stm_musb_context);
+#endif
+
+void
+ab8500_bm_usb_state_changed_wrapper(u8 bm_usb_state)
+{
+ if ((bm_usb_state == AB8500_BM_USB_STATE_RESET_HS) ||
+ (bm_usb_state == AB8500_BM_USB_STATE_RESET_FS)) {
+ musb_power = 0;
+ }
+
+ /*
+ * TODO: Instead of using callbacks, we should be using notify
+ * to tell the battery manager when there is s state change
+ */
+ ab8500_charger_usb_state_changed(bm_usb_state, musb_power);
+}
+
+#ifdef CONFIG_USB_OTG_20
+int musb_adp(void)
+{
+ if (userrequest == 0)
+ return 0;
+ else
+ return 1;
+}
+EXPORT_SYMBOL(musb_adp);
+#endif
+
+/* Sys interfaces */
+static struct kobject *usbstatus_kobj;
+static ssize_t usb_cable_status
+ (struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ u8 is_active = 0;
+
+ if (strcmp(attr->name, "cable_connect") == 0) {
+ is_active = musb_status->is_active;
+ sprintf(buf, "%d\n", is_active);
+ }
+ return strlen(buf);
+}
+
+static struct attribute usb_cable_connect_attribute = \
+ {.name = "cable_connect", .mode = S_IRUGO};
+static struct attribute *usb_status[] = {
+ &usb_cable_connect_attribute,
+ NULL
+};
+
+struct sysfs_ops usb_sysfs_ops = {
+ .show = usb_cable_status,
+};
+
+static struct kobj_type ktype_usbstatus = {
+ .sysfs_ops = &usb_sysfs_ops,
+ .default_attrs = usb_status,
+};
+
+/*
+ * A structure was declared as global for timer in USB host mode
+ */
+static struct timer_list notify_timer;
+
+/* TODO: Remove or use!! */
+#ifdef UNUSED_USB_STUFF
+
+/**
+ * ulpi_read_register() - Read the usb register from address writing into ULPI
+ * @musb: struct musb pointer.
+ * @address: address for reading from ULPI register of USB
+ *
+ * This function read the value from the specific address in USB host mode.
+ */
+static u8 ulpi_read_register(struct musb *musb, u8 address)
+{
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ int count = 200;
+ u8 val;
+
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
+ /* set ULPI register address */
+ musb_writeb(mbase, OTG_UREGADDR, address);
+
+ /* request a read access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_URW;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* perform access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_REGREQ;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* wait for completion with a time-out */
+ do {
+ udelay(10);
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ count--;
+ } while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
+
+ /* check for time-out */
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ if (printk_ratelimit())
+ printk(KERN_ALERT "U8500 USB : ULPI read timed out\n");
+ return 0;
+ }
+
+ /* acknowledge completion */
+ val &= ~OTG_UREGCTRL_REGCMP;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* get data */
+ val = musb_readb(mbase, OTG_UREGDATA);
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return val;
+}
+#endif
+
+/**
+ * ulpi_write_register() - Write to a usb phy's ULPI register
+ * using the Mentor ULPI wrapper functionality
+ * @musb: struct musb pointer.
+ * @address: address of ULPI register
+ * @data: data for ULPI register
+ * This function writes the value given by data to the specific address
+ */
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data)
+{
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ int count = 200;
+ u8 val;
+
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
+ /* First write to ULPI wrapper registers */
+ /* set ULPI register address */
+ musb_writeb(mbase, OTG_UREGADDR, address);
+
+ /* request a write access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val &= ~OTG_UREGCTRL_URW;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* Write data to ULPI wrapper data register */
+ musb_writeb(mbase, OTG_UREGDATA, data);
+
+ /* perform access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_REGREQ;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* wait for completion with a time-out */
+ do {
+ udelay(10);
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ count--;
+ } while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
+
+ /* check for time-out */
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ if (printk_ratelimit())
+ printk(KERN_ALERT "U8500 USB : ULPI write timed out\n");
+ return 0;
+ }
+
+ /* acknowledge completion */
+ val &= ~OTG_UREGCTRL_REGCMP;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return 0;
+
+}
+
+
+/**
+ * musb_stm_hs_otg_init() - Initialize the USB for paltform specific.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the USB with the given musb structure information.
+ */
+int __init musb_stm_hs_otg_init(struct musb *musb)
+{
+ u8 val;
+
+ if (musb->clock)
+ clk_enable(musb->clock);
+
+ /* enable ULPI interface */
+ val = musb_readb(musb->mregs, OTG_TOPCTRL);
+ val |= OTG_TOPCTRL_MODE_ULPI;
+ musb_writeb(musb->mregs, OTG_TOPCTRL, val);
+
+ /* do soft reset */
+ val = musb_readb(musb->mregs, 0x7F);
+ val |= 0x2;
+ musb_writeb(musb->mregs, 0x7F, val);
+
+ return 0;
+}
+/**
+ * musb_stm_fs_init() - Initialize the file system for the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the file system of USB.
+ */
+int __init musb_stm_fs_init(struct musb *musb)
+{
+ return 0;
+}
+/**
+ * musb_platform_enable() - Enable the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function enables the USB.
+ */
+void musb_platform_enable(struct musb *musb)
+{
+}
+/**
+ * musb_platform_disable() - Disable the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function disables the USB.
+ */
+void musb_platform_disable(struct musb *musb)
+{
+}
+
+/**
+ * musb_platform_try_idle() - Check the USB state active or not.
+ * @musb: struct musb pointer.
+ * @timeout: set the timeout to keep the host in idle mode.
+ *
+ * This function keeps the USB host in idle state based on the musb inforamtion.
+ */
+void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+{
+ if (musb->board_mode != MUSB_PERIPHERAL) {
+ unsigned long default_timeout =
+ jiffies + msecs_to_jiffies(10);
+ static unsigned long last_timer;
+
+ if (timeout == 0)
+ timeout = default_timeout;
+
+ /* Never idle if active, or when VBUS
+ timeout is not set as host */
+ if (musb->is_active || ((musb->a_wait_bcon == 0)
+ && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ DBG(4, "%s active, deleting timer\n",
+ otg_state_string(musb));
+ del_timer(&notify_timer);
+ last_timer = jiffies;
+ return;
+ }
+
+ if (time_after(last_timer, timeout)) {
+ if (!timer_pending(&notify_timer))
+ last_timer = timeout;
+ else {
+ DBG(4,
+ "Longer idle timer already pending,ignoring\n");
+ return;
+ }
+ }
+ last_timer = timeout;
+
+ DBG(4, "%s inactive, for idle timer for %lu ms\n",
+ otg_state_string(musb),
+ (unsigned long)jiffies_to_msecs(timeout - jiffies));
+ mod_timer(&notify_timer, timeout);
+ }
+}
+
+/**
+ * set_vbus() - Set the Vbus for the USB.
+ * @musb: struct musb pointer.
+ * @is_on: set Vbus for USB or not.
+ *
+ * This function set the Vbus for USB.
+ */
+static void set_vbus(struct musb *musb, int is_on)
+{
+ u8 devctl;
+ /* HDRC controls CPEN, but beware current surges during device
+ * connect. They can trigger transient overcurrent conditions
+ * that must be ignored.
+ */
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ if (is_on) {
+ musb->is_active = 1;
+ musb->xceiv->default_a = 1;
+ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ MUSB_HST_MODE(musb);
+ } else {
+ musb->is_active = 0;
+
+ /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
+ * jumping right to B_IDLE...
+ */
+
+ musb->xceiv->default_a = 0;
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+
+ MUSB_DEV_MODE(musb);
+ }
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ DBG(1, "VBUS %s, devctl %02x "
+ /* otg %3x conf %08x prcm %08x */ "\n",
+ otg_state_string(musb),
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+ if (!is_on) {
+ /* Discahrge the VBUS */
+ if (musb_status == NULL)
+ return;
+ ulpi_write_register(musb_status, ULPI_OCTRL, 0x08);
+ }
+}
+/**
+ * set_power() - Set the power for the USB transceiver.
+ * @x: struct usb_transceiver pointer.
+ * @mA: set mA power for USB.
+ *
+ * This function set the power for the USB.
+ */
+static int set_power(struct otg_transceiver *x, unsigned mA)
+{
+ if (mA > 100) {
+ /* AB V2 has eye diagram issues when drawing more
+ * than 100mA from VBUS.So setting charging current
+ * to 100mA in case of standard host
+ */
+ if (musb_get_abx500_rev() < 0x30)
+ mA = 100;
+ else
+ mA = 300;
+ }
+ musb_power = mA;
+ DBG(1, "Set VBUS Power = %d mA\n", mA);
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_CONFIGURED);
+ return 0;
+}
+/**
+ * musb_platform_set_mode() - Set the mode for the USB driver.
+ * @musb: struct musb pointer.
+ * @musb_mode: usb mode.
+ *
+ * This function set the mode for the USB.
+ */
+int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+{
+ u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ switch (musb_mode) {
+ case MUSB_HOST:
+ otg_set_host(musb->xceiv, musb->xceiv->host);
+ break;
+ case MUSB_PERIPHERAL:
+ otg_set_peripheral(musb->xceiv, musb->xceiv->gadget);
+ break;
+ case MUSB_OTG:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+/**
+ * funct_host_notify_timer() - Initialize the timer for USB host driver.
+ * @data: usb host data.
+ *
+ * This function runs the timer for the USB host mode.
+ */
+static void funct_host_notify_timer(unsigned long data)
+{
+ struct musb *musb = (void *)data;
+ unsigned long flags;
+ u8 devctl;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ } else {
+ musb->xceiv->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+#ifdef CONFIG_PM
+ stm_musb_context(USB_DISABLE);
+#endif
+
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+ DBG(1, "otg_state %s devctl %d\n", otg_state_string(musb), devctl);
+}
+
+/**
+ * musb_platform_init() - Initialize the platform USB driver.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the USB controller and Phy.
+ */
+int __init musb_platform_init(struct musb *musb, void *board_data)
+{
+ int ret;
+
+ usb_nop_xceiv_register();
+
+ musb->xceiv = otg_get_transceiver();
+ if (!musb->xceiv) {
+ pr_err("U8500 USB : no transceiver configured\n");
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = musb_stm_hs_otg_init(musb);
+ if (ret < 0)
+ goto done;
+ if (is_host_enabled(musb))
+ musb->board_set_vbus = set_vbus;
+ if (is_peripheral_enabled(musb))
+ musb->xceiv->set_power = set_power;
+
+ ret = musb_phy_en(musb->board_mode);
+ if (ret < 0)
+ goto done;
+
+ if (musb_status == NULL) {
+ musb_status = musb;
+ spin_lock_init(&musb_ulpi_spinlock);
+ }
+
+ /* Registering usb device for sysfs */
+ usbstatus_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
+
+ if (usbstatus_kobj == NULL)
+ ret = -ENOMEM;
+ usbstatus_kobj->ktype = &ktype_usbstatus;
+ kobject_init(usbstatus_kobj, usbstatus_kobj->ktype);
+
+ ret = kobject_set_name(usbstatus_kobj, "usb_status");
+ if (ret)
+ kfree(usbstatus_kobj);
+
+ ret = kobject_add(usbstatus_kobj, NULL, "usb_status");
+ if (ret)
+ kfree(usbstatus_kobj);
+
+ if (musb->board_mode != MUSB_PERIPHERAL) {
+ init_timer(&notify_timer);
+ notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ notify_timer.function = funct_host_notify_timer;
+ notify_timer.data = (unsigned long)musb;
+ add_timer(&notify_timer);
+ }
+
+ stm_usb_power_wq = create_singlethread_workqueue(
+ "stm_usb_power_wq");
+ if (stm_usb_power_wq == NULL)
+ return -ENOMEM;
+
+ INIT_WORK(&stm_prcmu_qos, stm_prcmu_qos_work);
+
+ ret = musb_force_detect(musb->board_mode);
+ if (ret < 0)
+ goto done;
+ return 0;
+
+done:
+ usb_nop_xceiv_unregister();
+ return ret;
+}
+/**
+ * musb_platform_exit() - unregister the platform USB driver.
+ * @musb: struct musb pointer.
+ *
+ * This function unregisters the USB controller.
+ */
+int musb_platform_exit(struct musb *musb)
+{
+ musb->clock = 0;
+
+ if (musb->board_mode != MUSB_PERIPHERAL)
+ del_timer_sync(&notify_timer);
+
+ usb_nop_xceiv_unregister();
+
+ musb_status = NULL;
+
+ return 0;
+}
diff --git a/drivers/usb/musb/stm_musb_dma.c b/drivers/usb/musb/stm_musb_dma.c
new file mode 100644
index 00000000000..468e96bdcea
--- /dev/null
+++ b/drivers/usb/musb/stm_musb_dma.c
@@ -0,0 +1,722 @@
+/* Copyright (C) 2009 ST-Ericsson SA
+ * Copyright (C) 2009 STMicroelectronics
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/pfn.h>
+#include "musb_core.h"
+#include "ste_config.h"
+#include "stm_musb_dma.h"
+#include <plat/ste_dma40.h>
+#include <mach/ste-dma40-db8500.h>
+
+/*
+ * U8500 system DMA used for USB can't transfer less
+ * than max packet size of Buldk EP which is 512
+ */
+#define U8500_USB_DMA_MIN_TRANSFER_SIZE 512
+
+/**
+ * dma_controller_start() - creates the logical channels pool and registers callbacks
+ * @c: pointer to DMA Controller
+ *
+ * This function requests the logical channels from the DMA driver and creates
+ * logical channels based on event lines and also registers the callbacks which
+ * are invoked after data transfer in the transmit or receive direction.
+*/
+
+static int dma_controller_start(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ struct stedma40_chan_cfg *info;
+ u8 bit;
+ struct dma_channel *channel = NULL;
+ /*bit 0 for receive and bit 1 for transmit*/
+#ifndef CONFIG_USB_U8500_DMA
+ for (bit = 0; bit < 2; bit++) {
+#else
+ for (bit = 0; bit < (U8500_DMA_END_POINTS*2); bit++) {
+#endif
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ musb_channel = &(controller->channel[bit]);
+ info = kzalloc(sizeof(struct stedma40_chan_cfg), GFP_KERNEL);
+ if (!info) {
+ ERR("could not allocate dma info structure\n");
+ return -1;
+ }
+ musb_channel->info = info;
+ musb_channel->controller = controller;
+#ifdef CONFIG_USB_U8500_DMA
+ info->high_priority = true;
+#else
+ info->mode = STEDMA40_MODE_PHYSICAL;
+ info->high_priority = true;
+#endif
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (bit) {
+#else
+ if ((bit <= TX_CHANNEL_7)) {
+#endif
+ int dst_dev_type;
+
+ info->dir = STEDMA40_MEM_TO_PERIPH;
+ info->src_dev_type = STEDMA40_DEV_SRC_MEMORY;
+
+#ifdef CONFIG_USB_U8500_DMA
+ switch (bit) {
+
+ case TX_CHANNEL_1:
+ dst_dev_type = DB8500_DMA_DEV38_USB_OTG_OEP_1_9;
+ break;
+ case TX_CHANNEL_2:
+ dst_dev_type =
+ DB8500_DMA_DEV37_USB_OTG_OEP_2_10;
+ break;
+ case TX_CHANNEL_3:
+ dst_dev_type =
+ DB8500_DMA_DEV36_USB_OTG_OEP_3_11;
+ break;
+ case TX_CHANNEL_4:
+ dst_dev_type =
+ DB8500_DMA_DEV19_USB_OTG_OEP_4_12;
+ break;
+ case TX_CHANNEL_5:
+ dst_dev_type =
+ DB8500_DMA_DEV18_USB_OTG_OEP_5_13;
+ break;
+ case TX_CHANNEL_6:
+ dst_dev_type =
+ DB8500_DMA_DEV17_USB_OTG_OEP_6_14;
+ break;
+ case TX_CHANNEL_7:
+ dst_dev_type =
+ DB8500_DMA_DEV16_USB_OTG_OEP_7_15;
+ break;
+
+ }
+
+ info->dst_dev_type = dst_dev_type;
+#endif
+
+ } else {
+ int src_dev_type;
+
+ info->dir = STEDMA40_PERIPH_TO_MEM;
+
+#ifdef CONFIG_USB_U8500_DMA
+ switch (bit) {
+ case RX_CHANNEL_1:
+ src_dev_type = DB8500_DMA_DEV38_USB_OTG_IEP_1_9;
+ break;
+ case RX_CHANNEL_2:
+ src_dev_type =
+ DB8500_DMA_DEV37_USB_OTG_IEP_2_10;
+ break;
+ case RX_CHANNEL_3:
+ src_dev_type =
+ DB8500_DMA_DEV36_USB_OTG_IEP_3_11;
+ break;
+ case RX_CHANNEL_4:
+ src_dev_type =
+ DB8500_DMA_DEV19_USB_OTG_IEP_4_12;
+ break;
+ case RX_CHANNEL_5:
+ src_dev_type =
+ DB8500_DMA_DEV18_USB_OTG_IEP_5_13;
+ break;
+ case RX_CHANNEL_6:
+ src_dev_type =
+ DB8500_DMA_DEV17_USB_OTG_IEP_6_14;
+ break;
+ case RX_CHANNEL_7:
+ src_dev_type =
+ DB8500_DMA_DEV16_USB_OTG_IEP_7_15;
+ break;
+ }
+
+ info->src_dev_type = src_dev_type;
+#endif
+ info->dst_dev_type = STEDMA40_DEV_DST_MEMORY;
+ }
+ info->src_info.data_width = STEDMA40_WORD_WIDTH;
+ info->src_info.psize = STEDMA40_PSIZE_LOG_16;
+
+ info->dst_info.data_width = STEDMA40_WORD_WIDTH ;
+ info->dst_info.psize = STEDMA40_PSIZE_LOG_16;
+ musb_channel->is_pipe_allocated = 1;
+ channel = &(musb_channel->channel);
+ channel->private_data = musb_channel;
+ channel->status = MUSB_DMA_STATUS_FREE;
+ channel->max_len = 0x10000;
+ /* Tx => mode 1; Rx => mode 0 */
+ channel->desired_mode = bit;
+ channel->actual_len = 0;
+
+ musb_channel->dma_chan = dma_request_channel(mask,
+ stedma40_filter,
+ info);
+ if (!musb_channel->dma_chan)
+ ERR("dma pipe can't be allocated\n");
+#ifndef CONFIG_USB_U8500_DMA
+ /* Tx => mode 1; Rx => mode 0 */
+ if (bit) {
+#else
+ if ((bit <= TX_CHANNEL_7)) {
+#endif
+ INIT_WORK(&musb_channel->channel_data_tx,
+ musb_channel_work_tx);
+ DBG(2, "channel allocated for TX, %s\n",
+ dma_chan_name(musb_channel->dma_chan));
+ } else {
+ INIT_WORK(&musb_channel->channel_data_rx,
+ musb_channel_work_rx);
+ DBG(2, "channel allocated for RX, %s\n",
+ dma_chan_name(musb_channel->dma_chan));
+ }
+
+ }
+ return 0;
+}
+
+static void dma_channel_release(struct dma_channel *channel);
+
+/**
+ * dma_controller_stop() - releases all the channels and frees the DMA pipes
+ * @c: pointer to DMA controller
+ *
+ * This function frees all of the logical channels and frees the DMA pipes
+*/
+
+static int dma_controller_stop(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel;
+ struct dma_channel *channel;
+ u8 bit;
+#ifndef CONFIG_USB_U8500_DMA
+ for (bit = 0; bit < 2; bit++) {
+#else
+ for (bit = 0; bit < (U8500_DMA_END_POINTS*2); bit++) {
+#endif
+ channel = &controller->channel[bit].channel;
+ musb_channel = channel->private_data;
+ dma_channel_release(channel);
+ if (musb_channel->info) {
+ dma_release_channel(musb_channel->dma_chan);
+ kfree(musb_channel->info);
+ musb_channel->info = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dma_controller_allocate() - allocates the DMA channels
+ * @c: pointer to DMA controller
+ * @hw_ep: pointer to endpoint
+ * @transmit: transmit or receive direction
+ *
+ * This function allocates the DMA channel and initializes
+ * the channel
+*/
+
+static struct dma_channel *dma_channel_allocate(struct dma_controller *c,
+ struct musb_hw_ep *hw_ep, u8 transmit)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ struct dma_channel *channel = NULL;
+ u8 bit;
+
+#ifndef CONFIG_USB_U8500_DMA
+
+ /*bit 0 for receive and bit 1 for transmit*/
+ for (bit = 0; bit < 2; bit++) {
+ if (!(controller->used_channels & (1 << bit))) {
+
+ if ((transmit && !bit))
+ continue;
+ if ((!transmit && bit))
+ break;
+#else
+ if (hw_ep->epnum > 0
+ && hw_ep->epnum <= U8500_DMA_END_POINTS) {
+ if (transmit)
+ bit = hw_ep->epnum - 1;
+ else
+ bit =
+ hw_ep->epnum + RX_END_POINT_OFFSET;
+ } else
+ return NULL;
+#endif
+ controller->used_channels |= (1 << bit);
+ musb_channel = &(controller->channel[bit]);
+ musb_channel->idx = bit;
+ musb_channel->epnum = hw_ep->epnum;
+ musb_channel->hw_ep = hw_ep;
+ musb_channel->transmit = transmit;
+ musb_channel->is_pipe_allocated = 1;
+ channel = &(musb_channel->channel);
+#ifndef CONFIG_USB_U8500_DMA
+ break;
+ }
+ }
+#endif
+ return channel;
+}
+
+/**
+ * dma_channel_release() - releases the DMA channel
+ * @channel: channel to be released
+ *
+ * This function releases the DMA channel
+ *
+*/
+
+static void dma_channel_release(struct dma_channel *channel)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ channel->actual_len = 0;
+ musb_channel->start_addr = 0;
+ musb_channel->len = 0;
+
+ DBG(2, "enter\n");
+ musb_channel->controller->used_channels &=
+ ~(1 << musb_channel->idx);
+
+ channel->status = MUSB_DMA_STATUS_FREE;
+ if (musb_channel->is_pipe_allocated)
+ musb_channel->is_pipe_allocated = 0;
+ DBG(2, "exit\n");
+}
+
+/**
+ * configure_channel() - configures the source, destination addresses and
+ * starts the transfer
+ * @channel: pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: Dma mode
+ * @dma_addr: DMA source address for transmit direction
+ * or DMA destination address for receive direction
+ * @len: length
+ * This function configures the source and destination addresses for DMA
+ * operation and initiates the DMA transfer
+*/
+
+static bool configure_channel(struct dma_channel *channel,
+ u16 packet_sz, u8 mode,
+ dma_addr_t dma_addr, u32 len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ u32 dma_count;
+ struct dma_chan *dma_chan = musb_channel->dma_chan;
+ struct dma_async_tx_descriptor *dma_desc;
+ enum dma_data_direction direction;
+ struct scatterlist sg;
+
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_qh *qh;
+ struct urb *urb;
+#endif
+ unsigned int usb_fifo_addr =
+ (unsigned int)(MUSB_FIFO_OFFSET(hw_ep->epnum) + mbase);
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (musb_channel->transmit)
+ qh = hw_ep->out_qh;
+ else
+ qh = hw_ep->in_qh;
+ urb = next_urb(qh);
+#endif
+
+ dma_count = len - (len % packet_sz);
+ musb_channel->cur_len = dma_count;
+ usb_fifo_addr =
+ U8500_USBOTG_BASE + ((unsigned int)usb_fifo_addr & 0xFFFF);
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ if (musb_channel->transmit)
+ dma_addr = musb->tx_dma_phy;
+ else
+ dma_addr = musb->rx_dma_phy;
+ }
+#endif
+
+ stedma40_set_dev_addr(dma_chan, usb_fifo_addr, usb_fifo_addr);
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_addr)), dma_count,
+ offset_in_page(dma_addr));
+ sg_dma_address(&sg) = dma_addr;
+ sg_dma_len(&sg) = dma_count;
+
+ direction = musb_channel->transmit ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ dma_desc = dma_chan->device->
+ device_prep_slave_sg(dma_chan, &sg, 1, direction,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!dma_desc)
+ return false;
+
+ dma_desc->callback = musb_channel->transmit ?
+ musb_tx_dma_controller_handler :
+ musb_rx_dma_controller_handler;
+ dma_desc->callback_param = channel;
+ dma_desc->tx_submit(dma_desc);
+ dma_async_issue_pending(dma_chan);
+
+ return true;
+}
+
+/**
+ * dma_channel_program() - Configures the channel and initiates transfer
+ * @channel: pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: mode
+ * @dma_addr: physical address of memory
+ * @len: length
+ *
+ * This function configures the channel and initiates the DMA transfer
+*/
+
+static int dma_channel_program(struct dma_channel *channel,
+ u16 packet_sz, u8 mode,
+ dma_addr_t dma_addr, u32 len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ bool ret;
+
+ BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
+ channel->status == MUSB_DMA_STATUS_BUSY);
+ if (len < U8500_USB_DMA_MIN_TRANSFER_SIZE)
+ return false;
+ if (!musb_channel->transmit && len < packet_sz)
+ return false;
+ channel->actual_len = 0;
+ musb_channel->start_addr = dma_addr;
+ musb_channel->len = len;
+ musb_channel->max_packet_sz = packet_sz;
+ channel->status = MUSB_DMA_STATUS_BUSY;
+
+
+ if ((mode == 1) && (len >= packet_sz))
+ ret = configure_channel(channel, packet_sz, 1, dma_addr, len);
+ else
+ ret = configure_channel(channel, packet_sz, 0, dma_addr, len);
+ return ret;
+}
+
+/**
+ * dma_channel_abort() - aborts the DMA transfer
+ * @channel: pointer to DMA channel.
+ *
+ * This function aborts the DMA transfer.
+*/
+
+static int dma_channel_abort(struct dma_channel *channel)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ void __iomem *mbase = musb_channel->controller->base;
+ u16 csr;
+ if (channel->status == MUSB_DMA_STATUS_BUSY) {
+ if (musb_channel->transmit) {
+
+ csr = musb_readw(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum,
+ MUSB_TXCSR));
+ csr &= ~(MUSB_TXCSR_AUTOSET |
+ MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_DMAMODE);
+ musb_writew(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum, MUSB_TXCSR),
+ csr);
+ } else {
+ csr = musb_readw(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum,
+ MUSB_RXCSR));
+ csr &= ~(MUSB_RXCSR_AUTOCLEAR |
+ MUSB_RXCSR_DMAENAB |
+ MUSB_RXCSR_DMAMODE);
+ musb_writew(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR),
+ csr);
+ }
+
+ if (musb_channel->is_pipe_allocated) {
+ musb_channel->dma_chan->device->
+ device_control(musb_channel->dma_chan, DMA_TERMINATE_ALL, 0);
+ channel->status = MUSB_DMA_STATUS_FREE;
+ }
+ }
+ return 0;
+}
+
+#include <linux/usb/composite.h>
+
+static int dma_is_compatible(struct dma_channel *channel,
+ struct musb_request *req)
+{
+
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ struct usb_descriptor_header **descriptors;
+ struct usb_function *f;
+
+ struct usb_gadget *gadget = &musb->g;
+
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+
+ if (!strcmp(f->name, "cdc_ethernet") ||
+ !strcmp(f->name, "rndis") ||
+ !strcmp(f->name, "adb")) {
+
+ if (gadget->speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+
+ for (; *descriptors; ++descriptors) {
+ struct usb_endpoint_descriptor *ep;
+
+ if ((*descriptors)->bDescriptorType !=
+ USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)
+ *descriptors;
+
+ if (ep->bEndpointAddress == req->epnum)
+ return 0;
+ }
+
+ }
+ }
+
+ if (req->request.length < U8500_USB_DMA_MIN_TRANSFER_SIZE)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * musb_rx_dma_controller_handler() - callback invoked when the data is received in the receive direction
+ * @private_data: DMA channel
+ *
+ * This callback is invoked when the DMA transfer is completed
+ * in the receive direction.
+*/
+void musb_rx_dma_controller_handler(void *private_data)
+{
+ struct dma_channel *channel = (struct dma_channel *)private_data;
+ struct musb_dma_channel *musb_channel = channel->private_data;
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags, pio;
+ unsigned int rxcsr;
+ struct musb_qh *qh = hw_ep->in_qh;
+ struct urb *urb;
+ spin_lock_irqsave(&musb->lock, flags);
+ urb = next_urb(qh);
+ musb_ep_select(mbase, hw_ep->epnum);
+ channel->actual_len = musb_channel->cur_len;
+ pio = musb_channel->len - channel->actual_len;
+ if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ memcpy(urb->transfer_buffer,
+ (void *)musb->rx_dma_log, channel->actual_len);
+ musb_memcpy(urb->transfer_buffer,
+ (void *)musb->rx_dma_log, channel->actual_len);
+ }
+ if (!pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+#else
+ schedule_work(&musb_channel->channel_data_rx);
+#endif
+}
+
+/**
+ * musb_tx_dma_controller_handler() - callback invoked on the transmit direction DMA data transfer
+ * @private_data: pointer to DMA channel.
+ *
+ * This callback is invoked when the DMA tranfer is completed
+ * in the transmit direction
+*/
+
+void musb_tx_dma_controller_handler(void *private_data)
+{
+ struct dma_channel *channel = (struct dma_channel *)private_data;
+ struct musb_dma_channel *musb_channel = channel->private_data;
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags, pio;
+ unsigned int txcsr;
+ struct musb_qh *qh = hw_ep->out_qh;
+ struct urb *urb;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_ep_select(mbase, hw_ep->epnum);
+ channel->actual_len = musb_channel->cur_len;
+ pio = musb_channel->len - channel->actual_len;
+ if (!pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ }
+ if (pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ urb = next_urb(qh);
+ qh->offset += channel->actual_len;
+ buf = urb->transfer_buffer + qh->offset;
+ musb_write_fifo(hw_ep, pio, buf);
+ qh->segsize = pio;
+ musb_writew(hw_ep->regs, MUSB_TXCSR, MUSB_TXCSR_TXPKTRDY);
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+#else
+ schedule_work(&musb_channel->channel_data_tx);
+#endif
+}
+
+/**
+ * dma_controller_destroy() - deallocates the DMA controller
+ * @c: pointer to dma controller.
+ *
+ * This function deallocates the DMA controller.
+*/
+
+void dma_controller_destroy(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ u8 bit;
+ if (!controller)
+ return;
+ for (bit = 0; bit < MUSB_HSDMA_CHANNELS; bit++)
+ musb_channel = &(controller->channel[bit]);
+ if (controller->irq)
+ free_irq(controller->irq, c);
+
+ kfree(controller);
+}
+
+/**
+ * musb_channel_work_tx() - Invoked by worker thread
+ * @data: worker queue data
+ *
+ * This function is invoked by worker thread when the DMA transfer
+ * is completed in the transmit direction.
+*/
+
+static void musb_channel_work_tx(struct work_struct *data)
+{
+ struct musb_dma_channel *musb_channel = container_of(data,
+ struct musb_dma_channel, channel_data_tx);
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ unsigned long flags;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_channel->channel.actual_len = musb_channel->cur_len;
+ musb_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ musb_ep_select(musb->mregs, hw_ep->epnum);
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+/**
+ * musb_channel_work_tx() - Invoked by worker thread
+ * @data: worker queue data
+ *
+ * This function is invoked by worker thread when the
+ * DMA transfer is completed in the receive direction.
+*/
+
+static void musb_channel_work_rx(struct work_struct *data)
+{
+ struct musb_dma_channel *musb_channel = container_of(data,
+ struct musb_dma_channel, channel_data_rx);
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_channel->channel.actual_len = musb_channel->cur_len;
+ musb_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ musb_ep_select(mbase, hw_ep->epnum);
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+/**
+ * dma_controller_create() - creates the dma controller and initializes callbacks
+ *
+ * @musb: pointer to mentor core driver data instance|
+ * @base: base address of musb registers.
+ *
+ * This function creates the DMA controller and initializes the callbacks
+ * that are invoked from the Mentor IP core.
+*/
+
+struct dma_controller *__init
+dma_controller_create(struct musb *musb, void __iomem *base)
+{
+ struct musb_dma_controller *controller;
+
+ controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return NULL;
+
+ controller->channel_count = MUSB_HSDMA_CHANNELS;
+ controller->private_data = musb;
+ controller->base = base;
+
+ controller->controller.start = dma_controller_start;
+ controller->controller.stop = dma_controller_stop;
+ controller->controller.channel_alloc = dma_channel_allocate;
+ controller->controller.channel_release = dma_channel_release;
+ controller->controller.channel_program = dma_channel_program;
+ controller->controller.channel_abort = dma_channel_abort;
+ controller->controller.is_compatible = dma_is_compatible;
+
+ return &controller->controller;
+}
diff --git a/drivers/usb/musb/stm_musb_dma.h b/drivers/usb/musb/stm_musb_dma.h
new file mode 100644
index 00000000000..753cf08a882
--- /dev/null
+++ b/drivers/usb/musb/stm_musb_dma.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM_MUSB_DMA_H__
+#define __STM_MUSB_DMA_H__
+
+#define MUSB_HSDMA_CHANNELS 16
+struct musb_dma_controller;
+struct dma_chan;
+struct stedma40_chan_cfg;
+struct musb_dma_channel {
+ struct dma_channel channel;
+ struct musb_dma_controller *controller;
+ struct stedma40_chan_cfg *info;
+ struct musb_hw_ep *hw_ep;
+ struct work_struct channel_data_tx;
+ struct work_struct channel_data_rx;
+ u32 start_addr;
+ u32 len;
+ u32 is_pipe_allocated;
+ u16 max_packet_sz;
+ u8 idx;
+ struct dma_chan *dma_chan;
+ unsigned int cur_len;
+ u8 epnum;
+ u8 last_xfer;
+ u8 transmit;
+};
+
+struct musb_dma_controller {
+ struct dma_controller controller;
+ struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS];
+ void *private_data;
+ void __iomem *base;
+ u8 channel_count;
+ u8 used_channels;
+ u8 irq;
+};
+void musb_rx_dma_controller_handler(void *private_data);
+void musb_tx_dma_controller_handler(void *private_data);
+static void musb_channel_work_tx(struct work_struct *data);
+static void musb_channel_work_rx(struct work_struct *data);
+#endif
+