Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: USB: fix usb-serial/ftdi build warning USB: fix usb-serial/generic build warning USB: another entry for the quirk list USB: remove duplicated device id in airprime driver USB: omap_udc: workaround dma_free_coherent() bogosity UHCI: Fix problem caused by lack of terminating QH
This commit is contained in:
commit
8b66a454bf
|
@ -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 },
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) |
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue