Update of /cvsroot/gc-linux/linux/drivers/usb/host
In directory ddv4jf1.ch3.sourceforge.com:/tmp/cvs-serv22863/drivers/usb/host
Modified Files:
Kconfig rvl-sthcd.c
Log Message:
- merge 2.6.27
- add gcnvi_udbg driver
- add starlet-gpio driver
- add initial SDHC support to rvl-stsd driver
- add support for MEM1+MEM2 as normal RAM
- enhanced rvl-sthcd device detection
Index: Kconfig
===================================================================
RCS file: /cvsroot/gc-linux/linux/drivers/usb/host/Kconfig,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- Kconfig 14 Sep 2008 19:20:31 -0000 1.3
+++ Kconfig 15 Nov 2008 20:10:15 -0000 1.4
@@ -308,7 +308,7 @@
config USB_WII_HCD
tristate "Nintendo Wii HCD support"
- depends on USB && WII && EXPERIMENTAL
+ depends on USB && WII && !HIGHMEM && EXPERIMENTAL
help
The Nintendo Wii includes a USB 1.1 host controller that can be
accessed through the API provided by the starlet subsystem.
Index: rvl-sthcd.c
===================================================================
RCS file: /cvsroot/gc-linux/linux/drivers/usb/host/rvl-sthcd.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- rvl-sthcd.c 13 Sep 2008 19:42:57 -0000 1.2
+++ rvl-sthcd.c 15 Nov 2008 20:10:15 -0000 1.3
@@ -20,6 +20,10 @@
*
*/
+#ifdef CONFIG_HIGHMEM
+#error Sorry, this driver cannot currently work if HIGHMEM is y
+#endif
+
#define DBG(fmt, arg...) drv_printk(KERN_DEBUG, fmt, ##arg)
#include <linux/device.h>
@@ -73,6 +77,9 @@
*/
#define STHCD_MAX_CHUNK_SIZE (2048)
+#define STHCD_PORT_MAX_RESETS 2 /* maximum number of consecutive
+ * allowed for a port */
+#define STHCD_RESCAN_INTERVAL 5 /* seconds */
#define starlet_ioh_sg_entry(sg, ptr) \
starlet_ioh_sg_set_buf((sg), (ptr), sizeof(*(ptr)))
@@ -82,31 +89,21 @@
struct sthcd_port;
struct sthcd_oh;
-enum {
- __STHCD_UDEV_REOPEN = 0,
- __STHCD_UDEV_ISAHUB,
-};
-
/*
* starlet USB device abstraction (udev).
*
*/
struct sthcd_udev {
- unsigned long flags;
-#define STHCD_UDEV_ISAHUB (1 << __STHCD_UDEV_ISAHUB)
- /* REVISIT, not finally used */
-#define STHCD_UDEV_REOPEN (1 << __STHCD_UDEV_REOPEN)
-
u16 idVendor;
u16 idProduct;
- int fd;
+ int fd; /* starlet file descriptor */
u16 devnum; /* USB address set by kernel */
struct list_head node; /* in list of connected devices */
- struct sthcd_oh *oh;
+ struct sthcd_oh *oh; /* parent Open Host controller */
- struct list_head pep_list; /* list of peps */
+ struct list_head pep_list; /* list of private endpoints */
};
/*
@@ -121,6 +118,7 @@
enum {
__STHCD_PORT_INUSE = 0,
+ __STHCD_PORT_DOOMED,
};
@@ -131,8 +129,10 @@
struct sthcd_port {
unsigned long flags;
#define STHCD_PORT_INUSE (1 << __STHCD_PORT_INUSE)
+#define STHCD_PORT_DOOMED (1 << __STHCD_PORT_DOOMED)
u32 status_change;
+ unsigned nr_resets;
struct sthcd_udev udev; /* one udev per port */
};
@@ -143,14 +143,14 @@
*/
struct sthcd_oh {
unsigned int index;
- int fd;
+ int fd; /* starlet file descriptor */
unsigned int max_devids;
struct sthcd_devid *new_devids;
struct sthcd_devid *devids;
- unsigned int nr_devids;
+ unsigned int nr_devids; /* actual no of devices */
- struct sthcd_hcd *hcd;
+ struct sthcd_hcd *hcd; /* parent Host Controller */
};
/*
@@ -162,11 +162,12 @@
struct sthcd_oh oh[2];
- struct sthcd_port *ports;
+ struct sthcd_port *ports; /* array of ports */
unsigned int nr_ports;
+
struct list_head device_list; /* list of connected devices */
- wait_queue_head_t rescan_waitq;
+ wait_queue_head_t rescan_waitq; /* wait queue for the rescan task */
struct task_struct *rescan_task;
};
@@ -224,8 +225,6 @@
void *io_buf; /* data buffer */
size_t io_buf_len; /* length of io_buf */
- unsigned long serial; /* transfer serial number */
-
int request; /* ioctlv request */
union {
struct sthcd_bulk_intr_xfer_ctx *bulk_intr;
@@ -243,6 +242,7 @@
*
*/
+#if 0
static inline void print_buffer(void *buf, u32 size)
{
int i;
@@ -253,7 +253,7 @@
);
}
}
-
+#endif
/*
* Type conversion routines.
@@ -570,7 +570,7 @@
int error = 0;
if (!pep_is_enabled(pep)) {
- error = -ENOSPC;
+ error = -ESHUTDOWN;
goto done;
}
@@ -647,7 +647,6 @@
*/
static int sthcd_pep_setup_xfer(struct sthcd_pep *pep)
{
- static atomic_t serial;
struct urb *urb = pep->urb;
int request;
int error = 0;
@@ -674,9 +673,6 @@
pep->request = request;
starlet_ioh_sg_set_buf(&pep->io[0],
pep->io_buf, pep->io_buf_len);
-
- atomic_inc(&serial);
- pep->serial = atomic_read(&serial);
}
if (error < 0)
@@ -758,8 +754,9 @@
*/
if (usb_urb_dir_in(urb)) {
/* device -> host */
+ BUG_ON(!urb->transfer_buffer);
memcpy(urb->transfer_buffer + pep->io_xfer_offset,
- pep->io_buf, xfer_len);
+ pep->io_buf, xfer_len);
}
pep->io_xfer_offset += xfer_len;
@@ -789,7 +786,7 @@
}
if (!pep_is_enabled(pep)) {
- error = -ENOSPC;
+ error = -ESHUTDOWN;
goto done;
}
@@ -797,11 +794,13 @@
if (pep->io_buf_len > 0) {
if (usb_urb_dir_out(urb)) {
/* host -> device */
+ BUG_ON(!urb->transfer_buffer);
memcpy(pep->io_buf,
urb->transfer_buffer + pep->io_xfer_offset,
pep->io_buf_len);
}
}
+
starlet_ioh_sg_set_buf(&pep->io[0],
pep->io_buf, pep->io_buf_len);
@@ -827,9 +826,6 @@
__releases(sthcd->lock) __acquires(sthcd->lock)
{
struct usb_hcd *hcd = sthcd_to_hcd(sthcd);
-
- if (status < 0)
- DBG("%s: urb %p: status = %d\n", __func__, urb, status);
/*
* Release the hcd lock here as the callback may need to
@@ -889,12 +885,31 @@
*/
static int sthcd_pep_send_urb(struct sthcd_pep *pep, struct urb *urb)
{
+ struct sthcd_port *port = NULL;
+ struct sthcd_hcd *sthcd;
struct usb_ctrlrequest *req;
u16 typeReq, wValue;
int retval, fake;
int error;
+ /*
+ * Unconditionally fail urbs targetted at doomed ports.
+ */
+ if (pep->udev) {
+ port = udev_to_port(pep->udev);
+ if (test_bit(__STHCD_PORT_DOOMED, &port->flags)) {
+ error = -ENODEV;
+ goto done;
+ }
+ }
+
if (test_and_set_bit(__STHCD_PEP_XFERBUSY, &pep->flags)) {
+ /*
+ * There is a pep xfer in progress.
+ * Our urb is already queued on the usb device, so do nothing
+ * here and rely on the pep xfer callback to do the actual
+ * work when it's done with the current urb in flight.
+ */
error = 0;
goto done;
}
@@ -926,9 +941,23 @@
"address change %u->%u\n",
urb->dev->devnum, wValue);
}
- /* we have an udev because the takein was successful */
+ /*
+ * We are guaranteed to have an udev because the takein
+ * was successful.
+ */
pep->udev->devnum = wValue;
urb->actual_length = 0;
+
+ /* clear the port reset count, we have an address */
+ if (wValue) {
+ /*
+ * We need to retrieve the port again
+ * as we might have entered the function
+ * without an udev assigned to the pep.
+ */
+ port = udev_to_port(pep->udev);
+ port->nr_resets = 0;
+ }
fake = 1;
break;
default:
@@ -937,9 +966,10 @@
}
if (fake) {
+ sthcd = pep->sthcd;
/* finish this fake urb synchronously... */
- usb_hcd_unlink_urb_from_ep(sthcd_to_hcd(pep->sthcd), urb);
- sthcd_giveback_urb(pep->sthcd, urb, 0);
+ usb_hcd_unlink_urb_from_ep(sthcd_to_hcd(sthcd), urb);
+ sthcd_giveback_urb(sthcd, urb, 0);
/* ... and proceed with the next urb, if applicable */
sthcd_pep_cond_send_next_urb(pep);
} else {
@@ -970,9 +1000,21 @@
static void sthcd_pep_print(struct sthcd_pep *pep)
{
- DBG("%s: req %lu: io_buf=%p, io_buf_len=%u, io_xfer_offset=%u\n",
- __func__,
- pep->serial,
+ struct usb_device *udev;
+ u16 idVendor, idProduct;
+
+ idVendor = idProduct = 0xffff;
+ if (pep->urb) {
+ udev = pep->urb->dev;
+ if (udev) {
+ idVendor = le16_to_cpu(udev->descriptor.idVendor);
+ idProduct = le16_to_cpu(udev->descriptor.idProduct);
+ }
+ }
+ DBG("(%04X:%04X) request=%d,"
+ " io_buf=%p, io_buf_len=%u, io_xfer_offset=%u\n",
+ idVendor, idProduct,
+ pep->request,
pep->io_buf,
pep->io_buf_len,
pep->io_xfer_offset);
@@ -987,6 +1029,7 @@
int xfer_len = req->result;
struct sthcd_pep *pep = req->done_data;
int status = 0;
+ struct sthcd_port *port;
struct sthcd_hcd *sthcd;
struct usb_hcd *hcd;
struct urb *urb;
@@ -1004,10 +1047,9 @@
urb = pep->urb;
if (!urb) {
- /* urb was already dequeued */
- drv_printk(KERN_INFO, "pep %p: request %lu already dequeued\n",
- pep, pep->serial);
/*
+ * starlet completed an URB that was already dequeued.
+ *
* We must free here the memory used by the pep, including
* I/O buffers, avoiding dereferencing any USB stack data
* pointed by the pep, as it may be invalid now.
@@ -1022,28 +1064,42 @@
status = xfer_len;
xfer_len = 0;
- if (status != -7004 && status != 7003) {
- drv_printk(KERN_ERR, "req %lu: completed with"
- " error %d\n", pep->serial, status);
+ if (status != -7004 && status != -7003) {
+ drv_printk(KERN_ERR, "request completed"
+ " with error %d\n", status);
+ sthcd_pep_print(pep);
}
switch(status) {
- case -7005:
- status = -ESHUTDOWN;
- break;
case -7003:
case -7004:
/* endpoint stall */
status = -EPIPE;
break;
+ case -7005:
+ /* endpoint shutdown? */
+ status = -ESHUTDOWN;
+ break;
case -7008:
+ case -7022:
case -4:
- set_bit(__STHCD_PEP_DISABLED, &pep->flags);
/* FALL-THROUGH */
default:
- status = -EPIPE;
+ /*
+ * We got an unknown, probably un-retryable, error.
+ * Flag the port as unuseable. The associated
+ * device will be disconnected ASAP.
+ */
+ port = udev_to_port(pep->udev);
+ set_bit(__STHCD_PORT_DOOMED, &port->flags);
+ DBG("%s: error %d on port %d, doomed!\n", __func__,
+ status, port - pep->sthcd->ports + 1);
+
+ /* also, do not use the pep for xfers anymore */
+ set_bit(__STHCD_PEP_DISABLED, &pep->flags);
+ status = -ENODEV;
+ break;
}
- sthcd_pep_print(pep);
} else {
if (usb_pipecontrol(urb->pipe)) {
/*
@@ -1054,10 +1110,9 @@
*/
xfer_len -= sizeof(struct usb_ctrlrequest);
if (xfer_len < 0) {
- drv_printk(KERN_ERR, "request %lu"
- " incomplete,"
+ drv_printk(KERN_ERR, "request incomplete,"
" %d bytes short\n",
- pep->serial, -xfer_len);
+ -xfer_len);
status = -EPIPE;
xfer_len = 0;
}
@@ -1414,20 +1469,32 @@
switch (typeReq) {
case GetPortStatus: /* 0xA300 */
+ if (test_bit(__STHCD_PORT_DOOMED, &port->flags)) {
+ /* disconnect */
+ if (!!(port->status_change & USB_PORT_STAT_CONNECTION))
+ port->status_change |=
+ (USB_PORT_STAT_C_CONNECTION<<16);
+ port->status_change &= ~USB_PORT_STAT_CONNECTION;
+ }
/* REVISIT wait 50ms before clearing the RESET state */
if (port->status_change & USB_PORT_STAT_RESET) {
- spin_unlock_irqrestore(&sthcd->lock, flags);
-
- spin_lock_irqsave(&sthcd->lock, flags);
- port->status_change = (USB_PORT_STAT_POWER |
- USB_PORT_STAT_ENABLE |
- USB_PORT_STAT_C_RESET << 16);
+ port->nr_resets++;
+ if (port->nr_resets > 2) {
+ DBG("%s: port %d was reset %u time(s),"
+ " doomed!\n", __func__,
+ wIndex+1, port->nr_resets);
+ set_bit(__STHCD_PORT_DOOMED, &port->flags);
+ }
+ if (!(port->status_change & USB_PORT_STAT_ENABLE))
+ port->status_change |=
+ (USB_PORT_STAT_C_ENABLE << 16);
+ port->status_change &= ~USB_PORT_STAT_RESET;
+ port->status_change |= (USB_PORT_STAT_ENABLE |
+ (USB_PORT_STAT_C_RESET << 16));
port->udev.devnum = 0;
}
- if (port->udev.oh)
- port->status_change |= USB_PORT_STAT_CONNECTION;
- ((__le32 *) buf)[0] = cpu_to_le32(port->status_change);
retval = 4;
+ ((__le32 *) buf)[0] = cpu_to_le32(port->status_change);
break;
case ClearPortFeature: /* 0x2301 */
switch (wValue) {
@@ -1527,20 +1594,40 @@
static int sthcd_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct sthcd_hcd *sthcd = hcd_to_sthcd(hcd);
- struct sthcd_port *port;
u16 *p = (u16 *)buf;
- int i;
+ struct sthcd_port *port;
+ unsigned long flags;
+ int i, result;
+
+ if (!HC_IS_RUNNING(hcd->state))
+ return -ESHUTDOWN;
+
+#if 0
+ if (timer_pending(&hcd->rh_timer))
+ return 0;
+#endif
/* FIXME, this code assumes at least 9 and no more than 15 ports */
BUG_ON(sthcd->nr_ports > 15 || sthcd->nr_ports < 8);
+ spin_lock_irqsave(&sthcd->lock, flags);
+
port = sthcd->ports;
for(i = 0, *p = 0; i < sthcd->nr_ports; i++, port++) {
- if ((port->status_change & 0xffff0000) != 0)
+ if ((port->status_change & 0xffff0000) != 0) {
*p |= 1 << (i+1);
+ /* REVISIT */
+ //break;
+ }
}
*p = le16_to_cpu(*p);
- return (*p != 0)?2:0;
+ result = (*p != 0)?2:0;
+
+ spin_unlock_irqrestore(&sthcd->lock, flags);
+
+// DBG("%s: poll cycle, changes=%04x\n", __func__, *p);
+
+ return result;
}
@@ -1552,11 +1639,16 @@
static int sthcd_oh_insert_udev(struct sthcd_oh *oh,
u16 idVendor, u16 idProduct)
{
+ struct sthcd_hcd *sthcd = oh->hcd;
struct sthcd_udev *udev;
struct sthcd_port *port;
+ unsigned long flags;
int error;
+
+ drv_printk(KERN_INFO, "inserting device %04X.%04X\n",
+ idVendor, idProduct);
- udev = sthcd_get_free_udev(oh->hcd);
+ udev = sthcd_get_free_udev(sthcd);
if (!udev) {
drv_printk(KERN_ERR, "no free udevs!\n");
return -EBUSY;
@@ -1564,11 +1656,15 @@
error = sthcd_udev_init(udev, oh, idVendor, idProduct);
if (!error) {
+ spin_lock_irqsave(&sthcd->lock, flags);
+
port = udev_to_port(udev);
- /* connect */
- port->status_change |= (USB_PORT_STAT_CONNECTION |
- USB_PORT_STAT_C_CONNECTION << 16);
- usb_hcd_poll_rh_status(sthcd_to_hcd(oh->hcd));
+ /* notify a connection event */
+ port->status_change = USB_PORT_STAT_POWER |
+ USB_PORT_STAT_CONNECTION |
+ (USB_PORT_STAT_C_CONNECTION<<16);
+
+ spin_unlock_irqrestore(&sthcd->lock, flags);
}
return error;
}
@@ -1576,24 +1672,35 @@
static int sthcd_oh_remove_udev(struct sthcd_oh *oh,
u16 idVendor, u16 idProduct)
{
+ struct sthcd_hcd *sthcd = oh->hcd;
struct sthcd_udev *udev;
struct sthcd_port *port;
+ u32 old_status;
+ unsigned long flags;
int error = 0;
- udev = sthcd_find_udev_by_ids(oh->hcd, idVendor, idProduct);
+ udev = sthcd_find_udev_by_ids(sthcd, idVendor, idProduct);
if (!udev) {
/* normally reached for ignored hubs */
error = -ENODEV;
} else {
+ drv_printk(KERN_INFO, "removing device %04X.%04X\n",
+ idVendor, idProduct);
sthcd_udev_exit(udev);
+
+ spin_lock_irqsave(&sthcd->lock, flags);
+
port = udev_to_port(udev);
clear_bit(__STHCD_PORT_INUSE, &port->flags);
- /* disconnect */
- port->status_change = (USB_PORT_STAT_POWER |
- USB_PORT_STAT_RESET |
- USB_PORT_STAT_C_RESET << 16);
- port->status_change |= (USB_PORT_STAT_C_CONNECTION << 16);
- usb_hcd_poll_rh_status(sthcd_to_hcd(oh->hcd));
+ clear_bit(__STHCD_PORT_DOOMED, &port->flags);
+ port->nr_resets = 0;
+ /* notify a disconnection event */
+ old_status = port->status_change;
+ port->status_change = USB_PORT_STAT_POWER;
+ if ((old_status & USB_PORT_STAT_CONNECTION) != 0)
+ port->status_change |= (USB_PORT_STAT_C_CONNECTION<<16);
+
+ spin_unlock_irqrestore(&sthcd->lock, flags);
}
return error;
}
@@ -1779,8 +1886,11 @@
static int sthcd_oh_rescan(struct sthcd_oh *oh)
{
+ static unsigned int poll_cycles = 0;
+ struct usb_hcd *hcd = sthcd_to_hcd(oh->hcd);
struct sthcd_devid *p;
int nr_new_devids, i;
+ int changes;
int error;
error = sthcd_get_device_list(oh->hcd, oh->fd, oh->new_devids,
@@ -1789,14 +1899,16 @@
return error;
nr_new_devids = error;
+ changes = 0;
for(i = 0; i < oh->nr_devids; i++) {
p = &oh->devids[i];
if (!sthcd_devid_find(oh->new_devids, nr_new_devids, p)) {
/* removal */
- drv_printk(KERN_INFO, "removing device %04X.%04X\n",
- p->idVendor, p->idProduct);
- sthcd_oh_remove_udev(oh, p->idVendor, p->idProduct);
+ error = sthcd_oh_remove_udev(oh, p->idVendor,
+ p->idProduct);
+ if (!error)
+ changes++;
}
}
@@ -1808,11 +1920,10 @@
p->idProduct);
if (error == 0) {
/* not a hub, register the usb device */
- drv_printk(KERN_INFO,
- "inserting device %04X.%04X\n",
- p->idVendor, p->idProduct);
- sthcd_oh_insert_udev(oh, p->idVendor,
- p->idProduct);
+ error = sthcd_oh_insert_udev(oh, p->idVendor,
+ p->idProduct);
+ if (!error)
+ changes++;
} else {
drv_printk(KERN_INFO,
"ignoring hub %04X.%04X\n",
@@ -1824,6 +1935,33 @@
memcpy(oh->devids, oh->new_devids, nr_new_devids * sizeof(*p));
oh->nr_devids = nr_new_devids;
+ /*
+ * FIXME
+ * We ask here the USB layer to explicitly poll for root hub changes
+ * until we get at least two complete rescan cycles without changes.
+ *
+ * Otherwise, for unknown reasons, we end up missing the detection of
+ * some devices, even if the insertion/removal of these devices is
+ * properly signaled in port->status_change.
+ */
+ if (changes) {
+#if 1
+ if (!poll_cycles) {
+ hcd->poll_rh = 1;
+ usb_hcd_poll_rh_status(hcd);
+ }
+ poll_cycles = 2;
+ } else {
+ if (!poll_cycles) {
+ hcd->poll_rh = 0;
+ } else {
+ poll_cycles--;
+ }
+#else
+ usb_hcd_poll_rh_status(hcd);
+#endif
+ }
+
return 0;
}
@@ -1868,22 +2006,6 @@
oh->devids = NULL;
}
-#if 0
-static void sthcd_oh_cond_reopen(struct sthcd_oh *oh)
-{
- struct sthcd_hcd *sthcd = oh->hcd;
- struct sthcd_udev *udev;
-
- list_for_each_entry(udev, &sthcd->device_list, node) {
- if (test_and_clear_bit(__STHCD_UDEV_REOPEN, &udev->flags)) {
- sthcd_udev_open(udev);
- sthcd_udev_resume(udev);
- sthcd_udev_suspend(udev);
- }
- }
-}
-#endif
-
static int sthcd_rescan_thread(void *arg)
{
struct sthcd_hcd *sthcd = arg;
@@ -1902,10 +2024,9 @@
while(!kthread_should_stop()) {
sthcd_oh_rescan(oh);
- /* sthcd_oh_cond_reopen(oh); */
-
- /* re-check every 5 seconds */
- sleep_on_timeout(&sthcd->rescan_waitq, 5*HZ);
+ /* re-check again after the configured interval */
+ sleep_on_timeout(&sthcd->rescan_waitq,
+ STHCD_RESCAN_INTERVAL*HZ);
}
return 0;
}
@@ -2046,8 +2167,6 @@
ep = urb->ep;
pep = ep_to_pep(ep);
if (pep && pep->urb == urb) {
- DBG("%s: (pep %p, sn %lu, urb %p) urb in transit!\n", __func__,
- pep, pep->serial, urb);
/*
* There is an urb in flight.
*
@@ -2065,8 +2184,10 @@
done:
spin_unlock_irqrestore(&sthcd->lock, flags);
+#if 0
if (error < 0)
DBG("%s: error=%d (%x)\n", __func__, error, error);
+#endif
return error;
}
@@ -2090,7 +2211,7 @@
*
* Disable the private endpoint and take the urb out of it.
* The callback function will take care of freeing the pep
- * when the IOS call completes.
+ * when the starlet call completes.
*/
set_bit(__STHCD_PEP_DISABLED, &pep->flags);
sthcd_pep_takeout_urb(pep);
|