diff options
-rw-r--r-- | drivers/usb/core/quirks.c | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/omap_udc.c | 103 | ||||
-rw-r--r-- | drivers/usb/host/uhci-debug.c | 26 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/uhci-q.c | 94 | ||||
-rw-r--r-- | drivers/usb/serial/airprime.c | 1 | ||||
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 6 | ||||
-rw-r--r-- | drivers/usb/serial/generic.c | 7 |
8 files changed, 145 insertions, 98 deletions
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 0e5c646cb4f..f08ec85a6d6 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -30,7 +30,8 @@ static const struct usb_device_id usb_quirk_list[] = { /* HP 5300/5370C scanner */ { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, - + /* Seiko Epson Corp - Perfection 1670 */ + { USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, /* Elsa MicroLink 56k (V.250) */ { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 8f9a2b61542..b394e63894d 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -296,6 +296,15 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ +/* + * dma-coherent memory allocation (for dma-capable endpoints) + * + * NOTE: the dma_*_coherent() API calls suck. Most implementations are + * (a) page-oriented, so small buffers lose big; and (b) asymmetric with + * respect to calls with irqs disabled: alloc is safe, free is not. + * We currently work around (b), but not (a). + */ + static void * omap_alloc_buffer( struct usb_ep *_ep, @@ -307,6 +316,9 @@ omap_alloc_buffer( void *retval; struct omap_ep *ep; + if (!_ep) + return NULL; + ep = container_of(_ep, struct omap_ep, ep); if (use_dma && ep->has_dma) { static int warned; @@ -326,6 +338,35 @@ omap_alloc_buffer( return retval; } +static DEFINE_SPINLOCK(buflock); +static LIST_HEAD(buffers); + +struct free_record { + struct list_head list; + struct device *dev; + unsigned bytes; + dma_addr_t dma; +}; + +static void do_free(unsigned long ignored) +{ + spin_lock_irq(&buflock); + while (!list_empty(&buffers)) { + struct free_record *buf; + + buf = list_entry(buffers.next, struct free_record, list); + list_del(&buf->list); + spin_unlock_irq(&buflock); + + dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma); + + spin_lock_irq(&buflock); + } + spin_unlock_irq(&buflock); +} + +static DECLARE_TASKLET(deferred_free, do_free, 0); + static void omap_free_buffer( struct usb_ep *_ep, void *buf, @@ -333,13 +374,29 @@ static void omap_free_buffer( unsigned bytes ) { - struct omap_ep *ep; + if (!_ep) { + WARN_ON(1); + return; + } - ep = container_of(_ep, struct omap_ep, ep); - if (use_dma && _ep && ep->has_dma) - dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); - else - kfree (buf); + /* free memory into the right allocator */ + if (dma != DMA_ADDR_INVALID) { + struct omap_ep *ep; + struct free_record *rec = buf; + unsigned long flags; + + ep = container_of(_ep, struct omap_ep, ep); + + rec->dev = ep->udc->gadget.dev.parent; + rec->bytes = bytes; + rec->dma = dma; + + spin_lock_irqsave(&buflock, flags); + list_add_tail(&rec->list, &buffers); + tasklet_schedule(&deferred_free); + spin_unlock_irqrestore(&buflock, flags); + } else + kfree(buf); } /*-------------------------------------------------------------------------*/ @@ -1691,12 +1748,38 @@ ep0out_status_stage: udc->ep0_pending = 0; break; case USB_REQ_GET_STATUS: + /* USB_ENDPOINT_HALT status? */ + if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) + goto intf_status; + + /* ep0 never stalls */ + if (!(w_index & 0xf)) + goto zero_status; + + /* only active endpoints count */ + ep = &udc->ep[w_index & 0xf]; + if (w_index & USB_DIR_IN) + ep += 16; + if (!ep->desc) + goto do_stall; + + /* iso never stalls */ + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + goto zero_status; + + /* FIXME don't assume non-halted endpoints!! */ + ERR("%s status, can't report\n", ep->ep.name); + goto do_stall; + +intf_status: /* return interface status. if we were pedantic, * we'd detect non-existent interfaces, and stall. */ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) goto delegate; + +zero_status: /* return two zero bytes */ UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; UDC_DATA_REG = 0; @@ -2068,7 +2151,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) /*-------------------------------------------------------------------------*/ -static inline int machine_needs_vbus_session(void) +static inline int machine_without_vbus_sense(void) { return (machine_is_omap_innovator() || machine_is_omap_osk() @@ -2156,7 +2239,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) /* boards that don't have VBUS sensing can't autogate 48MHz; * can't enter deep sleep while a gadget driver is active. */ - if (machine_needs_vbus_session()) + if (machine_without_vbus_sense()) omap_vbus_session(&udc->gadget, 1); done: @@ -2179,7 +2262,7 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) if (udc->dc_clk != NULL) omap_udc_enable_clock(1); - if (machine_needs_vbus_session()) + if (machine_without_vbus_sense()) omap_vbus_session(&udc->gadget, 0); if (udc->transceiver) @@ -2822,7 +2905,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) hmc = HMC_1510; type = "(unknown)"; - if (machine_is_omap_innovator() || machine_is_sx1()) { + if (machine_without_vbus_sense()) { /* just set up software VBUS detect, and then * later rig it so we always report VBUS. * FIXME without really sensing VBUS, we can't diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 8d24d3dc0a6..1497371583b 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -145,7 +145,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) return out - buf; } -static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) +static int uhci_show_qh(struct uhci_hcd *uhci, + struct uhci_qh *qh, char *buf, int len, int space) { char *out = buf; int i, nurbs; @@ -190,6 +191,9 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) if (list_empty(&qh->queue)) { out += sprintf(out, "%*s queue is empty\n", space, ""); + if (qh == uhci->skel_async_qh) + out += uhci_show_td(uhci->term_td, out, + len - (out - buf), 0); } else { struct urb_priv *urbp = list_entry(qh->queue.next, struct urb_priv, node); @@ -343,6 +347,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) struct list_head *tmp, *head; int nframes, nerrs; __le32 link; + __le32 fsbr_link; static const char * const qh_names[] = { "unlink", "iso", "int128", "int64", "int32", "int16", @@ -424,21 +429,22 @@ check_link: out += sprintf(out, "Skeleton QHs\n"); + fsbr_link = 0; for (i = 0; i < UHCI_NUM_SKELQH; ++i) { int cnt = 0; - __le32 fsbr_link = 0; qh = uhci->skelqh[i]; out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \ - out += uhci_show_qh(qh, out, len - (out - buf), 4); + out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); /* Last QH is the Terminating QH, it's different */ if (i == SKEL_TERM) { if (qh_element(qh) != LINK_TO_TD(uhci->term_td)) out += sprintf(out, " skel_term_qh element is not set to term_td!\n"); - if (link == LINK_TO_QH(uhci->skel_term_qh)) - goto check_qh_link; - continue; + link = fsbr_link; + if (!link) + link = LINK_TO_QH(uhci->skel_term_qh); + goto check_qh_link; } head = &qh->node; @@ -448,7 +454,7 @@ check_link: qh = list_entry(tmp, struct uhci_qh, node); tmp = tmp->next; if (++cnt <= 10) - out += uhci_show_qh(qh, out, + out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); if (!fsbr_link && qh->skel >= SKEL_FSBR) fsbr_link = LINK_TO_QH(qh); @@ -463,8 +469,6 @@ check_link: link = LINK_TO_QH(uhci->skel_async_qh); else if (!uhci->fsbr_is_on) ; - else if (fsbr_link) - link = fsbr_link; else link = LINK_TO_QH(uhci->skel_term_qh); check_qh_link: @@ -573,8 +577,8 @@ static const struct file_operations uhci_debug_operations = { static inline void lprintk(char *buf) {} -static inline int uhci_show_qh(struct uhci_qh *qh, char *buf, - int len, int space) +static inline int uhci_show_qh(struct uhci_hcd *uhci, + struct uhci_qh *qh, char *buf, int len, int space) { return 0; } diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 44da4334f1d..d22da26ff16 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -632,7 +632,8 @@ static int uhci_start(struct usb_hcd *hcd) */ for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i) uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh); - uhci->skel_async_qh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM; + uhci->skel_async_qh->link = UHCI_PTR_TERM; + uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh); /* This dummy TD is to work around a bug in Intel PIIX controllers */ uhci_fill_td(uhci->term_td, 0, uhci_explen(0) | diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index f4ebdb3e488..19a0cc02b9a 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -45,43 +45,27 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) */ static void uhci_fsbr_on(struct uhci_hcd *uhci) { - struct uhci_qh *fsbr_qh, *lqh, *tqh; + struct uhci_qh *lqh; + /* The terminating skeleton QH always points back to the first + * FSBR QH. Make the last async QH point to the terminating + * skeleton QH. */ uhci->fsbr_is_on = 1; lqh = list_entry(uhci->skel_async_qh->node.prev, struct uhci_qh, node); - - /* Find the first FSBR QH. Linear search through the list is - * acceptable because normally FSBR gets turned on as soon as - * one QH needs it. */ - fsbr_qh = NULL; - list_for_each_entry_reverse(tqh, &uhci->skel_async_qh->node, node) { - if (tqh->skel < SKEL_FSBR) - break; - fsbr_qh = tqh; - } - - /* No FSBR QH means we must insert the terminating skeleton QH */ - if (!fsbr_qh) { - uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh); - wmb(); - lqh->link = uhci->skel_term_qh->link; - - /* Otherwise loop the last QH to the first FSBR QH */ - } else - lqh->link = LINK_TO_QH(fsbr_qh); + lqh->link = LINK_TO_QH(uhci->skel_term_qh); } static void uhci_fsbr_off(struct uhci_hcd *uhci) { struct uhci_qh *lqh; + /* Remove the link from the last async QH to the terminating + * skeleton QH. */ uhci->fsbr_is_on = 0; lqh = list_entry(uhci->skel_async_qh->node.prev, struct uhci_qh, node); - - /* End the async list normally and unlink the terminating QH */ - lqh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM; + lqh->link = UHCI_PTR_TERM; } static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) @@ -464,9 +448,8 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) */ static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) { - struct uhci_qh *pqh, *lqh; + struct uhci_qh *pqh; __le32 link_to_new_qh; - __le32 *extra_link = &link_to_new_qh; /* Find the predecessor QH for our new one and insert it in the list. * The list of QHs is expected to be short, so linear search won't @@ -476,31 +459,17 @@ static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) break; } list_add(&qh->node, &pqh->node); - qh->link = pqh->link; - - link_to_new_qh = LINK_TO_QH(qh); - - /* If this is now the first FSBR QH, take special action */ - if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR && - qh->skel >= SKEL_FSBR) { - lqh = list_entry(uhci->skel_async_qh->node.prev, - struct uhci_qh, node); - - /* If the new QH is also the last one, we must unlink - * the terminating skeleton QH and make the new QH point - * back to itself. */ - if (qh == lqh) { - qh->link = link_to_new_qh; - extra_link = &uhci->skel_term_qh->link; - - /* Otherwise the last QH must point to the new QH */ - } else - extra_link = &lqh->link; - } /* Link it into the schedule */ + qh->link = pqh->link; wmb(); - *extra_link = pqh->link = link_to_new_qh; + link_to_new_qh = LINK_TO_QH(qh); + pqh->link = link_to_new_qh; + + /* If this is now the first FSBR QH, link the terminating skeleton + * QH to it. */ + if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR) + uhci->skel_term_qh->link = link_to_new_qh; } /* @@ -561,31 +530,16 @@ static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) */ static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh) { - struct uhci_qh *pqh, *lqh; + struct uhci_qh *pqh; __le32 link_to_next_qh = qh->link; pqh = list_entry(qh->node.prev, struct uhci_qh, node); - - /* If this is the first FSBQ QH, take special action */ - if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR && - qh->skel >= SKEL_FSBR) { - lqh = list_entry(uhci->skel_async_qh->node.prev, - struct uhci_qh, node); - - /* If this QH is also the last one, we must link in - * the terminating skeleton QH. */ - if (qh == lqh) { - link_to_next_qh = LINK_TO_QH(uhci->skel_term_qh); - uhci->skel_term_qh->link = link_to_next_qh; - wmb(); - qh->link = link_to_next_qh; - - /* Otherwise the last QH must point to the new first FSBR QH */ - } else - lqh->link = link_to_next_qh; - } - pqh->link = link_to_next_qh; + + /* If this was the old first FSBR QH, link the terminating skeleton + * QH to the next (new first FSBR) QH. */ + if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR) + uhci->skel_term_qh->link = link_to_next_qh; mb(); } @@ -1217,7 +1171,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) if (debug > 1 && errbuf) { /* Print the chain for debugging */ - uhci_show_qh(urbp->qh, errbuf, + uhci_show_qh(uhci, urbp->qh, errbuf, ERRBUF_LEN, 0); lprintk(errbuf); } diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index 7538c64a509..39a49836259 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -18,7 +18,6 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */ - { USB_DEVICE(0x1410, 0x1100) }, /* ExpressCard34 Qualcomm 3G CDMA */ { USB_DEVICE(0x413c, 0x8115) }, /* Dell Wireless HSDPA 5500 */ { }, }; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1633a0fd48e..8ff9d54b21e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -879,6 +879,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) break; case FT232BM: /* FT232BM chip */ case FT2232C: /* FT2232C chip */ + case FT232RL: if (baud <= 3000000) { div_value = ftdi_232bm_baud_to_divisor(baud); } else { @@ -1021,9 +1022,12 @@ static void ftdi_determine_type(struct usb_serial_port *port) /* (It might be a BM because of the iSerialNumber bug, * but it will still work as an AM device.) */ priv->chip_type = FT8U232AM; - } else { + } else if (version < 0x600) { /* Assume its an FT232BM (or FT245BM) */ priv->chip_type = FT232BM; + } else { + /* Assume its an FT232R */ + priv->chip_type = FT232RL; } info("Detected %s", ftdi_chip_name[priv->chip_type]); } diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 53baeec8f26..4f8282ad772 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -20,13 +20,14 @@ #include <linux/usb/serial.h> #include <asm/uaccess.h> -static int generic_probe(struct usb_interface *interface, - const struct usb_device_id *id); - static int debug; #ifdef CONFIG_USB_SERIAL_GENERIC + +static int generic_probe(struct usb_interface *interface, + const struct usb_device_id *id); + static __u16 vendor = 0x05f9; static __u16 product = 0xffff; |