ChangeSet 1.1026, 2003/03/05 14:49:41-08:00, greg@kroah.com [PATCH] USB: move the UHCI drivers into drivers/usb/host drivers/usb/uhci-debug.h | 573 ------ drivers/usb/uhci.c | 3172 -------------------------------------- drivers/usb/uhci.h | 441 ----- drivers/usb/usb-uhci-debug.h | 195 -- drivers/usb/usb-uhci.c | 3143 ------------------------------------- drivers/usb/usb-uhci.h | 308 --- drivers/usb/Config.in | 8 drivers/usb/Makefile | 4 drivers/usb/host/Config.in | 8 drivers/usb/host/Makefile | 2 drivers/usb/host/uhci-debug.h | 573 ++++++ drivers/usb/host/uhci.c | 3172 ++++++++++++++++++++++++++++++++++++++ drivers/usb/host/uhci.h | 441 +++++ drivers/usb/host/usb-uhci-debug.h | 195 ++ drivers/usb/host/usb-uhci.c | 3143 +++++++++++++++++++++++++++++++++++++ drivers/usb/host/usb-uhci.h | 308 +++ 16 files changed, 7844 insertions(+), 7842 deletions(-) diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Thu Mar 6 14:22:16 2003 +++ b/drivers/usb/Config.in Thu Mar 6 14:22:16 2003 @@ -18,14 +18,6 @@ comment 'USB Host Controller Drivers' source drivers/usb/host/Config.in - if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then - dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB - fi - if [ "$CONFIG_USB_UHCI" != "y" ]; then - dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB - else - define_bool CONFIG_USB_UHCI_ALT n - fi dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB comment 'USB Device Class drivers' diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Thu Mar 6 14:22:16 2003 +++ b/drivers/usb/Makefile Thu Mar 6 14:22:16 2003 @@ -56,14 +56,14 @@ obj-y += host/ehci-hcd.o endif -obj-$(CONFIG_USB_UHCI) += usb-uhci.o -obj-$(CONFIG_USB_UHCI_ALT) += uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o ifneq ($(CONFIG_USB_EHCI_HCD),n) usbcore-objs += hcd.o endif subdir-$(CONFIG_USB_EHCI_HCD) += host +subdir-$(CONFIG_USB_UHCI_ALT) += host +subdir-$(CONFIG_USB_UHCI) += host obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_HID) += hid.o diff -Nru a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in --- a/drivers/usb/host/Config.in Thu Mar 6 14:22:16 2003 +++ b/drivers/usb/host/Config.in Thu Mar 6 14:22:16 2003 @@ -4,4 +4,12 @@ dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL # dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL # dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL +if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then + dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB +fi +if [ "$CONFIG_USB_UHCI" != "y" ]; then + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB +else + define_bool CONFIG_USB_UHCI_ALT n +fi diff -Nru a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile --- a/drivers/usb/host/Makefile Thu Mar 6 14:22:16 2003 +++ b/drivers/usb/host/Makefile Thu Mar 6 14:22:16 2003 @@ -6,6 +6,8 @@ O_TARGET := obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o +obj-$(CONFIG_USB_UHCI_ALT) += uhci.o +obj-$(CONFIG_USB_UHCI) += usb-uhci.o # obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o # obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o diff -Nru a/drivers/usb/host/uhci-debug.h b/drivers/usb/host/uhci-debug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/uhci-debug.h Thu Mar 6 14:22:16 2003 @@ -0,0 +1,573 @@ +/* + * UHCI-specific debugging code. Invaluable when something + * goes wrong, but don't get in my face. + * + * Kernel visible pointers are surrounded in []'s and bus + * visible pointers are surrounded in ()'s + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2001 Johannes Erdfelt + */ + +#include +#include +#include +#include + +#include "uhci.h" + +/* Handle REALLY large printk's so we don't overflow buffers */ +static void inline lprintk(char *buf) +{ + char *p; + + /* Just write one line at a time */ + while (buf) { + p = strchr(buf, '\n'); + if (p) + *p = 0; + printk("%s\n", buf); + buf = p; + if (buf) + buf++; + } +} + +static int inline uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td) +{ + int i; + + for (i = 0; i < UHCI_NUM_SKELTD; i++) + if (td == uhci->skeltd[i]) + return 1; + + return 0; +} + +static int inline uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + int i; + + for (i = 0; i < UHCI_NUM_SKELQH; i++) + if (qh == uhci->skelqh[i]) + return 1; + + return 0; +} + +static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) +{ + char *out = buf; + char *spid; + + /* Try to make sure there's enough memory */ + if (len < 160) + return 0; + + out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, td->link); + out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", + ((td->status >> 27) & 3), + (td->status & TD_CTRL_SPD) ? "SPD " : "", + (td->status & TD_CTRL_LS) ? "LS " : "", + (td->status & TD_CTRL_IOC) ? "IOC " : "", + (td->status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->status & TD_CTRL_NAK) ? "NAK " : "", + (td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", + td->status & 0x7ff); + + switch (td->info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = "OUT"; + break; + case USB_PID_IN: + spid = "IN"; + break; + default: + spid = "?"; + break; + } + + out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", + td->info >> 21, + ((td->info >> 19) & 1), + (td->info >> 15) & 15, + (td->info >> 8) & 127, + (td->info & 0xff), + spid); + out += sprintf(out, "(buf=%08x)\n", td->buffer); + + return out - buf; +} + +static int uhci_show_sc(int port, unsigned short status, char *buf, int len) +{ + char *out = buf; + + /* Try to make sure there's enough memory */ + if (len < 80) + return 0; + + out += sprintf(out, " stat%d = %04x %s%s%s%s%s%s%s%s\n", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); + + return out - buf; +} + +static int uhci_show_status(struct uhci *uhci, char *buf, int len) +{ + char *out = buf; + unsigned int io_addr = uhci->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + /* Try to make sure there's enough memory */ + if (len < 80 * 6) + return 0; + + usbcmd = inw(io_addr + 0); + usbstat = inw(io_addr + 2); + usbint = inw(io_addr + 4); + usbfrnum = inw(io_addr + 6); + flbaseadd = inl(io_addr + 8); + sof = inb(io_addr + 12); + portsc1 = inw(io_addr + 16); + portsc2 = inw(io_addr + 18); + + out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + out += sprintf(out, " usbstat = %04x %s%s%s%s%s%s\n", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + out += sprintf(out, " usbint = %04x\n", usbint); + out += sprintf(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, + 0xfff & (4*(unsigned int)usbfrnum)); + out += sprintf(out, " flbaseadd = %08x\n", flbaseadd); + out += sprintf(out, " sof = %02x\n", sof); + out += uhci_show_sc(1, portsc1, out, len - (out - buf)); + out += uhci_show_sc(2, portsc2, out, len - (out - buf)); + + return out - buf; +} + +static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) +{ + char *out = buf; + struct urb_priv *urbp; + struct list_head *head, *tmp; + struct uhci_td *td; + int i = 0, checked = 0, prevactive = 0; + + /* Try to make sure there's enough memory */ + if (len < 80 * 6) + return 0; + + out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "", + qh, qh->link, qh->element); + + if (qh->element & UHCI_PTR_QH) + out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); + + if (qh->element & UHCI_PTR_DEPTH) + out += sprintf(out, "%*s Depth traverse\n", space, ""); + + if (qh->element & 8) + out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, ""); + + if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH))) + out += sprintf(out, "%*s Element is NULL (bug?)\n", space, ""); + + if (!qh->urbp) { + out += sprintf(out, "%*s urbp == NULL\n", space, ""); + goto out; + } + + urbp = qh->urbp; + + head = &urbp->td_list; + tmp = head->next; + + td = list_entry(tmp, struct uhci_td, list); + + if (td->dma_handle != (qh->element & ~UHCI_PTR_BITS)) + out += sprintf(out, "%*s Element != First TD\n", space, ""); + + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + out += sprintf(out, "%*s%d: ", space + 2, "", i++); + out += uhci_show_td(td, out, len - (out - buf), 0); + + if (i > 10 && !checked && prevactive && tmp != head && + debug <= 2) { + struct list_head *ntmp = tmp; + struct uhci_td *ntd = td; + int active = 1, ni = i; + + checked = 1; + + while (ntmp != head && ntmp->next != head && active) { + ntd = list_entry(ntmp, struct uhci_td, list); + + ntmp = ntmp->next; + + active = ntd->status & TD_CTRL_ACTIVE; + + ni++; + } + + if (active && ni > i) { + out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i); + tmp = ntmp; + td = ntd; + i = ni; + } + } + + prevactive = td->status & TD_CTRL_ACTIVE; + } + + if (list_empty(&urbp->queue_list) || urbp->queued) + goto out; + + out += sprintf(out, "%*sQueued QH's:\n", -space, "--"); + + head = &urbp->queue_list; + tmp = head->next; + + while (tmp != head) { + struct urb_priv *nurbp = list_entry(tmp, struct urb_priv, + queue_list); + tmp = tmp->next; + + out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space); + } + +out: + return out - buf; +} + +static const char *td_names[] = {"skel_int1_td", "skel_int2_td", + "skel_int4_td", "skel_int8_td", + "skel_int16_td", "skel_int32_td", + "skel_int64_td", "skel_int128_td", + "skel_int256_td", "skel_term_td" }; +static const char *qh_names[] = { "skel_ls_control_qh", "skel_hs_control_qh", + "skel_bulk_qh", "skel_term_qh" }; + +#define show_frame_num() \ + if (!shown) { \ + shown = 1; \ + out += sprintf(out, "- Frame %d\n", i); \ + } + +#define show_td_name() \ + if (!shown) { \ + shown = 1; \ + out += sprintf(out, "- %s\n", td_names[i]); \ + } + +#define show_qh_name() \ + if (!shown) { \ + shown = 1; \ + out += sprintf(out, "- %s\n", qh_names[i]); \ + } + +static int uhci_sprint_schedule(struct uhci *uhci, char *buf, int len) +{ + char *out = buf; + int i; + struct uhci_qh *qh; + struct uhci_td *td; + struct list_head *tmp, *head; + + out += sprintf(out, "HC status\n"); + out += uhci_show_status(uhci, out, len - (out - buf)); + + out += sprintf(out, "Frame List\n"); + for (i = 0; i < UHCI_NUMFRAMES; ++i) { + int shown = 0; + td = uhci->fl->frame_cpu[i]; + if (!td) + continue; + + if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) { + show_frame_num(); + out += sprintf(out, " frame list does not match td->dma_handle!\n"); + } + if (uhci_is_skeleton_td(uhci, td)) + continue; + show_frame_num(); + + head = &td->fl_list; + tmp = head; + do { + td = list_entry(tmp, struct uhci_td, fl_list); + tmp = tmp->next; + out += uhci_show_td(td, out, len - (out - buf), 4); + } while (tmp != head); + } + + out += sprintf(out, "Skeleton TD's\n"); + for (i = UHCI_NUM_SKELTD - 1; i >= 0; i--) { + int shown = 0; + + td = uhci->skeltd[i]; + + if (debug > 1) { + show_td_name(); + out += uhci_show_td(td, out, len - (out - buf), 4); + } + + if (list_empty(&td->fl_list)) { + /* TD 0 is the int1 TD and links to control_ls_qh */ + if (!i) { + if (td->link != + (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH)) { + show_td_name(); + out += sprintf(out, " skeleton TD not linked to ls_control QH!\n"); + } + } else if (i < 9) { + if (td->link != uhci->skeltd[i - 1]->dma_handle) { + show_td_name(); + out += sprintf(out, " skeleton TD not linked to next skeleton TD!\n"); + } + } else { + show_td_name(); + + if (td->link != td->dma_handle) + out += sprintf(out, " skel_term_td does not link to self\n"); + + /* Don't show it twice */ + if (debug <= 1) + out += uhci_show_td(td, out, len - (out - buf), 4); + } + + continue; + } + + show_td_name(); + + head = &td->fl_list; + tmp = head->next; + + while (tmp != head) { + td = list_entry(tmp, struct uhci_td, fl_list); + + tmp = tmp->next; + + out += uhci_show_td(td, out, len - (out - buf), 4); + } + + if (!i) { + if (td->link != + (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH)) + out += sprintf(out, " last TD not linked to ls_control QH!\n"); + } else if (i < 9) { + if (td->link != uhci->skeltd[i - 1]->dma_handle) + out += sprintf(out, " last TD not linked to next skeleton!\n"); + } + } + + out += sprintf(out, "Skeleton QH's\n"); + + for (i = 0; i < UHCI_NUM_SKELQH; ++i) { + int shown = 0; + + qh = uhci->skelqh[i]; + + if (debug > 1) { + show_qh_name(); + out += uhci_show_qh(qh, out, len - (out - buf), 4); + } + + /* QH 3 is the Terminating QH, it's different */ + if (i == 3) { + if (qh->link != UHCI_PTR_TERM) { + show_qh_name(); + out += sprintf(out, " bandwidth reclamation on!\n"); + } + + if (qh->element != uhci->skel_term_td->dma_handle) { + show_qh_name(); + out += sprintf(out, " skel_term_qh element is not set to skel_term_td\n"); + } + } + + if (list_empty(&qh->list)) { + if (i < 3) { + if (qh->link != + (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) { + show_qh_name(); + out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n"); + } + } + + continue; + } + + show_qh_name(); + + head = &qh->list; + tmp = head->next; + + while (tmp != head) { + qh = list_entry(tmp, struct uhci_qh, list); + + tmp = tmp->next; + + out += uhci_show_qh(qh, out, len - (out - buf), 4); + } + + if (i < 3) { + if (qh->link != + (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) + out += sprintf(out, " last QH not linked to next skeleton!\n"); + } + } + + return out - buf; +} + +#ifdef CONFIG_PROC_FS +#define MAX_OUTPUT (PAGE_SIZE * 8) + +static struct proc_dir_entry *uhci_proc_root = NULL; + +struct uhci_proc { + int size; + char *data; + struct uhci *uhci; +}; + +static int uhci_proc_open(struct inode *inode, struct file *file) +{ + const struct proc_dir_entry *dp = inode->u.generic_ip; + struct uhci *uhci = dp->data; + struct uhci_proc *up; + unsigned long flags; + int ret = -ENOMEM; + + lock_kernel(); + up = kmalloc(sizeof(*up), GFP_KERNEL); + if (!up) + goto out; + + up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL); + if (!up->data) { + kfree(up); + goto out; + } + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT); + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); + + file->private_data = up; + + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence) +{ + struct uhci_proc *up = file->private_data; + loff_t new; + + switch (whence) { + case 0: + new = off; + break; + case 1: + new = file->f_pos + off; + break; + case 2: + default: + return -EINVAL; + } + if (new < 0 || new > up->size) + return -EINVAL; + return (file->f_pos = new); +} + +static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes, + loff_t *ppos) +{ + struct uhci_proc *up = file->private_data; + unsigned int pos; + unsigned int size; + + pos = *ppos; + size = up->size; + if (pos >= size) + return 0; + if (nbytes >= size) + nbytes = size; + if (pos + nbytes > size) + nbytes = size - pos; + + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EINVAL; + + copy_to_user(buf, up->data + pos, nbytes); + + *ppos += nbytes; + + return nbytes; +} + +static int uhci_proc_release(struct inode *inode, struct file *file) +{ + struct uhci_proc *up = file->private_data; + + kfree(up->data); + kfree(up); + + return 0; +} + +static struct file_operations uhci_proc_operations = { + open: uhci_proc_open, + llseek: uhci_proc_lseek, + read: uhci_proc_read, +// write: uhci_proc_write, + release: uhci_proc_release, +}; +#endif + diff -Nru a/drivers/usb/host/uhci.c b/drivers/usb/host/uhci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/uhci.c Thu Mar 6 14:22:16 2003 @@ -0,0 +1,3172 @@ +/* + * Universal Host Controller Interface driver for USB. + * + * Maintainer: Johannes Erdfelt + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * + * Intel documents this fairly well, and as far as I know there + * are no royalties or anything like that, but even so there are + * people who decided that they want to do the same thing in a + * completely different way. + * + * WARNING! The USB documentation is downright evil. Most of it + * is just crap, written by a committee. You're better off ignoring + * most of it, the important stuff is: + * - the low-level protocol (fairly simple but lots of small details) + * - working around the horridness of the rest + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#else +#undef DEBUG +#endif +#include + +#include +#include +#include +#include + +#include "uhci.h" + +#include + +#include "../hcd.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.1" +#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber" +#define DRIVER_DESC "USB Universal Host Controller Interface driver" + +/* + * debug = 0, no debugging messages + * debug = 1, dump failed URB's except for stalls + * debug = 2, dump all failed URB's (including stalls) + * show all queues in /proc/uhci/hc* + * debug = 3, show all TD's in URB's when dumping + */ +#ifdef DEBUG +static int debug = 1; +#else +static int debug = 0; +#endif +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug level"); +static char *errbuf; +#define ERRBUF_LEN (PAGE_SIZE * 8) + +#include "uhci-debug.h" + +static kmem_cache_t *uhci_up_cachep; /* urb_priv */ + +static int rh_submit_urb(struct urb *urb); +static int rh_unlink_urb(struct urb *urb); +static int uhci_get_current_frame_number(struct usb_device *dev); +static int uhci_unlink_urb(struct urb *urb); +static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb); +static void uhci_call_completion(struct urb *urb); + +static int ports_active(struct uhci *uhci); +static void suspend_hc(struct uhci *uhci); +static void wakeup_hc(struct uhci *uhci); + +/* If a transfer is still active after this much time, turn off FSBR */ +#define IDLE_TIMEOUT (HZ / 20) /* 50 ms */ +#define FSBR_DELAY (HZ / 20) /* 50 ms */ + +/* When we timeout an idle transfer for FSBR, we'll switch it over to */ +/* depth first traversal. We'll do it in groups of this number of TD's */ +/* to make sure it doesn't hog all of the bandwidth */ +#define DEPTH_INTERVAL 5 + +#define MAX_URB_LOOP 2048 /* Maximum number of linked URB's */ + +/* + * Only the USB core should call uhci_alloc_dev and uhci_free_dev + */ +static int uhci_alloc_dev(struct usb_device *dev) +{ + return 0; +} + +static int uhci_free_dev(struct usb_device *dev) +{ + return 0; +} + +/* + * Technically, updating td->status here is a race, but it's not really a + * problem. The worst that can happen is that we set the IOC bit again + * generating a spurios interrupt. We could fix this by creating another + * QH and leaving the IOC bit always set, but then we would have to play + * games with the FSBR code to make sure we get the correct order in all + * the cases. I don't think it's worth the effort + */ +static inline void uhci_set_next_interrupt(struct uhci *uhci) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + uhci->skel_term_td->status |= TD_CTRL_IOC; + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +static inline void uhci_clear_next_interrupt(struct uhci *uhci) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + uhci->skel_term_td->status &= ~TD_CTRL_IOC; + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +static inline void uhci_add_complete(struct urb *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + unsigned long flags; + + spin_lock_irqsave(&uhci->complete_list_lock, flags); + list_add(&urbp->complete_list, &uhci->complete_list); + spin_unlock_irqrestore(&uhci->complete_list_lock, flags); +} + +static struct uhci_td *uhci_alloc_td(struct uhci *uhci, struct usb_device *dev) +{ + dma_addr_t dma_handle; + struct uhci_td *td; + + td = pci_pool_alloc(uhci->td_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); + if (!td) + return NULL; + + td->dma_handle = dma_handle; + + td->link = UHCI_PTR_TERM; + td->buffer = 0; + + td->frame = -1; + td->dev = dev; + + INIT_LIST_HEAD(&td->list); + INIT_LIST_HEAD(&td->fl_list); + + usb_inc_dev_use(dev); + + return td; +} + +static void inline uhci_fill_td(struct uhci_td *td, __u32 status, + __u32 info, __u32 buffer) +{ + td->status = status; + td->info = info; + td->buffer = buffer; +} + +static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td) +{ + unsigned long flags; + struct uhci_td *ltd; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + + ltd = list_entry(skeltd->fl_list.prev, struct uhci_td, fl_list); + + td->link = ltd->link; + mb(); + ltd->link = td->dma_handle; + + list_add_tail(&td->fl_list, &skeltd->fl_list); + + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +/* + * We insert Isochronous transfers directly into the frame list at the + * beginning + * The layout looks as follows: + * frame list pointer -> iso td's (if any) -> + * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh + */ +static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum) +{ + unsigned long flags; + + framenum %= UHCI_NUMFRAMES; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + + td->frame = framenum; + + /* Is there a TD already mapped there? */ + if (uhci->fl->frame_cpu[framenum]) { + struct uhci_td *ftd, *ltd; + + ftd = uhci->fl->frame_cpu[framenum]; + ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list); + + list_add_tail(&td->fl_list, &ftd->fl_list); + + td->link = ltd->link; + mb(); + ltd->link = td->dma_handle; + } else { + td->link = uhci->fl->frame[framenum]; + mb(); + uhci->fl->frame[framenum] = td->dma_handle; + uhci->fl->frame_cpu[framenum] = td; + } + + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + /* If it's not inserted, don't remove it */ + spin_lock_irqsave(&uhci->frame_list_lock, flags); + if (td->frame == -1 && list_empty(&td->fl_list)) + goto out; + + if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) { + if (list_empty(&td->fl_list)) { + uhci->fl->frame[td->frame] = td->link; + uhci->fl->frame_cpu[td->frame] = NULL; + } else { + struct uhci_td *ntd; + + ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); + uhci->fl->frame[td->frame] = ntd->dma_handle; + uhci->fl->frame_cpu[td->frame] = ntd; + } + } else { + struct uhci_td *ptd; + + ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list); + ptd->link = td->link; + } + + mb(); + td->link = UHCI_PTR_TERM; + + list_del_init(&td->fl_list); + td->frame = -1; + +out: + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +/* + * Inserts a td into qh list at the top. + */ +static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td, *ptd; + + if (list_empty(&urbp->td_list)) + return; + + head = &urbp->td_list; + tmp = head->next; + + /* Ordering isn't important here yet since the QH hasn't been */ + /* inserted into the schedule yet */ + td = list_entry(tmp, struct uhci_td, list); + + /* Add the first TD to the QH element pointer */ + qh->element = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH); + + ptd = td; + + /* Then link the rest of the TD's */ + tmp = tmp->next; + while (tmp != head) { + td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + ptd->link = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH); + + ptd = td; + } + + ptd->link = UHCI_PTR_TERM; +} + +static void uhci_free_td(struct uhci *uhci, struct uhci_td *td) +{ + if (!list_empty(&td->list) || !list_empty(&td->fl_list)) + dbg("td is still in URB list!"); + + if (td->dev) + usb_dec_dev_use(td->dev); + + pci_pool_free(uhci->td_pool, td, td->dma_handle); +} + +static struct uhci_qh *uhci_alloc_qh(struct uhci *uhci, struct usb_device *dev) +{ + dma_addr_t dma_handle; + struct uhci_qh *qh; + + qh = pci_pool_alloc(uhci->qh_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); + if (!qh) + return NULL; + + qh->dma_handle = dma_handle; + + qh->element = UHCI_PTR_TERM; + qh->link = UHCI_PTR_TERM; + + qh->dev = dev; + qh->urbp = NULL; + + INIT_LIST_HEAD(&qh->list); + INIT_LIST_HEAD(&qh->remove_list); + + usb_inc_dev_use(dev); + + return qh; +} + +static void uhci_free_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + if (!list_empty(&qh->list)) + dbg("qh list not empty!"); + if (!list_empty(&qh->remove_list)) + dbg("qh still in remove_list!"); + + if (qh->dev) + usb_dec_dev_use(qh->dev); + + pci_pool_free(uhci->qh_pool, qh, qh->dma_handle); +} + +/* + * MUST be called with uhci->frame_list_lock acquired + */ +static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct list_head *head, *tmp; + struct uhci_qh *lqh; + + /* Grab the last QH */ + lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); + + if (lqh->urbp) { + head = &lqh->urbp->queue_list; + tmp = head->next; + while (head != tmp) { + struct urb_priv *turbp = + list_entry(tmp, struct urb_priv, queue_list); + + tmp = tmp->next; + + turbp->qh->link = urbp->qh->dma_handle | UHCI_PTR_QH; + } + } + + head = &urbp->queue_list; + tmp = head->next; + while (head != tmp) { + struct urb_priv *turbp = + list_entry(tmp, struct urb_priv, queue_list); + + tmp = tmp->next; + + turbp->qh->link = lqh->link; + } + + urbp->qh->link = lqh->link; + mb(); /* Ordering is important */ + lqh->link = urbp->qh->dma_handle | UHCI_PTR_QH; + + list_add_tail(&urbp->qh->list, &skelqh->list); +} + +static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + _uhci_insert_qh(uhci, skelqh, urb); + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + unsigned long flags; + struct uhci_qh *pqh; + + if (!qh) + return; + + qh->urbp = NULL; + + /* Only go through the hoops if it's actually linked in */ + spin_lock_irqsave(&uhci->frame_list_lock, flags); + if (!list_empty(&qh->list)) { + pqh = list_entry(qh->list.prev, struct uhci_qh, list); + + if (pqh->urbp) { + struct list_head *head, *tmp; + + head = &pqh->urbp->queue_list; + tmp = head->next; + while (head != tmp) { + struct urb_priv *turbp = + list_entry(tmp, struct urb_priv, queue_list); + + tmp = tmp->next; + + turbp->qh->link = qh->link; + } + } + + pqh->link = qh->link; + mb(); + qh->element = qh->link = UHCI_PTR_TERM; + + list_del_init(&qh->list); + } + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); + + spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); + + /* Check to see if the remove list is empty. Set the IOC bit */ + /* to force an interrupt so we can remove the QH */ + if (list_empty(&uhci->qh_remove_list)) + uhci_set_next_interrupt(uhci); + + list_add(&qh->remove_list, &uhci->qh_remove_list); + + spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags); +} + +static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct list_head *head, *tmp; + + head = &urbp->td_list; + tmp = head->next; + while (head != tmp) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + if (toggle) + td->info |= TD_TOKEN_TOGGLE; + else + td->info &= ~TD_TOKEN_TOGGLE; + + toggle ^= 1; + } + + return toggle; +} + +/* This function will append one URB's QH to another URB's QH. This is for */ +/* USB_QUEUE_BULK support for bulk transfers and soon implicitily for */ +/* control transfers */ +static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb *urb) +{ + struct urb_priv *eurbp, *urbp, *furbp, *lurbp; + struct list_head *tmp; + struct uhci_td *lltd; + unsigned long flags; + + eurbp = eurb->hcpriv; + urbp = urb->hcpriv; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + + /* Find the first URB in the queue */ + if (eurbp->queued) { + struct list_head *head = &eurbp->queue_list; + + tmp = head->next; + while (tmp != head) { + struct urb_priv *turbp = + list_entry(tmp, struct urb_priv, queue_list); + + if (!turbp->queued) + break; + + tmp = tmp->next; + } + } else + tmp = &eurbp->queue_list; + + furbp = list_entry(tmp, struct urb_priv, queue_list); + lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); + + lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); + + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), + uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1)); + + /* All qh's in the queue need to link to the next queue */ + urbp->qh->link = eurbp->qh->link; + + mb(); /* Make sure we flush everything */ + /* Only support bulk right now, so no depth */ + lltd->link = urbp->qh->dma_handle | UHCI_PTR_QH; + + list_add_tail(&urbp->queue_list, &furbp->queue_list); + + urbp->queued = 1; + + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb) +{ + struct urb_priv *urbp, *nurbp; + struct list_head *head, *tmp; + struct urb_priv *purbp; + struct uhci_td *pltd; + unsigned int toggle; + unsigned long flags; + + urbp = urb->hcpriv; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + + if (list_empty(&urbp->queue_list)) + goto out; + + nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); + + /* Fix up the toggle for the next URB's */ + if (!urbp->queued) + /* We set the toggle when we unlink */ + toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + else { + /* If we're in the middle of the queue, grab the toggle */ + /* from the TD previous to us */ + purbp = list_entry(urbp->queue_list.prev, struct urb_priv, + queue_list); + + pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); + + toggle = uhci_toggle(pltd->info) ^ 1; + } + + head = &urbp->queue_list; + tmp = head->next; + while (head != tmp) { + struct urb_priv *turbp; + + turbp = list_entry(tmp, struct urb_priv, queue_list); + + tmp = tmp->next; + + if (!turbp->queued) + break; + + toggle = uhci_fixup_toggle(turbp->urb, toggle); + } + + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); + + if (!urbp->queued) { + nurbp->queued = 0; + + _uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb); + } else { + /* We're somewhere in the middle (or end). A bit trickier */ + /* than the head scenario */ + purbp = list_entry(urbp->queue_list.prev, struct urb_priv, + queue_list); + + pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); + if (nurbp->queued) + pltd->link = nurbp->qh->dma_handle | UHCI_PTR_QH; + else + /* The next URB happens to be the beginning, so */ + /* we're the last, end the chain */ + pltd->link = UHCI_PTR_TERM; + } + + list_del_init(&urbp->queue_list); + +out: + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb) +{ + struct urb_priv *urbp; + + urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); + if (!urbp) { + err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n"); + return NULL; + } + + memset((void *)urbp, 0, sizeof(*urbp)); + + urbp->inserttime = jiffies; + urbp->fsbrtime = jiffies; + urbp->urb = urb; + urbp->dev = urb->dev; + + INIT_LIST_HEAD(&urbp->td_list); + INIT_LIST_HEAD(&urbp->queue_list); + INIT_LIST_HEAD(&urbp->complete_list); + + urb->hcpriv = urbp; + + if (urb->dev != uhci->rh.dev) { + if (urb->transfer_buffer_length) { + urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev, + urb->transfer_buffer, urb->transfer_buffer_length, + usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : + PCI_DMA_TODEVICE); + if (!urbp->transfer_buffer_dma_handle) + return NULL; + } + + if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) { + urbp->setup_packet_dma_handle = pci_map_single(uhci->dev, + urb->setup_packet, sizeof(struct usb_ctrlrequest), + PCI_DMA_TODEVICE); + if (!urbp->setup_packet_dma_handle) + return NULL; + } + } + + return urbp; +} + +/* + * MUST be called with urb->lock acquired + */ +static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + td->urb = urb; + + list_add_tail(&td->list, &urbp->td_list); +} + +/* + * MUST be called with urb->lock acquired + */ +static void uhci_remove_td_from_urb(struct uhci_td *td) +{ + if (list_empty(&td->list)) + return; + + list_del_init(&td->list); + + td->urb = NULL; +} + +/* + * MUST be called with urb->lock acquired + */ +static void uhci_destroy_urb_priv(struct urb *urb) +{ + struct list_head *head, *tmp; + struct urb_priv *urbp; + struct uhci *uhci; + + urbp = (struct urb_priv *)urb->hcpriv; + if (!urbp) + return; + + if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) { + warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb); + return; + } + + if (!list_empty(&urb->urb_list)) + warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb); + + if (!list_empty(&urbp->complete_list)) + warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb); + + uhci = urbp->dev->bus->hcpriv; + + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + uhci_remove_td_from_urb(td); + uhci_remove_td(uhci, td); + uhci_free_td(uhci, td); + } + + if (urbp->setup_packet_dma_handle) { + pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle, + sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); + urbp->setup_packet_dma_handle = 0; + } + + if (urbp->transfer_buffer_dma_handle) { + pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle, + urb->transfer_buffer_length, usb_pipein(urb->pipe) ? + PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + urbp->transfer_buffer_dma_handle = 0; + } + + urb->hcpriv = NULL; + kmem_cache_free(uhci_up_cachep, urbp); +} + +static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + + if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) { + urbp->fsbr = 1; + if (!uhci->fsbr++ && !uhci->fsbrtimeout) + uhci->skel_term_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH; + } + + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + spin_lock_irqsave(&uhci->frame_list_lock, flags); + + if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) { + urbp->fsbr = 0; + if (!--uhci->fsbr) + uhci->fsbrtimeout = jiffies + FSBR_DELAY; + } + + spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +} + +/* + * Map status to standard result codes + * + * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] + * is True for output TDs and False for input TDs. + */ +static int uhci_map_status(int status, int dir_out) +{ + if (!status) + return 0; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return -EPROTO; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return -ETIMEDOUT; + else + return -EILSEQ; + } + if (status & TD_CTRL_NAK) /* NAK */ + return -ETIMEDOUT; + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EOVERFLOW; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return -ENOSR; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + if (status & TD_CTRL_ACTIVE) /* Active */ + return 0; + + return -EINVAL; +} + +/* + * Control transfers + */ +static int uhci_submit_control(struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + dma_addr_t data = urbp->transfer_buffer_dma_handle; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; + + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); + + /* + * Build the TD for the control request + */ + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | (7 << 21), + urbp->setup_packet_dma_handle); + + /* + * If direction is "send", change the frame from SETUP (0x2D) + * to OUT (0xE1). Else change it from SETUP to IN (0x69). + */ + destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; + + /* + * Build the DATA TD's + */ + while (len > 0) { + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + /* Alternate Data0/1 (start with Data1) */ + destination ^= TD_TOKEN_TOGGLE; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((pktsze - 1) << 21), + data); + + data += pktsze; + len -= pktsze; + } + + /* + * Build the final TD for control status + */ + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + /* + * It's IN if the pipe is an output pipe or we're not expecting + * data back. + */ + destination &= ~TD_TOKEN_PID_MASK; + if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; + + destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ + + status &= ~TD_CTRL_SPD; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status | TD_CTRL_IOC, + destination | (UHCI_NULL_DATA_SIZE << 21), 0); + + qh = uhci_alloc_qh(uhci, urb->dev); + if (!qh) + return -ENOMEM; + + urbp->qh = qh; + qh->urbp = urbp; + + /* Low speed or small transfers gets a different queue and treatment */ + if (urb->pipe & TD_CTRL_LS) { + uhci_insert_tds_in_qh(qh, urb, 0); + uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb); + } else { + uhci_insert_tds_in_qh(qh, urb, 1); + uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb); + uhci_inc_fsbr(uhci, urb); + } + + return -EINPROGRESS; +} + +static int usb_control_retrigger_status(struct urb *urb); + +static int uhci_result_control(struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + int ret = 0; + + if (list_empty(&urbp->td_list)) + return -EINVAL; + + head = &urbp->td_list; + + if (urbp->short_control_packet) { + tmp = head->prev; + goto status_phase; + } + + tmp = head->next; + td = list_entry(tmp, struct uhci_td, list); + + /* The first TD is the SETUP phase, check the status, but skip */ + /* the count */ + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + tmp = tmp->next; + while (tmp != head && tmp->next != head) { + td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + if (status) + goto td_error; + + /* Check to see if we received a short packet */ + if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { + if (urb->transfer_flags & USB_DISABLE_SPD) { + ret = -EREMOTEIO; + goto err; + } + + if (uhci_packetid(td->info) == USB_PID_IN) + return usb_control_retrigger_status(urb); + else + return 0; + } + } + +status_phase: + td = list_entry(tmp, struct uhci_td, list); + + /* Control status phase */ + status = uhci_status_bits(td->status); + +#ifdef I_HAVE_BUGGY_APC_BACKUPS + /* APC BackUPS Pro kludge */ + /* It tries to send all of the descriptor instead of the amount */ + /* we requested */ + if (td->status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ + status & TD_CTRL_ACTIVE && + status & TD_CTRL_NAK) + return 0; +#endif + + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + return 0; + +td_error: + ret = uhci_map_status(status, uhci_packetout(td->info)); + if (ret == -EPIPE) + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + +err: + if ((debug == 1 && ret != -EPIPE) || debug > 1) { + /* Some debugging code */ + dbg("uhci_result_control() failed with status %x", status); + + if (errbuf) { + /* Print the chain for debugging purposes */ + uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); + + lprintk(errbuf); + } + } + + return ret; +} + +static int usb_control_retrigger_status(struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci *uhci = urb->dev->bus->hcpriv; + + urbp->short_control_packet = 1; + + /* Create a new QH to avoid pointer overwriting problems */ + uhci_remove_qh(uhci, urbp->qh); + + /* Delete all of the TD's except for the status TD at the end */ + head = &urbp->td_list; + tmp = head->next; + while (tmp != head && tmp->next != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + uhci_remove_td_from_urb(td); + uhci_remove_td(uhci, td); + uhci_free_td(uhci, td); + } + + urbp->qh = uhci_alloc_qh(uhci, urb->dev); + if (!urbp->qh) { + err("unable to allocate new QH for control retrigger"); + return -ENOMEM; + } + + urbp->qh->urbp = urbp; + + /* One TD, who cares about Breadth first? */ + uhci_insert_tds_in_qh(urbp->qh, urb, 0); + + /* Low speed or small transfers gets a different queue and treatment */ + if (urb->pipe & TD_CTRL_LS) + uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb); + else + uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb); + + return -EINPROGRESS; +} + +/* + * Interrupt transfers + */ +static int uhci_submit_interrupt(struct urb *urb) +{ + struct uhci_td *td; + unsigned long destination, status; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC; + + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); + destination |= ((urb->transfer_buffer_length - 1) << 21); + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle); + + uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td); + + return -EINPROGRESS; +} + +static int uhci_result_interrupt(struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + int ret = 0; + + urb->actual_length = 0; + + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + if (status) + goto td_error; + + if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { + if (urb->transfer_flags & USB_DISABLE_SPD) { + ret = -EREMOTEIO; + goto err; + } else + return 0; + } + } + + return 0; + +td_error: + ret = uhci_map_status(status, uhci_packetout(td->info)); + if (ret == -EPIPE) + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + +err: + if ((debug == 1 && ret != -EPIPE) || debug > 1) { + /* Some debugging code */ + dbg("uhci_result_interrupt/bulk() failed with status %x", + status); + + if (errbuf) { + /* Print the chain for debugging purposes */ + if (urbp->qh) + uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); + else + uhci_show_td(td, errbuf, ERRBUF_LEN, 0); + + lprintk(errbuf); + } + } + + return ret; +} + +static void uhci_reset_interrupt(struct urb *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + unsigned long flags; + + spin_lock_irqsave(&urb->lock, flags); + + /* Root hub is special */ + if (urb->dev == uhci->rh.dev) + goto out; + + td = list_entry(urbp->td_list.next, struct uhci_td, list); + + td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; + td->info &= ~TD_TOKEN_TOGGLE; + td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + +out: + urb->status = -EINPROGRESS; + + spin_unlock_irqrestore(&urb->lock, flags); +} + +/* + * Bulk transfers + */ +static int uhci_submit_bulk(struct urb *urb, struct urb *eurb) +{ + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + dma_addr_t data = urbp->transfer_buffer_dma_handle; + + if (len < 0) + return -EINVAL; + + /* Can't have low speed bulk transfers */ + if (urb->pipe & TD_CTRL_LS) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + /* 3 errors */ + status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT); + + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; + + /* + * Build the DATA TD's + */ + do { /* Allow zero length packets */ + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | + (((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) | + (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), + data); + + data += pktsze; + len -= maxsze; + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + } while (len > 0); + + /* + * USB_ZERO_PACKET means adding a 0-length packet, if + * direction is OUT and the transfer_length was an + * exact multiple of maxsze, hence + * (len = transfer_length - N * maxsze) == 0 + * however, if transfer_length == 0, the zero packet + * was already prepared above. + */ + if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) && + !len && urb->transfer_buffer_length) { + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | + (UHCI_NULL_DATA_SIZE << 21) | + (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), + data); + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + } + + /* Set the flag on the last packet */ + td->status |= TD_CTRL_IOC; + + qh = uhci_alloc_qh(uhci, urb->dev); + if (!qh) + return -ENOMEM; + + urbp->qh = qh; + qh->urbp = urbp; + + /* Always assume breadth first */ + uhci_insert_tds_in_qh(qh, urb, 1); + + if (urb->transfer_flags & USB_QUEUE_BULK && eurb) + uhci_append_queued_urb(uhci, eurb, urb); + else + uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb); + + uhci_inc_fsbr(uhci, urb); + + return -EINPROGRESS; +} + +/* We can use the result interrupt since they're identical */ +#define uhci_result_bulk uhci_result_interrupt + +/* + * Isochronous transfers + */ +static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end) +{ + struct urb *last_urb = NULL; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct list_head *tmp, *head; + int ret = 0; + + head = &uhci->urb_list; + tmp = head->next; + while (tmp != head) { + struct urb *u = list_entry(tmp, struct urb, urb_list); + + tmp = tmp->next; + + /* look for pending URB's with identical pipe handle */ + if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && + (u->status == -EINPROGRESS) && (u != urb)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } + } + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; + ret = 0; + } else + ret = -1; /* no previous urb found */ + + return ret; +} + +static int isochronous_find_start(struct urb *urb) +{ + int limits; + unsigned int start = 0, end = 0; + + if (urb->number_of_packets > 900) /* 900? Why? */ + return -EFBIG; + + limits = isochronous_find_limits(urb, &start, &end); + + if (urb->transfer_flags & USB_ISO_ASAP) { + if (limits) { + int curframe; + + curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES; + urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES; + } else + urb->start_frame = end; + } else { + urb->start_frame %= UHCI_NUMFRAMES; + /* FIXME: Sanity check */ + } + + return 0; +} + +/* + * Isochronous transfers + */ +static int uhci_submit_isochronous(struct urb *urb) +{ + struct uhci_td *td; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + int i, ret, framenum; + int status, destination; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + ret = isochronous_find_start(urb); + if (ret) + return ret; + + framenum = urb->start_frame; + for (i = 0; i < urb->number_of_packets; i++, framenum++) { + if (!urb->iso_frame_desc[i].length) + continue; + + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21), + urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset); + + if (i + 1 >= urb->number_of_packets) + td->status |= TD_CTRL_IOC; + + uhci_insert_td_frame_list(uhci, td, framenum); + } + + return -EINPROGRESS; +} + +static int uhci_result_isochronous(struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + int status; + int i, ret = 0; + + urb->actual_length = 0; + + i = 0; + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + int actlength; + + tmp = tmp->next; + + if (td->status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + actlength = uhci_actual_length(td->status); + urb->iso_frame_desc[i].actual_length = actlength; + urb->actual_length += actlength; + + status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe)); + urb->iso_frame_desc[i].status = status; + if (status) { + urb->error_count++; + ret = status; + } + + i++; + } + + return ret; +} + +/* + * MUST be called with uhci->urb_list_lock acquired + */ +static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb) +{ + struct list_head *tmp, *head; + + /* We don't match Isoc transfers since they are special */ + if (usb_pipeisoc(urb->pipe)) + return NULL; + + head = &uhci->urb_list; + tmp = head->next; + while (tmp != head) { + struct urb *u = list_entry(tmp, struct urb, urb_list); + + tmp = tmp->next; + + if (u->dev == urb->dev && u->pipe == urb->pipe && + u->status == -EINPROGRESS) + return u; + } + + return NULL; +} + +static int uhci_submit_urb(struct urb *urb) +{ + int ret = -EINVAL; + struct uhci *uhci; + unsigned long flags; + struct urb *eurb; + int bustime; + + if (!urb) + return -EINVAL; + + if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) { + warn("uhci_submit_urb: urb %p belongs to disconnected device or bus?", urb); + return -ENODEV; + } + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + usb_inc_dev_use(urb->dev); + + spin_lock_irqsave(&uhci->urb_list_lock, flags); + spin_lock(&urb->lock); + + if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET || + urb->status == -ECONNABORTED) { + dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status); + /* Since we can have problems on the out path */ + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + usb_dec_dev_use(urb->dev); + + return ret; + } + + INIT_LIST_HEAD(&urb->urb_list); + if (!uhci_alloc_urb_priv(uhci, urb)) { + ret = -ENOMEM; + + goto out; + } + + eurb = uhci_find_urb_ep(uhci, urb); + if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) { + ret = -ENXIO; + + goto out; + } + + /* Short circuit the virtual root hub */ + if (urb->dev == uhci->rh.dev) { + ret = rh_submit_urb(urb); + + goto out; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_submit_control(urb); + break; + case PIPE_INTERRUPT: + if (urb->bandwidth == 0) { /* not yet checked/allocated */ + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) + ret = bustime; + else { + ret = uhci_submit_interrupt(urb); + if (ret == -EINPROGRESS) + usb_claim_bandwidth(urb->dev, urb, bustime, 0); + } + } else /* bandwidth is already set */ + ret = uhci_submit_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_submit_bulk(urb, eurb); + break; + case PIPE_ISOCHRONOUS: + if (urb->bandwidth == 0) { /* not yet checked/allocated */ + if (urb->number_of_packets <= 0) { + ret = -EINVAL; + break; + } + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) { + ret = bustime; + break; + } + + ret = uhci_submit_isochronous(urb); + if (ret == -EINPROGRESS) + usb_claim_bandwidth(urb->dev, urb, bustime, 1); + } else /* bandwidth is already set */ + ret = uhci_submit_isochronous(urb); + break; + } + +out: + urb->status = ret; + + if (ret == -EINPROGRESS) { + /* We use _tail to make find_urb_ep more efficient */ + list_add_tail(&urb->urb_list, &uhci->urb_list); + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + + return 0; + } + + uhci_unlink_generic(uhci, urb); + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + + /* Only call completion if it was successful */ + if (!ret) + uhci_call_completion(urb); + + return ret; +} + +/* + * Return the result of a transfer + * + * MUST be called with urb_list_lock acquired + */ +static void uhci_transfer_result(struct uhci *uhci, struct urb *urb) +{ + int ret = -EINVAL; + unsigned long flags; + struct urb_priv *urbp; + + /* The root hub is special */ + if (urb->dev == uhci->rh.dev) + return; + + spin_lock_irqsave(&urb->lock, flags); + + urbp = (struct urb_priv *)urb->hcpriv; + + if (urb->status != -EINPROGRESS) { + info("uhci_transfer_result: called for URB %p not in flight?", urb); + goto out; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_result_control(urb); + break; + case PIPE_INTERRUPT: + ret = uhci_result_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_result_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_result_isochronous(urb); + break; + } + + urbp->status = ret; + + if (ret == -EINPROGRESS) + goto out; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + case PIPE_ISOCHRONOUS: + /* Release bandwidth for Interrupt or Isoc. transfers */ + /* Spinlock needed ? */ + if (urb->bandwidth) + usb_release_bandwidth(urb->dev, urb, 1); + uhci_unlink_generic(uhci, urb); + break; + case PIPE_INTERRUPT: + /* Interrupts are an exception */ + if (urb->interval) + goto out_complete; + + /* Release bandwidth for Interrupt or Isoc. transfers */ + /* Spinlock needed ? */ + if (urb->bandwidth) + usb_release_bandwidth(urb->dev, urb, 0); + uhci_unlink_generic(uhci, urb); + break; + default: + info("uhci_transfer_result: unknown pipe type %d for urb %p\n", + usb_pipetype(urb->pipe), urb); + } + + /* Remove it from uhci->urb_list */ + list_del_init(&urb->urb_list); + +out_complete: + uhci_add_complete(urb); + +out: + spin_unlock_irqrestore(&urb->lock, flags); +} + +/* + * MUST be called with urb->lock acquired + */ +static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb) +{ + struct list_head *head, *tmp; + struct urb_priv *urbp = urb->hcpriv; + int prevactive = 1; + + /* We can get called when urbp allocation fails, so check */ + if (!urbp) + return; + + uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ + + /* + * Now we need to find out what the last successful toggle was + * so we can update the local data toggle for the next transfer + * + * There's 3 way's the last successful completed TD is found: + * + * 1) The TD is NOT active and the actual length < expected length + * 2) The TD is NOT active and it's the last TD in the chain + * 3) The TD is active and the previous TD is NOT active + * + * Control and Isochronous ignore the toggle, so this is safe + * for all types + */ + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + if (!(td->status & TD_CTRL_ACTIVE) && + (uhci_actual_length(td->status) < uhci_expected_length(td->info) || + tmp == head)) + usb_settoggle(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info), + uhci_toggle(td->info) ^ 1); + else if ((td->status & TD_CTRL_ACTIVE) && !prevactive) + usb_settoggle(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info), + uhci_toggle(td->info)); + + prevactive = td->status & TD_CTRL_ACTIVE; + } + + uhci_delete_queued_urb(uhci, urb); + + /* The interrupt loop will reclaim the QH's */ + uhci_remove_qh(uhci, urbp->qh); + urbp->qh = NULL; +} + +static int uhci_unlink_urb(struct urb *urb) +{ + struct uhci *uhci; + unsigned long flags; + struct urb_priv *urbp = urb->hcpriv; + + if (!urb) + return -EINVAL; + + if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) + return -ENODEV; + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + spin_lock_irqsave(&uhci->urb_list_lock, flags); + spin_lock(&urb->lock); + + /* Release bandwidth for Interrupt or Isoc. transfers */ + /* Spinlock needed ? */ + if (urb->bandwidth) { + switch (usb_pipetype(urb->pipe)) { + case PIPE_INTERRUPT: + usb_release_bandwidth(urb->dev, urb, 0); + break; + case PIPE_ISOCHRONOUS: + usb_release_bandwidth(urb->dev, urb, 1); + break; + default: + break; + } + } + + if (urb->status != -EINPROGRESS) { + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + return 0; + } + + list_del_init(&urb->urb_list); + + uhci_unlink_generic(uhci, urb); + + /* Short circuit the virtual root hub */ + if (urb->dev == uhci->rh.dev) { + rh_unlink_urb(urb); + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + + uhci_call_completion(urb); + } else { + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urbp->status = urb->status = -ECONNABORTED; + + spin_lock(&uhci->urb_remove_list_lock); + + /* If we're the first, set the next interrupt bit */ + if (list_empty(&uhci->urb_remove_list)) + uhci_set_next_interrupt(uhci); + + list_add(&urb->urb_list, &uhci->urb_remove_list); + + spin_unlock(&uhci->urb_remove_list_lock); + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + + } else { + urb->status = -ENOENT; + + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + + if (in_interrupt()) { /* wait at least 1 frame */ + static int errorcount = 10; + + if (errorcount--) + dbg("uhci_unlink_urb called from interrupt for urb %p", urb); + udelay(1000); + } else + schedule_timeout(1+1*HZ/1000); + + uhci_call_completion(urb); + } + } + + return 0; +} + +static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct list_head *head, *tmp; + int count = 0; + + uhci_dec_fsbr(uhci, urb); + + urbp->fsbr_timeout = 1; + + /* + * Ideally we would want to fix qh->element as well, but it's + * read/write by the HC, so that can introduce a race. It's not + * really worth the hassle + */ + + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + /* + * Make sure we don't do the last one (since it'll have the + * TERM bit set) as well as we skip every so many TD's to + * make sure it doesn't hog the bandwidth + */ + if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1)) + td->link |= UHCI_PTR_DEPTH; + + count++; + } + + return 0; +} + +/* + * uhci_get_current_frame_number() + * + * returns the current frame number for a USB bus/controller. + */ +static int uhci_get_current_frame_number(struct usb_device *dev) +{ + struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; + + return inw(uhci->io_addr + USBFRNUM); +} + +struct usb_operations uhci_device_operations = { + uhci_alloc_dev, + uhci_free_dev, + uhci_get_current_frame_number, + uhci_submit_urb, + uhci_unlink_urb +}; + +/* Virtual Root Hub */ + +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, + Bit 5 Remote-wakeup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ +static int rh_send_irq(struct urb *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + unsigned int io_addr = uhci->io_addr; + unsigned long flags; + int i, len = 1; + __u16 data = 0; + + spin_lock_irqsave(&urb->lock, flags); + for (i = 0; i < uhci->rh.numports; i++) { + data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + len = (i + 1) / 8 + 1; + } + + *(__u16 *) urb->transfer_buffer = cpu_to_le16(data); + urb->actual_length = len; + urbp->status = 0; + + spin_unlock_irqrestore(&urb->lock, flags); + + if ((data > 0) && (uhci->rh.send != 0)) { + dbg("root-hub INT complete: port1: %x port2: %x data: %x", + inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data); + uhci_call_completion(urb); + } + + return 0; +} + +/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ +static int rh_init_int_timer(struct urb *urb); + +static void rh_int_timer_do(unsigned long ptr) +{ + struct urb *urb = (struct urb *)ptr; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct list_head list, *tmp, *head; + unsigned long flags; + + if (uhci->rh.send) + rh_send_irq(urb); + + INIT_LIST_HEAD(&list); + + spin_lock_irqsave(&uhci->urb_list_lock, flags); + head = &uhci->urb_list; + tmp = head->next; + while (tmp != head) { + struct urb *u = list_entry(tmp, struct urb, urb_list); + struct urb_priv *up = (struct urb_priv *)u->hcpriv; + + tmp = tmp->next; + + spin_lock(&u->lock); + + /* Check if the FSBR timed out */ + if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT)) + uhci_fsbr_timeout(uhci, u); + + /* Check if the URB timed out */ + if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) { + list_del(&u->urb_list); + list_add_tail(&u->urb_list, &list); + } + + spin_unlock(&u->lock); + } + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); + + head = &list; + tmp = head->next; + while (tmp != head) { + struct urb *u = list_entry(tmp, struct urb, urb_list); + + tmp = tmp->next; + + u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED; + uhci_unlink_urb(u); + } + + /* Really disable FSBR */ + if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { + uhci->fsbrtimeout = 0; + uhci->skel_term_qh->link = UHCI_PTR_TERM; + } + + /* enter global suspend if nothing connected */ + if (!uhci->is_suspended && !ports_active(uhci)) + suspend_hc(uhci); + + rh_init_int_timer(urb); +} + +/* Root Hub INTs are polled by this timer */ +static int rh_init_int_timer(struct urb *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + uhci->rh.interval = urb->interval; + init_timer(&uhci->rh.rh_int_timer); + uhci->rh.rh_int_timer.function = rh_int_timer_do; + uhci->rh.rh_int_timer.data = (unsigned long)urb; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + add_timer(&uhci->rh.rh_int_timer); + + return 0; +} + +#define OK(x) len = (x); break + +#define CLR_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) & ~(x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + +#define SET_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) | (x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + + +/* Root Hub Control Pipe */ +static int rh_submit_urb(struct urb *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = 0; + int stat = 0; + int i; + unsigned int io_addr = uhci->io_addr; + __u16 cstatus; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipetype(pipe) == PIPE_INTERRUPT) { + uhci->rh.urb = urb; + uhci->rh.send = 1; + uhci->rh.interval = urb->interval; + rh_init_int_timer(urb); + + return -EINPROGRESS; + } + + bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8; + wValue = le16_to_cpu(cmd->wValue); + wIndex = le16_to_cpu(cmd->wIndex); + wLength = le16_to_cpu(cmd->wLength); + + for (i = 0; i < 8; i++) + uhci->rh.c_p_r[i] = 0; + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *)data = cpu_to_le16(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)data = cpu_to_le32(0); + OK(4); /* hub power */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(__u16 *)data = cpu_to_le16(status); + *(__u16 *)(data + 2) = cpu_to_le16(cstatus); + OK(4); + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case RH_ENDPOINT_STALL: + OK(0); + } + break; + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_OVER_CURRENT: + OK(0); /* hub power over current */ + } + break; + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_ENABLE: + CLR_RH_PORTSTAT(USBPORTSC_PE); + OK(0); + case RH_PORT_SUSPEND: + CLR_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power */ + case RH_C_PORT_CONNECTION: + SET_RH_PORTSTAT(USBPORTSC_CSC); + OK(0); + case RH_C_PORT_ENABLE: + SET_RH_PORTSTAT(USBPORTSC_PEC); + OK(0); + case RH_C_PORT_SUSPEND: + /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + OK(0); + case RH_C_PORT_OVER_CURRENT: + OK(0); /* port power over current */ + case RH_C_PORT_RESET: + uhci->rh.c_p_r[wIndex - 1] = 0; + OK(0); + } + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_SUSPEND: + SET_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_RESET: + SET_RH_PORTSTAT(USBPORTSC_PR); + mdelay(50); /* USB v1.1 7.1.7.3 */ + uhci->rh.c_p_r[wIndex - 1] = 1; + CLR_RH_PORTSTAT(USBPORTSC_PR); + udelay(10); + SET_RH_PORTSTAT(USBPORTSC_PE); + mdelay(10); + SET_RH_PORTSTAT(0xa); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power ** */ + case RH_PORT_ENABLE: + SET_RH_PORTSTAT(USBPORTSC_PE); + OK(0); + } + break; + case RH_SET_ADDRESS: + uhci->rh.devnum = wValue; + OK(0); + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case 0x01: /* device descriptor */ + len = min_t(unsigned int, leni, + min_t(unsigned int, + sizeof(root_hub_dev_des), wLength)); + memcpy(data, root_hub_dev_des, len); + OK(len); + case 0x02: /* configuration descriptor */ + len = min_t(unsigned int, leni, + min_t(unsigned int, + sizeof(root_hub_config_des), wLength)); + memcpy (data, root_hub_config_des, len); + OK(len); + case 0x03: /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, + uhci->io_addr, "UHCI-alt", + data, wLength); + if (len > 0) { + OK(min_t(int, leni, len)); + } else + stat = -EPIPE; + } + break; + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = uhci->rh.numports; + len = min_t(unsigned int, leni, + min_t(unsigned int, sizeof(root_hub_hub_des), wLength)); + memcpy(data, root_hub_hub_des, len); + OK(len); + case RH_GET_CONFIGURATION: + *(__u8 *)data = 0x01; + OK(1); + case RH_SET_CONFIGURATION: + OK(0); + case RH_GET_INTERFACE | RH_INTERFACE: + *(__u8 *)data = 0x00; + OK(1); + case RH_SET_INTERFACE | RH_INTERFACE: + OK(0); + default: + stat = -EPIPE; + } + + urb->actual_length = len; + + return stat; +} + +/* + * MUST be called with urb->lock acquired + */ +static int rh_unlink_urb(struct urb *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (uhci->rh.urb == urb) { + urb->status = -ENOENT; + uhci->rh.send = 0; + uhci->rh.urb = NULL; + del_timer(&uhci->rh.rh_int_timer); + } + return 0; +} + +static void uhci_free_pending_qhs(struct uhci *uhci) +{ + struct list_head *tmp, *head; + unsigned long flags; + + spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); + head = &uhci->qh_remove_list; + tmp = head->next; + while (tmp != head) { + struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list); + + tmp = tmp->next; + + list_del_init(&qh->remove_list); + + uhci_free_qh(uhci, qh); + } + spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags); +} + +static void uhci_call_completion(struct urb *urb) +{ + struct urb_priv *urbp; + struct usb_device *dev = urb->dev; + struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; + int is_ring = 0, killed, resubmit_interrupt, status; + struct urb *nurb; + unsigned long flags; + + spin_lock_irqsave(&urb->lock, flags); + + urbp = (struct urb_priv *)urb->hcpriv; + if (!urbp || !urb->dev) { + spin_unlock_irqrestore(&urb->lock, flags); + return; + } + + killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED || + urb->status == -ECONNRESET); + resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT && + urb->interval); + + nurb = urb->next; + if (nurb && !killed) { + int count = 0; + + while (nurb && nurb != urb && count < MAX_URB_LOOP) { + if (nurb->status == -ENOENT || + nurb->status == -ECONNABORTED || + nurb->status == -ECONNRESET) { + killed = 1; + break; + } + + nurb = nurb->next; + count++; + } + + if (count == MAX_URB_LOOP) + err("uhci_call_completion: too many linked URB's, loop? (first loop)"); + + /* Check to see if chain is a ring */ + is_ring = (nurb == urb); + } + + if (urbp->transfer_buffer_dma_handle) + pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle, + urb->transfer_buffer_length, usb_pipein(urb->pipe) ? + PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + + if (urbp->setup_packet_dma_handle) + pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle, + sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); + + status = urbp->status; + if (!resubmit_interrupt || killed) + /* We don't need urb_priv anymore */ + uhci_destroy_urb_priv(urb); + + if (!killed) + urb->status = status; + + urb->dev = NULL; + spin_unlock_irqrestore(&urb->lock, flags); + + if (urb->complete) + urb->complete(urb); + + if (resubmit_interrupt) + /* Recheck the status. The completion handler may have */ + /* unlinked the resubmitting interrupt URB */ + killed = (urb->status == -ENOENT || + urb->status == -ECONNABORTED || + urb->status == -ECONNRESET); + + if (resubmit_interrupt && !killed) { + urb->dev = dev; + uhci_reset_interrupt(urb); + } else { + if (is_ring && !killed) { + urb->dev = dev; + uhci_submit_urb(urb); + } else { + /* We decrement the usage count after we're done */ + /* with everything */ + usb_dec_dev_use(dev); + } + } +} + +static void uhci_finish_completion(struct uhci *uhci) +{ + struct list_head *tmp, *head; + unsigned long flags; + + spin_lock_irqsave(&uhci->complete_list_lock, flags); + head = &uhci->complete_list; + tmp = head->next; + while (tmp != head) { + struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list); + struct urb *urb = urbp->urb; + + list_del_init(&urbp->complete_list); + spin_unlock_irqrestore(&uhci->complete_list_lock, flags); + + uhci_call_completion(urb); + + spin_lock_irqsave(&uhci->complete_list_lock, flags); + head = &uhci->complete_list; + tmp = head->next; + } + spin_unlock_irqrestore(&uhci->complete_list_lock, flags); +} + +static void uhci_remove_pending_qhs(struct uhci *uhci) +{ + struct list_head *tmp, *head; + unsigned long flags; + + spin_lock_irqsave(&uhci->urb_remove_list_lock, flags); + head = &uhci->urb_remove_list; + tmp = head->next; + while (tmp != head) { + struct urb *urb = list_entry(tmp, struct urb, urb_list); + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + tmp = tmp->next; + + list_del_init(&urb->urb_list); + + urbp->status = urb->status = -ECONNRESET; + + uhci_add_complete(urb); + } + spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); +} + +static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) +{ + struct uhci *uhci = __uhci; + unsigned int io_addr = uhci->io_addr; + unsigned short status; + struct list_head *tmp, *head; + + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + status = inw(io_addr + USBSTS); + if (!status) /* shared interrupt, not mine */ + return; + outw(status, io_addr + USBSTS); /* Clear it */ + + if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { + if (status & USBSTS_HSE) + err("%x: host system error, PCI problems?", io_addr); + if (status & USBSTS_HCPE) + err("%x: host controller process error. something bad happened", io_addr); + if ((status & USBSTS_HCH) && !uhci->is_suspended) { + err("%x: host controller halted. very bad", io_addr); + /* FIXME: Reset the controller, fix the offending TD */ + } + } + + if (status & USBSTS_RD) + wakeup_hc(uhci); + + uhci_free_pending_qhs(uhci); + + uhci_remove_pending_qhs(uhci); + + uhci_clear_next_interrupt(uhci); + + /* Walk the list of pending URB's to see which ones completed */ + spin_lock(&uhci->urb_list_lock); + head = &uhci->urb_list; + tmp = head->next; + while (tmp != head) { + struct urb *urb = list_entry(tmp, struct urb, urb_list); + + tmp = tmp->next; + + /* Checks the status and does all of the magic necessary */ + uhci_transfer_result(uhci, urb); + } + spin_unlock(&uhci->urb_list_lock); + + uhci_finish_completion(uhci); +} + +static void reset_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + + /* Global reset for 50ms */ + outw(USBCMD_GRESET, io_addr + USBCMD); + wait_ms(50); + outw(0, io_addr + USBCMD); + wait_ms(10); +} + +static void suspend_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + + dbg("%x: suspend_hc", io_addr); + + outw(USBCMD_EGSM, io_addr + USBCMD); + + uhci->is_suspended = 1; +} + +static void wakeup_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + unsigned int status; + + dbg("%x: wakeup_hc", io_addr); + + outw(0, io_addr + USBCMD); + + /* wait for EOP to be sent */ + status = inw(io_addr + USBCMD); + while (status & USBCMD_FGR) + status = inw(io_addr + USBCMD); + + uhci->is_suspended = 0; + + /* Run and mark it configured with a 64-byte max packet */ + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); +} + +static int ports_active(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + int connection = 0; + int i; + + for (i = 0; i < uhci->rh.numports; i++) + connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1); + + return connection; +} + +static void start_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + int timeout = 1000; + + /* + * Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + outw(USBCMD_HCRESET, io_addr + USBCMD); + while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { + if (!--timeout) { + printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n"); + break; + } + } + + /* Turn on all interrupts */ + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, + io_addr + USBINTR); + + /* Start at frame 0 */ + outw(0, io_addr + USBFRNUM); + outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD); + + /* Run and mark it configured with a 64-byte max packet */ + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); +} + +#ifdef CONFIG_PROC_FS +static int uhci_num = 0; +#endif + +static void free_uhci(struct uhci *uhci) +{ + kfree(uhci); +} + +/* + * De-allocate all resources.. + */ +static void release_uhci(struct uhci *uhci) +{ + int i; +#ifdef CONFIG_PROC_FS + char buf[8]; +#endif + + if (uhci->irq >= 0) { + free_irq(uhci->irq, uhci); + uhci->irq = -1; + } + + for (i = 0; i < UHCI_NUM_SKELQH; i++) + if (uhci->skelqh[i]) { + uhci_free_qh(uhci, uhci->skelqh[i]); + uhci->skelqh[i] = NULL; + } + + for (i = 0; i < UHCI_NUM_SKELTD; i++) + if (uhci->skeltd[i]) { + uhci_free_td(uhci, uhci->skeltd[i]); + uhci->skeltd[i] = NULL; + } + + if (uhci->qh_pool) { + pci_pool_destroy(uhci->qh_pool); + uhci->qh_pool = NULL; + } + + if (uhci->td_pool) { + pci_pool_destroy(uhci->td_pool); + uhci->td_pool = NULL; + } + + if (uhci->fl) { + pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle); + uhci->fl = NULL; + } + + if (uhci->bus) { + usb_free_bus(uhci->bus); + uhci->bus = NULL; + } + +#ifdef CONFIG_PROC_FS + if (uhci->proc_entry) { + sprintf(buf, "hc%d", uhci->num); + + remove_proc_entry(buf, uhci_proc_root); + uhci->proc_entry = NULL; + } +#endif + + free_uhci(uhci); +} + +/* + * Allocate a frame list, and then setup the skeleton + * + * The hardware doesn't really know any difference + * in the queues, but the order does matter for the + * protocols higher up. The order is: + * + * - any isochronous events handled before any + * of the queues. We don't do that here, because + * we'll create the actual TD entries on demand. + * - The first queue is the interrupt queue. + * - The second queue is the control queue, split into low and high speed + * - The third queue is bulk queue. + * - The fourth queue is the bandwidth reclamation queue, which loops back + * to the high speed control queue. + */ +static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size) +{ + struct uhci *uhci; + int retval; + char buf[8], *bufp = buf; + int i, port; + struct usb_bus *bus; + dma_addr_t dma_handle; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *ent; +#endif + + retval = -ENODEV; + if (pci_enable_device(dev) < 0) { + err("couldn't enable PCI device"); + goto err_enable_device; + } + + if (!dev->irq) { + err("found UHCI device with no IRQ assigned. check BIOS settings!"); + goto err_invalid_irq; + } + + if (!pci_dma_supported(dev, 0xFFFFFFFF)) { + err("PCI subsystem doesn't support 32 bit addressing?"); + goto err_pci_dma_supported; + } + + retval = -EBUSY; + if (!request_region(io_addr, io_size, "usb-uhci")) { + err("couldn't allocate I/O range %x - %x", io_addr, + io_addr + io_size - 1); + goto err_request_region; + } + + pci_set_master(dev); + +#ifndef __sparc__ + sprintf(buf, "%d", dev->irq); +#else + bufp = __irq_itoa(dev->irq); +#endif + printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", + io_addr, bufp); + + if (pci_set_dma_mask(dev, 0xFFFFFFFF)) { + err("couldn't set PCI dma mask"); + retval = -ENODEV; + goto err_pci_set_dma_mask; + } + + uhci = kmalloc(sizeof(*uhci), GFP_KERNEL); + if (!uhci) { + err("couldn't allocate uhci structure"); + retval = -ENOMEM; + goto err_alloc_uhci; + } + + uhci->dev = dev; + uhci->irq = dev->irq; + uhci->io_addr = io_addr; + uhci->io_size = io_size; + pci_set_drvdata(dev, uhci); + +#ifdef CONFIG_PROC_FS + uhci->num = uhci_num++; + + sprintf(buf, "hc%d", uhci->num); + + ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root); + if (!ent) { + err("couldn't create uhci proc entry"); + retval = -ENOMEM; + goto err_create_proc_entry; + } + + ent->data = uhci; + ent->proc_fops = &uhci_proc_operations; + ent->size = 0; + uhci->proc_entry = ent; +#endif + + /* Reset here so we don't get any interrupts from an old setup */ + /* or broken setup */ + reset_hc(uhci); + + uhci->fsbr = 0; + uhci->fsbrtimeout = 0; + + uhci->is_suspended = 0; + + spin_lock_init(&uhci->qh_remove_list_lock); + INIT_LIST_HEAD(&uhci->qh_remove_list); + + spin_lock_init(&uhci->urb_remove_list_lock); + INIT_LIST_HEAD(&uhci->urb_remove_list); + + spin_lock_init(&uhci->urb_list_lock); + INIT_LIST_HEAD(&uhci->urb_list); + + spin_lock_init(&uhci->complete_list_lock); + INIT_LIST_HEAD(&uhci->complete_list); + + spin_lock_init(&uhci->frame_list_lock); + + /* We need exactly one page (per UHCI specs), how convenient */ + /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */ +#if PAGE_SIZE < (4 * 1024) +#error PAGE_SIZE is not atleast 4k +#endif + uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle); + if (!uhci->fl) { + err("unable to allocate consistent memory for frame list"); + goto err_alloc_fl; + } + + memset((void *)uhci->fl, 0, sizeof(*uhci->fl)); + + uhci->fl->dma_handle = dma_handle; + + uhci->td_pool = pci_pool_create("uhci_td", uhci->dev, + sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC); + if (!uhci->td_pool) { + err("unable to create td pci_pool"); + goto err_create_td_pool; + } + + uhci->qh_pool = pci_pool_create("uhci_qh", uhci->dev, + sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC); + if (!uhci->qh_pool) { + err("unable to create qh pci_pool"); + goto err_create_qh_pool; + } + + bus = usb_alloc_bus(&uhci_device_operations); + if (!bus) { + err("unable to allocate bus"); + goto err_alloc_bus; + } + + uhci->bus = bus; + bus->bus_name = dev->slot_name; + bus->hcpriv = uhci; + + usb_register_bus(uhci->bus); + + /* Initialize the root hub */ + + /* UHCI specs says devices must have 2 ports, but goes on to say */ + /* they may have more but give no way to determine how many they */ + /* have. However, according to the UHCI spec, Bit 7 is always set */ + /* to 1. So we try to use this to our advantage */ + for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) { + unsigned int portstatus; + + portstatus = inw(uhci->io_addr + 0x10 + (port * 2)); + if (!(portstatus & 0x0080)) + break; + } + if (debug) + info("detected %d ports", port); + + /* This is experimental so anything less than 2 or greater than 8 is */ + /* something weird and we'll ignore it */ + if (port < 2 || port > 8) { + info("port count misdetected? forcing to 2 ports"); + port = 2; + } + + uhci->rh.numports = port; + + uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus); + if (!uhci->rh.dev) { + err("unable to allocate root hub"); + goto err_alloc_root_hub; + } + + uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh.dev); + if (!uhci->skeltd[0]) { + err("unable to allocate TD 0"); + goto err_alloc_skeltd; + } + + /* + * 9 Interrupt queues; link int2 to int1, int4 to int2, etc + * then link int1 to control and control to bulk + */ + for (i = 1; i < 9; i++) { + struct uhci_td *td; + + td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh.dev); + if (!td) { + err("unable to allocate TD %d", i); + goto err_alloc_skeltd; + } + + uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + td->link = uhci->skeltd[i - 1]->dma_handle; + } + + uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh.dev); + if (!uhci->skel_term_td) { + err("unable to allocate skel TD term"); + goto err_alloc_skeltd; + } + + for (i = 0; i < UHCI_NUM_SKELQH; i++) { + uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh.dev); + if (!uhci->skelqh[i]) { + err("unable to allocate QH %d", i); + goto err_alloc_skelqh; + } + } + + uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + uhci->skel_int1_td->link = uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH; + + uhci->skel_ls_control_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH; + uhci->skel_ls_control_qh->element = UHCI_PTR_TERM; + + uhci->skel_hs_control_qh->link = uhci->skel_bulk_qh->dma_handle | UHCI_PTR_QH; + uhci->skel_hs_control_qh->element = UHCI_PTR_TERM; + + uhci->skel_bulk_qh->link = uhci->skel_term_qh->dma_handle | UHCI_PTR_QH; + uhci->skel_bulk_qh->element = UHCI_PTR_TERM; + + /* This dummy TD is to work around a bug in Intel PIIX controllers */ + uhci_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + uhci->skel_term_td->link = uhci->skel_term_td->dma_handle; + + uhci->skel_term_qh->link = UHCI_PTR_TERM; + uhci->skel_term_qh->element = uhci->skel_term_td->dma_handle; + + /* + * Fill the frame list: make all entries point to + * the proper interrupt queue. + * + * This is probably silly, but it's a simple way to + * scatter the interrupt queues in a way that gives + * us a reasonable dynamic range for irq latencies. + */ + for (i = 0; i < UHCI_NUMFRAMES; i++) { + int irq = 0; + + if (i & 1) { + irq++; + if (i & 2) { + irq++; + if (i & 4) { + irq++; + if (i & 8) { + irq++; + if (i & 16) { + irq++; + if (i & 32) { + irq++; + if (i & 64) + irq++; + } + } + } + } + } + } + + /* Only place we don't use the frame list routines */ + uhci->fl->frame[i] = uhci->skeltd[irq]->dma_handle; + } + + start_hc(uhci); + + if (request_irq(dev->irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci)) + goto err_request_irq; + + /* disable legacy emulation */ + pci_write_config_word(uhci->dev, USBLEGSUP, USBLEGSUP_DEFAULT); + + usb_connect(uhci->rh.dev); + + if (usb_new_device(uhci->rh.dev) != 0) { + err("unable to start root hub"); + retval = -ENOMEM; + goto err_start_root_hub; + } + + return 0; + +/* + * error exits: + */ +err_start_root_hub: + free_irq(uhci->irq, uhci); + uhci->irq = -1; + +err_request_irq: + for (i = 0; i < UHCI_NUM_SKELQH; i++) + if (uhci->skelqh[i]) { + uhci_free_qh(uhci, uhci->skelqh[i]); + uhci->skelqh[i] = NULL; + } + +err_alloc_skelqh: + for (i = 0; i < UHCI_NUM_SKELTD; i++) + if (uhci->skeltd[i]) { + uhci_free_td(uhci, uhci->skeltd[i]); + uhci->skeltd[i] = NULL; + } + +err_alloc_skeltd: + usb_free_dev(uhci->rh.dev); + uhci->rh.dev = NULL; + +err_alloc_root_hub: + usb_free_bus(uhci->bus); + uhci->bus = NULL; + +err_alloc_bus: + pci_pool_destroy(uhci->qh_pool); + uhci->qh_pool = NULL; + +err_create_qh_pool: + pci_pool_destroy(uhci->td_pool); + uhci->td_pool = NULL; + +err_create_td_pool: + pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle); + uhci->fl = NULL; + +err_alloc_fl: +#ifdef CONFIG_PROC_FS + remove_proc_entry(buf, uhci_proc_root); + uhci->proc_entry = NULL; + +err_create_proc_entry: + free_uhci(uhci); +#endif + +err_alloc_uhci: + +err_pci_set_dma_mask: + release_region(io_addr, io_size); + +err_request_region: + +err_pci_dma_supported: + +err_invalid_irq: + +err_enable_device: + + return retval; +} + +static int __devinit uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int i; + + /* Search for the IO base address.. */ + for (i = 0; i < 6; i++) { + unsigned int io_addr = pci_resource_start(dev, i); + unsigned int io_size = pci_resource_len(dev, i); + + /* IO address? */ + if (!(pci_resource_flags(dev, i) & IORESOURCE_IO)) + continue; + + return alloc_uhci(dev, io_addr, io_size); + } + + return -ENODEV; +} + +static void __devexit uhci_pci_remove(struct pci_dev *dev) +{ + struct uhci *uhci = pci_get_drvdata(dev); + + if (uhci->bus->root_hub) + usb_disconnect(&uhci->bus->root_hub); + + usb_deregister_bus(uhci->bus); + + /* + * At this point, we're guaranteed that no new connects can be made + * to this bus since there are no more parents + */ + uhci_free_pending_qhs(uhci); + uhci_remove_pending_qhs(uhci); + + reset_hc(uhci); + release_region(uhci->io_addr, uhci->io_size); + + uhci_free_pending_qhs(uhci); + + release_uhci(uhci); +} + +#ifdef CONFIG_PM +static int uhci_pci_suspend(struct pci_dev *dev, u32 state) +{ + suspend_hc((struct uhci *) pci_get_drvdata(dev)); + return 0; +} + +static int uhci_pci_resume(struct pci_dev *dev) +{ + reset_hc((struct uhci *) pci_get_drvdata(dev)); + start_hc((struct uhci *) pci_get_drvdata(dev)); + return 0; +} +#endif + +static const struct pci_device_id __devinitdata uhci_pci_ids[] = { { + + /* handle any USB UHCI controller */ + class: ((PCI_CLASS_SERIAL_USB << 8) | 0x00), + class_mask: ~0, + + /* no matter who makes it */ + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + + }, { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, uhci_pci_ids); + +static struct pci_driver uhci_pci_driver = { + name: "usb-uhci", + id_table: uhci_pci_ids, + + probe: uhci_pci_probe, + remove: __devexit_p(uhci_pci_remove), + +#ifdef CONFIG_PM + suspend: uhci_pci_suspend, + resume: uhci_pci_resume, +#endif /* PM */ +}; + + +static int __init uhci_hcd_init(void) +{ + int retval = -ENOMEM; + + info(DRIVER_DESC " " DRIVER_VERSION); + + if (debug) { + errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); + if (!errbuf) + goto errbuf_failed; + } + +#ifdef CONFIG_PROC_FS + uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, 0); + if (!uhci_proc_root) + goto proc_failed; +#endif + + uhci_up_cachep = kmem_cache_create("uhci_urb_priv", + sizeof(struct urb_priv), 0, 0, NULL, NULL); + if (!uhci_up_cachep) + goto up_failed; + + retval = pci_module_init(&uhci_pci_driver); + if (retval) + goto init_failed; + + return 0; + +init_failed: + if (kmem_cache_destroy(uhci_up_cachep)) + printk(KERN_INFO "uhci: not all urb_priv's were freed\n"); + +up_failed: + +#ifdef CONFIG_PROC_FS + remove_proc_entry("driver/uhci", 0); + +proc_failed: +#endif + if (errbuf) + kfree(errbuf); + +errbuf_failed: + + return retval; +} + +static void __exit uhci_hcd_cleanup(void) +{ + pci_unregister_driver(&uhci_pci_driver); + + if (kmem_cache_destroy(uhci_up_cachep)) + printk(KERN_INFO "uhci: not all urb_priv's were freed\n"); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("driver/uhci", 0); +#endif + + if (errbuf) + kfree(errbuf); +} + +module_init(uhci_hcd_init); +module_exit(uhci_hcd_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + diff -Nru a/drivers/usb/host/uhci.h b/drivers/usb/host/uhci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/uhci.h Thu Mar 6 14:22:16 2003 @@ -0,0 +1,441 @@ +#ifndef __LINUX_UHCI_H +#define __LINUX_UHCI_H + +#include +#include + +/* + * Universal Host Controller Interface data structures and defines + */ + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_LS 0x0030 /* Line Status */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ +#define USBPORTSC_SUSP 0x1000 /* Suspend */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 + +#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ + +struct uhci_frame_list { + __u32 frame[UHCI_NUMFRAMES]; + + void *frame_cpu[UHCI_NUMFRAMES]; + + dma_addr_t dma_handle; +}; + +struct urb_priv; + +struct uhci_qh { + /* Hardware fields */ + __u32 link; /* Next queue */ + __u32 element; /* Queue element pointer */ + + /* Software fields */ + dma_addr_t dma_handle; + + struct usb_device *dev; + struct urb_priv *urbp; + + struct list_head list; /* P: uhci->frame_list_lock */ + struct list_head remove_list; /* P: uhci->remove_list_lock */ +} __attribute__((aligned(16))); + +/* + * for TD : + */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_C_ERR_SHIFT 27 +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ + +/* + * for TD : (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE_SHIFT 19 +#define TD_TOKEN_TOGGLE (1 << 19) +#define TD_TOKEN_PID_MASK 0xFF +#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */ + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */ +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + +/* + * The documentation says "4 words for hardware, 4 words for software". + * + * That's silly, the hardware doesn't care. The hardware only cares that + * the hardware words are 16-byte aligned, and we can have any amount of + * sw space after the TD entry as far as I can tell. + * + * But let's just go with the documentation, at least for 32-bit machines. + * On 64-bit machines we probably want to take advantage of the fact that + * hw doesn't really care about the size of the sw-only area. + * + * Alas, not anymore, we have more than 4 words for software, woops. + * Everything still works tho, surprise! -jerdfelt + */ +struct uhci_td { + /* Hardware fields */ + __u32 link; + __u32 status; + __u32 info; + __u32 buffer; + + /* Software fields */ + dma_addr_t dma_handle; + + struct usb_device *dev; + struct urb *urb; + + struct list_head list; /* P: urb->lock */ + + int frame; + struct list_head fl_list; /* P: uhci->frame_list_lock */ +} __attribute__((aligned(16))); + +/* + * There are various standard queues. We set up several different + * queues for each of the three basic queue types: interrupt, + * control, and bulk. + * + * - There are various different interrupt latencies: ranging from + * every other USB frame (2 ms apart) to every 256 USB frames (ie + * 256 ms apart). Make your choice according to how obnoxious you + * want to be on the wire, vs how critical latency is for you. + * - The control list is done every frame. + * - There are 4 bulk lists, so that up to four devices can have a + * bulk list of their own and when run concurrently all four lists + * will be be serviced. + * + * This is a bit misleading, there are various interrupt latencies, but they + * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the + * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor + * problem + * + * In the case of the root hub, these QH's are just head's of qh's. Don't + * be scared, it kinda makes sense. Look at this wonderful picture care of + * Linus: + * + * generic- -> dev1- -> generic- -> dev1- -> control- -> bulk- -> ... + * iso-QH iso-QH irq-QH irq-QH QH QH + * | | | | | | + * End dev1-iso-TD1 End dev1-irq-TD1 ... ... + * | + * dev1-iso-TD2 + * | + * .... + * + * This may vary a bit (the UHCI docs don't explicitly say you can put iso + * transfers in QH's and all of their pictures don't have that either) but + * other than that, that is what we're doing now + * + * And now we don't put Iso transfers in QH's, so we don't waste one on it + * --jerdfelt + * + * To keep with Linus' nomenclature, this is called the QH skeleton. These + * labels (below) are only signficant to the root hub's QH's + */ + +#define UHCI_NUM_SKELTD 10 +#define skel_int1_td skeltd[0] +#define skel_int2_td skeltd[1] +#define skel_int4_td skeltd[2] +#define skel_int8_td skeltd[3] +#define skel_int16_td skeltd[4] +#define skel_int32_td skeltd[5] +#define skel_int64_td skeltd[6] +#define skel_int128_td skeltd[7] +#define skel_int256_td skeltd[8] +#define skel_term_td skeltd[9] /* To work around PIIX UHCI bug */ + +#define UHCI_NUM_SKELQH 4 +#define skel_ls_control_qh skelqh[0] +#define skel_hs_control_qh skelqh[1] +#define skel_bulk_qh skelqh[2] +#define skel_term_qh skelqh[3] + +/* + * Search tree for determining where fits in the + * skelqh[] skeleton. + * + * An interrupt request should be placed into the slowest skelqh[] + * which meets the interval/period/frequency requirement. + * An interrupt request is allowed to be faster than but not slower. + * + * For a given , this function returns the appropriate/matching + * skelqh[] index value. + * + * NOTE: For UHCI, we don't really need int256_qh since the maximum interval + * is 255 ms. However, we do need an int1_qh since 1 is a valid interval + * and we should meet that frequency when requested to do so. + * This will require some change(s) to the UHCI skeleton. + */ +static inline int __interval_to_skel(int interval) +{ + if (interval < 16) { + if (interval < 4) { + if (interval < 2) + return 0; /* int1 for 0-1 ms */ + return 1; /* int2 for 2-3 ms */ + } + if (interval < 8) + return 2; /* int4 for 4-7 ms */ + return 3; /* int8 for 8-15 ms */ + } + if (interval < 64) { + if (interval < 32) + return 4; /* int16 for 16-31 ms */ + return 5; /* int32 for 32-63 ms */ + } + if (interval < 128) + return 6; /* int64 for 64-127 ms */ + return 7; /* int128 for 128-255 ms (Max.) */ +} + +struct virt_root_hub { + struct usb_device *dev; + int devnum; /* Address of Root Hub endpoint */ + struct urb *urb; + void *int_addr; + int send; + int interval; + int numports; + int c_p_r[8]; + struct timer_list rh_int_timer; +}; + +/* + * This describes the full uhci information. + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. + */ +struct uhci { + struct pci_dev *dev; + +#ifdef CONFIG_PROC_FS + /* procfs */ + int num; + struct proc_dir_entry *proc_entry; +#endif + + /* Grabbed from PCI */ + int irq; + unsigned int io_addr; + unsigned int io_size; + + struct pci_pool *qh_pool; + struct pci_pool *td_pool; + + struct usb_bus *bus; + + struct uhci_td *skeltd[UHCI_NUM_SKELTD]; /* Skeleton TD's */ + struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ + + spinlock_t frame_list_lock; + struct uhci_frame_list *fl; /* P: uhci->frame_list_lock */ + int fsbr; /* Full speed bandwidth reclamation */ + unsigned long fsbrtimeout; /* FSBR delay */ + int is_suspended; + + /* Main list of URB's currently controlled by this HC */ + spinlock_t urb_list_lock; + struct list_head urb_list; /* P: uhci->urb_list_lock */ + + /* List of QH's that are done, but waiting to be unlinked (race) */ + spinlock_t qh_remove_list_lock; + struct list_head qh_remove_list; /* P: uhci->qh_remove_list_lock */ + + /* List of asynchronously unlinked URB's */ + spinlock_t urb_remove_list_lock; + struct list_head urb_remove_list; /* P: uhci->urb_remove_list_lock */ + + /* List of URB's awaiting completion callback */ + spinlock_t complete_list_lock; + struct list_head complete_list; /* P: uhci->complete_list_lock */ + + struct virt_root_hub rh; /* private data of the virtual root hub */ +}; + +struct urb_priv { + struct urb *urb; + struct usb_device *dev; + + dma_addr_t setup_packet_dma_handle; + dma_addr_t transfer_buffer_dma_handle; + + struct uhci_qh *qh; /* QH for this URB */ + struct list_head td_list; /* P: urb->lock */ + + int fsbr : 1; /* URB turned on FSBR */ + int fsbr_timeout : 1; /* URB timed out on FSBR */ + int queued : 1; /* QH was queued (not linked in) */ + int short_control_packet : 1; /* If we get a short packet during */ + /* a control transfer, retrigger */ + /* the status phase */ + + int status; /* Final status */ + + unsigned long inserttime; /* In jiffies */ + unsigned long fsbrtime; /* In jiffies */ + + struct list_head queue_list; /* P: uhci->frame_list_lock */ + struct list_head complete_list; /* P: uhci->complete_list_lock */ +}; + +/* + * Locking in uhci.c + * + * spinlocks are used extensively to protect the many lists and data + * structures we have. It's not that pretty, but it's necessary. We + * need to be done with all of the locks (except complete_list_lock) when + * we call urb->complete. I've tried to make it simple enough so I don't + * have to spend hours racking my brain trying to figure out if the + * locking is safe. + * + * Here's the safe locking order to prevent deadlocks: + * + * #1 uhci->urb_list_lock + * #2 urb->lock + * #3 uhci->urb_remove_list_lock, uhci->frame_list_lock, + * uhci->qh_remove_list_lock + * #4 uhci->complete_list_lock + * + * If you're going to grab 2 or more locks at once, ALWAYS grab the lock + * at the lowest level FIRST and NEVER grab locks at the same level at the + * same time. + * + * So, if you need uhci->urb_list_lock, grab it before you grab urb->lock + */ + +/* ------------------------------------------------------------------------- + Virtual Root HUB + ------------------------------------------------------------------------- */ +/* destination of request */ +#define RH_DEVICE 0x00 +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +#endif + diff -Nru a/drivers/usb/host/usb-uhci-debug.h b/drivers/usb/host/usb-uhci-debug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/usb-uhci-debug.h Thu Mar 6 14:22:16 2003 @@ -0,0 +1,195 @@ +#ifdef DEBUG +static void __attribute__((__unused__)) uhci_show_qh (puhci_desc_t qh) +{ + if (qh->type != QH_TYPE) { + dbg("qh has not QH_TYPE"); + return; + } + dbg("QH @ %p/%08llX:", qh, (unsigned long long)qh->dma_addr); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + dbg(" Head Terminate"); + else + dbg(" Head: %s @ %08X", + (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"), + qh->hw.qh.head & ~UHCI_PTR_BITS); + + if (qh->hw.qh.element & UHCI_PTR_TERM) + dbg(" Element Terminate"); + else + dbg(" Element: %s @ %08X", + (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"), + qh->hw.qh.element & ~UHCI_PTR_BITS); +} +#endif + +#if 0 +static void uhci_show_td (puhci_desc_t td) +{ + char *spid; + + switch (td->hw.td.info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = " OUT "; + break; + case USB_PID_IN: + spid = " IN "; + break; + default: + spid = " ? "; + break; + } + + warn(" TD @ %p/%08X, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x", + td, td->dma_addr, + td->hw.td.info >> 21, + ((td->hw.td.info >> 19) & 1), + (td->hw.td.info >> 15) & 15, + (td->hw.td.info >> 8) & 127, + spid, + td->hw.td.buffer); + + warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", + td->hw.td.status & 0x7ff, + ((td->hw.td.status >> 27) & 3), + (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", + (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", + (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", + (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", + (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" + ); + + if (td->hw.td.link & UHCI_PTR_TERM) + warn(" TD Link Terminate"); + else + warn(" Link points to %s @ %08x, %s", + (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"), + td->hw.td.link & ~UHCI_PTR_BITS, + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first")); +} +#endif + +#ifdef DEBUG +static void __attribute__((__unused__)) uhci_show_td_queue (puhci_desc_t td) +{ + //dbg("uhci_show_td_queue %p (%08lX):", td, td->dma_addr); +#if 1 + return; +#else + while (1) { + uhci_show_td (td); + if (td->hw.td.link & UHCI_PTR_TERM) + break; + if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) + td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); + else { + dbg("td points to itself!"); + break; + } + } +#endif +} + +static void __attribute__((__unused__)) uhci_show_queue (puhci_desc_t qh) +{ +#if 0 + uhci_desc_t *start_qh=qh; +#endif + + dbg("uhci_show_queue %p:", qh); +#if 1 + return; +#else + while (1) { + uhci_show_qh (qh); + + if (!(qh->hw.qh.element & UHCI_PTR_TERM)) + uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + break; + + if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) + qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); + else { + dbg("qh points to itself!"); + break; + } + + if (qh==start_qh) { // avoid loop + dbg("Loop detect"); + break; + } + } +#endif +} + +static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short status) +{ + dbg(" stat%d = %04x %s%s%s%s%s%s%s%s", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} + +void uhci_show_status (puhci_t s) +{ + unsigned int io_addr = s->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + usbcmd = inw (io_addr + 0); + usbstat = inw (io_addr + 2); + usbint = inw (io_addr + 4); + usbfrnum = inw (io_addr + 6); + flbaseadd = inl (io_addr + 8); + sof = inb (io_addr + 12); + portsc1 = inw (io_addr + 16); + portsc2 = inw (io_addr + 18); + + dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + dbg(" usbstat = %04x %s%s%s%s%s%s", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + dbg(" usbint = %04x", usbint); + dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1, + 0xfff & (4 * (unsigned int) usbfrnum)); + dbg(" flbaseadd = %08x", flbaseadd); + dbg(" sof = %02x", sof); + uhci_show_sc (1, portsc1); + uhci_show_sc (2, portsc2); +} +#endif diff -Nru a/drivers/usb/host/usb-uhci.c b/drivers/usb/host/usb-uhci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/usb-uhci.c Thu Mar 6 14:22:16 2003 @@ -0,0 +1,3143 @@ +/* + * Universal Host Controller Interface driver for USB (take II). + * + * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar) + * Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) + * Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) + * Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter) + * (c) 2000 Yggdrasil Computing, Inc. (port of new PCI interface support + * from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) 2000 David Brownell, david-b@pacbell.net (usb-ohci.c) + * + * HW-initalization based on material of + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Gregory P. Smith + * + * $Id: usb-uhci.c,v 1.275 2002/01/19 20:57:33 acher Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt() */ +#include +#include +#include +#include + +#include +#include +#include +#include + +/* This enables more detailed sanity checks in submit_iso */ +//#define ISO_SANITY_CHECK + +/* This enables debug printks */ +#define DEBUG + +/* This enables all symbols to be exported, to ease debugging oopses */ +//#define DEBUG_SYMBOLS + +/* This enables an extra UHCI slab for memory debugging */ +#define DEBUG_SLAB + +#define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__ + +#include +#include "usb-uhci.h" +#include "usb-uhci-debug.h" + +#include "../hcd.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.275" +#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber" +#define DRIVER_DESC "USB Universal Host Controller Interface driver" + +#undef DEBUG +#undef dbg +#define dbg(format, arg...) do {} while (0) +#define DEBUG_SYMBOLS +#ifdef DEBUG_SYMBOLS + #define _static + #ifndef EXPORT_SYMTAB + #define EXPORT_SYMTAB + #endif +#else + #define _static static +#endif + +#define queue_dbg dbg //err +#define async_dbg dbg //err + +#ifdef DEBUG_SLAB + static kmem_cache_t *urb_priv_kmem; +#endif + +#define SLAB_FLAG (in_interrupt () || current->state != TASK_RUNNING ? SLAB_ATOMIC : SLAB_KERNEL) +#define KMALLOC_FLAG (in_interrupt () || current->state != TASK_RUNNING ? GFP_ATOMIC : GFP_KERNEL) + +/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth + * Reclamation: feature that puts loop on descriptor loop when + * there's some transfer going on. With FSBR, USB performance + * is optimal, but PCI can be slowed down up-to 5 times, slowing down + * system performance (eg. framebuffer devices). + */ +#define CONFIG_USB_UHCI_HIGH_BANDWIDTH + +/* *_DEPTH_FIRST puts descriptor in depth-first mode. This has + * somehow similar effect to FSBR (higher speed), but does not + * slow PCI down. OTOH USB performace is slightly slower than + * in FSBR case and single device could hog whole USB, starving + * other devices. + */ +#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first +#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first + +/* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST + * will lead to <64KB/sec performance over USB for bulk transfers targeting + * one device's endpoint. You probably do not want to do that. + */ + +// stop bandwidth reclamation after (roughly) 50ms +#define IDLE_TIMEOUT (HZ/20) + +// Suppress HC interrupt error messages for 5s +#define ERROR_SUPPRESSION_TIME (HZ*5) + +_static int rh_submit_urb (struct urb *urb); +_static int rh_unlink_urb (struct urb *urb); +_static int delete_qh (uhci_t *s, uhci_desc_t *qh); +_static int process_transfer (uhci_t *s, struct urb *urb, int mode); +_static int process_interrupt (uhci_t *s, struct urb *urb); +_static int process_iso (uhci_t *s, struct urb *urb, int force); + +// How much URBs with ->next are walked +#define MAX_NEXT_COUNT 2048 + +static uhci_t *devs = NULL; + +/* used by userspace UHCI data structure dumper */ +uhci_t **uhci_devices = &devs; + +/*-------------------------------------------------------------------*/ +// Cleans up collected QHs, but not more than 100 in one go +void clean_descs(uhci_t *s, int force) +{ + struct list_head *q; + uhci_desc_t *qh; + int now=UHCI_GET_CURRENT_FRAME(s), n=0; + + q=s->free_desc.prev; + + while (q != &s->free_desc && (force || n<100)) { + qh = list_entry (q, uhci_desc_t, horizontal); + q=qh->horizontal.prev; + + if ((qh->last_used!=now) || force) + delete_qh(s,qh); + n++; + } +} +/*-------------------------------------------------------------------*/ +_static void uhci_switch_timer_int(uhci_t *s) +{ + + if (!list_empty(&s->urb_unlinked)) + set_td_ioc(s->td1ms); + else + clr_td_ioc(s->td1ms); + + if (s->timeout_urbs) + set_td_ioc(s->td32ms); + else + clr_td_ioc(s->td32ms); + wmb(); +} +/*-------------------------------------------------------------------*/ +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH +_static void enable_desc_loop(uhci_t *s, struct urb *urb) +{ + unsigned long flags; + + if (urb->transfer_flags & USB_NO_FSBR) + return; + + spin_lock_irqsave (&s->qh_lock, flags); + s->chain_end->hw.qh.head&=cpu_to_le32(~UHCI_PTR_TERM); + mb(); + s->loop_usage++; + ((urb_priv_t*)urb->hcpriv)->use_loop=1; + spin_unlock_irqrestore (&s->qh_lock, flags); +} +/*-------------------------------------------------------------------*/ +_static void disable_desc_loop(uhci_t *s, struct urb *urb) +{ + unsigned long flags; + + if (urb->transfer_flags & USB_NO_FSBR) + return; + + spin_lock_irqsave (&s->qh_lock, flags); + if (((urb_priv_t*)urb->hcpriv)->use_loop) { + s->loop_usage--; + + if (!s->loop_usage) { + s->chain_end->hw.qh.head|=cpu_to_le32(UHCI_PTR_TERM); + mb(); + } + ((urb_priv_t*)urb->hcpriv)->use_loop=0; + } + spin_unlock_irqrestore (&s->qh_lock, flags); +} +#endif +/*-------------------------------------------------------------------*/ +_static void queue_urb_unlocked (uhci_t *s, struct urb *urb) +{ + struct list_head *p=&urb->urb_list; +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + { + int type; + type=usb_pipetype (urb->pipe); + + if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) + enable_desc_loop(s, urb); + } +#endif + urb->status = -EINPROGRESS; + ((urb_priv_t*)urb->hcpriv)->started=jiffies; + list_add (p, &s->urb_list); + if (urb->timeout) + s->timeout_urbs++; + uhci_switch_timer_int(s); +} +/*-------------------------------------------------------------------*/ +_static void queue_urb (uhci_t *s, struct urb *urb) +{ + unsigned long flags=0; + + spin_lock_irqsave (&s->urb_list_lock, flags); + queue_urb_unlocked(s,urb); + spin_unlock_irqrestore (&s->urb_list_lock, flags); +} +/*-------------------------------------------------------------------*/ +_static void dequeue_urb (uhci_t *s, struct urb *urb) +{ +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + int type; + + type=usb_pipetype (urb->pipe); + + if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) + disable_desc_loop(s, urb); +#endif + + list_del (&urb->urb_list); + if (urb->timeout && s->timeout_urbs) + s->timeout_urbs--; + +} +/*-------------------------------------------------------------------*/ +_static int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags) +{ + dma_addr_t dma_handle; + + *new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); + if (!*new) + return -ENOMEM; + memset (*new, 0, sizeof (uhci_desc_t)); + (*new)->dma_addr = dma_handle; + set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS)); // last by default + (*new)->type = TD_TYPE; + mb(); + INIT_LIST_HEAD (&(*new)->vertical); + INIT_LIST_HEAD (&(*new)->horizontal); + + return 0; +} +/*-------------------------------------------------------------------*/ +// append a qh to td.link physically, the SW linkage is not affected +_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags) +{ + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); + + set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH); + + mb(); + spin_unlock_irqrestore (&s->td_lock, xxx); +} +/*-------------------------------------------------------------------*/ +/* insert td at last position in td-list of qh (vertical) */ +_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags) +{ + uhci_desc_t *prev; + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); + + list_add_tail (&new->vertical, &qh->vertical); + + prev = list_entry (new->vertical.prev, uhci_desc_t, vertical); + + if (qh == prev ) { + // virgin qh without any tds + set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM); + } + else { + // already tds inserted, implicitely remove TERM bit of prev + set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH)); + } + mb(); + spin_unlock_irqrestore (&s->td_lock, xxx); + + return 0; +} +/*-------------------------------------------------------------------*/ +/* insert new_td after td (horizontal) */ +_static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new) +{ + uhci_desc_t *next; + unsigned long flags; + + spin_lock_irqsave (&s->td_lock, flags); + + next = list_entry (td->horizontal.next, uhci_desc_t, horizontal); + list_add (&new->horizontal, &td->horizontal); + new->hw.td.link = td->hw.td.link; + set_td_link(td, new->dma_addr); + mb(); + spin_unlock_irqrestore (&s->td_lock, flags); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink) +{ + uhci_desc_t *next, *prev; + int dir = 0; + unsigned long flags; + + spin_lock_irqsave (&s->td_lock, flags); + + next = list_entry (element->vertical.next, uhci_desc_t, vertical); + + if (next == element) { + dir = 1; + prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); + } + else + prev = list_entry (element->vertical.prev, uhci_desc_t, vertical); + + if (phys_unlink) { + // really remove HW linking + if (prev->type == TD_TYPE) + prev->hw.td.link = element->hw.td.link; + else + prev->hw.qh.element = element->hw.td.link; + } + + mb (); + + if (dir == 0) + list_del (&element->vertical); + else + list_del (&element->horizontal); + + spin_unlock_irqrestore (&s->td_lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------*/ +_static int delete_desc (uhci_t *s, uhci_desc_t *element) +{ + pci_pool_free(s->desc_pool, element, element->dma_addr); + return 0; +} +/*-------------------------------------------------------------------*/ +// Allocates qh element +_static int alloc_qh (uhci_t *s, uhci_desc_t ** new) +{ + dma_addr_t dma_handle; + + *new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); + if (!*new) + return -ENOMEM; + memset (*new, 0, sizeof (uhci_desc_t)); + (*new)->dma_addr = dma_handle; + set_qh_head(*new, UHCI_PTR_TERM); + set_qh_element(*new, UHCI_PTR_TERM); + (*new)->type = QH_TYPE; + + mb(); + INIT_LIST_HEAD (&(*new)->horizontal); + INIT_LIST_HEAD (&(*new)->vertical); + + dbg("Allocated qh @ %p", *new); + + return 0; +} +/*-------------------------------------------------------------------*/ +// inserts new qh before/after the qh at pos +// flags: 0: insert before pos, 1: insert after pos (for low speed transfers) +_static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order) +{ + uhci_desc_t *old; + unsigned long flags; + + spin_lock_irqsave (&s->qh_lock, flags); + + if (!order) { + // (OLD) (POS) -> (OLD) (NEW) (POS) + old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal); + list_add_tail (&new->horizontal, &pos->horizontal); + set_qh_head(new, MAKE_QH_ADDR (pos)) ; + if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM))) + set_qh_head(old, MAKE_QH_ADDR (new)) ; + } + else { + // (POS) (OLD) -> (POS) (NEW) (OLD) + old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal); + list_add (&new->horizontal, &pos->horizontal); + set_qh_head(new, MAKE_QH_ADDR (old)); + set_qh_head(pos, MAKE_QH_ADDR (new)) ; + } + + mb (); + + spin_unlock_irqrestore (&s->qh_lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------*/ +_static int unlink_qh (uhci_t *s, uhci_desc_t *element) +{ + uhci_desc_t *prev; + unsigned long flags; + + spin_lock_irqsave (&s->qh_lock, flags); + + prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); + prev->hw.qh.head = element->hw.qh.head; + + dbg("unlink qh %p, pqh %p, nxqh %p, to %08x", element, prev, + list_entry (element->horizontal.next, uhci_desc_t, horizontal),le32_to_cpu(element->hw.qh.head) &~15); + + list_del(&element->horizontal); + + mb (); + spin_unlock_irqrestore (&s->qh_lock, flags); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int delete_qh (uhci_t *s, uhci_desc_t *qh) +{ + uhci_desc_t *td; + struct list_head *p; + + list_del (&qh->horizontal); + + while ((p = qh->vertical.next) != &qh->vertical) { + td = list_entry (p, uhci_desc_t, vertical); + dbg("unlink td @ %p",td); + unlink_td (s, td, 0); // no physical unlink + delete_desc (s, td); + } + + delete_desc (s, qh); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static void clean_td_chain (uhci_t *s, uhci_desc_t *td) +{ + struct list_head *p; + uhci_desc_t *td1; + + if (!td) + return; + + while ((p = td->horizontal.next) != &td->horizontal) { + td1 = list_entry (p, uhci_desc_t, horizontal); + delete_desc (s, td1); + } + + delete_desc (s, td); +} + +/*-------------------------------------------------------------------*/ +_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer) +{ + td->hw.td.status = cpu_to_le32(status); + td->hw.td.info = cpu_to_le32(info); + td->hw.td.buffer = cpu_to_le32(buffer); +} +/*-------------------------------------------------------------------*/ +// Removes ALL qhs in chain (paranoia!) +_static void cleanup_skel (uhci_t *s) +{ + unsigned int n; + uhci_desc_t *td; + + dbg("cleanup_skel"); + + clean_descs(s,1); + + + if (s->td32ms) { + + unlink_td(s,s->td32ms,1); + delete_desc(s, s->td32ms); + } + + for (n = 0; n < 8; n++) { + td = s->int_chain[n]; + clean_td_chain (s, td); + } + + if (s->iso_td) { + for (n = 0; n < 1024; n++) { + td = s->iso_td[n]; + clean_td_chain (s, td); + } + kfree (s->iso_td); + } + + if (s->framelist) + pci_free_consistent(s->uhci_pci, PAGE_SIZE, + s->framelist, s->framelist_dma); + + if (s->control_chain) { + // completed init_skel? + struct list_head *p; + uhci_desc_t *qh, *qh1; + + qh = s->control_chain; + while ((p = qh->horizontal.next) != &qh->horizontal) { + qh1 = list_entry (p, uhci_desc_t, horizontal); + delete_qh (s, qh1); + } + + delete_qh (s, qh); + } + else { + if (s->ls_control_chain) + delete_desc (s, s->ls_control_chain); + if (s->control_chain) + delete_desc (s, s->control_chain); + if (s->bulk_chain) + delete_desc (s, s->bulk_chain); + if (s->chain_end) + delete_desc (s, s->chain_end); + } + + if (s->desc_pool) { + pci_pool_destroy(s->desc_pool); + s->desc_pool = NULL; + } + + dbg("cleanup_skel finished"); +} +/*-------------------------------------------------------------------*/ +// allocates framelist and qh-skeletons +// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT) +_static int init_skel (uhci_t *s) +{ + int n, ret; + uhci_desc_t *qh, *td; + + dbg("init_skel"); + + s->framelist = pci_alloc_consistent(s->uhci_pci, PAGE_SIZE, + &s->framelist_dma); + + if (!s->framelist) + return -ENOMEM; + + memset (s->framelist, 0, 4096); + + dbg("creating descriptor pci_pool"); + + s->desc_pool = pci_pool_create("uhci_desc", s->uhci_pci, + sizeof(uhci_desc_t), 16, 0, + GFP_DMA | GFP_ATOMIC); + if (!s->desc_pool) + goto init_skel_cleanup; + + dbg("allocating iso desc pointer list"); + s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL); + + if (!s->iso_td) + goto init_skel_cleanup; + + s->ls_control_chain = NULL; + s->control_chain = NULL; + s->bulk_chain = NULL; + s->chain_end = NULL; + + dbg("allocating iso descs"); + for (n = 0; n < 1024; n++) { + // allocate skeleton iso/irq-tds + if (alloc_td (s, &td, 0)) + goto init_skel_cleanup; + + s->iso_td[n] = td; + s->framelist[n] = cpu_to_le32((__u32) td->dma_addr); + } + + dbg("allocating qh: chain_end"); + if (alloc_qh (s, &qh)) + goto init_skel_cleanup; + + s->chain_end = qh; + + if (alloc_td (s, &td, 0)) + goto init_skel_cleanup; + + fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand) + insert_td (s, qh, td, 0); + qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit + s->td1ms=td; + + dbg("allocating qh: bulk_chain"); + if (alloc_qh (s, &qh)) + goto init_skel_cleanup; + + insert_qh (s, s->chain_end, qh, 0); + s->bulk_chain = qh; + + dbg("allocating qh: control_chain"); + ret = alloc_qh (s, &qh); + if (ret) + goto init_skel_cleanup; + + insert_qh (s, s->bulk_chain, qh, 0); + s->control_chain = qh; + +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + // disabled reclamation loop + set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM); +#endif + + dbg("allocating qh: ls_control_chain"); + if (alloc_qh (s, &qh)) + goto init_skel_cleanup; + + insert_qh (s, s->control_chain, qh, 0); + s->ls_control_chain = qh; + + for (n = 0; n < 8; n++) + s->int_chain[n] = 0; + + dbg("allocating skeleton INT-TDs"); + + for (n = 0; n < 8; n++) { + uhci_desc_t *td; + + if (alloc_td (s, &td, 0)) + goto init_skel_cleanup; + + s->int_chain[n] = td; + if (n == 0) { + set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH); + } + else { + set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr); + } + } + + dbg("Linking skeleton INT-TDs"); + + for (n = 0; n < 1024; n++) { + // link all iso-tds to the interrupt chains + int m, o; + dbg("framelist[%i]=%x",n,le32_to_cpu(s->framelist[n])); + if ((n&127)==127) + ((uhci_desc_t*) s->iso_td[n])->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr); + else + for (o = 1, m = 2; m <= 128; o++, m += m) + if ((n & (m - 1)) == ((m - 1) / 2)) + set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr); + } + + if (alloc_td (s, &td, 0)) + goto init_skel_cleanup; + + fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later) + s->td32ms=td; + + insert_td_horizontal (s, s->int_chain[5], td); + + mb(); + //uhci_show_queue(s->control_chain); + dbg("init_skel exit"); + return 0; + + init_skel_cleanup: + cleanup_skel (s); + return -ENOMEM; +} + +/*-------------------------------------------------------------------*/ +// LOW LEVEL STUFF +// assembles QHs und TDs for control, bulk and iso +/*-------------------------------------------------------------------*/ +_static int uhci_submit_control_urb (struct urb *urb) +{ + uhci_desc_t *qh, *td; + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv; + unsigned long destination, status; + int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + unsigned long len; + char *data; + int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method + + dbg("uhci_submit_control start"); + if (alloc_qh (s, &qh)) // alloc qh for this request + return -ENOMEM; + + if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) // get td for setup stage + { + delete_qh (s, qh); + return -ENOMEM; + } + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; + + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); + + /* Build the TD for the control request, try forever, 8 bytes of data */ + fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma); + + insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh +#if 0 + { + char *sp=urb->setup_packet; + dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe, + sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]); + } + //uhci_show_td(td); +#endif + + len = urb->transfer_buffer_length; + data = urb->transfer_buffer; + + /* If direction is "send", change the frame from SETUP (0x2D) + to OUT (0xE1). Else change it from SETUP to IN (0x69). */ + + destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN); + + while (len > 0) { + int pktsze = len; + + if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) + goto fail_unmap_enomem; + + if (pktsze > maxsze) + pktsze = maxsze; + + destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1 + + // Status, pktsze bytes of data + fill_td (td, status, destination | ((pktsze - 1) << 21), + urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer)); + + insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue 'data stage'-td in qh + + data += pktsze; + len -= pktsze; + } + + /* Build the final TD for control status */ + /* It's only IN if the pipe is out AND we aren't expecting data */ + + destination &= ~UHCI_PID; + + if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0)) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; + + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ + + if (alloc_td (s, &td, UHCI_PTR_DEPTH)) + goto fail_unmap_enomem; + + status &=~TD_CTRL_SPD; + + /* no limit on errors on final packet , 0 bytes of data */ + fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21), + 0); + + insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue status td + + list_add (&qh->desc_list, &urb_priv->desc_list); + + queue_urb (s, urb); // queue before inserting in desc chain + + qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); + + //uhci_show_queue(qh); + /* Start it up... put low speed first */ + if (urb->pipe & TD_CTRL_LS) + insert_qh (s, s->control_chain, qh, 0); + else + insert_qh (s, s->bulk_chain, qh, 0); + + dbg("uhci_submit_control end"); + return 0; + +fail_unmap_enomem: + delete_qh(s, qh); + return -ENOMEM; +} +/*-------------------------------------------------------------------*/ +// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh) +// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock! + +_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL; + uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL; + unsigned long destination, status; + char *data; + unsigned int pipe = urb->pipe; + int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); + int info, len, last; + int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method + + if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) + return -EPIPE; + + queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i", + urb,bulk_urb,urb->pipe,urb->transfer_buffer_length); + + upriv = (urb_priv_t*)urb->hcpriv; + + if (!bulk_urb) { + if (alloc_qh (s, &qh)) // get qh for this request + return -ENOMEM; + + if (urb->transfer_flags & USB_QUEUE_BULK) { + if (alloc_qh(s, &nqh)) // placeholder for clean unlink + { + delete_desc (s, qh); + return -ENOMEM; + } + upriv->next_qh = nqh; + queue_dbg("new next qh %p",nqh); + } + } + else { + bpriv = (urb_priv_t*)bulk_urb->hcpriv; + qh = bpriv->bottom_qh; // re-use bottom qh and next qh + nqh = bpriv->next_qh; + upriv->next_qh=nqh; + upriv->prev_queued_urb=bulk_urb; + } + + if (urb->transfer_flags & USB_QUEUE_BULK) { + if (alloc_qh (s, &bqh)) // "bottom" QH + { + if (!bulk_urb) { + delete_desc(s, qh); + delete_desc(s, nqh); + } + return -ENOMEM; + } + set_qh_element(bqh, UHCI_PTR_TERM); + set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element + upriv->bottom_qh = bqh; + } + queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh); + + /* The "pipe" thing contains the destination in bits 8--18. */ + destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); + + /* 3 errors */ + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | + ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); + + /* Build the TDs for the bulk request */ + len = urb->transfer_buffer_length; + data = urb->transfer_buffer; + + do { // TBD: Really allow zero-length packets? + int pktsze = len; + + if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) + { + delete_qh (s, qh); + return -ENOMEM; + } + + if (pktsze > maxsze) + pktsze = maxsze; + + // pktsze bytes of data + info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) | + (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); + + fill_td (td, status, info, + urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer)); + + data += pktsze; + len -= pktsze; + // Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet + last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET))); + + if (last) + set_td_ioc(td); // last one generates INT + + insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); + if (!first_td) + first_td=td; + usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + + } while (!last); + + if (bulk_urb && bpriv) // everything went OK, link with old bulk URB + bpriv->next_queued_urb=urb; + + list_add (&qh->desc_list, &urb_priv->desc_list); + + if (urb->transfer_flags & USB_QUEUE_BULK) + append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first); + + queue_urb_unlocked (s, urb); + + if (urb->transfer_flags & USB_QUEUE_BULK) + set_qh_element(qh, first_td->dma_addr); + else + qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // arm QH + + if (!bulk_urb) { // new bulk queue + if (urb->transfer_flags & USB_QUEUE_BULK) { + spin_lock (&s->td_lock); // both QHs in one go + insert_qh (s, s->chain_end, qh, 0); // Main QH + insert_qh (s, s->chain_end, nqh, 0); // Helper QH + spin_unlock (&s->td_lock); + } + else + insert_qh (s, s->chain_end, qh, 0); + } + + //uhci_show_queue(s->bulk_chain); + //dbg("uhci_submit_bulk_urb: exit\n"); + return 0; +} +/*-------------------------------------------------------------------*/ +_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv) +{ + struct list_head *p; + uhci_desc_t *td; + + for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) { + td = list_entry (p, uhci_desc_t, desc_list); + unlink_td (s, td, 1); + } +} +/*-------------------------------------------------------------------*/ +_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv) +{ + struct list_head *p; + uhci_desc_t *td; + + while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { + td = list_entry (p, uhci_desc_t, desc_list); + list_del (p); + delete_desc (s, td); + } +} +/*-------------------------------------------------------------------*/ +/* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink) + CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark) + CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink) + looks a bit complicated because of all the bulk queueing goodies +*/ + +_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode) +{ + uhci_desc_t *bqh, *nqh, *prevqh, *prevtd; + int now; + urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; + + now=UHCI_GET_CURRENT_FRAME(s); + + bqh=priv->bottom_qh; + + if (!priv->next_queued_urb) { // no more appended bulk queues + + queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", urb, qh, bqh, priv->next_qh); + + if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) { // qh not top of the queue + unsigned long flags; + urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; + + spin_lock_irqsave (&s->qh_lock, flags); + prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); + prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical); + set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh + mb(); + queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd); + spin_unlock_irqrestore (&s->qh_lock, flags); + + ppriv->bottom_qh = priv->bottom_qh; + ppriv->next_queued_urb = NULL; + } + else { // queue is dead, qh is top of the queue + + if (mode != CLEAN_TRANSFER_DELETION_MARK) + unlink_qh(s, qh); // remove qh from horizontal chain + + if (bqh) { // remove remainings of bulk queue + nqh=priv->next_qh; + + if (mode != CLEAN_TRANSFER_DELETION_MARK) + unlink_qh(s, nqh); // remove nqh from horizontal chain + + if (mode != CLEAN_TRANSFER_NO_DELETION) { // add helper QHs to free desc list + nqh->last_used = bqh->last_used = now; + list_add_tail (&nqh->horizontal, &s->free_desc); + list_add_tail (&bqh->horizontal, &s->free_desc); + } + } + } + } + else { // there are queued urbs following + + queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p", + urb, priv->prev_queued_urb, priv->next_queued_urb, qh, bqh, priv->next_qh); + + if (mode != CLEAN_TRANSFER_DELETION_MARK) { // no work for cleanup at unlink-completion + struct urb *nurb; + unsigned long flags; + + nurb = priv->next_queued_urb; + spin_lock_irqsave (&s->qh_lock, flags); + + if (!priv->prev_queued_urb) { // top QH + + prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal); + set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); + list_del (&qh->horizontal); // remove this qh form horizontal chain + list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain + } + else { // intermediate QH + urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; + urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv; + uhci_desc_t * bnqh; + + bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list); + ppriv->bottom_qh = bnqh; + ppriv->next_queued_urb = nurb; + prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); + set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); + } + + mb(); + ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb; + spin_unlock_irqrestore (&s->qh_lock, flags); + } + } + + if (mode != CLEAN_TRANSFER_NO_DELETION) { + qh->last_used = now; + list_add_tail (&qh->horizontal, &s->free_desc); // mark qh for later deletion/kfree + } +} +/*-------------------------------------------------------------------*/ +// Release bandwidth for Interrupt or Isoc. transfers +_static void uhci_release_bandwidth(struct urb *urb) +{ + if (urb->bandwidth) { + switch (usb_pipetype(urb->pipe)) { + case PIPE_INTERRUPT: + usb_release_bandwidth (urb->dev, urb, 0); + break; + case PIPE_ISOCHRONOUS: + usb_release_bandwidth (urb->dev, urb, 1); + break; + default: + break; + } + } +} + +_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv) +{ + if (urb_priv->setup_packet_dma) + pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma, + sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); + + if (urb_priv->transfer_buffer_dma) + pci_dma_sync_single(s->uhci_pci, urb_priv->transfer_buffer_dma, + urb->transfer_buffer_length, + usb_pipein(urb->pipe) ? + PCI_DMA_FROMDEVICE : + PCI_DMA_TODEVICE); +} + +_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv) +{ + if (urb_priv->setup_packet_dma) { + pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma, + sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); + urb_priv->setup_packet_dma = 0; + } + if (urb_priv->transfer_buffer_dma) { + pci_unmap_single(s->uhci_pci, urb_priv->transfer_buffer_dma, + urb->transfer_buffer_length, + usb_pipein(urb->pipe) ? + PCI_DMA_FROMDEVICE : + PCI_DMA_TODEVICE); + urb_priv->transfer_buffer_dma = 0; + } +} +/*-------------------------------------------------------------------*/ +/* needs urb_list_lock! + mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list + UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list +*/ +_static int uhci_unlink_urb_async (uhci_t *s,struct urb *urb, int mode) +{ + uhci_desc_t *qh; + urb_priv_t *urb_priv; + + async_dbg("unlink_urb_async called %p",urb); + + if ((urb->status == -EINPROGRESS) || + ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags)) + { + ((urb_priv_t*)urb->hcpriv)->started = ~0; // mark + dequeue_urb (s, urb); + + if (mode==UNLINK_ASYNC_STORE_URB) + list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb + + uhci_switch_timer_int(s); + s->unlink_urb_done = 1; + uhci_release_bandwidth(urb); + + urb->status = -ECONNABORTED; // mark urb as "waiting to be killed" + urb_priv = (urb_priv_t*)urb->hcpriv; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_INTERRUPT: + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + + case PIPE_ISOCHRONOUS: + uhci_clean_iso_step1 (s, urb_priv); + break; + + case PIPE_BULK: + case PIPE_CONTROL: + qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); + uhci_clean_transfer (s, urb, qh, CLEAN_TRANSFER_NO_DELETION); + break; + } + ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s); + return -EINPROGRESS; // completion will follow + } + + return 0; // URB already dead +} +/*-------------------------------------------------------------------*/ +// kills an urb by unlinking descriptors and waiting for at least one frame +_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb) +{ + uhci_desc_t *qh; + urb_priv_t *urb_priv; + unsigned long flags=0; + struct usb_device *usb_dev; + + spin_lock_irqsave (&s->urb_list_lock, flags); + + if (urb->status == -EINPROGRESS) { + + // move descriptors out of the running chains, dequeue urb + uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_DONT_STORE); + + urb_priv = urb->hcpriv; + urb->status = -ENOENT; // prevent from double deletion after unlock + spin_unlock_irqrestore (&s->urb_list_lock, flags); + + // cleanup the rest + switch (usb_pipetype (urb->pipe)) { + + case PIPE_INTERRUPT: + case PIPE_ISOCHRONOUS: + uhci_wait_ms(1); + uhci_clean_iso_step2(s, urb_priv); + break; + + case PIPE_BULK: + case PIPE_CONTROL: + qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); + uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK); + uhci_wait_ms(1); + } + urb->status = -ENOENT; // mark urb as killed + + uhci_urb_dma_unmap(s, urb, urb->hcpriv); + +#ifdef DEBUG_SLAB + kmem_cache_free (urb_priv_kmem, urb->hcpriv); +#else + kfree (urb->hcpriv); +#endif + usb_dev = urb->dev; + if (urb->complete) { + dbg("unlink_urb: calling completion"); + urb->dev = NULL; + urb->complete ((struct urb *) urb); + } + usb_dec_dev_use (usb_dev); + } + else + spin_unlock_irqrestore (&s->urb_list_lock, flags); + + return 0; +} +/*-------------------------------------------------------------------*/ +// async unlink_urb completion/cleanup work +// has to be protected by urb_list_lock! +// features: if set in transfer_flags, the resulting status of the killed +// transaction is not overwritten + +_static void uhci_cleanup_unlink(uhci_t *s, int force) +{ + struct list_head *q; + struct urb *urb; + struct usb_device *dev; + int now, type; + urb_priv_t *urb_priv; + + q=s->urb_unlinked.next; + now=UHCI_GET_CURRENT_FRAME(s); + + while (q != &s->urb_unlinked) { + + urb = list_entry (q, struct urb, urb_list); + + urb_priv = (urb_priv_t*)urb->hcpriv; + q = urb->urb_list.next; + + if (!urb_priv) // avoid crash when URB is corrupted + break; + + if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) { + async_dbg("async cleanup %p",urb); + type=usb_pipetype (urb->pipe); + + switch (type) { // process descriptors + case PIPE_CONTROL: + process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) + break; + case PIPE_BULK: + if (!s->avoid_bulk.counter) + process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) + else + continue; + break; + case PIPE_ISOCHRONOUS: + process_iso (s, urb, PROCESS_ISO_FORCE); // force, don't unlink + break; + case PIPE_INTERRUPT: + process_interrupt (s, urb); + break; + } + + if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) + urb->status = -ECONNRESET; // mark as asynchronously killed + + dev = urb->dev; // completion may destroy all... + urb_priv = urb->hcpriv; + list_del (&urb->urb_list); + + uhci_urb_dma_sync(s, urb, urb_priv); + if (urb->complete) { + spin_unlock(&s->urb_list_lock); + urb->dev = NULL; + urb->complete ((struct urb *) urb); + spin_lock(&s->urb_list_lock); + } + + if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) + urb->status = -ENOENT; // now the urb is really dead + + switch (type) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + uhci_clean_iso_step2(s, urb_priv); + break; + } + + uhci_urb_dma_unmap(s, urb, urb_priv); + + usb_dec_dev_use (dev); +#ifdef DEBUG_SLAB + kmem_cache_free (urb_priv_kmem, urb_priv); +#else + kfree (urb_priv); +#endif + + } + } +} + +/*-------------------------------------------------------------------*/ +_static int uhci_unlink_urb (struct urb *urb) +{ + uhci_t *s; + unsigned long flags=0; + dbg("uhci_unlink_urb called for %p",urb); + if (!urb || !urb->dev) // you never know... + return -EINVAL; + + s = (uhci_t*) urb->dev->bus->hcpriv; + + if (usb_pipedevice (urb->pipe) == s->rh.devnum) + return rh_unlink_urb (urb); + + if (!urb->hcpriv) + return -EINVAL; + + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + int ret; + spin_lock_irqsave (&s->urb_list_lock, flags); + + uhci_release_bandwidth(urb); + ret = uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); + + spin_unlock_irqrestore (&s->urb_list_lock, flags); + return ret; + } + else + return uhci_unlink_urb_sync(s, urb); +} +/*-------------------------------------------------------------------*/ +// In case of ASAP iso transfer, search the URB-list for already queued URBs +// for this EP and calculate the earliest start frame for the new +// URB (easy seamless URB continuation!) +_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end) +{ + struct urb *u, *last_urb = NULL; + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + struct list_head *p; + int ret=-1; + unsigned long flags; + + spin_lock_irqsave (&s->urb_list_lock, flags); + p=s->urb_list.prev; + + for (; p != &s->urb_list; p = p->prev) { + u = list_entry (p, struct urb, urb_list); + // look for pending URBs with identical pipe handle + // works only because iso doesn't toggle the data bit! + if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } + } + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; + ret=0; + } + + spin_unlock_irqrestore(&s->urb_list_lock, flags); + + return ret; +} +/*-------------------------------------------------------------------*/ +// adjust start_frame according to scheduling constraints (ASAP etc) + +_static int iso_find_start (struct urb *urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + unsigned int now; + unsigned int start_limit = 0, stop_limit = 0, queued_size; + int limits; + + now = UHCI_GET_CURRENT_FRAME (s) & 1023; + + if ((unsigned) urb->number_of_packets > 900) + return -EFBIG; + + limits = find_iso_limits (urb, &start_limit, &stop_limit); + queued_size = (stop_limit - start_limit) & 1023; + + if (urb->transfer_flags & USB_ISO_ASAP) { + // first iso + if (limits) { + // 10ms setup should be enough //FIXME! + urb->start_frame = (now + 10) & 1023; + } + else { + urb->start_frame = stop_limit; //seamless linkage + + if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) { + info("iso_find_start: gap in seamless isochronous scheduling"); + dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x", + now, urb->start_frame, urb->number_of_packets, urb->pipe); + urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME! + } + } + } + else { + urb->start_frame &= 1023; + if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) { + dbg("iso_find_start: now between start_frame and end"); + return -EAGAIN; + } + } + + /* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */ + if (limits) + return 0; + + if (((urb->start_frame - start_limit) & 1023) < queued_size || + ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) { + dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u", + urb->start_frame, urb->number_of_packets, start_limit, stop_limit); + return -EAGAIN; + } + + return 0; +} +/*-------------------------------------------------------------------*/ +// submits USB interrupt (ie. polling ;-) +// ASAP-flag set implicitely +// if period==0, the transfer is only done once + +_static int uhci_submit_int_urb (struct urb *urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv; + int nint, n; + uhci_desc_t *td; + int status, destination; + int info; + unsigned int pipe = urb->pipe; + + if (urb->interval < 0 || urb->interval >= 256) + return -EINVAL; + + if (urb->interval == 0) + nint = 0; + else { + for (nint = 0, n = 1; nint <= 8; nint++, n += n) // round interval down to 2^n + { + if (urb->interval < n) { + urb->interval = n / 2; + break; + } + } + nint--; + } + + dbg("Rounded interval to %i, chain %i", urb->interval, nint); + + urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case... + + urb->number_of_packets = 1; + + // INT allows only one packet + if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe))) + return -EINVAL; + + if (alloc_td (s, &td, UHCI_PTR_DEPTH)) + return -ENOMEM; + + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); + + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) | + (((urb->transfer_buffer_length - 1) & 0x7ff) << 21); + + + info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); + + fill_td (td, status, info, urb_priv->transfer_buffer_dma); + list_add_tail (&td->desc_list, &urb_priv->desc_list); + + queue_urb (s, urb); + + insert_td_horizontal (s, s->int_chain[nint], td); // store in INT-TDs + + usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int uhci_submit_iso_urb (struct urb *urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv; +#ifdef ISO_SANITY_CHECK + int pipe=urb->pipe; + int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); +#endif + int n, ret, last=0; + uhci_desc_t *td, **tdm; + int status, destination; + unsigned long flags; + + __save_flags(flags); + __cli(); // Disable IRQs to schedule all ISO-TDs in time + ret = iso_find_start (urb); // adjusts urb->start_frame for later use + + if (ret) + goto err; + + tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG); + + if (!tdm) { + ret = -ENOMEM; + goto err; + } + + memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*)); + + // First try to get all TDs. Cause: Removing already inserted TDs can only be done + // racefree in three steps: unlink TDs, wait one frame, delete TDs. + // So, this solutions seems simpler... + + for (n = 0; n < urb->number_of_packets; n++) { + dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length); + if (!urb->iso_frame_desc[n].length) + continue; // allows ISO striping by setting length to zero in iso_descriptor + + +#ifdef ISO_SANITY_CHECK + if(urb->iso_frame_desc[n].length > maxsze) { + + err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze); + ret=-EINVAL; + } + else +#endif + if (alloc_td (s, &td, UHCI_PTR_DEPTH)) { + int i; // Cleanup allocated TDs + + for (i = 0; i < n; n++) + if (tdm[i]) + delete_desc(s, tdm[i]); + kfree (tdm); + goto err; + } + last=n; + tdm[n] = td; + } + + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; + + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe); + + // Queue all allocated TDs + for (n = 0; n < urb->number_of_packets; n++) { + td = tdm[n]; + if (!td) + continue; + + if (n == last) { + status |= TD_CTRL_IOC; + queue_urb (s, urb); + } + + fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21), + urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset); + list_add_tail (&td->desc_list, &urb_priv->desc_list); + + insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td); // store in iso-tds + } + + kfree (tdm); + dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023); + ret = 0; + + err: + __restore_flags(flags); + return ret; +} +/*-------------------------------------------------------------------*/ +// returns: 0 (no transfer queued), urb* (this urb already queued) + +_static struct urb* search_dev_ep (uhci_t *s, struct urb *urb) +{ + struct list_head *p; + struct urb *tmp; + unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0); + + dbg("search_dev_ep:"); + + p=s->urb_list.next; + + for (; p != &s->urb_list; p = p->next) { + tmp = list_entry (p, struct urb, urb_list); + dbg("urb: %p", tmp); + // we can accept this urb if it is not queued at this time + // or if non-iso transfer requests should be scheduled for the same device and pipe + if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) || + (urb == tmp)) { + return tmp; // found another urb already queued for processing + } + } + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int uhci_submit_urb (struct urb *urb) +{ + uhci_t *s; + urb_priv_t *urb_priv; + int ret = 0, type; + unsigned long flags; + struct urb *queued_urb=NULL; + int bustime; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + s = (uhci_t*) urb->dev->bus->hcpriv; + //dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe)); + + if (!s->running) + return -ENODEV; + + type = usb_pipetype (urb->pipe); + + if (usb_pipedevice (urb->pipe) == s->rh.devnum) + return rh_submit_urb (urb); /* virtual root hub */ + + // Sanity checks + if (usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)) <= 0) { + err("uhci_submit_urb: pipesize for pipe %x is zero", urb->pipe); + return -EMSGSIZE; + } + + if (urb->transfer_buffer_length < 0 && type != PIPE_ISOCHRONOUS) { + err("uhci_submit_urb: Negative transfer length for urb %p", urb); + return -EINVAL; + } + + usb_inc_dev_use (urb->dev); + + spin_lock_irqsave (&s->urb_list_lock, flags); + + queued_urb = search_dev_ep (s, urb); // returns already queued urb for that pipe + + if (queued_urb) { + + queue_dbg("found bulk urb %p\n", queued_urb); + + if (( type != PIPE_BULK) || + ((type == PIPE_BULK) && + (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + usb_dec_dev_use (urb->dev); + err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb); + return -ENXIO; // urb already queued + } + } + +#ifdef DEBUG_SLAB + urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG); +#else + urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG); +#endif + if (!urb_priv) { + usb_dec_dev_use (urb->dev); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + return -ENOMEM; + } + + memset(urb_priv, 0, sizeof(urb_priv_t)); + urb->hcpriv = urb_priv; + INIT_LIST_HEAD (&urb_priv->desc_list); + + dbg("submit_urb: scheduling %p", urb); + + if (type == PIPE_CONTROL) + urb_priv->setup_packet_dma = pci_map_single(s->uhci_pci, urb->setup_packet, + sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); + + if (urb->transfer_buffer_length) + urb_priv->transfer_buffer_dma = pci_map_single(s->uhci_pci, + urb->transfer_buffer, + urb->transfer_buffer_length, + usb_pipein(urb->pipe) ? + PCI_DMA_FROMDEVICE : + PCI_DMA_TODEVICE); + + if (type == PIPE_BULK) { + + if (queued_urb) { + while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb) // find last queued bulk + queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb; + + ((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb; + } + atomic_inc (&s->avoid_bulk); + ret = uhci_submit_bulk_urb (urb, queued_urb); + atomic_dec (&s->avoid_bulk); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + } + else { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + switch (type) { + case PIPE_ISOCHRONOUS: + if (urb->bandwidth == 0) { /* not yet checked/allocated */ + if (urb->number_of_packets <= 0) { + ret = -EINVAL; + break; + } + + bustime = usb_check_bandwidth (urb->dev, urb); + if (bustime < 0) + ret = bustime; + else { + ret = uhci_submit_iso_urb(urb); + if (ret == 0) + usb_claim_bandwidth (urb->dev, urb, bustime, 1); + } + } else { /* bandwidth is already set */ + ret = uhci_submit_iso_urb(urb); + } + break; + case PIPE_INTERRUPT: + if (urb->bandwidth == 0) { /* not yet checked/allocated */ + bustime = usb_check_bandwidth (urb->dev, urb); + if (bustime < 0) + ret = bustime; + else { + ret = uhci_submit_int_urb(urb); + if (ret == 0) + usb_claim_bandwidth (urb->dev, urb, bustime, 0); + } + } else { /* bandwidth is already set */ + ret = uhci_submit_int_urb(urb); + } + break; + case PIPE_CONTROL: + ret = uhci_submit_control_urb (urb); + break; + default: + ret = -EINVAL; + } + } + + dbg("submit_urb: scheduled with ret: %d", ret); + + if (ret != 0) { + uhci_urb_dma_unmap(s, urb, urb_priv); + usb_dec_dev_use (urb->dev); +#ifdef DEBUG_SLAB + kmem_cache_free(urb_priv_kmem, urb_priv); +#else + kfree (urb_priv); +#endif + return ret; + } + + return 0; +} + +// Checks for URB timeout and removes bandwidth reclamation if URB idles too long +_static void uhci_check_timeouts(uhci_t *s) +{ + struct list_head *p,*p2; + struct urb *urb; + int type; + + p = s->urb_list.prev; + + while (p != &s->urb_list) { + urb_priv_t *hcpriv; + + p2 = p; + p = p->prev; + urb = list_entry (p2, struct urb, urb_list); + type = usb_pipetype (urb->pipe); + + hcpriv = (urb_priv_t*)urb->hcpriv; + + if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) { + urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; + async_dbg("uhci_check_timeout: timeout for %p",urb); + uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); + } +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) && + (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT)) + disable_desc_loop(s, urb); +#endif + + } + s->timeout_check=jiffies; +} + +/*------------------------------------------------------------------- + Virtual Root Hub + -------------------------------------------------------------------*/ + +_static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +_static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + + +_static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/*-------------------------------------------------------------------------*/ +/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ +_static int rh_send_irq (struct urb *urb) +{ + int len = 1; + int i; + uhci_t *uhci = urb->dev->bus->hcpriv; + unsigned int io_addr = uhci->io_addr; + __u16 data = 0; + + for (i = 0; i < uhci->rh.numports; i++) { + data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + len = (i + 1) / 8 + 1; + } + + *(__u16 *) urb->transfer_buffer = cpu_to_le16 (data); + urb->actual_length = len; + urb->status = 0; + + if ((data > 0) && (uhci->rh.send != 0)) { + dbg("Root-Hub INT complete: port1: %x port2: %x data: %x", + inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data); + urb->complete (urb); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ +_static int rh_init_int_timer (struct urb *urb); + +_static void rh_int_timer_do (unsigned long ptr) +{ + int len; + struct urb *urb = (struct urb*) ptr; + uhci_t *uhci = urb->dev->bus->hcpriv; + + if (uhci->rh.send) { + len = rh_send_irq (urb); + if (len > 0) { + urb->actual_length = len; + if (urb->complete) + urb->complete (urb); + } + } + rh_init_int_timer (urb); +} + +/*-------------------------------------------------------------------------*/ +/* Root Hub INTs are polled by this timer, polling interval 20ms */ + +_static int rh_init_int_timer (struct urb *urb) +{ + uhci_t *uhci = urb->dev->bus->hcpriv; + + uhci->rh.interval = urb->interval; + init_timer (&uhci->rh.rh_int_timer); + uhci->rh.rh_int_timer.function = rh_int_timer_do; + uhci->rh.rh_int_timer.data = (unsigned long) urb; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000; + add_timer (&uhci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +#define OK(x) len = (x); break + +#define CLR_RH_PORTSTAT(x) \ + status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ + status = (status & 0xfff5) & ~(x); \ + outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) + +#define SET_RH_PORTSTAT(x) \ + status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ + status = (status & 0xfff5) | (x); \ + outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) + + +/*-------------------------------------------------------------------------*/ +/**** + ** Root Hub Control Pipe + *************************/ + + +_static int rh_submit_urb (struct urb *urb) +{ + struct usb_device *usb_dev = urb->dev; + uhci_t *uhci = usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = 0; + int stat = 0; + int i; + unsigned int io_addr = uhci->io_addr; + __u16 cstatus; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipetype (pipe) == PIPE_INTERRUPT) { + dbg("Root-Hub submit IRQ: every %d ms", urb->interval); + uhci->rh.urb = urb; + uhci->rh.send = 1; + uhci->rh.interval = urb->interval; + rh_init_int_timer (urb); + + return 0; + } + + + bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8; + wValue = le16_to_cpu (cmd->wValue); + wIndex = le16_to_cpu (cmd->wIndex); + wLength = le16_to_cpu (cmd->wLength); + + for (i = 0; i < 8; i++) + uhci->rh.c_p_r[i] = 0; + + dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x", + uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data = cpu_to_le16 (1); + OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data = cpu_to_le16 (0); + OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data = cpu_to_le16 (0); + OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data = cpu_to_le32 (0); + OK (4); /* hub power ** */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on ** */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(__u16 *) data = cpu_to_le16 (status); + *(__u16 *) (data + 2) = cpu_to_le16 (cstatus); + OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case (RH_C_HUB_OVER_CURRENT): + OK (0); /* hub power over current ** */ + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + CLR_RH_PORTSTAT (USBPORTSC_PE); + OK (0); + case (RH_PORT_SUSPEND): + CLR_RH_PORTSTAT (USBPORTSC_SUSP); + OK (0); + case (RH_PORT_POWER): + OK (0); /* port power ** */ + case (RH_C_PORT_CONNECTION): + SET_RH_PORTSTAT (USBPORTSC_CSC); + OK (0); + case (RH_C_PORT_ENABLE): + SET_RH_PORTSTAT (USBPORTSC_PEC); + OK (0); + case (RH_C_PORT_SUSPEND): +/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + OK (0); + case (RH_C_PORT_OVER_CURRENT): + OK (0); /* port power over current ** */ + case (RH_C_PORT_RESET): + uhci->rh.c_p_r[wIndex - 1] = 0; + OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + SET_RH_PORTSTAT (USBPORTSC_SUSP); + OK (0); + case (RH_PORT_RESET): + SET_RH_PORTSTAT (USBPORTSC_PR); + uhci_wait_ms (10); + uhci->rh.c_p_r[wIndex - 1] = 1; + CLR_RH_PORTSTAT (USBPORTSC_PR); + udelay (10); + SET_RH_PORTSTAT (USBPORTSC_PE); + uhci_wait_ms (10); + SET_RH_PORTSTAT (0xa); + OK (0); + case (RH_PORT_POWER): + OK (0); /* port power ** */ + case (RH_PORT_ENABLE): + SET_RH_PORTSTAT (USBPORTSC_PE); + OK (0); + } + break; + + case RH_SET_ADDRESS: + uhci->rh.devnum = wValue; + OK (0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, leni, + min_t(unsigned int, + sizeof (root_hub_dev_des), wLength)); + memcpy (data, root_hub_dev_des, len); + OK (len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, leni, + min_t(unsigned int, + sizeof (root_hub_config_des), wLength)); + memcpy (data, root_hub_config_des, len); + OK (len); + case (0x03): /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, + uhci->io_addr, "UHCI", + data, wLength); + if (len > 0) { + OK(min_t(int, leni, len)); + } else + stat = -EPIPE; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = uhci->rh.numports; + len = min_t(unsigned int, leni, + min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); + memcpy (data, root_hub_hub_des, len); + OK (len); + + case RH_GET_CONFIGURATION: + *(__u8 *) data = 0x01; + OK (1); + + case RH_SET_CONFIGURATION: + OK (0); + default: + stat = -EPIPE; + } + + dbg("Root-Hub stat port1: %x port2: %x", + inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2)); + + urb->actual_length = len; + urb->status = stat; + urb->dev=NULL; + if (urb->complete) + urb->complete (urb); + return 0; +} +/*-------------------------------------------------------------------------*/ + +_static int rh_unlink_urb (struct urb *urb) +{ + uhci_t *uhci = urb->dev->bus->hcpriv; + + if (uhci->rh.urb==urb) { + dbg("Root-Hub unlink IRQ"); + uhci->rh.send = 0; + del_timer (&uhci->rh.rh_int_timer); + } + return 0; +} +/*-------------------------------------------------------------------*/ + +/* + * Map status to standard result codes + * + * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) + * is True for output TDs and False for input TDs. + */ +_static int uhci_map_status (int status, int dir_out) +{ + if (!status) + return 0; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return -EPROTO; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return -ETIMEDOUT; + else + return -EILSEQ; + } + if (status & TD_CTRL_NAK) /* NAK */ + return -ETIMEDOUT; + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EOVERFLOW; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return -ENOSR; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + if (status & TD_CTRL_ACTIVE) /* Active */ + return 0; + + return -EPROTO; +} + +/* + * Only the USB core should call uhci_alloc_dev and uhci_free_dev + */ +_static int uhci_alloc_dev (struct usb_device *usb_dev) +{ + return 0; +} + +_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all) +{ + unsigned long flags; + struct list_head *p; + struct list_head *p2; + struct urb *urb; + + spin_lock_irqsave (&s->urb_list_lock, flags); + p = s->urb_list.prev; + while (p != &s->urb_list) { + p2 = p; + p = p->prev ; + urb = list_entry (p2, struct urb, urb_list); + dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev); + + //urb->transfer_flags |=USB_ASYNC_UNLINK; + + if (remove_all || (usb_dev == urb->dev)) { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + warn("forced removing of queued URB %p due to disconnect",urb); + uhci_unlink_urb(urb); + urb->dev = NULL; // avoid further processing of this URB + spin_lock_irqsave (&s->urb_list_lock, flags); + p = s->urb_list.prev; + } + } + spin_unlock_irqrestore (&s->urb_list_lock, flags); +} + +_static int uhci_free_dev (struct usb_device *usb_dev) +{ + uhci_t *s; + + + if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv) + return -EINVAL; + + s=(uhci_t*) usb_dev->bus->hcpriv; + uhci_unlink_urbs(s, usb_dev, 0); + + return 0; +} + +/* + * uhci_get_current_frame_number() + * + * returns the current frame number for a USB bus/controller. + */ +_static int uhci_get_current_frame_number (struct usb_device *usb_dev) +{ + return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv); +} + +struct usb_operations uhci_device_operations = +{ + uhci_alloc_dev, + uhci_free_dev, + uhci_get_current_frame_number, + uhci_submit_urb, + uhci_unlink_urb +}; + +_static void correct_data_toggles(struct urb *urb) +{ + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), + !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))); + + while(urb) { + urb_priv_t *priv=urb->hcpriv; + uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list); + struct list_head *p = qh->vertical.next; + uhci_desc_t *td; + dbg("URB to correct %p\n", urb); + + for (; p != &qh->vertical; p = p->next) { + td = list_entry (p, uhci_desc_t, vertical); + td->hw.td.info^=cpu_to_le32(1<next_queued_urb; + } +} + +/* + * For IN-control transfers, process_transfer gets a bit more complicated, + * since there are devices that return less data (eg. strings) than they + * have announced. This leads to a queue abort due to the short packet, + * the status stage is not executed. If this happens, the status stage + * is manually re-executed. + * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH) + * PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb) + */ + +_static int process_transfer (uhci_t *s, struct urb *urb, int mode) +{ + int ret = 0; + urb_priv_t *urb_priv = urb->hcpriv; + struct list_head *qhl = urb_priv->desc_list.next; + uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list); + struct list_head *p = qh->vertical.next; + uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); + uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical); + int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle + int maxlength; // extracted and remapped info from TD + int actual_length; + int status = 0; + + //dbg("process_transfer: urb %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc); + + /* if the status phase has been retriggered and the + queue is empty or the last status-TD is inactive, the retriggered + status stage is completed + */ + + if (urb_priv->flags && + ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_active(desc))) + goto transfer_finished; + + urb->actual_length=0; + + for (; p != &qh->vertical; p = p->next) { + desc = list_entry (p, uhci_desc_t, vertical); + + if (is_td_active(desc)) { // do not process active TDs + if (mode == CLEAN_TRANSFER_DELETION_MARK) // if called from async_unlink + uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK); + return ret; + } + + actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); // extract transfer parameters from TD + maxlength = (((le32_to_cpu(desc->hw.td.info) >> 21) & 0x7ff) + 1) & 0x7ff; + status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); + + if (status == -EPIPE) { // see if EP is stalled + // set up stalled condition + usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } + + if (status && (status != -EPIPE)) { // if any error occurred stop processing of further TDs + // only set ret if status returned an error + is_error: + ret = status; + urb->error_count++; + break; + } + else if ((le32_to_cpu(desc->hw.td.info) & 0xff) != USB_PID_SETUP) + urb->actual_length += actual_length; + + // got less data than requested + if ( (actual_length < maxlength)) { + if (urb->transfer_flags & USB_DISABLE_SPD) { + status = -EREMOTEIO; // treat as real error + dbg("process_transfer: SPD!!"); + break; // exit after this TD because SP was detected + } + + // short read during control-IN: re-start status stage + if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) { + if (uhci_packetid(le32_to_cpu(last_desc->hw.td.info)) == USB_PID_OUT) { + + set_qh_element(qh, last_desc->dma_addr); // re-trigger status stage + dbg("short packet during control transfer, retrigger status stage @ %p",last_desc); + urb_priv->flags = 1; // mark as short control packet + return 0; + } + } + // all other cases: short read is OK + data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info)); + break; + } + else if (status) + goto is_error; + + data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info)); + queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, le32_to_cpu(desc->hw.td.status),status, data_toggle); + + } + + if (usb_pipetype (urb->pipe) == PIPE_BULK ) { /* toggle correction for short bulk transfers (nonqueued/queued) */ + + urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; + struct urb *next_queued_urb=priv->next_queued_urb; + + if (next_queued_urb) { + urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv; + uhci_desc_t *qh = list_entry (next_priv->desc_list.next, uhci_desc_t, desc_list); + uhci_desc_t *first_td=list_entry (qh->vertical.next, uhci_desc_t, vertical); + + if (data_toggle == uhci_toggle (le32_to_cpu(first_td->hw.td.info))) { + err("process_transfer: fixed toggle"); + correct_data_toggles(next_queued_urb); + } + } + else + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle); + } + + transfer_finished: + + uhci_clean_transfer(s, urb, qh, mode); + + urb->status = status; + +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + disable_desc_loop(s,urb); +#endif + + queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d", + urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count); + return ret; +} + +_static int process_interrupt (uhci_t *s, struct urb *urb) +{ + int i, ret = -EINPROGRESS; + urb_priv_t *urb_priv = urb->hcpriv; + struct list_head *p = urb_priv->desc_list.next; + uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); + + int actual_length; + int status = 0; + + //dbg("urb contains interrupt request"); + + for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-) + { + desc = list_entry (p, uhci_desc_t, desc_list); + + if (is_td_active(desc)) { + // do not process active TDs + //dbg("TD ACT Status @%p %08x",desc,le32_to_cpu(desc->hw.td.status)); + break; + } + + if (!(desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC))) { + // do not process one-shot TDs, no recycling + break; + } + // extract transfer parameters from TD + + actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); + status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); + + // see if EP is stalled + if (status == -EPIPE) { + // set up stalled condition + usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } + + // if any error occurred: ignore this td, and continue + if (status != 0) { + //uhci_show_td (desc); + urb->error_count++; + goto recycle; + } + else + urb->actual_length = actual_length; + + recycle: + uhci_urb_dma_sync(s, urb, urb->hcpriv); + if (urb->complete) { + //dbg("process_interrupt: calling completion, status %i",status); + urb->status = status; + ((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion + + spin_unlock(&s->urb_list_lock); + + urb->complete ((struct urb *) urb); + + spin_lock(&s->urb_list_lock); + + ((urb_priv_t*)urb->hcpriv)->flags=0; + } + + if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) && + (urb->status != -ENOENT)) { + + urb->status = -EINPROGRESS; + + // Recycle INT-TD if interval!=0, else mark TD as one-shot + if (urb->interval) { + + desc->hw.td.info &= cpu_to_le32(~(1 << TD_TOKEN_TOGGLE)); + if (status==0) { + ((urb_priv_t*)urb->hcpriv)->started=jiffies; + desc->hw.td.info |= cpu_to_le32((usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE)); + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } else { + desc->hw.td.info |= cpu_to_le32((!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE)); + } + desc->hw.td.status= cpu_to_le32((urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27)); + mb(); + } + else { + uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); + uhci_clean_iso_step2(s, urb_priv); + // correct toggle after unlink + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + clr_td_ioc(desc); // inactivate TD + } + } + } + + return ret; +} + +// mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs +// mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked) + +_static int process_iso (uhci_t *s, struct urb *urb, int mode) +{ + int i; + int ret = 0; + urb_priv_t *urb_priv = urb->hcpriv; + struct list_head *p = urb_priv->desc_list.next, *p_tmp; + uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); + + dbg("urb contains iso request"); + if (is_td_active(desc) && mode==PROCESS_ISO_REGULAR) + return -EXDEV; // last TD not finished + + urb->error_count = 0; + urb->actual_length = 0; + urb->status = 0; + dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s), + urb->number_of_packets,mode,le32_to_cpu(desc->hw.td.status)); + + for (i = 0; p != &urb_priv->desc_list; i++) { + desc = list_entry (p, uhci_desc_t, desc_list); + + //uhci_show_td(desc); + if (is_td_active(desc)) { + // means we have completed the last TD, but not the TDs before + desc->hw.td.status &= cpu_to_le32(~TD_CTRL_ACTIVE); + dbg("TD still active (%x)- grrr. paranoia!", le32_to_cpu(desc->hw.td.status)); + ret = -EXDEV; + urb->iso_frame_desc[i].status = ret; + unlink_td (s, desc, 1); + // FIXME: immediate deletion may be dangerous + goto err; + } + + if (mode == PROCESS_ISO_REGULAR) + unlink_td (s, desc, 1); + + if (urb->number_of_packets <= i) { + dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i); + ret = -EINVAL; + goto err; + } + + urb->iso_frame_desc[i].actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); + urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); + urb->actual_length += urb->iso_frame_desc[i].actual_length; + + err: + + if (urb->iso_frame_desc[i].status != 0) { + urb->error_count++; + urb->status = urb->iso_frame_desc[i].status; + } + dbg("process_iso: %i: len:%d %08x status:%x", + i, urb->iso_frame_desc[i].actual_length, le32_to_cpu(desc->hw.td.status),urb->iso_frame_desc[i].status); + + p_tmp = p; + p = p->next; + list_del (p_tmp); + delete_desc (s, desc); + } + + dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length); + return ret; +} + + +_static int process_urb (uhci_t *s, struct list_head *p) +{ + int ret = 0; + struct urb *urb; + + urb=list_entry (p, struct urb, urb_list); + //dbg("process_urb: found queued urb: %p", urb); + + switch (usb_pipetype (urb->pipe)) { + case PIPE_CONTROL: + ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR); + break; + case PIPE_BULK: + if (!s->avoid_bulk.counter) + ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR); + else + return 0; + break; + case PIPE_ISOCHRONOUS: + ret = process_iso (s, urb, PROCESS_ISO_REGULAR); + break; + case PIPE_INTERRUPT: + ret = process_interrupt (s, urb); + break; + } + + if (urb->status != -EINPROGRESS) { + urb_priv_t *urb_priv; + struct usb_device *usb_dev; + + usb_dev=urb->dev; + + /* Release bandwidth for Interrupt or Iso transfers */ + if (urb->bandwidth) { + if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS) + usb_release_bandwidth (urb->dev, urb, 1); + else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval) + usb_release_bandwidth (urb->dev, urb, 0); + } + + dbg("dequeued urb: %p", urb); + dequeue_urb (s, urb); + + urb_priv = urb->hcpriv; + + uhci_urb_dma_unmap(s, urb, urb_priv); + +#ifdef DEBUG_SLAB + kmem_cache_free(urb_priv_kmem, urb_priv); +#else + kfree (urb_priv); +#endif + + if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) { // process_interrupt does completion on its own + struct urb *next_urb = urb->next; + int is_ring = 0; + int contains_killed = 0; + int loop_count=0; + + if (next_urb) { + // Find out if the URBs are linked to a ring + while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) { + if (next_urb->status == -ENOENT) {// killed URBs break ring structure & resubmission + contains_killed = 1; + break; + } + next_urb = next_urb->next; + loop_count++; + } + + if (loop_count == MAX_NEXT_COUNT) + err("process_urb: Too much linked URBs in ring detection!"); + + if (next_urb == urb) + is_ring=1; + } + + // Submit idle/non-killed URBs linked with urb->next + // Stop before the current URB + + next_urb = urb->next; + if (next_urb && !contains_killed) { + int ret_submit; + next_urb = urb->next; + + loop_count=0; + while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) { + if (next_urb->status != -EINPROGRESS) { + + if (next_urb->status == -ENOENT) + break; + + spin_unlock(&s->urb_list_lock); + + ret_submit=uhci_submit_urb(next_urb); + spin_lock(&s->urb_list_lock); + + if (ret_submit) + break; + } + loop_count++; + next_urb = next_urb->next; + } + if (loop_count == MAX_NEXT_COUNT) + err("process_urb: Too much linked URBs in resubmission!"); + } + + // Completion + if (urb->complete) { + int was_unlinked = (urb->status == -ENOENT); + urb->dev = NULL; + spin_unlock(&s->urb_list_lock); + + urb->complete ((struct urb *) urb); + + // Re-submit the URB if ring-linked + if (is_ring && !was_unlinked && !contains_killed) { + urb->dev=usb_dev; + uhci_submit_urb (urb); + } + spin_lock(&s->urb_list_lock); + } + + usb_dec_dev_use (usb_dev); + } + } + + return ret; +} + +_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs) +{ + uhci_t *s = __uhci; + unsigned int io_addr = s->io_addr; + unsigned short status; + struct list_head *p, *p2; + int restarts, work_done; + + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + + status = inw (io_addr + USBSTS); + + if (!status) /* shared interrupt, not mine */ + return; + + dbg("interrupt"); + + if (status != 1) { + // Avoid too much error messages at a time + if (time_after(jiffies, s->last_error_time + ERROR_SUPPRESSION_TIME)) { + warn("interrupt, status %x, frame# %i", status, + UHCI_GET_CURRENT_FRAME(s)); + s->last_error_time = jiffies; + } + + // remove host controller halted state + if ((status&0x20) && (s->running)) { + err("Host controller halted, trying to restart."); + outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD); + } + //uhci_show_status (s); + } + /* + * traverse the list in *reverse* direction, because new entries + * may be added at the end. + * also, because process_urb may unlink the current urb, + * we need to advance the list before + * New: check for max. workload and restart count + */ + + spin_lock (&s->urb_list_lock); + + restarts=0; + work_done=0; + +restart: + s->unlink_urb_done=0; + p = s->urb_list.prev; + + while (p != &s->urb_list && (work_done < 1024)) { + p2 = p; + p = p->prev; + + process_urb (s, p2); + + work_done++; + + if (s->unlink_urb_done) { + s->unlink_urb_done=0; + restarts++; + + if (restarts<16) // avoid endless restarts + goto restart; + else + break; + } + } + if (time_after(jiffies, s->timeout_check + (HZ/30))) + uhci_check_timeouts(s); + + clean_descs(s, CLEAN_NOT_FORCED); + uhci_cleanup_unlink(s, CLEAN_NOT_FORCED); + uhci_switch_timer_int(s); + + spin_unlock (&s->urb_list_lock); + + outw (status, io_addr + USBSTS); + + //dbg("uhci_interrupt: done"); +} + +_static void reset_hc (uhci_t *s) +{ + unsigned int io_addr = s->io_addr; + + s->apm_state = 0; + /* Global reset for 50ms */ + outw (USBCMD_GRESET, io_addr + USBCMD); + uhci_wait_ms (50); + outw (0, io_addr + USBCMD); + uhci_wait_ms (10); +} + +_static void start_hc (uhci_t *s) +{ + unsigned int io_addr = s->io_addr; + int timeout = 10; + + /* + * Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + outw (USBCMD_HCRESET, io_addr + USBCMD); + + while (inw (io_addr + USBCMD) & USBCMD_HCRESET) { + if (!--timeout) { + err("USBCMD_HCRESET timed out!"); + break; + } + udelay(1); + } + + /* Turn on all interrupts */ + outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); + + /* Start at frame 0 */ + outw (0, io_addr + USBFRNUM); + outl (s->framelist_dma, io_addr + USBFLBASEADD); + + /* Run and mark it configured with a 64-byte max packet */ + outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); + s->apm_state = 1; + s->running = 1; +} + +/* No __devexit, since it maybe called from alloc_uhci() */ +_static void +uhci_pci_remove (struct pci_dev *dev) +{ + uhci_t *s = pci_get_drvdata(dev); + struct usb_device *root_hub = s->bus->root_hub; + + s->running = 0; // Don't allow submit_urb + + if (root_hub) + usb_disconnect (&root_hub); + + reset_hc (s); + wait_ms (1); + + uhci_unlink_urbs (s, 0, CLEAN_FORCED); // Forced unlink of remaining URBs + uhci_cleanup_unlink (s, CLEAN_FORCED); // force cleanup of async killed URBs + + usb_deregister_bus (s->bus); + + release_region (s->io_addr, s->io_size); + free_irq (s->irq, s); + usb_free_bus (s->bus); + cleanup_skel (s); + kfree (s); +} + +_static int __init uhci_start_usb (uhci_t *s) +{ /* start it up */ + /* connect the virtual root hub */ + struct usb_device *usb_dev; + + usb_dev = usb_alloc_dev (NULL, s->bus); + if (!usb_dev) + return -1; + + s->bus->root_hub = usb_dev; + usb_connect (usb_dev); + + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); + return -1; + } + + return 0; +} + +#ifdef CONFIG_PM +_static int +uhci_pci_suspend (struct pci_dev *dev, u32 state) +{ + reset_hc((uhci_t *) pci_get_drvdata(dev)); + return 0; +} + +_static int +uhci_pci_resume (struct pci_dev *dev) +{ + start_hc((uhci_t *) pci_get_drvdata(dev)); + return 0; +} +#endif + +_static int __devinit alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size) +{ + uhci_t *s; + struct usb_bus *bus; + char buf[8], *bufp = buf; + +#ifndef __sparc__ + sprintf(buf, "%d", irq); +#else + bufp = __irq_itoa(irq); +#endif + printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", + io_addr, bufp); + + s = kmalloc (sizeof (uhci_t), GFP_KERNEL); + if (!s) + return -1; + + memset (s, 0, sizeof (uhci_t)); + INIT_LIST_HEAD (&s->free_desc); + INIT_LIST_HEAD (&s->urb_list); + INIT_LIST_HEAD (&s->urb_unlinked); + spin_lock_init (&s->urb_list_lock); + spin_lock_init (&s->qh_lock); + spin_lock_init (&s->td_lock); + atomic_set(&s->avoid_bulk, 0); + s->irq = -1; + s->io_addr = io_addr; + s->io_size = io_size; + s->uhci_pci=dev; + + bus = usb_alloc_bus (&uhci_device_operations); + if (!bus) { + kfree (s); + return -1; + } + + s->bus = bus; + bus->bus_name = dev->slot_name; + bus->hcpriv = s; + + /* UHCI specs says devices must have 2 ports, but goes on to say */ + /* they may have more but give no way to determine how many they */ + /* have, so default to 2 */ + /* According to the UHCI spec, Bit 7 is always set to 1. So we try */ + /* to use this to our advantage */ + + for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) { + unsigned int portstatus; + + portstatus = inw (io_addr + 0x10 + (s->maxports * 2)); + dbg("port %i, adr %x status %x", s->maxports, + io_addr + 0x10 + (s->maxports * 2), portstatus); + if (!(portstatus & 0x0080)) + break; + } + warn("Detected %d ports", s->maxports); + + /* This is experimental so anything less than 2 or greater than 8 is */ + /* something weird and we'll ignore it */ + if (s->maxports < 2 || s->maxports > 8) { + dbg("Port count misdetected, forcing to 2 ports"); + s->maxports = 2; + } + + s->rh.numports = s->maxports; + s->loop_usage=0; + if (init_skel (s)) { + usb_free_bus (bus); + kfree(s); + return -1; + } + + request_region (s->io_addr, io_size, MODNAME); + reset_hc (s); + usb_register_bus (s->bus); + + start_hc (s); + + if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) { + err("request_irq %d failed!",irq); + usb_free_bus (bus); + reset_hc (s); + release_region (s->io_addr, s->io_size); + cleanup_skel(s); + kfree(s); + return -1; + } + + /* Enable PIRQ */ + pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT); + + s->irq = irq; + + if(uhci_start_usb (s) < 0) { + uhci_pci_remove(dev); + return -1; + } + + //chain new uhci device into global list + pci_set_drvdata(dev, s); + devs=s; + + return 0; +} + +_static int __devinit +uhci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) +{ + int i; + + if (pci_enable_device(dev) < 0) + return -ENODEV; + + if (!dev->irq) { + err("found UHCI device with no IRQ assigned. check BIOS settings!"); + return -ENODEV; + } + + pci_set_master(dev); + + /* Search for the IO base address.. */ + for (i = 0; i < 6; i++) { + + unsigned int io_addr = pci_resource_start(dev, i); + unsigned int io_size = pci_resource_len(dev, i); + if (!(pci_resource_flags(dev,i) & IORESOURCE_IO)) + continue; + + /* Is it already in use? */ + if (check_region (io_addr, io_size)) + break; + /* disable legacy emulation */ + pci_write_config_word (dev, USBLEGSUP, 0); + + return alloc_uhci(dev, dev->irq, io_addr, io_size); + } + return -ENODEV; +} + +/*-------------------------------------------------------------------------*/ + +static const struct pci_device_id __devinitdata uhci_pci_ids [] = { { + + /* handle any USB UHCI controller */ + class: ((PCI_CLASS_SERIAL_USB << 8) | 0x00), + class_mask: ~0, + + /* no matter who makes it */ + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + + }, { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE (pci, uhci_pci_ids); + +static struct pci_driver uhci_pci_driver = { + name: "usb-uhci", + id_table: &uhci_pci_ids [0], + + probe: uhci_pci_probe, + remove: uhci_pci_remove, + +#ifdef CONFIG_PM + suspend: uhci_pci_suspend, + resume: uhci_pci_resume, +#endif /* PM */ + +}; + +/*-------------------------------------------------------------------------*/ + +static int __init uhci_hcd_init (void) +{ + int retval; + +#ifdef DEBUG_SLAB + urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + if(!urb_priv_kmem) { + err("kmem_cache_create for urb_priv_t failed (out of memory)"); + return -ENOMEM; + } +#endif + info(VERSTR); + +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + info("High bandwidth mode enabled"); +#endif + + retval = pci_module_init (&uhci_pci_driver); + +#ifdef DEBUG_SLAB + if (retval < 0 ) { + if (kmem_cache_destroy(urb_priv_kmem)) + err("urb_priv_kmem remained"); + } +#endif + + info(DRIVER_VERSION ":" DRIVER_DESC); + + return retval; +} + +static void __exit uhci_hcd_cleanup (void) +{ + pci_unregister_driver (&uhci_pci_driver); + +#ifdef DEBUG_SLAB + if(kmem_cache_destroy(urb_priv_kmem)) + err("urb_priv_kmem remained"); +#endif +} + +module_init (uhci_hcd_init); +module_exit (uhci_hcd_cleanup); + + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/host/usb-uhci.h b/drivers/usb/host/usb-uhci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/usb-uhci.h Thu Mar 6 14:22:16 2003 @@ -0,0 +1,308 @@ +#ifndef __LINUX_UHCI_H +#define __LINUX_UHCI_H + +/* + $Id: usb-uhci.h,v 1.58 2001/08/28 16:45:00 acher Exp $ + */ +#define MODNAME "usb-uhci" +#define UHCI_LATENCY_TIMER 0 + +static __inline__ void uhci_wait_ms(unsigned int ms) +{ + if(!in_interrupt()) + { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1 + ms * HZ / 1000); + } + else + mdelay(ms); +} + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_LS 0x0030 /* Line Status */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ +#define USBPORTSC_SUSP 0x1000 /* Suspend */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */ +#define UHCI_PID 0xff /* PID MASK */ + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 + +#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ + +/* + * for TD : + */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) + +/* + * for TD : + */ +#define UHCI_TD_REMOVE 0x0001 /* Remove when done */ + +/* + * for TD : (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE 19 + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + +/* ------------------------------------------------------------------------------------ + New TD/QH-structures + ------------------------------------------------------------------------------------ */ +typedef enum { + TD_TYPE, QH_TYPE +} uhci_desc_type_t; + +typedef struct { + __u32 link; + __u32 status; + __u32 info; + __u32 buffer; +} uhci_td_t, *puhci_td_t; + +typedef struct { + __u32 head; + __u32 element; /* Queue element pointer */ +} uhci_qh_t, *puhci_qh_t; + +typedef struct { + union { + uhci_td_t td; + uhci_qh_t qh; + } hw; + uhci_desc_type_t type; + dma_addr_t dma_addr; + struct list_head horizontal; + struct list_head vertical; + struct list_head desc_list; + int last_used; +} uhci_desc_t, *puhci_desc_t; + +typedef struct { + struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request + dma_addr_t setup_packet_dma; + dma_addr_t transfer_buffer_dma; + unsigned long started; + struct urb *next_queued_urb; // next queued urb for this EP + struct urb *prev_queued_urb; + uhci_desc_t *bottom_qh; + uhci_desc_t *next_qh; // next helper QH + char use_loop; + char flags; +} urb_priv_t, *purb_priv_t; + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; + void *int_addr; + int send; + int interval; + int numports; + int c_p_r[8]; + struct timer_list rh_int_timer; +}; + +typedef struct uhci { + int irq; + unsigned int io_addr; + unsigned int io_size; + unsigned int maxports; + int running; + + int apm_state; + + struct uhci *next; // chain of uhci device contexts + + struct list_head urb_list; // list of all pending urbs + + spinlock_t urb_list_lock; // lock to keep consistency + + int unlink_urb_done; + atomic_t avoid_bulk; + + struct usb_bus *bus; // our bus + + __u32 *framelist; + dma_addr_t framelist_dma; + uhci_desc_t **iso_td; + uhci_desc_t *int_chain[8]; + uhci_desc_t *ls_control_chain; + uhci_desc_t *control_chain; + uhci_desc_t *bulk_chain; + uhci_desc_t *chain_end; + uhci_desc_t *td1ms; + uhci_desc_t *td32ms; + struct list_head free_desc; + spinlock_t qh_lock; + spinlock_t td_lock; + struct virt_root_hub rh; //private data of the virtual root hub + int loop_usage; // URBs using bandwidth reclamation + + struct list_head urb_unlinked; // list of all unlinked urbs + long timeout_check; + int timeout_urbs; + struct pci_dev *uhci_pci; + struct pci_pool *desc_pool; + long last_error_time; // last error output in uhci_interrupt() +} uhci_t, *puhci_t; + + +#define MAKE_TD_ADDR(a) ((a)->dma_addr&~UHCI_PTR_QH) +#define MAKE_QH_ADDR(a) ((a)->dma_addr|UHCI_PTR_QH) +#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM)) + +#define CLEAN_TRANSFER_NO_DELETION 0 +#define CLEAN_TRANSFER_REGULAR 1 +#define CLEAN_TRANSFER_DELETION_MARK 2 + +#define CLEAN_NOT_FORCED 0 +#define CLEAN_FORCED 1 + +#define PROCESS_ISO_REGULAR 0 +#define PROCESS_ISO_FORCE 1 + +#define UNLINK_ASYNC_STORE_URB 0 +#define UNLINK_ASYNC_DONT_STORE 1 + +#define is_td_active(desc) (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE)) + +#define set_qh_head(desc,val) (desc)->hw.qh.head=cpu_to_le32(val) +#define set_qh_element(desc,val) (desc)->hw.qh.element=cpu_to_le32(val) +#define set_td_link(desc,val) (desc)->hw.td.link=cpu_to_le32(val) +#define set_td_ioc(desc) (desc)->hw.td.status |= cpu_to_le32(TD_CTRL_IOC) +#define clr_td_ioc(desc) (desc)->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC) + + +/* ------------------------------------------------------------------------------------ + Virtual Root HUB + ------------------------------------------------------------------------------------ */ +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +#endif diff -Nru a/drivers/usb/uhci-debug.h b/drivers/usb/uhci-debug.h --- a/drivers/usb/uhci-debug.h Thu Mar 6 14:22:16 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,573 +0,0 @@ -/* - * UHCI-specific debugging code. Invaluable when something - * goes wrong, but don't get in my face. - * - * Kernel visible pointers are surrounded in []'s and bus - * visible pointers are surrounded in ()'s - * - * (C) Copyright 1999 Linus Torvalds - * (C) Copyright 1999-2001 Johannes Erdfelt - */ - -#include -#include -#include -#include - -#include "uhci.h" - -/* Handle REALLY large printk's so we don't overflow buffers */ -static void inline lprintk(char *buf) -{ - char *p; - - /* Just write one line at a time */ - while (buf) { - p = strchr(buf, '\n'); - if (p) - *p = 0; - printk("%s\n", buf); - buf = p; - if (buf) - buf++; - } -} - -static int inline uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td) -{ - int i; - - for (i = 0; i < UHCI_NUM_SKELTD; i++) - if (td == uhci->skeltd[i]) - return 1; - - return 0; -} - -static int inline uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) -{ - int i; - - for (i = 0; i < UHCI_NUM_SKELQH; i++) - if (qh == uhci->skelqh[i]) - return 1; - - return 0; -} - -static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) -{ - char *out = buf; - char *spid; - - /* Try to make sure there's enough memory */ - if (len < 160) - return 0; - - out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, td->link); - out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", - ((td->status >> 27) & 3), - (td->status & TD_CTRL_SPD) ? "SPD " : "", - (td->status & TD_CTRL_LS) ? "LS " : "", - (td->status & TD_CTRL_IOC) ? "IOC " : "", - (td->status & TD_CTRL_ACTIVE) ? "Active " : "", - (td->status & TD_CTRL_STALLED) ? "Stalled " : "", - (td->status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", - (td->status & TD_CTRL_BABBLE) ? "Babble " : "", - (td->status & TD_CTRL_NAK) ? "NAK " : "", - (td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", - (td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", - td->status & 0x7ff); - - switch (td->info & 0xff) { - case USB_PID_SETUP: - spid = "SETUP"; - break; - case USB_PID_OUT: - spid = "OUT"; - break; - case USB_PID_IN: - spid = "IN"; - break; - default: - spid = "?"; - break; - } - - out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", - td->info >> 21, - ((td->info >> 19) & 1), - (td->info >> 15) & 15, - (td->info >> 8) & 127, - (td->info & 0xff), - spid); - out += sprintf(out, "(buf=%08x)\n", td->buffer); - - return out - buf; -} - -static int uhci_show_sc(int port, unsigned short status, char *buf, int len) -{ - char *out = buf; - - /* Try to make sure there's enough memory */ - if (len < 80) - return 0; - - out += sprintf(out, " stat%d = %04x %s%s%s%s%s%s%s%s\n", - port, - status, - (status & USBPORTSC_SUSP) ? "PortSuspend " : "", - (status & USBPORTSC_PR) ? "PortReset " : "", - (status & USBPORTSC_LSDA) ? "LowSpeed " : "", - (status & USBPORTSC_RD) ? "ResumeDetect " : "", - (status & USBPORTSC_PEC) ? "EnableChange " : "", - (status & USBPORTSC_PE) ? "PortEnabled " : "", - (status & USBPORTSC_CSC) ? "ConnectChange " : "", - (status & USBPORTSC_CCS) ? "PortConnected " : ""); - - return out - buf; -} - -static int uhci_show_status(struct uhci *uhci, char *buf, int len) -{ - char *out = buf; - unsigned int io_addr = uhci->io_addr; - unsigned short usbcmd, usbstat, usbint, usbfrnum; - unsigned int flbaseadd; - unsigned char sof; - unsigned short portsc1, portsc2; - - /* Try to make sure there's enough memory */ - if (len < 80 * 6) - return 0; - - usbcmd = inw(io_addr + 0); - usbstat = inw(io_addr + 2); - usbint = inw(io_addr + 4); - usbfrnum = inw(io_addr + 6); - flbaseadd = inl(io_addr + 8); - sof = inb(io_addr + 12); - portsc1 = inw(io_addr + 16); - portsc2 = inw(io_addr + 18); - - out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n", - usbcmd, - (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", - (usbcmd & USBCMD_CF) ? "CF " : "", - (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", - (usbcmd & USBCMD_FGR) ? "FGR " : "", - (usbcmd & USBCMD_EGSM) ? "EGSM " : "", - (usbcmd & USBCMD_GRESET) ? "GRESET " : "", - (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", - (usbcmd & USBCMD_RS) ? "RS " : ""); - - out += sprintf(out, " usbstat = %04x %s%s%s%s%s%s\n", - usbstat, - (usbstat & USBSTS_HCH) ? "HCHalted " : "", - (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", - (usbstat & USBSTS_HSE) ? "HostSystemError " : "", - (usbstat & USBSTS_RD) ? "ResumeDetect " : "", - (usbstat & USBSTS_ERROR) ? "USBError " : "", - (usbstat & USBSTS_USBINT) ? "USBINT " : ""); - - out += sprintf(out, " usbint = %04x\n", usbint); - out += sprintf(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, - 0xfff & (4*(unsigned int)usbfrnum)); - out += sprintf(out, " flbaseadd = %08x\n", flbaseadd); - out += sprintf(out, " sof = %02x\n", sof); - out += uhci_show_sc(1, portsc1, out, len - (out - buf)); - out += uhci_show_sc(2, portsc2, out, len - (out - buf)); - - return out - buf; -} - -static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) -{ - char *out = buf; - struct urb_priv *urbp; - struct list_head *head, *tmp; - struct uhci_td *td; - int i = 0, checked = 0, prevactive = 0; - - /* Try to make sure there's enough memory */ - if (len < 80 * 6) - return 0; - - out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "", - qh, qh->link, qh->element); - - if (qh->element & UHCI_PTR_QH) - out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); - - if (qh->element & UHCI_PTR_DEPTH) - out += sprintf(out, "%*s Depth traverse\n", space, ""); - - if (qh->element & 8) - out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, ""); - - if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH))) - out += sprintf(out, "%*s Element is NULL (bug?)\n", space, ""); - - if (!qh->urbp) { - out += sprintf(out, "%*s urbp == NULL\n", space, ""); - goto out; - } - - urbp = qh->urbp; - - head = &urbp->td_list; - tmp = head->next; - - td = list_entry(tmp, struct uhci_td, list); - - if (td->dma_handle != (qh->element & ~UHCI_PTR_BITS)) - out += sprintf(out, "%*s Element != First TD\n", space, ""); - - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - out += sprintf(out, "%*s%d: ", space + 2, "", i++); - out += uhci_show_td(td, out, len - (out - buf), 0); - - if (i > 10 && !checked && prevactive && tmp != head && - debug <= 2) { - struct list_head *ntmp = tmp; - struct uhci_td *ntd = td; - int active = 1, ni = i; - - checked = 1; - - while (ntmp != head && ntmp->next != head && active) { - ntd = list_entry(ntmp, struct uhci_td, list); - - ntmp = ntmp->next; - - active = ntd->status & TD_CTRL_ACTIVE; - - ni++; - } - - if (active && ni > i) { - out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i); - tmp = ntmp; - td = ntd; - i = ni; - } - } - - prevactive = td->status & TD_CTRL_ACTIVE; - } - - if (list_empty(&urbp->queue_list) || urbp->queued) - goto out; - - out += sprintf(out, "%*sQueued QH's:\n", -space, "--"); - - head = &urbp->queue_list; - tmp = head->next; - - while (tmp != head) { - struct urb_priv *nurbp = list_entry(tmp, struct urb_priv, - queue_list); - tmp = tmp->next; - - out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space); - } - -out: - return out - buf; -} - -static const char *td_names[] = {"skel_int1_td", "skel_int2_td", - "skel_int4_td", "skel_int8_td", - "skel_int16_td", "skel_int32_td", - "skel_int64_td", "skel_int128_td", - "skel_int256_td", "skel_term_td" }; -static const char *qh_names[] = { "skel_ls_control_qh", "skel_hs_control_qh", - "skel_bulk_qh", "skel_term_qh" }; - -#define show_frame_num() \ - if (!shown) { \ - shown = 1; \ - out += sprintf(out, "- Frame %d\n", i); \ - } - -#define show_td_name() \ - if (!shown) { \ - shown = 1; \ - out += sprintf(out, "- %s\n", td_names[i]); \ - } - -#define show_qh_name() \ - if (!shown) { \ - shown = 1; \ - out += sprintf(out, "- %s\n", qh_names[i]); \ - } - -static int uhci_sprint_schedule(struct uhci *uhci, char *buf, int len) -{ - char *out = buf; - int i; - struct uhci_qh *qh; - struct uhci_td *td; - struct list_head *tmp, *head; - - out += sprintf(out, "HC status\n"); - out += uhci_show_status(uhci, out, len - (out - buf)); - - out += sprintf(out, "Frame List\n"); - for (i = 0; i < UHCI_NUMFRAMES; ++i) { - int shown = 0; - td = uhci->fl->frame_cpu[i]; - if (!td) - continue; - - if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) { - show_frame_num(); - out += sprintf(out, " frame list does not match td->dma_handle!\n"); - } - if (uhci_is_skeleton_td(uhci, td)) - continue; - show_frame_num(); - - head = &td->fl_list; - tmp = head; - do { - td = list_entry(tmp, struct uhci_td, fl_list); - tmp = tmp->next; - out += uhci_show_td(td, out, len - (out - buf), 4); - } while (tmp != head); - } - - out += sprintf(out, "Skeleton TD's\n"); - for (i = UHCI_NUM_SKELTD - 1; i >= 0; i--) { - int shown = 0; - - td = uhci->skeltd[i]; - - if (debug > 1) { - show_td_name(); - out += uhci_show_td(td, out, len - (out - buf), 4); - } - - if (list_empty(&td->fl_list)) { - /* TD 0 is the int1 TD and links to control_ls_qh */ - if (!i) { - if (td->link != - (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH)) { - show_td_name(); - out += sprintf(out, " skeleton TD not linked to ls_control QH!\n"); - } - } else if (i < 9) { - if (td->link != uhci->skeltd[i - 1]->dma_handle) { - show_td_name(); - out += sprintf(out, " skeleton TD not linked to next skeleton TD!\n"); - } - } else { - show_td_name(); - - if (td->link != td->dma_handle) - out += sprintf(out, " skel_term_td does not link to self\n"); - - /* Don't show it twice */ - if (debug <= 1) - out += uhci_show_td(td, out, len - (out - buf), 4); - } - - continue; - } - - show_td_name(); - - head = &td->fl_list; - tmp = head->next; - - while (tmp != head) { - td = list_entry(tmp, struct uhci_td, fl_list); - - tmp = tmp->next; - - out += uhci_show_td(td, out, len - (out - buf), 4); - } - - if (!i) { - if (td->link != - (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH)) - out += sprintf(out, " last TD not linked to ls_control QH!\n"); - } else if (i < 9) { - if (td->link != uhci->skeltd[i - 1]->dma_handle) - out += sprintf(out, " last TD not linked to next skeleton!\n"); - } - } - - out += sprintf(out, "Skeleton QH's\n"); - - for (i = 0; i < UHCI_NUM_SKELQH; ++i) { - int shown = 0; - - qh = uhci->skelqh[i]; - - if (debug > 1) { - show_qh_name(); - out += uhci_show_qh(qh, out, len - (out - buf), 4); - } - - /* QH 3 is the Terminating QH, it's different */ - if (i == 3) { - if (qh->link != UHCI_PTR_TERM) { - show_qh_name(); - out += sprintf(out, " bandwidth reclamation on!\n"); - } - - if (qh->element != uhci->skel_term_td->dma_handle) { - show_qh_name(); - out += sprintf(out, " skel_term_qh element is not set to skel_term_td\n"); - } - } - - if (list_empty(&qh->list)) { - if (i < 3) { - if (qh->link != - (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) { - show_qh_name(); - out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n"); - } - } - - continue; - } - - show_qh_name(); - - head = &qh->list; - tmp = head->next; - - while (tmp != head) { - qh = list_entry(tmp, struct uhci_qh, list); - - tmp = tmp->next; - - out += uhci_show_qh(qh, out, len - (out - buf), 4); - } - - if (i < 3) { - if (qh->link != - (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) - out += sprintf(out, " last QH not linked to next skeleton!\n"); - } - } - - return out - buf; -} - -#ifdef CONFIG_PROC_FS -#define MAX_OUTPUT (PAGE_SIZE * 8) - -static struct proc_dir_entry *uhci_proc_root = NULL; - -struct uhci_proc { - int size; - char *data; - struct uhci *uhci; -}; - -static int uhci_proc_open(struct inode *inode, struct file *file) -{ - const struct proc_dir_entry *dp = inode->u.generic_ip; - struct uhci *uhci = dp->data; - struct uhci_proc *up; - unsigned long flags; - int ret = -ENOMEM; - - lock_kernel(); - up = kmalloc(sizeof(*up), GFP_KERNEL); - if (!up) - goto out; - - up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL); - if (!up->data) { - kfree(up); - goto out; - } - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT); - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); - - file->private_data = up; - - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence) -{ - struct uhci_proc *up = file->private_data; - loff_t new; - - switch (whence) { - case 0: - new = off; - break; - case 1: - new = file->f_pos + off; - break; - case 2: - default: - return -EINVAL; - } - if (new < 0 || new > up->size) - return -EINVAL; - return (file->f_pos = new); -} - -static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes, - loff_t *ppos) -{ - struct uhci_proc *up = file->private_data; - unsigned int pos; - unsigned int size; - - pos = *ppos; - size = up->size; - if (pos >= size) - return 0; - if (nbytes >= size) - nbytes = size; - if (pos + nbytes > size) - nbytes = size - pos; - - if (!access_ok(VERIFY_WRITE, buf, nbytes)) - return -EINVAL; - - copy_to_user(buf, up->data + pos, nbytes); - - *ppos += nbytes; - - return nbytes; -} - -static int uhci_proc_release(struct inode *inode, struct file *file) -{ - struct uhci_proc *up = file->private_data; - - kfree(up->data); - kfree(up); - - return 0; -} - -static struct file_operations uhci_proc_operations = { - open: uhci_proc_open, - llseek: uhci_proc_lseek, - read: uhci_proc_read, -// write: uhci_proc_write, - release: uhci_proc_release, -}; -#endif - diff -Nru a/drivers/usb/uhci.c b/drivers/usb/uhci.c --- a/drivers/usb/uhci.c Thu Mar 6 14:22:16 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3172 +0,0 @@ -/* - * Universal Host Controller Interface driver for USB. - * - * Maintainer: Johannes Erdfelt - * - * (C) Copyright 1999 Linus Torvalds - * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com - * (C) Copyright 1999 Randy Dunlap - * (C) Copyright 1999 Georg Acher, acher@in.tum.de - * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de - * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch - * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at - * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface - * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). - * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) - * - * Intel documents this fairly well, and as far as I know there - * are no royalties or anything like that, but even so there are - * people who decided that they want to do the same thing in a - * completely different way. - * - * WARNING! The USB documentation is downright evil. Most of it - * is just crap, written by a committee. You're better off ignoring - * most of it, the important stuff is: - * - the low-level protocol (fairly simple but lots of small details) - * - working around the horridness of the rest - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_USB_DEBUG -#define DEBUG -#else -#undef DEBUG -#endif -#include - -#include -#include -#include -#include - -#include "uhci.h" - -#include - -#include "hcd.h" - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.1" -#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber" -#define DRIVER_DESC "USB Universal Host Controller Interface driver" - -/* - * debug = 0, no debugging messages - * debug = 1, dump failed URB's except for stalls - * debug = 2, dump all failed URB's (including stalls) - * show all queues in /proc/uhci/hc* - * debug = 3, show all TD's in URB's when dumping - */ -#ifdef DEBUG -static int debug = 1; -#else -static int debug = 0; -#endif -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debug level"); -static char *errbuf; -#define ERRBUF_LEN (PAGE_SIZE * 8) - -#include "uhci-debug.h" - -static kmem_cache_t *uhci_up_cachep; /* urb_priv */ - -static int rh_submit_urb(struct urb *urb); -static int rh_unlink_urb(struct urb *urb); -static int uhci_get_current_frame_number(struct usb_device *dev); -static int uhci_unlink_urb(struct urb *urb); -static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb); -static void uhci_call_completion(struct urb *urb); - -static int ports_active(struct uhci *uhci); -static void suspend_hc(struct uhci *uhci); -static void wakeup_hc(struct uhci *uhci); - -/* If a transfer is still active after this much time, turn off FSBR */ -#define IDLE_TIMEOUT (HZ / 20) /* 50 ms */ -#define FSBR_DELAY (HZ / 20) /* 50 ms */ - -/* When we timeout an idle transfer for FSBR, we'll switch it over to */ -/* depth first traversal. We'll do it in groups of this number of TD's */ -/* to make sure it doesn't hog all of the bandwidth */ -#define DEPTH_INTERVAL 5 - -#define MAX_URB_LOOP 2048 /* Maximum number of linked URB's */ - -/* - * Only the USB core should call uhci_alloc_dev and uhci_free_dev - */ -static int uhci_alloc_dev(struct usb_device *dev) -{ - return 0; -} - -static int uhci_free_dev(struct usb_device *dev) -{ - return 0; -} - -/* - * Technically, updating td->status here is a race, but it's not really a - * problem. The worst that can happen is that we set the IOC bit again - * generating a spurios interrupt. We could fix this by creating another - * QH and leaving the IOC bit always set, but then we would have to play - * games with the FSBR code to make sure we get the correct order in all - * the cases. I don't think it's worth the effort - */ -static inline void uhci_set_next_interrupt(struct uhci *uhci) -{ - unsigned long flags; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - uhci->skel_term_td->status |= TD_CTRL_IOC; - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -static inline void uhci_clear_next_interrupt(struct uhci *uhci) -{ - unsigned long flags; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - uhci->skel_term_td->status &= ~TD_CTRL_IOC; - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -static inline void uhci_add_complete(struct urb *urb) -{ - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - unsigned long flags; - - spin_lock_irqsave(&uhci->complete_list_lock, flags); - list_add(&urbp->complete_list, &uhci->complete_list); - spin_unlock_irqrestore(&uhci->complete_list_lock, flags); -} - -static struct uhci_td *uhci_alloc_td(struct uhci *uhci, struct usb_device *dev) -{ - dma_addr_t dma_handle; - struct uhci_td *td; - - td = pci_pool_alloc(uhci->td_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); - if (!td) - return NULL; - - td->dma_handle = dma_handle; - - td->link = UHCI_PTR_TERM; - td->buffer = 0; - - td->frame = -1; - td->dev = dev; - - INIT_LIST_HEAD(&td->list); - INIT_LIST_HEAD(&td->fl_list); - - usb_inc_dev_use(dev); - - return td; -} - -static void inline uhci_fill_td(struct uhci_td *td, __u32 status, - __u32 info, __u32 buffer) -{ - td->status = status; - td->info = info; - td->buffer = buffer; -} - -static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td) -{ - unsigned long flags; - struct uhci_td *ltd; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - - ltd = list_entry(skeltd->fl_list.prev, struct uhci_td, fl_list); - - td->link = ltd->link; - mb(); - ltd->link = td->dma_handle; - - list_add_tail(&td->fl_list, &skeltd->fl_list); - - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -/* - * We insert Isochronous transfers directly into the frame list at the - * beginning - * The layout looks as follows: - * frame list pointer -> iso td's (if any) -> - * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh - */ -static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum) -{ - unsigned long flags; - - framenum %= UHCI_NUMFRAMES; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - - td->frame = framenum; - - /* Is there a TD already mapped there? */ - if (uhci->fl->frame_cpu[framenum]) { - struct uhci_td *ftd, *ltd; - - ftd = uhci->fl->frame_cpu[framenum]; - ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list); - - list_add_tail(&td->fl_list, &ftd->fl_list); - - td->link = ltd->link; - mb(); - ltd->link = td->dma_handle; - } else { - td->link = uhci->fl->frame[framenum]; - mb(); - uhci->fl->frame[framenum] = td->dma_handle; - uhci->fl->frame_cpu[framenum] = td; - } - - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) -{ - unsigned long flags; - - /* If it's not inserted, don't remove it */ - spin_lock_irqsave(&uhci->frame_list_lock, flags); - if (td->frame == -1 && list_empty(&td->fl_list)) - goto out; - - if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) { - if (list_empty(&td->fl_list)) { - uhci->fl->frame[td->frame] = td->link; - uhci->fl->frame_cpu[td->frame] = NULL; - } else { - struct uhci_td *ntd; - - ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); - uhci->fl->frame[td->frame] = ntd->dma_handle; - uhci->fl->frame_cpu[td->frame] = ntd; - } - } else { - struct uhci_td *ptd; - - ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list); - ptd->link = td->link; - } - - mb(); - td->link = UHCI_PTR_TERM; - - list_del_init(&td->fl_list); - td->frame = -1; - -out: - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -/* - * Inserts a td into qh list at the top. - */ -static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth) -{ - struct list_head *tmp, *head; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td, *ptd; - - if (list_empty(&urbp->td_list)) - return; - - head = &urbp->td_list; - tmp = head->next; - - /* Ordering isn't important here yet since the QH hasn't been */ - /* inserted into the schedule yet */ - td = list_entry(tmp, struct uhci_td, list); - - /* Add the first TD to the QH element pointer */ - qh->element = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH); - - ptd = td; - - /* Then link the rest of the TD's */ - tmp = tmp->next; - while (tmp != head) { - td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - ptd->link = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH); - - ptd = td; - } - - ptd->link = UHCI_PTR_TERM; -} - -static void uhci_free_td(struct uhci *uhci, struct uhci_td *td) -{ - if (!list_empty(&td->list) || !list_empty(&td->fl_list)) - dbg("td is still in URB list!"); - - if (td->dev) - usb_dec_dev_use(td->dev); - - pci_pool_free(uhci->td_pool, td, td->dma_handle); -} - -static struct uhci_qh *uhci_alloc_qh(struct uhci *uhci, struct usb_device *dev) -{ - dma_addr_t dma_handle; - struct uhci_qh *qh; - - qh = pci_pool_alloc(uhci->qh_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); - if (!qh) - return NULL; - - qh->dma_handle = dma_handle; - - qh->element = UHCI_PTR_TERM; - qh->link = UHCI_PTR_TERM; - - qh->dev = dev; - qh->urbp = NULL; - - INIT_LIST_HEAD(&qh->list); - INIT_LIST_HEAD(&qh->remove_list); - - usb_inc_dev_use(dev); - - return qh; -} - -static void uhci_free_qh(struct uhci *uhci, struct uhci_qh *qh) -{ - if (!list_empty(&qh->list)) - dbg("qh list not empty!"); - if (!list_empty(&qh->remove_list)) - dbg("qh still in remove_list!"); - - if (qh->dev) - usb_dec_dev_use(qh->dev); - - pci_pool_free(uhci->qh_pool, qh, qh->dma_handle); -} - -/* - * MUST be called with uhci->frame_list_lock acquired - */ -static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head, *tmp; - struct uhci_qh *lqh; - - /* Grab the last QH */ - lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); - - if (lqh->urbp) { - head = &lqh->urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - tmp = tmp->next; - - turbp->qh->link = urbp->qh->dma_handle | UHCI_PTR_QH; - } - } - - head = &urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - tmp = tmp->next; - - turbp->qh->link = lqh->link; - } - - urbp->qh->link = lqh->link; - mb(); /* Ordering is important */ - lqh->link = urbp->qh->dma_handle | UHCI_PTR_QH; - - list_add_tail(&urbp->qh->list, &skelqh->list); -} - -static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb) -{ - unsigned long flags; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - _uhci_insert_qh(uhci, skelqh, urb); - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) -{ - unsigned long flags; - struct uhci_qh *pqh; - - if (!qh) - return; - - qh->urbp = NULL; - - /* Only go through the hoops if it's actually linked in */ - spin_lock_irqsave(&uhci->frame_list_lock, flags); - if (!list_empty(&qh->list)) { - pqh = list_entry(qh->list.prev, struct uhci_qh, list); - - if (pqh->urbp) { - struct list_head *head, *tmp; - - head = &pqh->urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - tmp = tmp->next; - - turbp->qh->link = qh->link; - } - } - - pqh->link = qh->link; - mb(); - qh->element = qh->link = UHCI_PTR_TERM; - - list_del_init(&qh->list); - } - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); - - spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); - - /* Check to see if the remove list is empty. Set the IOC bit */ - /* to force an interrupt so we can remove the QH */ - if (list_empty(&uhci->qh_remove_list)) - uhci_set_next_interrupt(uhci); - - list_add(&qh->remove_list, &uhci->qh_remove_list); - - spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags); -} - -static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head, *tmp; - - head = &urbp->td_list; - tmp = head->next; - while (head != tmp) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - if (toggle) - td->info |= TD_TOKEN_TOGGLE; - else - td->info &= ~TD_TOKEN_TOGGLE; - - toggle ^= 1; - } - - return toggle; -} - -/* This function will append one URB's QH to another URB's QH. This is for */ -/* USB_QUEUE_BULK support for bulk transfers and soon implicitily for */ -/* control transfers */ -static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb *urb) -{ - struct urb_priv *eurbp, *urbp, *furbp, *lurbp; - struct list_head *tmp; - struct uhci_td *lltd; - unsigned long flags; - - eurbp = eurb->hcpriv; - urbp = urb->hcpriv; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - - /* Find the first URB in the queue */ - if (eurbp->queued) { - struct list_head *head = &eurbp->queue_list; - - tmp = head->next; - while (tmp != head) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - if (!turbp->queued) - break; - - tmp = tmp->next; - } - } else - tmp = &eurbp->queue_list; - - furbp = list_entry(tmp, struct urb_priv, queue_list); - lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); - - lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); - - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), - uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1)); - - /* All qh's in the queue need to link to the next queue */ - urbp->qh->link = eurbp->qh->link; - - mb(); /* Make sure we flush everything */ - /* Only support bulk right now, so no depth */ - lltd->link = urbp->qh->dma_handle | UHCI_PTR_QH; - - list_add_tail(&urbp->queue_list, &furbp->queue_list); - - urbp->queued = 1; - - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb) -{ - struct urb_priv *urbp, *nurbp; - struct list_head *head, *tmp; - struct urb_priv *purbp; - struct uhci_td *pltd; - unsigned int toggle; - unsigned long flags; - - urbp = urb->hcpriv; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - - if (list_empty(&urbp->queue_list)) - goto out; - - nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); - - /* Fix up the toggle for the next URB's */ - if (!urbp->queued) - /* We set the toggle when we unlink */ - toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - else { - /* If we're in the middle of the queue, grab the toggle */ - /* from the TD previous to us */ - purbp = list_entry(urbp->queue_list.prev, struct urb_priv, - queue_list); - - pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); - - toggle = uhci_toggle(pltd->info) ^ 1; - } - - head = &urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp; - - turbp = list_entry(tmp, struct urb_priv, queue_list); - - tmp = tmp->next; - - if (!turbp->queued) - break; - - toggle = uhci_fixup_toggle(turbp->urb, toggle); - } - - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - - if (!urbp->queued) { - nurbp->queued = 0; - - _uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb); - } else { - /* We're somewhere in the middle (or end). A bit trickier */ - /* than the head scenario */ - purbp = list_entry(urbp->queue_list.prev, struct urb_priv, - queue_list); - - pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); - if (nurbp->queued) - pltd->link = nurbp->qh->dma_handle | UHCI_PTR_QH; - else - /* The next URB happens to be the beginning, so */ - /* we're the last, end the chain */ - pltd->link = UHCI_PTR_TERM; - } - - list_del_init(&urbp->queue_list); - -out: - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb) -{ - struct urb_priv *urbp; - - urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); - if (!urbp) { - err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n"); - return NULL; - } - - memset((void *)urbp, 0, sizeof(*urbp)); - - urbp->inserttime = jiffies; - urbp->fsbrtime = jiffies; - urbp->urb = urb; - urbp->dev = urb->dev; - - INIT_LIST_HEAD(&urbp->td_list); - INIT_LIST_HEAD(&urbp->queue_list); - INIT_LIST_HEAD(&urbp->complete_list); - - urb->hcpriv = urbp; - - if (urb->dev != uhci->rh.dev) { - if (urb->transfer_buffer_length) { - urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : - PCI_DMA_TODEVICE); - if (!urbp->transfer_buffer_dma_handle) - return NULL; - } - - if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) { - urbp->setup_packet_dma_handle = pci_map_single(uhci->dev, - urb->setup_packet, sizeof(struct usb_ctrlrequest), - PCI_DMA_TODEVICE); - if (!urbp->setup_packet_dma_handle) - return NULL; - } - } - - return urbp; -} - -/* - * MUST be called with urb->lock acquired - */ -static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - td->urb = urb; - - list_add_tail(&td->list, &urbp->td_list); -} - -/* - * MUST be called with urb->lock acquired - */ -static void uhci_remove_td_from_urb(struct uhci_td *td) -{ - if (list_empty(&td->list)) - return; - - list_del_init(&td->list); - - td->urb = NULL; -} - -/* - * MUST be called with urb->lock acquired - */ -static void uhci_destroy_urb_priv(struct urb *urb) -{ - struct list_head *head, *tmp; - struct urb_priv *urbp; - struct uhci *uhci; - - urbp = (struct urb_priv *)urb->hcpriv; - if (!urbp) - return; - - if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) { - warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb); - return; - } - - if (!list_empty(&urb->urb_list)) - warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb); - - if (!list_empty(&urbp->complete_list)) - warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb); - - uhci = urbp->dev->bus->hcpriv; - - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - uhci_remove_td_from_urb(td); - uhci_remove_td(uhci, td); - uhci_free_td(uhci, td); - } - - if (urbp->setup_packet_dma_handle) { - pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle, - sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); - urbp->setup_packet_dma_handle = 0; - } - - if (urbp->transfer_buffer_dma_handle) { - pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle, - urb->transfer_buffer_length, usb_pipein(urb->pipe) ? - PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - urbp->transfer_buffer_dma_handle = 0; - } - - urb->hcpriv = NULL; - kmem_cache_free(uhci_up_cachep, urbp); -} - -static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb) -{ - unsigned long flags; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - - if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) { - urbp->fsbr = 1; - if (!uhci->fsbr++ && !uhci->fsbrtimeout) - uhci->skel_term_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH; - } - - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb) -{ - unsigned long flags; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - spin_lock_irqsave(&uhci->frame_list_lock, flags); - - if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) { - urbp->fsbr = 0; - if (!--uhci->fsbr) - uhci->fsbrtimeout = jiffies + FSBR_DELAY; - } - - spin_unlock_irqrestore(&uhci->frame_list_lock, flags); -} - -/* - * Map status to standard result codes - * - * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] - * is True for output TDs and False for input TDs. - */ -static int uhci_map_status(int status, int dir_out) -{ - if (!status) - return 0; - if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ - return -EPROTO; - if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ - if (dir_out) - return -ETIMEDOUT; - else - return -EILSEQ; - } - if (status & TD_CTRL_NAK) /* NAK */ - return -ETIMEDOUT; - if (status & TD_CTRL_BABBLE) /* Babble */ - return -EOVERFLOW; - if (status & TD_CTRL_DBUFERR) /* Buffer error */ - return -ENOSR; - if (status & TD_CTRL_STALLED) /* Stalled */ - return -EPIPE; - if (status & TD_CTRL_ACTIVE) /* Active */ - return 0; - - return -EINVAL; -} - -/* - * Control transfers - */ -static int uhci_submit_control(struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - struct uhci_td *td; - struct uhci_qh *qh; - unsigned long destination, status; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - int len = urb->transfer_buffer_length; - dma_addr_t data = urbp->transfer_buffer_dma_handle; - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; - - /* 3 errors */ - status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); - - /* - * Build the TD for the control request - */ - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | (7 << 21), - urbp->setup_packet_dma_handle); - - /* - * If direction is "send", change the frame from SETUP (0x2D) - * to OUT (0xE1). Else change it from SETUP to IN (0x69). - */ - destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe)); - - if (!(urb->transfer_flags & USB_DISABLE_SPD)) - status |= TD_CTRL_SPD; - - /* - * Build the DATA TD's - */ - while (len > 0) { - int pktsze = len; - - if (pktsze > maxsze) - pktsze = maxsze; - - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - /* Alternate Data0/1 (start with Data1) */ - destination ^= TD_TOKEN_TOGGLE; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | ((pktsze - 1) << 21), - data); - - data += pktsze; - len -= pktsze; - } - - /* - * Build the final TD for control status - */ - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - /* - * It's IN if the pipe is an output pipe or we're not expecting - * data back. - */ - destination &= ~TD_TOKEN_PID_MASK; - if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) - destination |= USB_PID_IN; - else - destination |= USB_PID_OUT; - - destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ - - status &= ~TD_CTRL_SPD; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status | TD_CTRL_IOC, - destination | (UHCI_NULL_DATA_SIZE << 21), 0); - - qh = uhci_alloc_qh(uhci, urb->dev); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; - - /* Low speed or small transfers gets a different queue and treatment */ - if (urb->pipe & TD_CTRL_LS) { - uhci_insert_tds_in_qh(qh, urb, 0); - uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb); - } else { - uhci_insert_tds_in_qh(qh, urb, 1); - uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb); - uhci_inc_fsbr(uhci, urb); - } - - return -EINPROGRESS; -} - -static int usb_control_retrigger_status(struct urb *urb); - -static int uhci_result_control(struct urb *urb) -{ - struct list_head *tmp, *head; - struct urb_priv *urbp = urb->hcpriv; - struct uhci_td *td; - unsigned int status; - int ret = 0; - - if (list_empty(&urbp->td_list)) - return -EINVAL; - - head = &urbp->td_list; - - if (urbp->short_control_packet) { - tmp = head->prev; - goto status_phase; - } - - tmp = head->next; - td = list_entry(tmp, struct uhci_td, list); - - /* The first TD is the SETUP phase, check the status, but skip */ - /* the count */ - status = uhci_status_bits(td->status); - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - if (status) - goto td_error; - - urb->actual_length = 0; - - /* The rest of the TD's (but the last) are data */ - tmp = tmp->next; - while (tmp != head && tmp->next != head) { - td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - status = uhci_status_bits(td->status); - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - urb->actual_length += uhci_actual_length(td->status); - - if (status) - goto td_error; - - /* Check to see if we received a short packet */ - if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { - if (urb->transfer_flags & USB_DISABLE_SPD) { - ret = -EREMOTEIO; - goto err; - } - - if (uhci_packetid(td->info) == USB_PID_IN) - return usb_control_retrigger_status(urb); - else - return 0; - } - } - -status_phase: - td = list_entry(tmp, struct uhci_td, list); - - /* Control status phase */ - status = uhci_status_bits(td->status); - -#ifdef I_HAVE_BUGGY_APC_BACKUPS - /* APC BackUPS Pro kludge */ - /* It tries to send all of the descriptor instead of the amount */ - /* we requested */ - if (td->status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ - status & TD_CTRL_ACTIVE && - status & TD_CTRL_NAK) - return 0; -#endif - - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - if (status) - goto td_error; - - return 0; - -td_error: - ret = uhci_map_status(status, uhci_packetout(td->info)); - if (ret == -EPIPE) - /* endpoint has stalled - mark it halted */ - usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), - uhci_packetout(td->info)); - -err: - if ((debug == 1 && ret != -EPIPE) || debug > 1) { - /* Some debugging code */ - dbg("uhci_result_control() failed with status %x", status); - - if (errbuf) { - /* Print the chain for debugging purposes */ - uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); - - lprintk(errbuf); - } - } - - return ret; -} - -static int usb_control_retrigger_status(struct urb *urb) -{ - struct list_head *tmp, *head; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci *uhci = urb->dev->bus->hcpriv; - - urbp->short_control_packet = 1; - - /* Create a new QH to avoid pointer overwriting problems */ - uhci_remove_qh(uhci, urbp->qh); - - /* Delete all of the TD's except for the status TD at the end */ - head = &urbp->td_list; - tmp = head->next; - while (tmp != head && tmp->next != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - uhci_remove_td_from_urb(td); - uhci_remove_td(uhci, td); - uhci_free_td(uhci, td); - } - - urbp->qh = uhci_alloc_qh(uhci, urb->dev); - if (!urbp->qh) { - err("unable to allocate new QH for control retrigger"); - return -ENOMEM; - } - - urbp->qh->urbp = urbp; - - /* One TD, who cares about Breadth first? */ - uhci_insert_tds_in_qh(urbp->qh, urb, 0); - - /* Low speed or small transfers gets a different queue and treatment */ - if (urb->pipe & TD_CTRL_LS) - uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb); - else - uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb); - - return -EINPROGRESS; -} - -/* - * Interrupt transfers - */ -static int uhci_submit_interrupt(struct urb *urb) -{ - struct uhci_td *td; - unsigned long destination, status; - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) - return -EINVAL; - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - - status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC; - - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); - destination |= ((urb->transfer_buffer_length - 1) << 21); - - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle); - - uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td); - - return -EINPROGRESS; -} - -static int uhci_result_interrupt(struct urb *urb) -{ - struct list_head *tmp, *head; - struct urb_priv *urbp = urb->hcpriv; - struct uhci_td *td; - unsigned int status; - int ret = 0; - - urb->actual_length = 0; - - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - status = uhci_status_bits(td->status); - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - urb->actual_length += uhci_actual_length(td->status); - - if (status) - goto td_error; - - if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { - if (urb->transfer_flags & USB_DISABLE_SPD) { - ret = -EREMOTEIO; - goto err; - } else - return 0; - } - } - - return 0; - -td_error: - ret = uhci_map_status(status, uhci_packetout(td->info)); - if (ret == -EPIPE) - /* endpoint has stalled - mark it halted */ - usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), - uhci_packetout(td->info)); - -err: - if ((debug == 1 && ret != -EPIPE) || debug > 1) { - /* Some debugging code */ - dbg("uhci_result_interrupt/bulk() failed with status %x", - status); - - if (errbuf) { - /* Print the chain for debugging purposes */ - if (urbp->qh) - uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); - else - uhci_show_td(td, errbuf, ERRBUF_LEN, 0); - - lprintk(errbuf); - } - } - - return ret; -} - -static void uhci_reset_interrupt(struct urb *urb) -{ - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - unsigned long flags; - - spin_lock_irqsave(&urb->lock, flags); - - /* Root hub is special */ - if (urb->dev == uhci->rh.dev) - goto out; - - td = list_entry(urbp->td_list.next, struct uhci_td, list); - - td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; - td->info &= ~TD_TOKEN_TOGGLE; - td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - -out: - urb->status = -EINPROGRESS; - - spin_unlock_irqrestore(&urb->lock, flags); -} - -/* - * Bulk transfers - */ -static int uhci_submit_bulk(struct urb *urb, struct urb *eurb) -{ - struct uhci_td *td; - struct uhci_qh *qh; - unsigned long destination, status; - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - int len = urb->transfer_buffer_length; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - dma_addr_t data = urbp->transfer_buffer_dma_handle; - - if (len < 0) - return -EINVAL; - - /* Can't have low speed bulk transfers */ - if (urb->pipe & TD_CTRL_LS) - return -EINVAL; - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - - /* 3 errors */ - status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT); - - if (!(urb->transfer_flags & USB_DISABLE_SPD)) - status |= TD_CTRL_SPD; - - /* - * Build the DATA TD's - */ - do { /* Allow zero length packets */ - int pktsze = len; - - if (pktsze > maxsze) - pktsze = maxsze; - - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | - (((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) | - (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), - data); - - data += pktsze; - len -= maxsze; - - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - } while (len > 0); - - /* - * USB_ZERO_PACKET means adding a 0-length packet, if - * direction is OUT and the transfer_length was an - * exact multiple of maxsze, hence - * (len = transfer_length - N * maxsze) == 0 - * however, if transfer_length == 0, the zero packet - * was already prepared above. - */ - if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) && - !len && urb->transfer_buffer_length) { - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | - (UHCI_NULL_DATA_SIZE << 21) | - (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), - data); - - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - } - - /* Set the flag on the last packet */ - td->status |= TD_CTRL_IOC; - - qh = uhci_alloc_qh(uhci, urb->dev); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; - - /* Always assume breadth first */ - uhci_insert_tds_in_qh(qh, urb, 1); - - if (urb->transfer_flags & USB_QUEUE_BULK && eurb) - uhci_append_queued_urb(uhci, eurb, urb); - else - uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb); - - uhci_inc_fsbr(uhci, urb); - - return -EINPROGRESS; -} - -/* We can use the result interrupt since they're identical */ -#define uhci_result_bulk uhci_result_interrupt - -/* - * Isochronous transfers - */ -static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end) -{ - struct urb *last_urb = NULL; - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - struct list_head *tmp, *head; - int ret = 0; - - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb *u = list_entry(tmp, struct urb, urb_list); - - tmp = tmp->next; - - /* look for pending URB's with identical pipe handle */ - if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && - (u->status == -EINPROGRESS) && (u != urb)) { - if (!last_urb) - *start = u->start_frame; - last_urb = u; - } - } - - if (last_urb) { - *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; - ret = 0; - } else - ret = -1; /* no previous urb found */ - - return ret; -} - -static int isochronous_find_start(struct urb *urb) -{ - int limits; - unsigned int start = 0, end = 0; - - if (urb->number_of_packets > 900) /* 900? Why? */ - return -EFBIG; - - limits = isochronous_find_limits(urb, &start, &end); - - if (urb->transfer_flags & USB_ISO_ASAP) { - if (limits) { - int curframe; - - curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES; - urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES; - } else - urb->start_frame = end; - } else { - urb->start_frame %= UHCI_NUMFRAMES; - /* FIXME: Sanity check */ - } - - return 0; -} - -/* - * Isochronous transfers - */ -static int uhci_submit_isochronous(struct urb *urb) -{ - struct uhci_td *td; - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - int i, ret, framenum; - int status, destination; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - status = TD_CTRL_ACTIVE | TD_CTRL_IOS; - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - - ret = isochronous_find_start(urb); - if (ret) - return ret; - - framenum = urb->start_frame; - for (i = 0; i < urb->number_of_packets; i++, framenum++) { - if (!urb->iso_frame_desc[i].length) - continue; - - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21), - urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset); - - if (i + 1 >= urb->number_of_packets) - td->status |= TD_CTRL_IOC; - - uhci_insert_td_frame_list(uhci, td, framenum); - } - - return -EINPROGRESS; -} - -static int uhci_result_isochronous(struct urb *urb) -{ - struct list_head *tmp, *head; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - int status; - int i, ret = 0; - - urb->actual_length = 0; - - i = 0; - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - int actlength; - - tmp = tmp->next; - - if (td->status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - actlength = uhci_actual_length(td->status); - urb->iso_frame_desc[i].actual_length = actlength; - urb->actual_length += actlength; - - status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe)); - urb->iso_frame_desc[i].status = status; - if (status) { - urb->error_count++; - ret = status; - } - - i++; - } - - return ret; -} - -/* - * MUST be called with uhci->urb_list_lock acquired - */ -static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb) -{ - struct list_head *tmp, *head; - - /* We don't match Isoc transfers since they are special */ - if (usb_pipeisoc(urb->pipe)) - return NULL; - - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb *u = list_entry(tmp, struct urb, urb_list); - - tmp = tmp->next; - - if (u->dev == urb->dev && u->pipe == urb->pipe && - u->status == -EINPROGRESS) - return u; - } - - return NULL; -} - -static int uhci_submit_urb(struct urb *urb) -{ - int ret = -EINVAL; - struct uhci *uhci; - unsigned long flags; - struct urb *eurb; - int bustime; - - if (!urb) - return -EINVAL; - - if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) { - warn("uhci_submit_urb: urb %p belongs to disconnected device or bus?", urb); - return -ENODEV; - } - - uhci = (struct uhci *)urb->dev->bus->hcpriv; - - usb_inc_dev_use(urb->dev); - - spin_lock_irqsave(&uhci->urb_list_lock, flags); - spin_lock(&urb->lock); - - if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET || - urb->status == -ECONNABORTED) { - dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status); - /* Since we can have problems on the out path */ - spin_unlock(&urb->lock); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - usb_dec_dev_use(urb->dev); - - return ret; - } - - INIT_LIST_HEAD(&urb->urb_list); - if (!uhci_alloc_urb_priv(uhci, urb)) { - ret = -ENOMEM; - - goto out; - } - - eurb = uhci_find_urb_ep(uhci, urb); - if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) { - ret = -ENXIO; - - goto out; - } - - /* Short circuit the virtual root hub */ - if (urb->dev == uhci->rh.dev) { - ret = rh_submit_urb(urb); - - goto out; - } - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - ret = uhci_submit_control(urb); - break; - case PIPE_INTERRUPT: - if (urb->bandwidth == 0) { /* not yet checked/allocated */ - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) - ret = bustime; - else { - ret = uhci_submit_interrupt(urb); - if (ret == -EINPROGRESS) - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - } - } else /* bandwidth is already set */ - ret = uhci_submit_interrupt(urb); - break; - case PIPE_BULK: - ret = uhci_submit_bulk(urb, eurb); - break; - case PIPE_ISOCHRONOUS: - if (urb->bandwidth == 0) { /* not yet checked/allocated */ - if (urb->number_of_packets <= 0) { - ret = -EINVAL; - break; - } - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - ret = bustime; - break; - } - - ret = uhci_submit_isochronous(urb); - if (ret == -EINPROGRESS) - usb_claim_bandwidth(urb->dev, urb, bustime, 1); - } else /* bandwidth is already set */ - ret = uhci_submit_isochronous(urb); - break; - } - -out: - urb->status = ret; - - if (ret == -EINPROGRESS) { - /* We use _tail to make find_urb_ep more efficient */ - list_add_tail(&urb->urb_list, &uhci->urb_list); - - spin_unlock(&urb->lock); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - return 0; - } - - uhci_unlink_generic(uhci, urb); - - spin_unlock(&urb->lock); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - /* Only call completion if it was successful */ - if (!ret) - uhci_call_completion(urb); - - return ret; -} - -/* - * Return the result of a transfer - * - * MUST be called with urb_list_lock acquired - */ -static void uhci_transfer_result(struct uhci *uhci, struct urb *urb) -{ - int ret = -EINVAL; - unsigned long flags; - struct urb_priv *urbp; - - /* The root hub is special */ - if (urb->dev == uhci->rh.dev) - return; - - spin_lock_irqsave(&urb->lock, flags); - - urbp = (struct urb_priv *)urb->hcpriv; - - if (urb->status != -EINPROGRESS) { - info("uhci_transfer_result: called for URB %p not in flight?", urb); - goto out; - } - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - ret = uhci_result_control(urb); - break; - case PIPE_INTERRUPT: - ret = uhci_result_interrupt(urb); - break; - case PIPE_BULK: - ret = uhci_result_bulk(urb); - break; - case PIPE_ISOCHRONOUS: - ret = uhci_result_isochronous(urb); - break; - } - - urbp->status = ret; - - if (ret == -EINPROGRESS) - goto out; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: - case PIPE_ISOCHRONOUS: - /* Release bandwidth for Interrupt or Isoc. transfers */ - /* Spinlock needed ? */ - if (urb->bandwidth) - usb_release_bandwidth(urb->dev, urb, 1); - uhci_unlink_generic(uhci, urb); - break; - case PIPE_INTERRUPT: - /* Interrupts are an exception */ - if (urb->interval) - goto out_complete; - - /* Release bandwidth for Interrupt or Isoc. transfers */ - /* Spinlock needed ? */ - if (urb->bandwidth) - usb_release_bandwidth(urb->dev, urb, 0); - uhci_unlink_generic(uhci, urb); - break; - default: - info("uhci_transfer_result: unknown pipe type %d for urb %p\n", - usb_pipetype(urb->pipe), urb); - } - - /* Remove it from uhci->urb_list */ - list_del_init(&urb->urb_list); - -out_complete: - uhci_add_complete(urb); - -out: - spin_unlock_irqrestore(&urb->lock, flags); -} - -/* - * MUST be called with urb->lock acquired - */ -static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb) -{ - struct list_head *head, *tmp; - struct urb_priv *urbp = urb->hcpriv; - int prevactive = 1; - - /* We can get called when urbp allocation fails, so check */ - if (!urbp) - return; - - uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ - - /* - * Now we need to find out what the last successful toggle was - * so we can update the local data toggle for the next transfer - * - * There's 3 way's the last successful completed TD is found: - * - * 1) The TD is NOT active and the actual length < expected length - * 2) The TD is NOT active and it's the last TD in the chain - * 3) The TD is active and the previous TD is NOT active - * - * Control and Isochronous ignore the toggle, so this is safe - * for all types - */ - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - if (!(td->status & TD_CTRL_ACTIVE) && - (uhci_actual_length(td->status) < uhci_expected_length(td->info) || - tmp == head)) - usb_settoggle(urb->dev, uhci_endpoint(td->info), - uhci_packetout(td->info), - uhci_toggle(td->info) ^ 1); - else if ((td->status & TD_CTRL_ACTIVE) && !prevactive) - usb_settoggle(urb->dev, uhci_endpoint(td->info), - uhci_packetout(td->info), - uhci_toggle(td->info)); - - prevactive = td->status & TD_CTRL_ACTIVE; - } - - uhci_delete_queued_urb(uhci, urb); - - /* The interrupt loop will reclaim the QH's */ - uhci_remove_qh(uhci, urbp->qh); - urbp->qh = NULL; -} - -static int uhci_unlink_urb(struct urb *urb) -{ - struct uhci *uhci; - unsigned long flags; - struct urb_priv *urbp = urb->hcpriv; - - if (!urb) - return -EINVAL; - - if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) - return -ENODEV; - - uhci = (struct uhci *)urb->dev->bus->hcpriv; - - spin_lock_irqsave(&uhci->urb_list_lock, flags); - spin_lock(&urb->lock); - - /* Release bandwidth for Interrupt or Isoc. transfers */ - /* Spinlock needed ? */ - if (urb->bandwidth) { - switch (usb_pipetype(urb->pipe)) { - case PIPE_INTERRUPT: - usb_release_bandwidth(urb->dev, urb, 0); - break; - case PIPE_ISOCHRONOUS: - usb_release_bandwidth(urb->dev, urb, 1); - break; - default: - break; - } - } - - if (urb->status != -EINPROGRESS) { - spin_unlock(&urb->lock); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - return 0; - } - - list_del_init(&urb->urb_list); - - uhci_unlink_generic(uhci, urb); - - /* Short circuit the virtual root hub */ - if (urb->dev == uhci->rh.dev) { - rh_unlink_urb(urb); - - spin_unlock(&urb->lock); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - uhci_call_completion(urb); - } else { - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - urbp->status = urb->status = -ECONNABORTED; - - spin_lock(&uhci->urb_remove_list_lock); - - /* If we're the first, set the next interrupt bit */ - if (list_empty(&uhci->urb_remove_list)) - uhci_set_next_interrupt(uhci); - - list_add(&urb->urb_list, &uhci->urb_remove_list); - - spin_unlock(&uhci->urb_remove_list_lock); - - spin_unlock(&urb->lock); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - } else { - urb->status = -ENOENT; - - spin_unlock(&urb->lock); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - if (in_interrupt()) { /* wait at least 1 frame */ - static int errorcount = 10; - - if (errorcount--) - dbg("uhci_unlink_urb called from interrupt for urb %p", urb); - udelay(1000); - } else - schedule_timeout(1+1*HZ/1000); - - uhci_call_completion(urb); - } - } - - return 0; -} - -static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head, *tmp; - int count = 0; - - uhci_dec_fsbr(uhci, urb); - - urbp->fsbr_timeout = 1; - - /* - * Ideally we would want to fix qh->element as well, but it's - * read/write by the HC, so that can introduce a race. It's not - * really worth the hassle - */ - - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - /* - * Make sure we don't do the last one (since it'll have the - * TERM bit set) as well as we skip every so many TD's to - * make sure it doesn't hog the bandwidth - */ - if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1)) - td->link |= UHCI_PTR_DEPTH; - - count++; - } - - return 0; -} - -/* - * uhci_get_current_frame_number() - * - * returns the current frame number for a USB bus/controller. - */ -static int uhci_get_current_frame_number(struct usb_device *dev) -{ - struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; - - return inw(uhci->io_addr + USBFRNUM); -} - -struct usb_operations uhci_device_operations = { - uhci_alloc_dev, - uhci_free_dev, - uhci_get_current_frame_number, - uhci_submit_urb, - uhci_unlink_urb -}; - -/* Virtual Root Hub */ - -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, /* __u16 bcdUSB; v1.0 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, - Bit 5 Remote-wakeup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static __u8 root_hub_hub_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x29, /* __u8 bDescriptorType; Hub-descriptor */ - 0x02, /* __u8 bNbrPorts; */ - 0x00, /* __u16 wHubCharacteristics; */ - 0x00, - 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ - 0x00, /* __u8 bHubContrCurrent; 0 mA */ - 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ - 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ -}; - -/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ -static int rh_send_irq(struct urb *urb) -{ - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - unsigned int io_addr = uhci->io_addr; - unsigned long flags; - int i, len = 1; - __u16 data = 0; - - spin_lock_irqsave(&urb->lock, flags); - for (i = 0; i < uhci->rh.numports; i++) { - data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); - len = (i + 1) / 8 + 1; - } - - *(__u16 *) urb->transfer_buffer = cpu_to_le16(data); - urb->actual_length = len; - urbp->status = 0; - - spin_unlock_irqrestore(&urb->lock, flags); - - if ((data > 0) && (uhci->rh.send != 0)) { - dbg("root-hub INT complete: port1: %x port2: %x data: %x", - inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data); - uhci_call_completion(urb); - } - - return 0; -} - -/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ -static int rh_init_int_timer(struct urb *urb); - -static void rh_int_timer_do(unsigned long ptr) -{ - struct urb *urb = (struct urb *)ptr; - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - struct list_head list, *tmp, *head; - unsigned long flags; - - if (uhci->rh.send) - rh_send_irq(urb); - - INIT_LIST_HEAD(&list); - - spin_lock_irqsave(&uhci->urb_list_lock, flags); - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb *u = list_entry(tmp, struct urb, urb_list); - struct urb_priv *up = (struct urb_priv *)u->hcpriv; - - tmp = tmp->next; - - spin_lock(&u->lock); - - /* Check if the FSBR timed out */ - if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT)) - uhci_fsbr_timeout(uhci, u); - - /* Check if the URB timed out */ - if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) { - list_del(&u->urb_list); - list_add_tail(&u->urb_list, &list); - } - - spin_unlock(&u->lock); - } - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - head = &list; - tmp = head->next; - while (tmp != head) { - struct urb *u = list_entry(tmp, struct urb, urb_list); - - tmp = tmp->next; - - u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED; - uhci_unlink_urb(u); - } - - /* Really disable FSBR */ - if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { - uhci->fsbrtimeout = 0; - uhci->skel_term_qh->link = UHCI_PTR_TERM; - } - - /* enter global suspend if nothing connected */ - if (!uhci->is_suspended && !ports_active(uhci)) - suspend_hc(uhci); - - rh_init_int_timer(urb); -} - -/* Root Hub INTs are polled by this timer */ -static int rh_init_int_timer(struct urb *urb) -{ - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - - uhci->rh.interval = urb->interval; - init_timer(&uhci->rh.rh_int_timer); - uhci->rh.rh_int_timer.function = rh_int_timer_do; - uhci->rh.rh_int_timer.data = (unsigned long)urb; - uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; - add_timer(&uhci->rh.rh_int_timer); - - return 0; -} - -#define OK(x) len = (x); break - -#define CLR_RH_PORTSTAT(x) \ - status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ - status = (status & 0xfff5) & ~(x); \ - outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) - -#define SET_RH_PORTSTAT(x) \ - status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ - status = (status & 0xfff5) | (x); \ - outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) - - -/* Root Hub Control Pipe */ -static int rh_submit_urb(struct urb *urb) -{ - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int status = 0; - int stat = 0; - int i; - unsigned int io_addr = uhci->io_addr; - __u16 cstatus; - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - if (usb_pipetype(pipe) == PIPE_INTERRUPT) { - uhci->rh.urb = urb; - uhci->rh.send = 1; - uhci->rh.interval = urb->interval; - rh_init_int_timer(urb); - - return -EINPROGRESS; - } - - bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8; - wValue = le16_to_cpu(cmd->wValue); - wIndex = le16_to_cpu(cmd->wIndex); - wLength = le16_to_cpu(cmd->wLength); - - for (i = 0; i < 8; i++) - uhci->rh.c_p_r[i] = 0; - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *)data = cpu_to_le16(1); - OK(2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *)data = cpu_to_le16(0); - OK(2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *)data = cpu_to_le16(0); - OK(2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *)data = cpu_to_le32(0); - OK(4); /* hub power */ - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); - cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | - ((status & USBPORTSC_PEC) >> (3 - 1)) | - (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); - status = (status & USBPORTSC_CCS) | - ((status & USBPORTSC_PE) >> (2 - 1)) | - ((status & USBPORTSC_SUSP) >> (12 - 2)) | - ((status & USBPORTSC_PR) >> (9 - 4)) | - (1 << 8) | /* power on */ - ((status & USBPORTSC_LSDA) << (-8 + 9)); - - *(__u16 *)data = cpu_to_le16(status); - *(__u16 *)(data + 2) = cpu_to_le16(cstatus); - OK(4); - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case RH_ENDPOINT_STALL: - OK(0); - } - break; - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_OVER_CURRENT: - OK(0); /* hub power over current */ - } - break; - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case RH_PORT_ENABLE: - CLR_RH_PORTSTAT(USBPORTSC_PE); - OK(0); - case RH_PORT_SUSPEND: - CLR_RH_PORTSTAT(USBPORTSC_SUSP); - OK(0); - case RH_PORT_POWER: - OK(0); /* port power */ - case RH_C_PORT_CONNECTION: - SET_RH_PORTSTAT(USBPORTSC_CSC); - OK(0); - case RH_C_PORT_ENABLE: - SET_RH_PORTSTAT(USBPORTSC_PEC); - OK(0); - case RH_C_PORT_SUSPEND: - /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ - OK(0); - case RH_C_PORT_OVER_CURRENT: - OK(0); /* port power over current */ - case RH_C_PORT_RESET: - uhci->rh.c_p_r[wIndex - 1] = 0; - OK(0); - } - break; - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case RH_PORT_SUSPEND: - SET_RH_PORTSTAT(USBPORTSC_SUSP); - OK(0); - case RH_PORT_RESET: - SET_RH_PORTSTAT(USBPORTSC_PR); - mdelay(50); /* USB v1.1 7.1.7.3 */ - uhci->rh.c_p_r[wIndex - 1] = 1; - CLR_RH_PORTSTAT(USBPORTSC_PR); - udelay(10); - SET_RH_PORTSTAT(USBPORTSC_PE); - mdelay(10); - SET_RH_PORTSTAT(0xa); - OK(0); - case RH_PORT_POWER: - OK(0); /* port power ** */ - case RH_PORT_ENABLE: - SET_RH_PORTSTAT(USBPORTSC_PE); - OK(0); - } - break; - case RH_SET_ADDRESS: - uhci->rh.devnum = wValue; - OK(0); - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case 0x01: /* device descriptor */ - len = min_t(unsigned int, leni, - min_t(unsigned int, - sizeof(root_hub_dev_des), wLength)); - memcpy(data, root_hub_dev_des, len); - OK(len); - case 0x02: /* configuration descriptor */ - len = min_t(unsigned int, leni, - min_t(unsigned int, - sizeof(root_hub_config_des), wLength)); - memcpy (data, root_hub_config_des, len); - OK(len); - case 0x03: /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, - uhci->io_addr, "UHCI-alt", - data, wLength); - if (len > 0) { - OK(min_t(int, leni, len)); - } else - stat = -EPIPE; - } - break; - case RH_GET_DESCRIPTOR | RH_CLASS: - root_hub_hub_des[2] = uhci->rh.numports; - len = min_t(unsigned int, leni, - min_t(unsigned int, sizeof(root_hub_hub_des), wLength)); - memcpy(data, root_hub_hub_des, len); - OK(len); - case RH_GET_CONFIGURATION: - *(__u8 *)data = 0x01; - OK(1); - case RH_SET_CONFIGURATION: - OK(0); - case RH_GET_INTERFACE | RH_INTERFACE: - *(__u8 *)data = 0x00; - OK(1); - case RH_SET_INTERFACE | RH_INTERFACE: - OK(0); - default: - stat = -EPIPE; - } - - urb->actual_length = len; - - return stat; -} - -/* - * MUST be called with urb->lock acquired - */ -static int rh_unlink_urb(struct urb *urb) -{ - struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - - if (uhci->rh.urb == urb) { - urb->status = -ENOENT; - uhci->rh.send = 0; - uhci->rh.urb = NULL; - del_timer(&uhci->rh.rh_int_timer); - } - return 0; -} - -static void uhci_free_pending_qhs(struct uhci *uhci) -{ - struct list_head *tmp, *head; - unsigned long flags; - - spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); - head = &uhci->qh_remove_list; - tmp = head->next; - while (tmp != head) { - struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list); - - tmp = tmp->next; - - list_del_init(&qh->remove_list); - - uhci_free_qh(uhci, qh); - } - spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags); -} - -static void uhci_call_completion(struct urb *urb) -{ - struct urb_priv *urbp; - struct usb_device *dev = urb->dev; - struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; - int is_ring = 0, killed, resubmit_interrupt, status; - struct urb *nurb; - unsigned long flags; - - spin_lock_irqsave(&urb->lock, flags); - - urbp = (struct urb_priv *)urb->hcpriv; - if (!urbp || !urb->dev) { - spin_unlock_irqrestore(&urb->lock, flags); - return; - } - - killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED || - urb->status == -ECONNRESET); - resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT && - urb->interval); - - nurb = urb->next; - if (nurb && !killed) { - int count = 0; - - while (nurb && nurb != urb && count < MAX_URB_LOOP) { - if (nurb->status == -ENOENT || - nurb->status == -ECONNABORTED || - nurb->status == -ECONNRESET) { - killed = 1; - break; - } - - nurb = nurb->next; - count++; - } - - if (count == MAX_URB_LOOP) - err("uhci_call_completion: too many linked URB's, loop? (first loop)"); - - /* Check to see if chain is a ring */ - is_ring = (nurb == urb); - } - - if (urbp->transfer_buffer_dma_handle) - pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle, - urb->transfer_buffer_length, usb_pipein(urb->pipe) ? - PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - - if (urbp->setup_packet_dma_handle) - pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle, - sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); - - status = urbp->status; - if (!resubmit_interrupt || killed) - /* We don't need urb_priv anymore */ - uhci_destroy_urb_priv(urb); - - if (!killed) - urb->status = status; - - urb->dev = NULL; - spin_unlock_irqrestore(&urb->lock, flags); - - if (urb->complete) - urb->complete(urb); - - if (resubmit_interrupt) - /* Recheck the status. The completion handler may have */ - /* unlinked the resubmitting interrupt URB */ - killed = (urb->status == -ENOENT || - urb->status == -ECONNABORTED || - urb->status == -ECONNRESET); - - if (resubmit_interrupt && !killed) { - urb->dev = dev; - uhci_reset_interrupt(urb); - } else { - if (is_ring && !killed) { - urb->dev = dev; - uhci_submit_urb(urb); - } else { - /* We decrement the usage count after we're done */ - /* with everything */ - usb_dec_dev_use(dev); - } - } -} - -static void uhci_finish_completion(struct uhci *uhci) -{ - struct list_head *tmp, *head; - unsigned long flags; - - spin_lock_irqsave(&uhci->complete_list_lock, flags); - head = &uhci->complete_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list); - struct urb *urb = urbp->urb; - - list_del_init(&urbp->complete_list); - spin_unlock_irqrestore(&uhci->complete_list_lock, flags); - - uhci_call_completion(urb); - - spin_lock_irqsave(&uhci->complete_list_lock, flags); - head = &uhci->complete_list; - tmp = head->next; - } - spin_unlock_irqrestore(&uhci->complete_list_lock, flags); -} - -static void uhci_remove_pending_qhs(struct uhci *uhci) -{ - struct list_head *tmp, *head; - unsigned long flags; - - spin_lock_irqsave(&uhci->urb_remove_list_lock, flags); - head = &uhci->urb_remove_list; - tmp = head->next; - while (tmp != head) { - struct urb *urb = list_entry(tmp, struct urb, urb_list); - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - tmp = tmp->next; - - list_del_init(&urb->urb_list); - - urbp->status = urb->status = -ECONNRESET; - - uhci_add_complete(urb); - } - spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); -} - -static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) -{ - struct uhci *uhci = __uhci; - unsigned int io_addr = uhci->io_addr; - unsigned short status; - struct list_head *tmp, *head; - - /* - * Read the interrupt status, and write it back to clear the - * interrupt cause - */ - status = inw(io_addr + USBSTS); - if (!status) /* shared interrupt, not mine */ - return; - outw(status, io_addr + USBSTS); /* Clear it */ - - if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { - if (status & USBSTS_HSE) - err("%x: host system error, PCI problems?", io_addr); - if (status & USBSTS_HCPE) - err("%x: host controller process error. something bad happened", io_addr); - if ((status & USBSTS_HCH) && !uhci->is_suspended) { - err("%x: host controller halted. very bad", io_addr); - /* FIXME: Reset the controller, fix the offending TD */ - } - } - - if (status & USBSTS_RD) - wakeup_hc(uhci); - - uhci_free_pending_qhs(uhci); - - uhci_remove_pending_qhs(uhci); - - uhci_clear_next_interrupt(uhci); - - /* Walk the list of pending URB's to see which ones completed */ - spin_lock(&uhci->urb_list_lock); - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb *urb = list_entry(tmp, struct urb, urb_list); - - tmp = tmp->next; - - /* Checks the status and does all of the magic necessary */ - uhci_transfer_result(uhci, urb); - } - spin_unlock(&uhci->urb_list_lock); - - uhci_finish_completion(uhci); -} - -static void reset_hc(struct uhci *uhci) -{ - unsigned int io_addr = uhci->io_addr; - - /* Global reset for 50ms */ - outw(USBCMD_GRESET, io_addr + USBCMD); - wait_ms(50); - outw(0, io_addr + USBCMD); - wait_ms(10); -} - -static void suspend_hc(struct uhci *uhci) -{ - unsigned int io_addr = uhci->io_addr; - - dbg("%x: suspend_hc", io_addr); - - outw(USBCMD_EGSM, io_addr + USBCMD); - - uhci->is_suspended = 1; -} - -static void wakeup_hc(struct uhci *uhci) -{ - unsigned int io_addr = uhci->io_addr; - unsigned int status; - - dbg("%x: wakeup_hc", io_addr); - - outw(0, io_addr + USBCMD); - - /* wait for EOP to be sent */ - status = inw(io_addr + USBCMD); - while (status & USBCMD_FGR) - status = inw(io_addr + USBCMD); - - uhci->is_suspended = 0; - - /* Run and mark it configured with a 64-byte max packet */ - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); -} - -static int ports_active(struct uhci *uhci) -{ - unsigned int io_addr = uhci->io_addr; - int connection = 0; - int i; - - for (i = 0; i < uhci->rh.numports; i++) - connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1); - - return connection; -} - -static void start_hc(struct uhci *uhci) -{ - unsigned int io_addr = uhci->io_addr; - int timeout = 1000; - - /* - * Reset the HC - this will force us to get a - * new notification of any already connected - * ports due to the virtual disconnect that it - * implies. - */ - outw(USBCMD_HCRESET, io_addr + USBCMD); - while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { - if (!--timeout) { - printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n"); - break; - } - } - - /* Turn on all interrupts */ - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, - io_addr + USBINTR); - - /* Start at frame 0 */ - outw(0, io_addr + USBFRNUM); - outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD); - - /* Run and mark it configured with a 64-byte max packet */ - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); -} - -#ifdef CONFIG_PROC_FS -static int uhci_num = 0; -#endif - -static void free_uhci(struct uhci *uhci) -{ - kfree(uhci); -} - -/* - * De-allocate all resources.. - */ -static void release_uhci(struct uhci *uhci) -{ - int i; -#ifdef CONFIG_PROC_FS - char buf[8]; -#endif - - if (uhci->irq >= 0) { - free_irq(uhci->irq, uhci); - uhci->irq = -1; - } - - for (i = 0; i < UHCI_NUM_SKELQH; i++) - if (uhci->skelqh[i]) { - uhci_free_qh(uhci, uhci->skelqh[i]); - uhci->skelqh[i] = NULL; - } - - for (i = 0; i < UHCI_NUM_SKELTD; i++) - if (uhci->skeltd[i]) { - uhci_free_td(uhci, uhci->skeltd[i]); - uhci->skeltd[i] = NULL; - } - - if (uhci->qh_pool) { - pci_pool_destroy(uhci->qh_pool); - uhci->qh_pool = NULL; - } - - if (uhci->td_pool) { - pci_pool_destroy(uhci->td_pool); - uhci->td_pool = NULL; - } - - if (uhci->fl) { - pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle); - uhci->fl = NULL; - } - - if (uhci->bus) { - usb_free_bus(uhci->bus); - uhci->bus = NULL; - } - -#ifdef CONFIG_PROC_FS - if (uhci->proc_entry) { - sprintf(buf, "hc%d", uhci->num); - - remove_proc_entry(buf, uhci_proc_root); - uhci->proc_entry = NULL; - } -#endif - - free_uhci(uhci); -} - -/* - * Allocate a frame list, and then setup the skeleton - * - * The hardware doesn't really know any difference - * in the queues, but the order does matter for the - * protocols higher up. The order is: - * - * - any isochronous events handled before any - * of the queues. We don't do that here, because - * we'll create the actual TD entries on demand. - * - The first queue is the interrupt queue. - * - The second queue is the control queue, split into low and high speed - * - The third queue is bulk queue. - * - The fourth queue is the bandwidth reclamation queue, which loops back - * to the high speed control queue. - */ -static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size) -{ - struct uhci *uhci; - int retval; - char buf[8], *bufp = buf; - int i, port; - struct usb_bus *bus; - dma_addr_t dma_handle; -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *ent; -#endif - - retval = -ENODEV; - if (pci_enable_device(dev) < 0) { - err("couldn't enable PCI device"); - goto err_enable_device; - } - - if (!dev->irq) { - err("found UHCI device with no IRQ assigned. check BIOS settings!"); - goto err_invalid_irq; - } - - if (!pci_dma_supported(dev, 0xFFFFFFFF)) { - err("PCI subsystem doesn't support 32 bit addressing?"); - goto err_pci_dma_supported; - } - - retval = -EBUSY; - if (!request_region(io_addr, io_size, "usb-uhci")) { - err("couldn't allocate I/O range %x - %x", io_addr, - io_addr + io_size - 1); - goto err_request_region; - } - - pci_set_master(dev); - -#ifndef __sparc__ - sprintf(buf, "%d", dev->irq); -#else - bufp = __irq_itoa(dev->irq); -#endif - printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", - io_addr, bufp); - - if (pci_set_dma_mask(dev, 0xFFFFFFFF)) { - err("couldn't set PCI dma mask"); - retval = -ENODEV; - goto err_pci_set_dma_mask; - } - - uhci = kmalloc(sizeof(*uhci), GFP_KERNEL); - if (!uhci) { - err("couldn't allocate uhci structure"); - retval = -ENOMEM; - goto err_alloc_uhci; - } - - uhci->dev = dev; - uhci->irq = dev->irq; - uhci->io_addr = io_addr; - uhci->io_size = io_size; - pci_set_drvdata(dev, uhci); - -#ifdef CONFIG_PROC_FS - uhci->num = uhci_num++; - - sprintf(buf, "hc%d", uhci->num); - - ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root); - if (!ent) { - err("couldn't create uhci proc entry"); - retval = -ENOMEM; - goto err_create_proc_entry; - } - - ent->data = uhci; - ent->proc_fops = &uhci_proc_operations; - ent->size = 0; - uhci->proc_entry = ent; -#endif - - /* Reset here so we don't get any interrupts from an old setup */ - /* or broken setup */ - reset_hc(uhci); - - uhci->fsbr = 0; - uhci->fsbrtimeout = 0; - - uhci->is_suspended = 0; - - spin_lock_init(&uhci->qh_remove_list_lock); - INIT_LIST_HEAD(&uhci->qh_remove_list); - - spin_lock_init(&uhci->urb_remove_list_lock); - INIT_LIST_HEAD(&uhci->urb_remove_list); - - spin_lock_init(&uhci->urb_list_lock); - INIT_LIST_HEAD(&uhci->urb_list); - - spin_lock_init(&uhci->complete_list_lock); - INIT_LIST_HEAD(&uhci->complete_list); - - spin_lock_init(&uhci->frame_list_lock); - - /* We need exactly one page (per UHCI specs), how convenient */ - /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */ -#if PAGE_SIZE < (4 * 1024) -#error PAGE_SIZE is not atleast 4k -#endif - uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle); - if (!uhci->fl) { - err("unable to allocate consistent memory for frame list"); - goto err_alloc_fl; - } - - memset((void *)uhci->fl, 0, sizeof(*uhci->fl)); - - uhci->fl->dma_handle = dma_handle; - - uhci->td_pool = pci_pool_create("uhci_td", uhci->dev, - sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC); - if (!uhci->td_pool) { - err("unable to create td pci_pool"); - goto err_create_td_pool; - } - - uhci->qh_pool = pci_pool_create("uhci_qh", uhci->dev, - sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC); - if (!uhci->qh_pool) { - err("unable to create qh pci_pool"); - goto err_create_qh_pool; - } - - bus = usb_alloc_bus(&uhci_device_operations); - if (!bus) { - err("unable to allocate bus"); - goto err_alloc_bus; - } - - uhci->bus = bus; - bus->bus_name = dev->slot_name; - bus->hcpriv = uhci; - - usb_register_bus(uhci->bus); - - /* Initialize the root hub */ - - /* UHCI specs says devices must have 2 ports, but goes on to say */ - /* they may have more but give no way to determine how many they */ - /* have. However, according to the UHCI spec, Bit 7 is always set */ - /* to 1. So we try to use this to our advantage */ - for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) { - unsigned int portstatus; - - portstatus = inw(uhci->io_addr + 0x10 + (port * 2)); - if (!(portstatus & 0x0080)) - break; - } - if (debug) - info("detected %d ports", port); - - /* This is experimental so anything less than 2 or greater than 8 is */ - /* something weird and we'll ignore it */ - if (port < 2 || port > 8) { - info("port count misdetected? forcing to 2 ports"); - port = 2; - } - - uhci->rh.numports = port; - - uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus); - if (!uhci->rh.dev) { - err("unable to allocate root hub"); - goto err_alloc_root_hub; - } - - uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh.dev); - if (!uhci->skeltd[0]) { - err("unable to allocate TD 0"); - goto err_alloc_skeltd; - } - - /* - * 9 Interrupt queues; link int2 to int1, int4 to int2, etc - * then link int1 to control and control to bulk - */ - for (i = 1; i < 9; i++) { - struct uhci_td *td; - - td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh.dev); - if (!td) { - err("unable to allocate TD %d", i); - goto err_alloc_skeltd; - } - - uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); - td->link = uhci->skeltd[i - 1]->dma_handle; - } - - uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh.dev); - if (!uhci->skel_term_td) { - err("unable to allocate skel TD term"); - goto err_alloc_skeltd; - } - - for (i = 0; i < UHCI_NUM_SKELQH; i++) { - uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh.dev); - if (!uhci->skelqh[i]) { - err("unable to allocate QH %d", i); - goto err_alloc_skelqh; - } - } - - uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); - uhci->skel_int1_td->link = uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH; - - uhci->skel_ls_control_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH; - uhci->skel_ls_control_qh->element = UHCI_PTR_TERM; - - uhci->skel_hs_control_qh->link = uhci->skel_bulk_qh->dma_handle | UHCI_PTR_QH; - uhci->skel_hs_control_qh->element = UHCI_PTR_TERM; - - uhci->skel_bulk_qh->link = uhci->skel_term_qh->dma_handle | UHCI_PTR_QH; - uhci->skel_bulk_qh->element = UHCI_PTR_TERM; - - /* This dummy TD is to work around a bug in Intel PIIX controllers */ - uhci_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); - uhci->skel_term_td->link = uhci->skel_term_td->dma_handle; - - uhci->skel_term_qh->link = UHCI_PTR_TERM; - uhci->skel_term_qh->element = uhci->skel_term_td->dma_handle; - - /* - * Fill the frame list: make all entries point to - * the proper interrupt queue. - * - * This is probably silly, but it's a simple way to - * scatter the interrupt queues in a way that gives - * us a reasonable dynamic range for irq latencies. - */ - for (i = 0; i < UHCI_NUMFRAMES; i++) { - int irq = 0; - - if (i & 1) { - irq++; - if (i & 2) { - irq++; - if (i & 4) { - irq++; - if (i & 8) { - irq++; - if (i & 16) { - irq++; - if (i & 32) { - irq++; - if (i & 64) - irq++; - } - } - } - } - } - } - - /* Only place we don't use the frame list routines */ - uhci->fl->frame[i] = uhci->skeltd[irq]->dma_handle; - } - - start_hc(uhci); - - if (request_irq(dev->irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci)) - goto err_request_irq; - - /* disable legacy emulation */ - pci_write_config_word(uhci->dev, USBLEGSUP, USBLEGSUP_DEFAULT); - - usb_connect(uhci->rh.dev); - - if (usb_new_device(uhci->rh.dev) != 0) { - err("unable to start root hub"); - retval = -ENOMEM; - goto err_start_root_hub; - } - - return 0; - -/* - * error exits: - */ -err_start_root_hub: - free_irq(uhci->irq, uhci); - uhci->irq = -1; - -err_request_irq: - for (i = 0; i < UHCI_NUM_SKELQH; i++) - if (uhci->skelqh[i]) { - uhci_free_qh(uhci, uhci->skelqh[i]); - uhci->skelqh[i] = NULL; - } - -err_alloc_skelqh: - for (i = 0; i < UHCI_NUM_SKELTD; i++) - if (uhci->skeltd[i]) { - uhci_free_td(uhci, uhci->skeltd[i]); - uhci->skeltd[i] = NULL; - } - -err_alloc_skeltd: - usb_free_dev(uhci->rh.dev); - uhci->rh.dev = NULL; - -err_alloc_root_hub: - usb_free_bus(uhci->bus); - uhci->bus = NULL; - -err_alloc_bus: - pci_pool_destroy(uhci->qh_pool); - uhci->qh_pool = NULL; - -err_create_qh_pool: - pci_pool_destroy(uhci->td_pool); - uhci->td_pool = NULL; - -err_create_td_pool: - pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle); - uhci->fl = NULL; - -err_alloc_fl: -#ifdef CONFIG_PROC_FS - remove_proc_entry(buf, uhci_proc_root); - uhci->proc_entry = NULL; - -err_create_proc_entry: - free_uhci(uhci); -#endif - -err_alloc_uhci: - -err_pci_set_dma_mask: - release_region(io_addr, io_size); - -err_request_region: - -err_pci_dma_supported: - -err_invalid_irq: - -err_enable_device: - - return retval; -} - -static int __devinit uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int i; - - /* Search for the IO base address.. */ - for (i = 0; i < 6; i++) { - unsigned int io_addr = pci_resource_start(dev, i); - unsigned int io_size = pci_resource_len(dev, i); - - /* IO address? */ - if (!(pci_resource_flags(dev, i) & IORESOURCE_IO)) - continue; - - return alloc_uhci(dev, io_addr, io_size); - } - - return -ENODEV; -} - -static void __devexit uhci_pci_remove(struct pci_dev *dev) -{ - struct uhci *uhci = pci_get_drvdata(dev); - - if (uhci->bus->root_hub) - usb_disconnect(&uhci->bus->root_hub); - - usb_deregister_bus(uhci->bus); - - /* - * At this point, we're guaranteed that no new connects can be made - * to this bus since there are no more parents - */ - uhci_free_pending_qhs(uhci); - uhci_remove_pending_qhs(uhci); - - reset_hc(uhci); - release_region(uhci->io_addr, uhci->io_size); - - uhci_free_pending_qhs(uhci); - - release_uhci(uhci); -} - -#ifdef CONFIG_PM -static int uhci_pci_suspend(struct pci_dev *dev, u32 state) -{ - suspend_hc((struct uhci *) pci_get_drvdata(dev)); - return 0; -} - -static int uhci_pci_resume(struct pci_dev *dev) -{ - reset_hc((struct uhci *) pci_get_drvdata(dev)); - start_hc((struct uhci *) pci_get_drvdata(dev)); - return 0; -} -#endif - -static const struct pci_device_id __devinitdata uhci_pci_ids[] = { { - - /* handle any USB UHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x00), - class_mask: ~0, - - /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - - }, { /* end: all zeroes */ } -}; - -MODULE_DEVICE_TABLE(pci, uhci_pci_ids); - -static struct pci_driver uhci_pci_driver = { - name: "usb-uhci", - id_table: uhci_pci_ids, - - probe: uhci_pci_probe, - remove: __devexit_p(uhci_pci_remove), - -#ifdef CONFIG_PM - suspend: uhci_pci_suspend, - resume: uhci_pci_resume, -#endif /* PM */ -}; - - -static int __init uhci_hcd_init(void) -{ - int retval = -ENOMEM; - - info(DRIVER_DESC " " DRIVER_VERSION); - - if (debug) { - errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); - if (!errbuf) - goto errbuf_failed; - } - -#ifdef CONFIG_PROC_FS - uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, 0); - if (!uhci_proc_root) - goto proc_failed; -#endif - - uhci_up_cachep = kmem_cache_create("uhci_urb_priv", - sizeof(struct urb_priv), 0, 0, NULL, NULL); - if (!uhci_up_cachep) - goto up_failed; - - retval = pci_module_init(&uhci_pci_driver); - if (retval) - goto init_failed; - - return 0; - -init_failed: - if (kmem_cache_destroy(uhci_up_cachep)) - printk(KERN_INFO "uhci: not all urb_priv's were freed\n"); - -up_failed: - -#ifdef CONFIG_PROC_FS - remove_proc_entry("driver/uhci", 0); - -proc_failed: -#endif - if (errbuf) - kfree(errbuf); - -errbuf_failed: - - return retval; -} - -static void __exit uhci_hcd_cleanup(void) -{ - pci_unregister_driver(&uhci_pci_driver); - - if (kmem_cache_destroy(uhci_up_cachep)) - printk(KERN_INFO "uhci: not all urb_priv's were freed\n"); - -#ifdef CONFIG_PROC_FS - remove_proc_entry("driver/uhci", 0); -#endif - - if (errbuf) - kfree(errbuf); -} - -module_init(uhci_hcd_init); -module_exit(uhci_hcd_cleanup); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - diff -Nru a/drivers/usb/uhci.h b/drivers/usb/uhci.h --- a/drivers/usb/uhci.h Thu Mar 6 14:22:16 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,441 +0,0 @@ -#ifndef __LINUX_UHCI_H -#define __LINUX_UHCI_H - -#include -#include - -/* - * Universal Host Controller Interface data structures and defines - */ - -/* Command register */ -#define USBCMD 0 -#define USBCMD_RS 0x0001 /* Run/Stop */ -#define USBCMD_HCRESET 0x0002 /* Host reset */ -#define USBCMD_GRESET 0x0004 /* Global reset */ -#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ -#define USBCMD_FGR 0x0010 /* Force Global Resume */ -#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ -#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ -#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ - -/* Status register */ -#define USBSTS 2 -#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ -#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ -#define USBSTS_RD 0x0004 /* Resume Detect */ -#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ -#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ -#define USBSTS_HCH 0x0020 /* HC Halted */ - -/* Interrupt enable register */ -#define USBINTR 4 -#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ -#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ -#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ -#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ - -#define USBFRNUM 6 -#define USBFLBASEADD 8 -#define USBSOF 12 - -/* USB port status and control registers */ -#define USBPORTSC1 16 -#define USBPORTSC2 18 -#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ -#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ -#define USBPORTSC_PE 0x0004 /* Port Enable */ -#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ -#define USBPORTSC_LS 0x0030 /* Line Status */ -#define USBPORTSC_RD 0x0040 /* Resume Detect */ -#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ -#define USBPORTSC_PR 0x0200 /* Port Reset */ -#define USBPORTSC_SUSP 0x1000 /* Suspend */ - -/* Legacy support register */ -#define USBLEGSUP 0xc0 -#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ - -#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ - -#define UHCI_PTR_BITS 0x000F -#define UHCI_PTR_TERM 0x0001 -#define UHCI_PTR_QH 0x0002 -#define UHCI_PTR_DEPTH 0x0004 - -#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ -#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ -#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ - -struct uhci_frame_list { - __u32 frame[UHCI_NUMFRAMES]; - - void *frame_cpu[UHCI_NUMFRAMES]; - - dma_addr_t dma_handle; -}; - -struct urb_priv; - -struct uhci_qh { - /* Hardware fields */ - __u32 link; /* Next queue */ - __u32 element; /* Queue element pointer */ - - /* Software fields */ - dma_addr_t dma_handle; - - struct usb_device *dev; - struct urb_priv *urbp; - - struct list_head list; /* P: uhci->frame_list_lock */ - struct list_head remove_list; /* P: uhci->remove_list_lock */ -} __attribute__((aligned(16))); - -/* - * for TD : - */ -#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ -#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ -#define TD_CTRL_C_ERR_SHIFT 27 -#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ -#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ -#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ -#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ -#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ -#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ -#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ -#define TD_CTRL_NAK (1 << 19) /* NAK Received */ -#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ -#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ -#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ - -#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ - TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) - -#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) -#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ - -/* - * for TD : (a.k.a. Token) - */ -#define TD_TOKEN_TOGGLE_SHIFT 19 -#define TD_TOKEN_TOGGLE (1 << 19) -#define TD_TOKEN_PID_MASK 0xFF -#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */ - -#define uhci_maxlen(token) ((token) >> 21) -#define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */ -#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1) -#define uhci_endpoint(token) (((token) >> 15) & 0xf) -#define uhci_devaddr(token) (((token) >> 8) & 0x7f) -#define uhci_devep(token) (((token) >> 8) & 0x7ff) -#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK) -#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) -#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) - -/* - * The documentation says "4 words for hardware, 4 words for software". - * - * That's silly, the hardware doesn't care. The hardware only cares that - * the hardware words are 16-byte aligned, and we can have any amount of - * sw space after the TD entry as far as I can tell. - * - * But let's just go with the documentation, at least for 32-bit machines. - * On 64-bit machines we probably want to take advantage of the fact that - * hw doesn't really care about the size of the sw-only area. - * - * Alas, not anymore, we have more than 4 words for software, woops. - * Everything still works tho, surprise! -jerdfelt - */ -struct uhci_td { - /* Hardware fields */ - __u32 link; - __u32 status; - __u32 info; - __u32 buffer; - - /* Software fields */ - dma_addr_t dma_handle; - - struct usb_device *dev; - struct urb *urb; - - struct list_head list; /* P: urb->lock */ - - int frame; - struct list_head fl_list; /* P: uhci->frame_list_lock */ -} __attribute__((aligned(16))); - -/* - * There are various standard queues. We set up several different - * queues for each of the three basic queue types: interrupt, - * control, and bulk. - * - * - There are various different interrupt latencies: ranging from - * every other USB frame (2 ms apart) to every 256 USB frames (ie - * 256 ms apart). Make your choice according to how obnoxious you - * want to be on the wire, vs how critical latency is for you. - * - The control list is done every frame. - * - There are 4 bulk lists, so that up to four devices can have a - * bulk list of their own and when run concurrently all four lists - * will be be serviced. - * - * This is a bit misleading, there are various interrupt latencies, but they - * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the - * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor - * problem - * - * In the case of the root hub, these QH's are just head's of qh's. Don't - * be scared, it kinda makes sense. Look at this wonderful picture care of - * Linus: - * - * generic- -> dev1- -> generic- -> dev1- -> control- -> bulk- -> ... - * iso-QH iso-QH irq-QH irq-QH QH QH - * | | | | | | - * End dev1-iso-TD1 End dev1-irq-TD1 ... ... - * | - * dev1-iso-TD2 - * | - * .... - * - * This may vary a bit (the UHCI docs don't explicitly say you can put iso - * transfers in QH's and all of their pictures don't have that either) but - * other than that, that is what we're doing now - * - * And now we don't put Iso transfers in QH's, so we don't waste one on it - * --jerdfelt - * - * To keep with Linus' nomenclature, this is called the QH skeleton. These - * labels (below) are only signficant to the root hub's QH's - */ - -#define UHCI_NUM_SKELTD 10 -#define skel_int1_td skeltd[0] -#define skel_int2_td skeltd[1] -#define skel_int4_td skeltd[2] -#define skel_int8_td skeltd[3] -#define skel_int16_td skeltd[4] -#define skel_int32_td skeltd[5] -#define skel_int64_td skeltd[6] -#define skel_int128_td skeltd[7] -#define skel_int256_td skeltd[8] -#define skel_term_td skeltd[9] /* To work around PIIX UHCI bug */ - -#define UHCI_NUM_SKELQH 4 -#define skel_ls_control_qh skelqh[0] -#define skel_hs_control_qh skelqh[1] -#define skel_bulk_qh skelqh[2] -#define skel_term_qh skelqh[3] - -/* - * Search tree for determining where fits in the - * skelqh[] skeleton. - * - * An interrupt request should be placed into the slowest skelqh[] - * which meets the interval/period/frequency requirement. - * An interrupt request is allowed to be faster than but not slower. - * - * For a given , this function returns the appropriate/matching - * skelqh[] index value. - * - * NOTE: For UHCI, we don't really need int256_qh since the maximum interval - * is 255 ms. However, we do need an int1_qh since 1 is a valid interval - * and we should meet that frequency when requested to do so. - * This will require some change(s) to the UHCI skeleton. - */ -static inline int __interval_to_skel(int interval) -{ - if (interval < 16) { - if (interval < 4) { - if (interval < 2) - return 0; /* int1 for 0-1 ms */ - return 1; /* int2 for 2-3 ms */ - } - if (interval < 8) - return 2; /* int4 for 4-7 ms */ - return 3; /* int8 for 8-15 ms */ - } - if (interval < 64) { - if (interval < 32) - return 4; /* int16 for 16-31 ms */ - return 5; /* int32 for 32-63 ms */ - } - if (interval < 128) - return 6; /* int64 for 64-127 ms */ - return 7; /* int128 for 128-255 ms (Max.) */ -} - -struct virt_root_hub { - struct usb_device *dev; - int devnum; /* Address of Root Hub endpoint */ - struct urb *urb; - void *int_addr; - int send; - int interval; - int numports; - int c_p_r[8]; - struct timer_list rh_int_timer; -}; - -/* - * This describes the full uhci information. - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. - */ -struct uhci { - struct pci_dev *dev; - -#ifdef CONFIG_PROC_FS - /* procfs */ - int num; - struct proc_dir_entry *proc_entry; -#endif - - /* Grabbed from PCI */ - int irq; - unsigned int io_addr; - unsigned int io_size; - - struct pci_pool *qh_pool; - struct pci_pool *td_pool; - - struct usb_bus *bus; - - struct uhci_td *skeltd[UHCI_NUM_SKELTD]; /* Skeleton TD's */ - struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ - - spinlock_t frame_list_lock; - struct uhci_frame_list *fl; /* P: uhci->frame_list_lock */ - int fsbr; /* Full speed bandwidth reclamation */ - unsigned long fsbrtimeout; /* FSBR delay */ - int is_suspended; - - /* Main list of URB's currently controlled by this HC */ - spinlock_t urb_list_lock; - struct list_head urb_list; /* P: uhci->urb_list_lock */ - - /* List of QH's that are done, but waiting to be unlinked (race) */ - spinlock_t qh_remove_list_lock; - struct list_head qh_remove_list; /* P: uhci->qh_remove_list_lock */ - - /* List of asynchronously unlinked URB's */ - spinlock_t urb_remove_list_lock; - struct list_head urb_remove_list; /* P: uhci->urb_remove_list_lock */ - - /* List of URB's awaiting completion callback */ - spinlock_t complete_list_lock; - struct list_head complete_list; /* P: uhci->complete_list_lock */ - - struct virt_root_hub rh; /* private data of the virtual root hub */ -}; - -struct urb_priv { - struct urb *urb; - struct usb_device *dev; - - dma_addr_t setup_packet_dma_handle; - dma_addr_t transfer_buffer_dma_handle; - - struct uhci_qh *qh; /* QH for this URB */ - struct list_head td_list; /* P: urb->lock */ - - int fsbr : 1; /* URB turned on FSBR */ - int fsbr_timeout : 1; /* URB timed out on FSBR */ - int queued : 1; /* QH was queued (not linked in) */ - int short_control_packet : 1; /* If we get a short packet during */ - /* a control transfer, retrigger */ - /* the status phase */ - - int status; /* Final status */ - - unsigned long inserttime; /* In jiffies */ - unsigned long fsbrtime; /* In jiffies */ - - struct list_head queue_list; /* P: uhci->frame_list_lock */ - struct list_head complete_list; /* P: uhci->complete_list_lock */ -}; - -/* - * Locking in uhci.c - * - * spinlocks are used extensively to protect the many lists and data - * structures we have. It's not that pretty, but it's necessary. We - * need to be done with all of the locks (except complete_list_lock) when - * we call urb->complete. I've tried to make it simple enough so I don't - * have to spend hours racking my brain trying to figure out if the - * locking is safe. - * - * Here's the safe locking order to prevent deadlocks: - * - * #1 uhci->urb_list_lock - * #2 urb->lock - * #3 uhci->urb_remove_list_lock, uhci->frame_list_lock, - * uhci->qh_remove_list_lock - * #4 uhci->complete_list_lock - * - * If you're going to grab 2 or more locks at once, ALWAYS grab the lock - * at the lowest level FIRST and NEVER grab locks at the same level at the - * same time. - * - * So, if you need uhci->urb_list_lock, grab it before you grab urb->lock - */ - -/* ------------------------------------------------------------------------- - Virtual Root HUB - ------------------------------------------------------------------------- */ -/* destination of request */ -#define RH_DEVICE 0x00 -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -/* Our Vendor Specific feature */ -#define RH_REMOVE_EP 0x00 - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -#endif - diff -Nru a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h --- a/drivers/usb/usb-uhci-debug.h Thu Mar 6 14:22:16 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,195 +0,0 @@ -#ifdef DEBUG -static void __attribute__((__unused__)) uhci_show_qh (puhci_desc_t qh) -{ - if (qh->type != QH_TYPE) { - dbg("qh has not QH_TYPE"); - return; - } - dbg("QH @ %p/%08llX:", qh, (unsigned long long)qh->dma_addr); - - if (qh->hw.qh.head & UHCI_PTR_TERM) - dbg(" Head Terminate"); - else - dbg(" Head: %s @ %08X", - (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"), - qh->hw.qh.head & ~UHCI_PTR_BITS); - - if (qh->hw.qh.element & UHCI_PTR_TERM) - dbg(" Element Terminate"); - else - dbg(" Element: %s @ %08X", - (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"), - qh->hw.qh.element & ~UHCI_PTR_BITS); -} -#endif - -#if 0 -static void uhci_show_td (puhci_desc_t td) -{ - char *spid; - - switch (td->hw.td.info & 0xff) { - case USB_PID_SETUP: - spid = "SETUP"; - break; - case USB_PID_OUT: - spid = " OUT "; - break; - case USB_PID_IN: - spid = " IN "; - break; - default: - spid = " ? "; - break; - } - - warn(" TD @ %p/%08X, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x", - td, td->dma_addr, - td->hw.td.info >> 21, - ((td->hw.td.info >> 19) & 1), - (td->hw.td.info >> 15) & 15, - (td->hw.td.info >> 8) & 127, - spid, - td->hw.td.buffer); - - warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", - td->hw.td.status & 0x7ff, - ((td->hw.td.status >> 27) & 3), - (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", - (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", - (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", - (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", - (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", - (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", - (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", - (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", - (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", - (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" - ); - - if (td->hw.td.link & UHCI_PTR_TERM) - warn(" TD Link Terminate"); - else - warn(" Link points to %s @ %08x, %s", - (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"), - td->hw.td.link & ~UHCI_PTR_BITS, - (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first")); -} -#endif - -#ifdef DEBUG -static void __attribute__((__unused__)) uhci_show_td_queue (puhci_desc_t td) -{ - //dbg("uhci_show_td_queue %p (%08lX):", td, td->dma_addr); -#if 1 - return; -#else - while (1) { - uhci_show_td (td); - if (td->hw.td.link & UHCI_PTR_TERM) - break; - if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) - td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); - else { - dbg("td points to itself!"); - break; - } - } -#endif -} - -static void __attribute__((__unused__)) uhci_show_queue (puhci_desc_t qh) -{ -#if 0 - uhci_desc_t *start_qh=qh; -#endif - - dbg("uhci_show_queue %p:", qh); -#if 1 - return; -#else - while (1) { - uhci_show_qh (qh); - - if (!(qh->hw.qh.element & UHCI_PTR_TERM)) - uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); - - if (qh->hw.qh.head & UHCI_PTR_TERM) - break; - - if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) - qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); - else { - dbg("qh points to itself!"); - break; - } - - if (qh==start_qh) { // avoid loop - dbg("Loop detect"); - break; - } - } -#endif -} - -static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short status) -{ - dbg(" stat%d = %04x %s%s%s%s%s%s%s%s", - port, - status, - (status & USBPORTSC_SUSP) ? "PortSuspend " : "", - (status & USBPORTSC_PR) ? "PortReset " : "", - (status & USBPORTSC_LSDA) ? "LowSpeed " : "", - (status & USBPORTSC_RD) ? "ResumeDetect " : "", - (status & USBPORTSC_PEC) ? "EnableChange " : "", - (status & USBPORTSC_PE) ? "PortEnabled " : "", - (status & USBPORTSC_CSC) ? "ConnectChange " : "", - (status & USBPORTSC_CCS) ? "PortConnected " : ""); -} - -void uhci_show_status (puhci_t s) -{ - unsigned int io_addr = s->io_addr; - unsigned short usbcmd, usbstat, usbint, usbfrnum; - unsigned int flbaseadd; - unsigned char sof; - unsigned short portsc1, portsc2; - - usbcmd = inw (io_addr + 0); - usbstat = inw (io_addr + 2); - usbint = inw (io_addr + 4); - usbfrnum = inw (io_addr + 6); - flbaseadd = inl (io_addr + 8); - sof = inb (io_addr + 12); - portsc1 = inw (io_addr + 16); - portsc2 = inw (io_addr + 18); - - dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s", - usbcmd, - (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", - (usbcmd & USBCMD_CF) ? "CF " : "", - (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", - (usbcmd & USBCMD_FGR) ? "FGR " : "", - (usbcmd & USBCMD_EGSM) ? "EGSM " : "", - (usbcmd & USBCMD_GRESET) ? "GRESET " : "", - (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", - (usbcmd & USBCMD_RS) ? "RS " : ""); - - dbg(" usbstat = %04x %s%s%s%s%s%s", - usbstat, - (usbstat & USBSTS_HCH) ? "HCHalted " : "", - (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", - (usbstat & USBSTS_HSE) ? "HostSystemError " : "", - (usbstat & USBSTS_RD) ? "ResumeDetect " : "", - (usbstat & USBSTS_ERROR) ? "USBError " : "", - (usbstat & USBSTS_USBINT) ? "USBINT " : ""); - - dbg(" usbint = %04x", usbint); - dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1, - 0xfff & (4 * (unsigned int) usbfrnum)); - dbg(" flbaseadd = %08x", flbaseadd); - dbg(" sof = %02x", sof); - uhci_show_sc (1, portsc1); - uhci_show_sc (2, portsc2); -} -#endif diff -Nru a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c --- a/drivers/usb/usb-uhci.c Thu Mar 6 14:22:16 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3143 +0,0 @@ -/* - * Universal Host Controller Interface driver for USB (take II). - * - * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar) - * Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) - * Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) - * Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter) - * (c) 2000 Yggdrasil Computing, Inc. (port of new PCI interface support - * from usb-ohci.c by Adam Richter, adam@yggdrasil.com). - * (C) 2000 David Brownell, david-b@pacbell.net (usb-ohci.c) - * - * HW-initalization based on material of - * - * (C) Copyright 1999 Linus Torvalds - * (C) Copyright 1999 Johannes Erdfelt - * (C) Copyright 1999 Randy Dunlap - * (C) Copyright 1999 Gregory P. Smith - * - * $Id: usb-uhci.c,v 1.275 2002/01/19 20:57:33 acher Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for in_interrupt() */ -#include -#include -#include -#include - -#include -#include -#include -#include - -/* This enables more detailed sanity checks in submit_iso */ -//#define ISO_SANITY_CHECK - -/* This enables debug printks */ -#define DEBUG - -/* This enables all symbols to be exported, to ease debugging oopses */ -//#define DEBUG_SYMBOLS - -/* This enables an extra UHCI slab for memory debugging */ -#define DEBUG_SLAB - -#define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__ - -#include -#include "usb-uhci.h" -#include "usb-uhci-debug.h" - -#include "hcd.h" - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.275" -#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber" -#define DRIVER_DESC "USB Universal Host Controller Interface driver" - -#undef DEBUG -#undef dbg -#define dbg(format, arg...) do {} while (0) -#define DEBUG_SYMBOLS -#ifdef DEBUG_SYMBOLS - #define _static - #ifndef EXPORT_SYMTAB - #define EXPORT_SYMTAB - #endif -#else - #define _static static -#endif - -#define queue_dbg dbg //err -#define async_dbg dbg //err - -#ifdef DEBUG_SLAB - static kmem_cache_t *urb_priv_kmem; -#endif - -#define SLAB_FLAG (in_interrupt () || current->state != TASK_RUNNING ? SLAB_ATOMIC : SLAB_KERNEL) -#define KMALLOC_FLAG (in_interrupt () || current->state != TASK_RUNNING ? GFP_ATOMIC : GFP_KERNEL) - -/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth - * Reclamation: feature that puts loop on descriptor loop when - * there's some transfer going on. With FSBR, USB performance - * is optimal, but PCI can be slowed down up-to 5 times, slowing down - * system performance (eg. framebuffer devices). - */ -#define CONFIG_USB_UHCI_HIGH_BANDWIDTH - -/* *_DEPTH_FIRST puts descriptor in depth-first mode. This has - * somehow similar effect to FSBR (higher speed), but does not - * slow PCI down. OTOH USB performace is slightly slower than - * in FSBR case and single device could hog whole USB, starving - * other devices. - */ -#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first -#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first - -/* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST - * will lead to <64KB/sec performance over USB for bulk transfers targeting - * one device's endpoint. You probably do not want to do that. - */ - -// stop bandwidth reclamation after (roughly) 50ms -#define IDLE_TIMEOUT (HZ/20) - -// Suppress HC interrupt error messages for 5s -#define ERROR_SUPPRESSION_TIME (HZ*5) - -_static int rh_submit_urb (struct urb *urb); -_static int rh_unlink_urb (struct urb *urb); -_static int delete_qh (uhci_t *s, uhci_desc_t *qh); -_static int process_transfer (uhci_t *s, struct urb *urb, int mode); -_static int process_interrupt (uhci_t *s, struct urb *urb); -_static int process_iso (uhci_t *s, struct urb *urb, int force); - -// How much URBs with ->next are walked -#define MAX_NEXT_COUNT 2048 - -static uhci_t *devs = NULL; - -/* used by userspace UHCI data structure dumper */ -uhci_t **uhci_devices = &devs; - -/*-------------------------------------------------------------------*/ -// Cleans up collected QHs, but not more than 100 in one go -void clean_descs(uhci_t *s, int force) -{ - struct list_head *q; - uhci_desc_t *qh; - int now=UHCI_GET_CURRENT_FRAME(s), n=0; - - q=s->free_desc.prev; - - while (q != &s->free_desc && (force || n<100)) { - qh = list_entry (q, uhci_desc_t, horizontal); - q=qh->horizontal.prev; - - if ((qh->last_used!=now) || force) - delete_qh(s,qh); - n++; - } -} -/*-------------------------------------------------------------------*/ -_static void uhci_switch_timer_int(uhci_t *s) -{ - - if (!list_empty(&s->urb_unlinked)) - set_td_ioc(s->td1ms); - else - clr_td_ioc(s->td1ms); - - if (s->timeout_urbs) - set_td_ioc(s->td32ms); - else - clr_td_ioc(s->td32ms); - wmb(); -} -/*-------------------------------------------------------------------*/ -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH -_static void enable_desc_loop(uhci_t *s, struct urb *urb) -{ - unsigned long flags; - - if (urb->transfer_flags & USB_NO_FSBR) - return; - - spin_lock_irqsave (&s->qh_lock, flags); - s->chain_end->hw.qh.head&=cpu_to_le32(~UHCI_PTR_TERM); - mb(); - s->loop_usage++; - ((urb_priv_t*)urb->hcpriv)->use_loop=1; - spin_unlock_irqrestore (&s->qh_lock, flags); -} -/*-------------------------------------------------------------------*/ -_static void disable_desc_loop(uhci_t *s, struct urb *urb) -{ - unsigned long flags; - - if (urb->transfer_flags & USB_NO_FSBR) - return; - - spin_lock_irqsave (&s->qh_lock, flags); - if (((urb_priv_t*)urb->hcpriv)->use_loop) { - s->loop_usage--; - - if (!s->loop_usage) { - s->chain_end->hw.qh.head|=cpu_to_le32(UHCI_PTR_TERM); - mb(); - } - ((urb_priv_t*)urb->hcpriv)->use_loop=0; - } - spin_unlock_irqrestore (&s->qh_lock, flags); -} -#endif -/*-------------------------------------------------------------------*/ -_static void queue_urb_unlocked (uhci_t *s, struct urb *urb) -{ - struct list_head *p=&urb->urb_list; -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH - { - int type; - type=usb_pipetype (urb->pipe); - - if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) - enable_desc_loop(s, urb); - } -#endif - urb->status = -EINPROGRESS; - ((urb_priv_t*)urb->hcpriv)->started=jiffies; - list_add (p, &s->urb_list); - if (urb->timeout) - s->timeout_urbs++; - uhci_switch_timer_int(s); -} -/*-------------------------------------------------------------------*/ -_static void queue_urb (uhci_t *s, struct urb *urb) -{ - unsigned long flags=0; - - spin_lock_irqsave (&s->urb_list_lock, flags); - queue_urb_unlocked(s,urb); - spin_unlock_irqrestore (&s->urb_list_lock, flags); -} -/*-------------------------------------------------------------------*/ -_static void dequeue_urb (uhci_t *s, struct urb *urb) -{ -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH - int type; - - type=usb_pipetype (urb->pipe); - - if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) - disable_desc_loop(s, urb); -#endif - - list_del (&urb->urb_list); - if (urb->timeout && s->timeout_urbs) - s->timeout_urbs--; - -} -/*-------------------------------------------------------------------*/ -_static int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags) -{ - dma_addr_t dma_handle; - - *new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); - if (!*new) - return -ENOMEM; - memset (*new, 0, sizeof (uhci_desc_t)); - (*new)->dma_addr = dma_handle; - set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS)); // last by default - (*new)->type = TD_TYPE; - mb(); - INIT_LIST_HEAD (&(*new)->vertical); - INIT_LIST_HEAD (&(*new)->horizontal); - - return 0; -} -/*-------------------------------------------------------------------*/ -// append a qh to td.link physically, the SW linkage is not affected -_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags) -{ - unsigned long xxx; - - spin_lock_irqsave (&s->td_lock, xxx); - - set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH); - - mb(); - spin_unlock_irqrestore (&s->td_lock, xxx); -} -/*-------------------------------------------------------------------*/ -/* insert td at last position in td-list of qh (vertical) */ -_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags) -{ - uhci_desc_t *prev; - unsigned long xxx; - - spin_lock_irqsave (&s->td_lock, xxx); - - list_add_tail (&new->vertical, &qh->vertical); - - prev = list_entry (new->vertical.prev, uhci_desc_t, vertical); - - if (qh == prev ) { - // virgin qh without any tds - set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM); - } - else { - // already tds inserted, implicitely remove TERM bit of prev - set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH)); - } - mb(); - spin_unlock_irqrestore (&s->td_lock, xxx); - - return 0; -} -/*-------------------------------------------------------------------*/ -/* insert new_td after td (horizontal) */ -_static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new) -{ - uhci_desc_t *next; - unsigned long flags; - - spin_lock_irqsave (&s->td_lock, flags); - - next = list_entry (td->horizontal.next, uhci_desc_t, horizontal); - list_add (&new->horizontal, &td->horizontal); - new->hw.td.link = td->hw.td.link; - set_td_link(td, new->dma_addr); - mb(); - spin_unlock_irqrestore (&s->td_lock, flags); - - return 0; -} -/*-------------------------------------------------------------------*/ -_static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink) -{ - uhci_desc_t *next, *prev; - int dir = 0; - unsigned long flags; - - spin_lock_irqsave (&s->td_lock, flags); - - next = list_entry (element->vertical.next, uhci_desc_t, vertical); - - if (next == element) { - dir = 1; - prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); - } - else - prev = list_entry (element->vertical.prev, uhci_desc_t, vertical); - - if (phys_unlink) { - // really remove HW linking - if (prev->type == TD_TYPE) - prev->hw.td.link = element->hw.td.link; - else - prev->hw.qh.element = element->hw.td.link; - } - - mb (); - - if (dir == 0) - list_del (&element->vertical); - else - list_del (&element->horizontal); - - spin_unlock_irqrestore (&s->td_lock, flags); - - return 0; -} - -/*-------------------------------------------------------------------*/ -_static int delete_desc (uhci_t *s, uhci_desc_t *element) -{ - pci_pool_free(s->desc_pool, element, element->dma_addr); - return 0; -} -/*-------------------------------------------------------------------*/ -// Allocates qh element -_static int alloc_qh (uhci_t *s, uhci_desc_t ** new) -{ - dma_addr_t dma_handle; - - *new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); - if (!*new) - return -ENOMEM; - memset (*new, 0, sizeof (uhci_desc_t)); - (*new)->dma_addr = dma_handle; - set_qh_head(*new, UHCI_PTR_TERM); - set_qh_element(*new, UHCI_PTR_TERM); - (*new)->type = QH_TYPE; - - mb(); - INIT_LIST_HEAD (&(*new)->horizontal); - INIT_LIST_HEAD (&(*new)->vertical); - - dbg("Allocated qh @ %p", *new); - - return 0; -} -/*-------------------------------------------------------------------*/ -// inserts new qh before/after the qh at pos -// flags: 0: insert before pos, 1: insert after pos (for low speed transfers) -_static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order) -{ - uhci_desc_t *old; - unsigned long flags; - - spin_lock_irqsave (&s->qh_lock, flags); - - if (!order) { - // (OLD) (POS) -> (OLD) (NEW) (POS) - old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal); - list_add_tail (&new->horizontal, &pos->horizontal); - set_qh_head(new, MAKE_QH_ADDR (pos)) ; - if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM))) - set_qh_head(old, MAKE_QH_ADDR (new)) ; - } - else { - // (POS) (OLD) -> (POS) (NEW) (OLD) - old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal); - list_add (&new->horizontal, &pos->horizontal); - set_qh_head(new, MAKE_QH_ADDR (old)); - set_qh_head(pos, MAKE_QH_ADDR (new)) ; - } - - mb (); - - spin_unlock_irqrestore (&s->qh_lock, flags); - - return 0; -} - -/*-------------------------------------------------------------------*/ -_static int unlink_qh (uhci_t *s, uhci_desc_t *element) -{ - uhci_desc_t *prev; - unsigned long flags; - - spin_lock_irqsave (&s->qh_lock, flags); - - prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); - prev->hw.qh.head = element->hw.qh.head; - - dbg("unlink qh %p, pqh %p, nxqh %p, to %08x", element, prev, - list_entry (element->horizontal.next, uhci_desc_t, horizontal),le32_to_cpu(element->hw.qh.head) &~15); - - list_del(&element->horizontal); - - mb (); - spin_unlock_irqrestore (&s->qh_lock, flags); - - return 0; -} -/*-------------------------------------------------------------------*/ -_static int delete_qh (uhci_t *s, uhci_desc_t *qh) -{ - uhci_desc_t *td; - struct list_head *p; - - list_del (&qh->horizontal); - - while ((p = qh->vertical.next) != &qh->vertical) { - td = list_entry (p, uhci_desc_t, vertical); - dbg("unlink td @ %p",td); - unlink_td (s, td, 0); // no physical unlink - delete_desc (s, td); - } - - delete_desc (s, qh); - - return 0; -} -/*-------------------------------------------------------------------*/ -_static void clean_td_chain (uhci_t *s, uhci_desc_t *td) -{ - struct list_head *p; - uhci_desc_t *td1; - - if (!td) - return; - - while ((p = td->horizontal.next) != &td->horizontal) { - td1 = list_entry (p, uhci_desc_t, horizontal); - delete_desc (s, td1); - } - - delete_desc (s, td); -} - -/*-------------------------------------------------------------------*/ -_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer) -{ - td->hw.td.status = cpu_to_le32(status); - td->hw.td.info = cpu_to_le32(info); - td->hw.td.buffer = cpu_to_le32(buffer); -} -/*-------------------------------------------------------------------*/ -// Removes ALL qhs in chain (paranoia!) -_static void cleanup_skel (uhci_t *s) -{ - unsigned int n; - uhci_desc_t *td; - - dbg("cleanup_skel"); - - clean_descs(s,1); - - - if (s->td32ms) { - - unlink_td(s,s->td32ms,1); - delete_desc(s, s->td32ms); - } - - for (n = 0; n < 8; n++) { - td = s->int_chain[n]; - clean_td_chain (s, td); - } - - if (s->iso_td) { - for (n = 0; n < 1024; n++) { - td = s->iso_td[n]; - clean_td_chain (s, td); - } - kfree (s->iso_td); - } - - if (s->framelist) - pci_free_consistent(s->uhci_pci, PAGE_SIZE, - s->framelist, s->framelist_dma); - - if (s->control_chain) { - // completed init_skel? - struct list_head *p; - uhci_desc_t *qh, *qh1; - - qh = s->control_chain; - while ((p = qh->horizontal.next) != &qh->horizontal) { - qh1 = list_entry (p, uhci_desc_t, horizontal); - delete_qh (s, qh1); - } - - delete_qh (s, qh); - } - else { - if (s->ls_control_chain) - delete_desc (s, s->ls_control_chain); - if (s->control_chain) - delete_desc (s, s->control_chain); - if (s->bulk_chain) - delete_desc (s, s->bulk_chain); - if (s->chain_end) - delete_desc (s, s->chain_end); - } - - if (s->desc_pool) { - pci_pool_destroy(s->desc_pool); - s->desc_pool = NULL; - } - - dbg("cleanup_skel finished"); -} -/*-------------------------------------------------------------------*/ -// allocates framelist and qh-skeletons -// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT) -_static int init_skel (uhci_t *s) -{ - int n, ret; - uhci_desc_t *qh, *td; - - dbg("init_skel"); - - s->framelist = pci_alloc_consistent(s->uhci_pci, PAGE_SIZE, - &s->framelist_dma); - - if (!s->framelist) - return -ENOMEM; - - memset (s->framelist, 0, 4096); - - dbg("creating descriptor pci_pool"); - - s->desc_pool = pci_pool_create("uhci_desc", s->uhci_pci, - sizeof(uhci_desc_t), 16, 0, - GFP_DMA | GFP_ATOMIC); - if (!s->desc_pool) - goto init_skel_cleanup; - - dbg("allocating iso desc pointer list"); - s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL); - - if (!s->iso_td) - goto init_skel_cleanup; - - s->ls_control_chain = NULL; - s->control_chain = NULL; - s->bulk_chain = NULL; - s->chain_end = NULL; - - dbg("allocating iso descs"); - for (n = 0; n < 1024; n++) { - // allocate skeleton iso/irq-tds - if (alloc_td (s, &td, 0)) - goto init_skel_cleanup; - - s->iso_td[n] = td; - s->framelist[n] = cpu_to_le32((__u32) td->dma_addr); - } - - dbg("allocating qh: chain_end"); - if (alloc_qh (s, &qh)) - goto init_skel_cleanup; - - s->chain_end = qh; - - if (alloc_td (s, &td, 0)) - goto init_skel_cleanup; - - fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand) - insert_td (s, qh, td, 0); - qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit - s->td1ms=td; - - dbg("allocating qh: bulk_chain"); - if (alloc_qh (s, &qh)) - goto init_skel_cleanup; - - insert_qh (s, s->chain_end, qh, 0); - s->bulk_chain = qh; - - dbg("allocating qh: control_chain"); - ret = alloc_qh (s, &qh); - if (ret) - goto init_skel_cleanup; - - insert_qh (s, s->bulk_chain, qh, 0); - s->control_chain = qh; - -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH - // disabled reclamation loop - set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM); -#endif - - dbg("allocating qh: ls_control_chain"); - if (alloc_qh (s, &qh)) - goto init_skel_cleanup; - - insert_qh (s, s->control_chain, qh, 0); - s->ls_control_chain = qh; - - for (n = 0; n < 8; n++) - s->int_chain[n] = 0; - - dbg("allocating skeleton INT-TDs"); - - for (n = 0; n < 8; n++) { - uhci_desc_t *td; - - if (alloc_td (s, &td, 0)) - goto init_skel_cleanup; - - s->int_chain[n] = td; - if (n == 0) { - set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH); - } - else { - set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr); - } - } - - dbg("Linking skeleton INT-TDs"); - - for (n = 0; n < 1024; n++) { - // link all iso-tds to the interrupt chains - int m, o; - dbg("framelist[%i]=%x",n,le32_to_cpu(s->framelist[n])); - if ((n&127)==127) - ((uhci_desc_t*) s->iso_td[n])->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr); - else - for (o = 1, m = 2; m <= 128; o++, m += m) - if ((n & (m - 1)) == ((m - 1) / 2)) - set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr); - } - - if (alloc_td (s, &td, 0)) - goto init_skel_cleanup; - - fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later) - s->td32ms=td; - - insert_td_horizontal (s, s->int_chain[5], td); - - mb(); - //uhci_show_queue(s->control_chain); - dbg("init_skel exit"); - return 0; - - init_skel_cleanup: - cleanup_skel (s); - return -ENOMEM; -} - -/*-------------------------------------------------------------------*/ -// LOW LEVEL STUFF -// assembles QHs und TDs for control, bulk and iso -/*-------------------------------------------------------------------*/ -_static int uhci_submit_control_urb (struct urb *urb) -{ - uhci_desc_t *qh, *td; - uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; - urb_priv_t *urb_priv = urb->hcpriv; - unsigned long destination, status; - int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - unsigned long len; - char *data; - int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method - - dbg("uhci_submit_control start"); - if (alloc_qh (s, &qh)) // alloc qh for this request - return -ENOMEM; - - if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) // get td for setup stage - { - delete_qh (s, qh); - return -ENOMEM; - } - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; - - /* 3 errors */ - status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | - (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); - - /* Build the TD for the control request, try forever, 8 bytes of data */ - fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma); - - insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh -#if 0 - { - char *sp=urb->setup_packet; - dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe, - sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]); - } - //uhci_show_td(td); -#endif - - len = urb->transfer_buffer_length; - data = urb->transfer_buffer; - - /* If direction is "send", change the frame from SETUP (0x2D) - to OUT (0xE1). Else change it from SETUP to IN (0x69). */ - - destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN); - - while (len > 0) { - int pktsze = len; - - if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) - goto fail_unmap_enomem; - - if (pktsze > maxsze) - pktsze = maxsze; - - destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1 - - // Status, pktsze bytes of data - fill_td (td, status, destination | ((pktsze - 1) << 21), - urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer)); - - insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue 'data stage'-td in qh - - data += pktsze; - len -= pktsze; - } - - /* Build the final TD for control status */ - /* It's only IN if the pipe is out AND we aren't expecting data */ - - destination &= ~UHCI_PID; - - if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0)) - destination |= USB_PID_IN; - else - destination |= USB_PID_OUT; - - destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ - - if (alloc_td (s, &td, UHCI_PTR_DEPTH)) - goto fail_unmap_enomem; - - status &=~TD_CTRL_SPD; - - /* no limit on errors on final packet , 0 bytes of data */ - fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21), - 0); - - insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue status td - - list_add (&qh->desc_list, &urb_priv->desc_list); - - queue_urb (s, urb); // queue before inserting in desc chain - - qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); - - //uhci_show_queue(qh); - /* Start it up... put low speed first */ - if (urb->pipe & TD_CTRL_LS) - insert_qh (s, s->control_chain, qh, 0); - else - insert_qh (s, s->bulk_chain, qh, 0); - - dbg("uhci_submit_control end"); - return 0; - -fail_unmap_enomem: - delete_qh(s, qh); - return -ENOMEM; -} -/*-------------------------------------------------------------------*/ -// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh) -// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock! - -_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb) -{ - uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; - urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL; - uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL; - unsigned long destination, status; - char *data; - unsigned int pipe = urb->pipe; - int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); - int info, len, last; - int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method - - if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) - return -EPIPE; - - queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i", - urb,bulk_urb,urb->pipe,urb->transfer_buffer_length); - - upriv = (urb_priv_t*)urb->hcpriv; - - if (!bulk_urb) { - if (alloc_qh (s, &qh)) // get qh for this request - return -ENOMEM; - - if (urb->transfer_flags & USB_QUEUE_BULK) { - if (alloc_qh(s, &nqh)) // placeholder for clean unlink - { - delete_desc (s, qh); - return -ENOMEM; - } - upriv->next_qh = nqh; - queue_dbg("new next qh %p",nqh); - } - } - else { - bpriv = (urb_priv_t*)bulk_urb->hcpriv; - qh = bpriv->bottom_qh; // re-use bottom qh and next qh - nqh = bpriv->next_qh; - upriv->next_qh=nqh; - upriv->prev_queued_urb=bulk_urb; - } - - if (urb->transfer_flags & USB_QUEUE_BULK) { - if (alloc_qh (s, &bqh)) // "bottom" QH - { - if (!bulk_urb) { - delete_desc(s, qh); - delete_desc(s, nqh); - } - return -ENOMEM; - } - set_qh_element(bqh, UHCI_PTR_TERM); - set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element - upriv->bottom_qh = bqh; - } - queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh); - - /* The "pipe" thing contains the destination in bits 8--18. */ - destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); - - /* 3 errors */ - status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | - ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); - - /* Build the TDs for the bulk request */ - len = urb->transfer_buffer_length; - data = urb->transfer_buffer; - - do { // TBD: Really allow zero-length packets? - int pktsze = len; - - if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) - { - delete_qh (s, qh); - return -ENOMEM; - } - - if (pktsze > maxsze) - pktsze = maxsze; - - // pktsze bytes of data - info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) | - (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); - - fill_td (td, status, info, - urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer)); - - data += pktsze; - len -= pktsze; - // Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet - last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET))); - - if (last) - set_td_ioc(td); // last one generates INT - - insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); - if (!first_td) - first_td=td; - usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); - - } while (!last); - - if (bulk_urb && bpriv) // everything went OK, link with old bulk URB - bpriv->next_queued_urb=urb; - - list_add (&qh->desc_list, &urb_priv->desc_list); - - if (urb->transfer_flags & USB_QUEUE_BULK) - append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first); - - queue_urb_unlocked (s, urb); - - if (urb->transfer_flags & USB_QUEUE_BULK) - set_qh_element(qh, first_td->dma_addr); - else - qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // arm QH - - if (!bulk_urb) { // new bulk queue - if (urb->transfer_flags & USB_QUEUE_BULK) { - spin_lock (&s->td_lock); // both QHs in one go - insert_qh (s, s->chain_end, qh, 0); // Main QH - insert_qh (s, s->chain_end, nqh, 0); // Helper QH - spin_unlock (&s->td_lock); - } - else - insert_qh (s, s->chain_end, qh, 0); - } - - //uhci_show_queue(s->bulk_chain); - //dbg("uhci_submit_bulk_urb: exit\n"); - return 0; -} -/*-------------------------------------------------------------------*/ -_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv) -{ - struct list_head *p; - uhci_desc_t *td; - - for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) { - td = list_entry (p, uhci_desc_t, desc_list); - unlink_td (s, td, 1); - } -} -/*-------------------------------------------------------------------*/ -_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv) -{ - struct list_head *p; - uhci_desc_t *td; - - while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { - td = list_entry (p, uhci_desc_t, desc_list); - list_del (p); - delete_desc (s, td); - } -} -/*-------------------------------------------------------------------*/ -/* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink) - CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark) - CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink) - looks a bit complicated because of all the bulk queueing goodies -*/ - -_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode) -{ - uhci_desc_t *bqh, *nqh, *prevqh, *prevtd; - int now; - urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; - - now=UHCI_GET_CURRENT_FRAME(s); - - bqh=priv->bottom_qh; - - if (!priv->next_queued_urb) { // no more appended bulk queues - - queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", urb, qh, bqh, priv->next_qh); - - if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) { // qh not top of the queue - unsigned long flags; - urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; - - spin_lock_irqsave (&s->qh_lock, flags); - prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); - prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical); - set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh - mb(); - queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd); - spin_unlock_irqrestore (&s->qh_lock, flags); - - ppriv->bottom_qh = priv->bottom_qh; - ppriv->next_queued_urb = NULL; - } - else { // queue is dead, qh is top of the queue - - if (mode != CLEAN_TRANSFER_DELETION_MARK) - unlink_qh(s, qh); // remove qh from horizontal chain - - if (bqh) { // remove remainings of bulk queue - nqh=priv->next_qh; - - if (mode != CLEAN_TRANSFER_DELETION_MARK) - unlink_qh(s, nqh); // remove nqh from horizontal chain - - if (mode != CLEAN_TRANSFER_NO_DELETION) { // add helper QHs to free desc list - nqh->last_used = bqh->last_used = now; - list_add_tail (&nqh->horizontal, &s->free_desc); - list_add_tail (&bqh->horizontal, &s->free_desc); - } - } - } - } - else { // there are queued urbs following - - queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p", - urb, priv->prev_queued_urb, priv->next_queued_urb, qh, bqh, priv->next_qh); - - if (mode != CLEAN_TRANSFER_DELETION_MARK) { // no work for cleanup at unlink-completion - struct urb *nurb; - unsigned long flags; - - nurb = priv->next_queued_urb; - spin_lock_irqsave (&s->qh_lock, flags); - - if (!priv->prev_queued_urb) { // top QH - - prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal); - set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); - list_del (&qh->horizontal); // remove this qh form horizontal chain - list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain - } - else { // intermediate QH - urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; - urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv; - uhci_desc_t * bnqh; - - bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list); - ppriv->bottom_qh = bnqh; - ppriv->next_queued_urb = nurb; - prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); - set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); - } - - mb(); - ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb; - spin_unlock_irqrestore (&s->qh_lock, flags); - } - } - - if (mode != CLEAN_TRANSFER_NO_DELETION) { - qh->last_used = now; - list_add_tail (&qh->horizontal, &s->free_desc); // mark qh for later deletion/kfree - } -} -/*-------------------------------------------------------------------*/ -// Release bandwidth for Interrupt or Isoc. transfers -_static void uhci_release_bandwidth(struct urb *urb) -{ - if (urb->bandwidth) { - switch (usb_pipetype(urb->pipe)) { - case PIPE_INTERRUPT: - usb_release_bandwidth (urb->dev, urb, 0); - break; - case PIPE_ISOCHRONOUS: - usb_release_bandwidth (urb->dev, urb, 1); - break; - default: - break; - } - } -} - -_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv) -{ - if (urb_priv->setup_packet_dma) - pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma, - sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); - - if (urb_priv->transfer_buffer_dma) - pci_dma_sync_single(s->uhci_pci, urb_priv->transfer_buffer_dma, - urb->transfer_buffer_length, - usb_pipein(urb->pipe) ? - PCI_DMA_FROMDEVICE : - PCI_DMA_TODEVICE); -} - -_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv) -{ - if (urb_priv->setup_packet_dma) { - pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma, - sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); - urb_priv->setup_packet_dma = 0; - } - if (urb_priv->transfer_buffer_dma) { - pci_unmap_single(s->uhci_pci, urb_priv->transfer_buffer_dma, - urb->transfer_buffer_length, - usb_pipein(urb->pipe) ? - PCI_DMA_FROMDEVICE : - PCI_DMA_TODEVICE); - urb_priv->transfer_buffer_dma = 0; - } -} -/*-------------------------------------------------------------------*/ -/* needs urb_list_lock! - mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list - UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list -*/ -_static int uhci_unlink_urb_async (uhci_t *s,struct urb *urb, int mode) -{ - uhci_desc_t *qh; - urb_priv_t *urb_priv; - - async_dbg("unlink_urb_async called %p",urb); - - if ((urb->status == -EINPROGRESS) || - ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags)) - { - ((urb_priv_t*)urb->hcpriv)->started = ~0; // mark - dequeue_urb (s, urb); - - if (mode==UNLINK_ASYNC_STORE_URB) - list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb - - uhci_switch_timer_int(s); - s->unlink_urb_done = 1; - uhci_release_bandwidth(urb); - - urb->status = -ECONNABORTED; // mark urb as "waiting to be killed" - urb_priv = (urb_priv_t*)urb->hcpriv; - - switch (usb_pipetype (urb->pipe)) { - case PIPE_INTERRUPT: - usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); - - case PIPE_ISOCHRONOUS: - uhci_clean_iso_step1 (s, urb_priv); - break; - - case PIPE_BULK: - case PIPE_CONTROL: - qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); - uhci_clean_transfer (s, urb, qh, CLEAN_TRANSFER_NO_DELETION); - break; - } - ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s); - return -EINPROGRESS; // completion will follow - } - - return 0; // URB already dead -} -/*-------------------------------------------------------------------*/ -// kills an urb by unlinking descriptors and waiting for at least one frame -_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb) -{ - uhci_desc_t *qh; - urb_priv_t *urb_priv; - unsigned long flags=0; - struct usb_device *usb_dev; - - spin_lock_irqsave (&s->urb_list_lock, flags); - - if (urb->status == -EINPROGRESS) { - - // move descriptors out of the running chains, dequeue urb - uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_DONT_STORE); - - urb_priv = urb->hcpriv; - urb->status = -ENOENT; // prevent from double deletion after unlock - spin_unlock_irqrestore (&s->urb_list_lock, flags); - - // cleanup the rest - switch (usb_pipetype (urb->pipe)) { - - case PIPE_INTERRUPT: - case PIPE_ISOCHRONOUS: - uhci_wait_ms(1); - uhci_clean_iso_step2(s, urb_priv); - break; - - case PIPE_BULK: - case PIPE_CONTROL: - qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); - uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK); - uhci_wait_ms(1); - } - urb->status = -ENOENT; // mark urb as killed - - uhci_urb_dma_unmap(s, urb, urb->hcpriv); - -#ifdef DEBUG_SLAB - kmem_cache_free (urb_priv_kmem, urb->hcpriv); -#else - kfree (urb->hcpriv); -#endif - usb_dev = urb->dev; - if (urb->complete) { - dbg("unlink_urb: calling completion"); - urb->dev = NULL; - urb->complete ((struct urb *) urb); - } - usb_dec_dev_use (usb_dev); - } - else - spin_unlock_irqrestore (&s->urb_list_lock, flags); - - return 0; -} -/*-------------------------------------------------------------------*/ -// async unlink_urb completion/cleanup work -// has to be protected by urb_list_lock! -// features: if set in transfer_flags, the resulting status of the killed -// transaction is not overwritten - -_static void uhci_cleanup_unlink(uhci_t *s, int force) -{ - struct list_head *q; - struct urb *urb; - struct usb_device *dev; - int now, type; - urb_priv_t *urb_priv; - - q=s->urb_unlinked.next; - now=UHCI_GET_CURRENT_FRAME(s); - - while (q != &s->urb_unlinked) { - - urb = list_entry (q, struct urb, urb_list); - - urb_priv = (urb_priv_t*)urb->hcpriv; - q = urb->urb_list.next; - - if (!urb_priv) // avoid crash when URB is corrupted - break; - - if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) { - async_dbg("async cleanup %p",urb); - type=usb_pipetype (urb->pipe); - - switch (type) { // process descriptors - case PIPE_CONTROL: - process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) - break; - case PIPE_BULK: - if (!s->avoid_bulk.counter) - process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) - else - continue; - break; - case PIPE_ISOCHRONOUS: - process_iso (s, urb, PROCESS_ISO_FORCE); // force, don't unlink - break; - case PIPE_INTERRUPT: - process_interrupt (s, urb); - break; - } - - if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) - urb->status = -ECONNRESET; // mark as asynchronously killed - - dev = urb->dev; // completion may destroy all... - urb_priv = urb->hcpriv; - list_del (&urb->urb_list); - - uhci_urb_dma_sync(s, urb, urb_priv); - if (urb->complete) { - spin_unlock(&s->urb_list_lock); - urb->dev = NULL; - urb->complete ((struct urb *) urb); - spin_lock(&s->urb_list_lock); - } - - if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) - urb->status = -ENOENT; // now the urb is really dead - - switch (type) { - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - uhci_clean_iso_step2(s, urb_priv); - break; - } - - uhci_urb_dma_unmap(s, urb, urb_priv); - - usb_dec_dev_use (dev); -#ifdef DEBUG_SLAB - kmem_cache_free (urb_priv_kmem, urb_priv); -#else - kfree (urb_priv); -#endif - - } - } -} - -/*-------------------------------------------------------------------*/ -_static int uhci_unlink_urb (struct urb *urb) -{ - uhci_t *s; - unsigned long flags=0; - dbg("uhci_unlink_urb called for %p",urb); - if (!urb || !urb->dev) // you never know... - return -EINVAL; - - s = (uhci_t*) urb->dev->bus->hcpriv; - - if (usb_pipedevice (urb->pipe) == s->rh.devnum) - return rh_unlink_urb (urb); - - if (!urb->hcpriv) - return -EINVAL; - - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - int ret; - spin_lock_irqsave (&s->urb_list_lock, flags); - - uhci_release_bandwidth(urb); - ret = uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); - - spin_unlock_irqrestore (&s->urb_list_lock, flags); - return ret; - } - else - return uhci_unlink_urb_sync(s, urb); -} -/*-------------------------------------------------------------------*/ -// In case of ASAP iso transfer, search the URB-list for already queued URBs -// for this EP and calculate the earliest start frame for the new -// URB (easy seamless URB continuation!) -_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end) -{ - struct urb *u, *last_urb = NULL; - uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; - struct list_head *p; - int ret=-1; - unsigned long flags; - - spin_lock_irqsave (&s->urb_list_lock, flags); - p=s->urb_list.prev; - - for (; p != &s->urb_list; p = p->prev) { - u = list_entry (p, struct urb, urb_list); - // look for pending URBs with identical pipe handle - // works only because iso doesn't toggle the data bit! - if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) { - if (!last_urb) - *start = u->start_frame; - last_urb = u; - } - } - - if (last_urb) { - *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; - ret=0; - } - - spin_unlock_irqrestore(&s->urb_list_lock, flags); - - return ret; -} -/*-------------------------------------------------------------------*/ -// adjust start_frame according to scheduling constraints (ASAP etc) - -_static int iso_find_start (struct urb *urb) -{ - uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; - unsigned int now; - unsigned int start_limit = 0, stop_limit = 0, queued_size; - int limits; - - now = UHCI_GET_CURRENT_FRAME (s) & 1023; - - if ((unsigned) urb->number_of_packets > 900) - return -EFBIG; - - limits = find_iso_limits (urb, &start_limit, &stop_limit); - queued_size = (stop_limit - start_limit) & 1023; - - if (urb->transfer_flags & USB_ISO_ASAP) { - // first iso - if (limits) { - // 10ms setup should be enough //FIXME! - urb->start_frame = (now + 10) & 1023; - } - else { - urb->start_frame = stop_limit; //seamless linkage - - if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) { - info("iso_find_start: gap in seamless isochronous scheduling"); - dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x", - now, urb->start_frame, urb->number_of_packets, urb->pipe); - urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME! - } - } - } - else { - urb->start_frame &= 1023; - if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) { - dbg("iso_find_start: now between start_frame and end"); - return -EAGAIN; - } - } - - /* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */ - if (limits) - return 0; - - if (((urb->start_frame - start_limit) & 1023) < queued_size || - ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) { - dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u", - urb->start_frame, urb->number_of_packets, start_limit, stop_limit); - return -EAGAIN; - } - - return 0; -} -/*-------------------------------------------------------------------*/ -// submits USB interrupt (ie. polling ;-) -// ASAP-flag set implicitely -// if period==0, the transfer is only done once - -_static int uhci_submit_int_urb (struct urb *urb) -{ - uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; - urb_priv_t *urb_priv = urb->hcpriv; - int nint, n; - uhci_desc_t *td; - int status, destination; - int info; - unsigned int pipe = urb->pipe; - - if (urb->interval < 0 || urb->interval >= 256) - return -EINVAL; - - if (urb->interval == 0) - nint = 0; - else { - for (nint = 0, n = 1; nint <= 8; nint++, n += n) // round interval down to 2^n - { - if (urb->interval < n) { - urb->interval = n / 2; - break; - } - } - nint--; - } - - dbg("Rounded interval to %i, chain %i", urb->interval, nint); - - urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case... - - urb->number_of_packets = 1; - - // INT allows only one packet - if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe))) - return -EINVAL; - - if (alloc_td (s, &td, UHCI_PTR_DEPTH)) - return -ENOMEM; - - status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | - (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); - - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) | - (((urb->transfer_buffer_length - 1) & 0x7ff) << 21); - - - info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); - - fill_td (td, status, info, urb_priv->transfer_buffer_dma); - list_add_tail (&td->desc_list, &urb_priv->desc_list); - - queue_urb (s, urb); - - insert_td_horizontal (s, s->int_chain[nint], td); // store in INT-TDs - - usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); - - return 0; -} -/*-------------------------------------------------------------------*/ -_static int uhci_submit_iso_urb (struct urb *urb) -{ - uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; - urb_priv_t *urb_priv = urb->hcpriv; -#ifdef ISO_SANITY_CHECK - int pipe=urb->pipe; - int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); -#endif - int n, ret, last=0; - uhci_desc_t *td, **tdm; - int status, destination; - unsigned long flags; - - __save_flags(flags); - __cli(); // Disable IRQs to schedule all ISO-TDs in time - ret = iso_find_start (urb); // adjusts urb->start_frame for later use - - if (ret) - goto err; - - tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG); - - if (!tdm) { - ret = -ENOMEM; - goto err; - } - - memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*)); - - // First try to get all TDs. Cause: Removing already inserted TDs can only be done - // racefree in three steps: unlink TDs, wait one frame, delete TDs. - // So, this solutions seems simpler... - - for (n = 0; n < urb->number_of_packets; n++) { - dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length); - if (!urb->iso_frame_desc[n].length) - continue; // allows ISO striping by setting length to zero in iso_descriptor - - -#ifdef ISO_SANITY_CHECK - if(urb->iso_frame_desc[n].length > maxsze) { - - err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze); - ret=-EINVAL; - } - else -#endif - if (alloc_td (s, &td, UHCI_PTR_DEPTH)) { - int i; // Cleanup allocated TDs - - for (i = 0; i < n; n++) - if (tdm[i]) - delete_desc(s, tdm[i]); - kfree (tdm); - goto err; - } - last=n; - tdm[n] = td; - } - - status = TD_CTRL_ACTIVE | TD_CTRL_IOS; - - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe); - - // Queue all allocated TDs - for (n = 0; n < urb->number_of_packets; n++) { - td = tdm[n]; - if (!td) - continue; - - if (n == last) { - status |= TD_CTRL_IOC; - queue_urb (s, urb); - } - - fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21), - urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset); - list_add_tail (&td->desc_list, &urb_priv->desc_list); - - insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td); // store in iso-tds - } - - kfree (tdm); - dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023); - ret = 0; - - err: - __restore_flags(flags); - return ret; -} -/*-------------------------------------------------------------------*/ -// returns: 0 (no transfer queued), urb* (this urb already queued) - -_static struct urb* search_dev_ep (uhci_t *s, struct urb *urb) -{ - struct list_head *p; - struct urb *tmp; - unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0); - - dbg("search_dev_ep:"); - - p=s->urb_list.next; - - for (; p != &s->urb_list; p = p->next) { - tmp = list_entry (p, struct urb, urb_list); - dbg("urb: %p", tmp); - // we can accept this urb if it is not queued at this time - // or if non-iso transfer requests should be scheduled for the same device and pipe - if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) || - (urb == tmp)) { - return tmp; // found another urb already queued for processing - } - } - - return 0; -} -/*-------------------------------------------------------------------*/ -_static int uhci_submit_urb (struct urb *urb) -{ - uhci_t *s; - urb_priv_t *urb_priv; - int ret = 0, type; - unsigned long flags; - struct urb *queued_urb=NULL; - int bustime; - - if (!urb->dev || !urb->dev->bus) - return -ENODEV; - - s = (uhci_t*) urb->dev->bus->hcpriv; - //dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe)); - - if (!s->running) - return -ENODEV; - - type = usb_pipetype (urb->pipe); - - if (usb_pipedevice (urb->pipe) == s->rh.devnum) - return rh_submit_urb (urb); /* virtual root hub */ - - // Sanity checks - if (usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)) <= 0) { - err("uhci_submit_urb: pipesize for pipe %x is zero", urb->pipe); - return -EMSGSIZE; - } - - if (urb->transfer_buffer_length < 0 && type != PIPE_ISOCHRONOUS) { - err("uhci_submit_urb: Negative transfer length for urb %p", urb); - return -EINVAL; - } - - usb_inc_dev_use (urb->dev); - - spin_lock_irqsave (&s->urb_list_lock, flags); - - queued_urb = search_dev_ep (s, urb); // returns already queued urb for that pipe - - if (queued_urb) { - - queue_dbg("found bulk urb %p\n", queued_urb); - - if (( type != PIPE_BULK) || - ((type == PIPE_BULK) && - (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) { - spin_unlock_irqrestore (&s->urb_list_lock, flags); - usb_dec_dev_use (urb->dev); - err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb); - return -ENXIO; // urb already queued - } - } - -#ifdef DEBUG_SLAB - urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG); -#else - urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG); -#endif - if (!urb_priv) { - usb_dec_dev_use (urb->dev); - spin_unlock_irqrestore (&s->urb_list_lock, flags); - return -ENOMEM; - } - - memset(urb_priv, 0, sizeof(urb_priv_t)); - urb->hcpriv = urb_priv; - INIT_LIST_HEAD (&urb_priv->desc_list); - - dbg("submit_urb: scheduling %p", urb); - - if (type == PIPE_CONTROL) - urb_priv->setup_packet_dma = pci_map_single(s->uhci_pci, urb->setup_packet, - sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); - - if (urb->transfer_buffer_length) - urb_priv->transfer_buffer_dma = pci_map_single(s->uhci_pci, - urb->transfer_buffer, - urb->transfer_buffer_length, - usb_pipein(urb->pipe) ? - PCI_DMA_FROMDEVICE : - PCI_DMA_TODEVICE); - - if (type == PIPE_BULK) { - - if (queued_urb) { - while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb) // find last queued bulk - queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb; - - ((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb; - } - atomic_inc (&s->avoid_bulk); - ret = uhci_submit_bulk_urb (urb, queued_urb); - atomic_dec (&s->avoid_bulk); - spin_unlock_irqrestore (&s->urb_list_lock, flags); - } - else { - spin_unlock_irqrestore (&s->urb_list_lock, flags); - switch (type) { - case PIPE_ISOCHRONOUS: - if (urb->bandwidth == 0) { /* not yet checked/allocated */ - if (urb->number_of_packets <= 0) { - ret = -EINVAL; - break; - } - - bustime = usb_check_bandwidth (urb->dev, urb); - if (bustime < 0) - ret = bustime; - else { - ret = uhci_submit_iso_urb(urb); - if (ret == 0) - usb_claim_bandwidth (urb->dev, urb, bustime, 1); - } - } else { /* bandwidth is already set */ - ret = uhci_submit_iso_urb(urb); - } - break; - case PIPE_INTERRUPT: - if (urb->bandwidth == 0) { /* not yet checked/allocated */ - bustime = usb_check_bandwidth (urb->dev, urb); - if (bustime < 0) - ret = bustime; - else { - ret = uhci_submit_int_urb(urb); - if (ret == 0) - usb_claim_bandwidth (urb->dev, urb, bustime, 0); - } - } else { /* bandwidth is already set */ - ret = uhci_submit_int_urb(urb); - } - break; - case PIPE_CONTROL: - ret = uhci_submit_control_urb (urb); - break; - default: - ret = -EINVAL; - } - } - - dbg("submit_urb: scheduled with ret: %d", ret); - - if (ret != 0) { - uhci_urb_dma_unmap(s, urb, urb_priv); - usb_dec_dev_use (urb->dev); -#ifdef DEBUG_SLAB - kmem_cache_free(urb_priv_kmem, urb_priv); -#else - kfree (urb_priv); -#endif - return ret; - } - - return 0; -} - -// Checks for URB timeout and removes bandwidth reclamation if URB idles too long -_static void uhci_check_timeouts(uhci_t *s) -{ - struct list_head *p,*p2; - struct urb *urb; - int type; - - p = s->urb_list.prev; - - while (p != &s->urb_list) { - urb_priv_t *hcpriv; - - p2 = p; - p = p->prev; - urb = list_entry (p2, struct urb, urb_list); - type = usb_pipetype (urb->pipe); - - hcpriv = (urb_priv_t*)urb->hcpriv; - - if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) { - urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; - async_dbg("uhci_check_timeout: timeout for %p",urb); - uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); - } -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH - else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) && - (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT)) - disable_desc_loop(s, urb); -#endif - - } - s->timeout_check=jiffies; -} - -/*------------------------------------------------------------------- - Virtual Root Hub - -------------------------------------------------------------------*/ - -_static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, /* __u16 bcdUSB; v1.0 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - - -/* Configuration descriptor */ -_static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - - -_static __u8 root_hub_hub_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x29, /* __u8 bDescriptorType; Hub-descriptor */ - 0x02, /* __u8 bNbrPorts; */ - 0x00, /* __u16 wHubCharacteristics; */ - 0x00, - 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ - 0x00, /* __u8 bHubContrCurrent; 0 mA */ - 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ - 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ -}; - -/*-------------------------------------------------------------------------*/ -/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ -_static int rh_send_irq (struct urb *urb) -{ - int len = 1; - int i; - uhci_t *uhci = urb->dev->bus->hcpriv; - unsigned int io_addr = uhci->io_addr; - __u16 data = 0; - - for (i = 0; i < uhci->rh.numports; i++) { - data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); - len = (i + 1) / 8 + 1; - } - - *(__u16 *) urb->transfer_buffer = cpu_to_le16 (data); - urb->actual_length = len; - urb->status = 0; - - if ((data > 0) && (uhci->rh.send != 0)) { - dbg("Root-Hub INT complete: port1: %x port2: %x data: %x", - inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data); - urb->complete (urb); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ -/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ -_static int rh_init_int_timer (struct urb *urb); - -_static void rh_int_timer_do (unsigned long ptr) -{ - int len; - struct urb *urb = (struct urb*) ptr; - uhci_t *uhci = urb->dev->bus->hcpriv; - - if (uhci->rh.send) { - len = rh_send_irq (urb); - if (len > 0) { - urb->actual_length = len; - if (urb->complete) - urb->complete (urb); - } - } - rh_init_int_timer (urb); -} - -/*-------------------------------------------------------------------------*/ -/* Root Hub INTs are polled by this timer, polling interval 20ms */ - -_static int rh_init_int_timer (struct urb *urb) -{ - uhci_t *uhci = urb->dev->bus->hcpriv; - - uhci->rh.interval = urb->interval; - init_timer (&uhci->rh.rh_int_timer); - uhci->rh.rh_int_timer.function = rh_int_timer_do; - uhci->rh.rh_int_timer.data = (unsigned long) urb; - uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000; - add_timer (&uhci->rh.rh_int_timer); - - return 0; -} - -/*-------------------------------------------------------------------------*/ -#define OK(x) len = (x); break - -#define CLR_RH_PORTSTAT(x) \ - status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ - status = (status & 0xfff5) & ~(x); \ - outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) - -#define SET_RH_PORTSTAT(x) \ - status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ - status = (status & 0xfff5) | (x); \ - outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) - - -/*-------------------------------------------------------------------------*/ -/**** - ** Root Hub Control Pipe - *************************/ - - -_static int rh_submit_urb (struct urb *urb) -{ - struct usb_device *usb_dev = urb->dev; - uhci_t *uhci = usb_dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int status = 0; - int stat = 0; - int i; - unsigned int io_addr = uhci->io_addr; - __u16 cstatus; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - if (usb_pipetype (pipe) == PIPE_INTERRUPT) { - dbg("Root-Hub submit IRQ: every %d ms", urb->interval); - uhci->rh.urb = urb; - uhci->rh.send = 1; - uhci->rh.interval = urb->interval; - rh_init_int_timer (urb); - - return 0; - } - - - bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8; - wValue = le16_to_cpu (cmd->wValue); - wIndex = le16_to_cpu (cmd->wIndex); - wLength = le16_to_cpu (cmd->wLength); - - for (i = 0; i < 8; i++) - uhci->rh.c_p_r[i] = 0; - - dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x", - uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data = cpu_to_le16 (1); - OK (2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data = cpu_to_le32 (0); - OK (4); /* hub power ** */ - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1)); - cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | - ((status & USBPORTSC_PEC) >> (3 - 1)) | - (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); - status = (status & USBPORTSC_CCS) | - ((status & USBPORTSC_PE) >> (2 - 1)) | - ((status & USBPORTSC_SUSP) >> (12 - 2)) | - ((status & USBPORTSC_PR) >> (9 - 4)) | - (1 << 8) | /* power on ** */ - ((status & USBPORTSC_LSDA) << (-8 + 9)); - - *(__u16 *) data = cpu_to_le16 (status); - *(__u16 *) (data + 2) = cpu_to_le16 (cstatus); - OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case (RH_C_HUB_OVER_CURRENT): - OK (0); /* hub power over current ** */ - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - CLR_RH_PORTSTAT (USBPORTSC_PE); - OK (0); - case (RH_PORT_SUSPEND): - CLR_RH_PORTSTAT (USBPORTSC_SUSP); - OK (0); - case (RH_PORT_POWER): - OK (0); /* port power ** */ - case (RH_C_PORT_CONNECTION): - SET_RH_PORTSTAT (USBPORTSC_CSC); - OK (0); - case (RH_C_PORT_ENABLE): - SET_RH_PORTSTAT (USBPORTSC_PEC); - OK (0); - case (RH_C_PORT_SUSPEND): -/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ - OK (0); - case (RH_C_PORT_OVER_CURRENT): - OK (0); /* port power over current ** */ - case (RH_C_PORT_RESET): - uhci->rh.c_p_r[wIndex - 1] = 0; - OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - SET_RH_PORTSTAT (USBPORTSC_SUSP); - OK (0); - case (RH_PORT_RESET): - SET_RH_PORTSTAT (USBPORTSC_PR); - uhci_wait_ms (10); - uhci->rh.c_p_r[wIndex - 1] = 1; - CLR_RH_PORTSTAT (USBPORTSC_PR); - udelay (10); - SET_RH_PORTSTAT (USBPORTSC_PE); - uhci_wait_ms (10); - SET_RH_PORTSTAT (0xa); - OK (0); - case (RH_PORT_POWER): - OK (0); /* port power ** */ - case (RH_PORT_ENABLE): - SET_RH_PORTSTAT (USBPORTSC_PE); - OK (0); - } - break; - - case RH_SET_ADDRESS: - uhci->rh.devnum = wValue; - OK (0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, leni, - min_t(unsigned int, - sizeof (root_hub_dev_des), wLength)); - memcpy (data, root_hub_dev_des, len); - OK (len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, leni, - min_t(unsigned int, - sizeof (root_hub_config_des), wLength)); - memcpy (data, root_hub_config_des, len); - OK (len); - case (0x03): /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, - uhci->io_addr, "UHCI", - data, wLength); - if (len > 0) { - OK(min_t(int, leni, len)); - } else - stat = -EPIPE; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - root_hub_hub_des[2] = uhci->rh.numports; - len = min_t(unsigned int, leni, - min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); - memcpy (data, root_hub_hub_des, len); - OK (len); - - case RH_GET_CONFIGURATION: - *(__u8 *) data = 0x01; - OK (1); - - case RH_SET_CONFIGURATION: - OK (0); - default: - stat = -EPIPE; - } - - dbg("Root-Hub stat port1: %x port2: %x", - inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2)); - - urb->actual_length = len; - urb->status = stat; - urb->dev=NULL; - if (urb->complete) - urb->complete (urb); - return 0; -} -/*-------------------------------------------------------------------------*/ - -_static int rh_unlink_urb (struct urb *urb) -{ - uhci_t *uhci = urb->dev->bus->hcpriv; - - if (uhci->rh.urb==urb) { - dbg("Root-Hub unlink IRQ"); - uhci->rh.send = 0; - del_timer (&uhci->rh.rh_int_timer); - } - return 0; -} -/*-------------------------------------------------------------------*/ - -/* - * Map status to standard result codes - * - * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) - * is True for output TDs and False for input TDs. - */ -_static int uhci_map_status (int status, int dir_out) -{ - if (!status) - return 0; - if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ - return -EPROTO; - if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ - if (dir_out) - return -ETIMEDOUT; - else - return -EILSEQ; - } - if (status & TD_CTRL_NAK) /* NAK */ - return -ETIMEDOUT; - if (status & TD_CTRL_BABBLE) /* Babble */ - return -EOVERFLOW; - if (status & TD_CTRL_DBUFERR) /* Buffer error */ - return -ENOSR; - if (status & TD_CTRL_STALLED) /* Stalled */ - return -EPIPE; - if (status & TD_CTRL_ACTIVE) /* Active */ - return 0; - - return -EPROTO; -} - -/* - * Only the USB core should call uhci_alloc_dev and uhci_free_dev - */ -_static int uhci_alloc_dev (struct usb_device *usb_dev) -{ - return 0; -} - -_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all) -{ - unsigned long flags; - struct list_head *p; - struct list_head *p2; - struct urb *urb; - - spin_lock_irqsave (&s->urb_list_lock, flags); - p = s->urb_list.prev; - while (p != &s->urb_list) { - p2 = p; - p = p->prev ; - urb = list_entry (p2, struct urb, urb_list); - dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev); - - //urb->transfer_flags |=USB_ASYNC_UNLINK; - - if (remove_all || (usb_dev == urb->dev)) { - spin_unlock_irqrestore (&s->urb_list_lock, flags); - warn("forced removing of queued URB %p due to disconnect",urb); - uhci_unlink_urb(urb); - urb->dev = NULL; // avoid further processing of this URB - spin_lock_irqsave (&s->urb_list_lock, flags); - p = s->urb_list.prev; - } - } - spin_unlock_irqrestore (&s->urb_list_lock, flags); -} - -_static int uhci_free_dev (struct usb_device *usb_dev) -{ - uhci_t *s; - - - if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv) - return -EINVAL; - - s=(uhci_t*) usb_dev->bus->hcpriv; - uhci_unlink_urbs(s, usb_dev, 0); - - return 0; -} - -/* - * uhci_get_current_frame_number() - * - * returns the current frame number for a USB bus/controller. - */ -_static int uhci_get_current_frame_number (struct usb_device *usb_dev) -{ - return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv); -} - -struct usb_operations uhci_device_operations = -{ - uhci_alloc_dev, - uhci_free_dev, - uhci_get_current_frame_number, - uhci_submit_urb, - uhci_unlink_urb -}; - -_static void correct_data_toggles(struct urb *urb) -{ - usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), - !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))); - - while(urb) { - urb_priv_t *priv=urb->hcpriv; - uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list); - struct list_head *p = qh->vertical.next; - uhci_desc_t *td; - dbg("URB to correct %p\n", urb); - - for (; p != &qh->vertical; p = p->next) { - td = list_entry (p, uhci_desc_t, vertical); - td->hw.td.info^=cpu_to_le32(1<next_queued_urb; - } -} - -/* - * For IN-control transfers, process_transfer gets a bit more complicated, - * since there are devices that return less data (eg. strings) than they - * have announced. This leads to a queue abort due to the short packet, - * the status stage is not executed. If this happens, the status stage - * is manually re-executed. - * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH) - * PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb) - */ - -_static int process_transfer (uhci_t *s, struct urb *urb, int mode) -{ - int ret = 0; - urb_priv_t *urb_priv = urb->hcpriv; - struct list_head *qhl = urb_priv->desc_list.next; - uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list); - struct list_head *p = qh->vertical.next; - uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); - uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical); - int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle - int maxlength; // extracted and remapped info from TD - int actual_length; - int status = 0; - - //dbg("process_transfer: urb %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc); - - /* if the status phase has been retriggered and the - queue is empty or the last status-TD is inactive, the retriggered - status stage is completed - */ - - if (urb_priv->flags && - ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_active(desc))) - goto transfer_finished; - - urb->actual_length=0; - - for (; p != &qh->vertical; p = p->next) { - desc = list_entry (p, uhci_desc_t, vertical); - - if (is_td_active(desc)) { // do not process active TDs - if (mode == CLEAN_TRANSFER_DELETION_MARK) // if called from async_unlink - uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK); - return ret; - } - - actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); // extract transfer parameters from TD - maxlength = (((le32_to_cpu(desc->hw.td.info) >> 21) & 0x7ff) + 1) & 0x7ff; - status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); - - if (status == -EPIPE) { // see if EP is stalled - // set up stalled condition - usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); - } - - if (status && (status != -EPIPE)) { // if any error occurred stop processing of further TDs - // only set ret if status returned an error - is_error: - ret = status; - urb->error_count++; - break; - } - else if ((le32_to_cpu(desc->hw.td.info) & 0xff) != USB_PID_SETUP) - urb->actual_length += actual_length; - - // got less data than requested - if ( (actual_length < maxlength)) { - if (urb->transfer_flags & USB_DISABLE_SPD) { - status = -EREMOTEIO; // treat as real error - dbg("process_transfer: SPD!!"); - break; // exit after this TD because SP was detected - } - - // short read during control-IN: re-start status stage - if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) { - if (uhci_packetid(le32_to_cpu(last_desc->hw.td.info)) == USB_PID_OUT) { - - set_qh_element(qh, last_desc->dma_addr); // re-trigger status stage - dbg("short packet during control transfer, retrigger status stage @ %p",last_desc); - urb_priv->flags = 1; // mark as short control packet - return 0; - } - } - // all other cases: short read is OK - data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info)); - break; - } - else if (status) - goto is_error; - - data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info)); - queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, le32_to_cpu(desc->hw.td.status),status, data_toggle); - - } - - if (usb_pipetype (urb->pipe) == PIPE_BULK ) { /* toggle correction for short bulk transfers (nonqueued/queued) */ - - urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; - struct urb *next_queued_urb=priv->next_queued_urb; - - if (next_queued_urb) { - urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv; - uhci_desc_t *qh = list_entry (next_priv->desc_list.next, uhci_desc_t, desc_list); - uhci_desc_t *first_td=list_entry (qh->vertical.next, uhci_desc_t, vertical); - - if (data_toggle == uhci_toggle (le32_to_cpu(first_td->hw.td.info))) { - err("process_transfer: fixed toggle"); - correct_data_toggles(next_queued_urb); - } - } - else - usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle); - } - - transfer_finished: - - uhci_clean_transfer(s, urb, qh, mode); - - urb->status = status; - -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH - disable_desc_loop(s,urb); -#endif - - queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d", - urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count); - return ret; -} - -_static int process_interrupt (uhci_t *s, struct urb *urb) -{ - int i, ret = -EINPROGRESS; - urb_priv_t *urb_priv = urb->hcpriv; - struct list_head *p = urb_priv->desc_list.next; - uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); - - int actual_length; - int status = 0; - - //dbg("urb contains interrupt request"); - - for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-) - { - desc = list_entry (p, uhci_desc_t, desc_list); - - if (is_td_active(desc)) { - // do not process active TDs - //dbg("TD ACT Status @%p %08x",desc,le32_to_cpu(desc->hw.td.status)); - break; - } - - if (!(desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC))) { - // do not process one-shot TDs, no recycling - break; - } - // extract transfer parameters from TD - - actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); - status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); - - // see if EP is stalled - if (status == -EPIPE) { - // set up stalled condition - usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); - } - - // if any error occurred: ignore this td, and continue - if (status != 0) { - //uhci_show_td (desc); - urb->error_count++; - goto recycle; - } - else - urb->actual_length = actual_length; - - recycle: - uhci_urb_dma_sync(s, urb, urb->hcpriv); - if (urb->complete) { - //dbg("process_interrupt: calling completion, status %i",status); - urb->status = status; - ((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion - - spin_unlock(&s->urb_list_lock); - - urb->complete ((struct urb *) urb); - - spin_lock(&s->urb_list_lock); - - ((urb_priv_t*)urb->hcpriv)->flags=0; - } - - if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) && - (urb->status != -ENOENT)) { - - urb->status = -EINPROGRESS; - - // Recycle INT-TD if interval!=0, else mark TD as one-shot - if (urb->interval) { - - desc->hw.td.info &= cpu_to_le32(~(1 << TD_TOKEN_TOGGLE)); - if (status==0) { - ((urb_priv_t*)urb->hcpriv)->started=jiffies; - desc->hw.td.info |= cpu_to_le32((usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE)); - usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); - } else { - desc->hw.td.info |= cpu_to_le32((!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE)); - } - desc->hw.td.status= cpu_to_le32((urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | - (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27)); - mb(); - } - else { - uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); - uhci_clean_iso_step2(s, urb_priv); - // correct toggle after unlink - usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); - clr_td_ioc(desc); // inactivate TD - } - } - } - - return ret; -} - -// mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs -// mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked) - -_static int process_iso (uhci_t *s, struct urb *urb, int mode) -{ - int i; - int ret = 0; - urb_priv_t *urb_priv = urb->hcpriv; - struct list_head *p = urb_priv->desc_list.next, *p_tmp; - uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); - - dbg("urb contains iso request"); - if (is_td_active(desc) && mode==PROCESS_ISO_REGULAR) - return -EXDEV; // last TD not finished - - urb->error_count = 0; - urb->actual_length = 0; - urb->status = 0; - dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s), - urb->number_of_packets,mode,le32_to_cpu(desc->hw.td.status)); - - for (i = 0; p != &urb_priv->desc_list; i++) { - desc = list_entry (p, uhci_desc_t, desc_list); - - //uhci_show_td(desc); - if (is_td_active(desc)) { - // means we have completed the last TD, but not the TDs before - desc->hw.td.status &= cpu_to_le32(~TD_CTRL_ACTIVE); - dbg("TD still active (%x)- grrr. paranoia!", le32_to_cpu(desc->hw.td.status)); - ret = -EXDEV; - urb->iso_frame_desc[i].status = ret; - unlink_td (s, desc, 1); - // FIXME: immediate deletion may be dangerous - goto err; - } - - if (mode == PROCESS_ISO_REGULAR) - unlink_td (s, desc, 1); - - if (urb->number_of_packets <= i) { - dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i); - ret = -EINVAL; - goto err; - } - - urb->iso_frame_desc[i].actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); - urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); - urb->actual_length += urb->iso_frame_desc[i].actual_length; - - err: - - if (urb->iso_frame_desc[i].status != 0) { - urb->error_count++; - urb->status = urb->iso_frame_desc[i].status; - } - dbg("process_iso: %i: len:%d %08x status:%x", - i, urb->iso_frame_desc[i].actual_length, le32_to_cpu(desc->hw.td.status),urb->iso_frame_desc[i].status); - - p_tmp = p; - p = p->next; - list_del (p_tmp); - delete_desc (s, desc); - } - - dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length); - return ret; -} - - -_static int process_urb (uhci_t *s, struct list_head *p) -{ - int ret = 0; - struct urb *urb; - - urb=list_entry (p, struct urb, urb_list); - //dbg("process_urb: found queued urb: %p", urb); - - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR); - break; - case PIPE_BULK: - if (!s->avoid_bulk.counter) - ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR); - else - return 0; - break; - case PIPE_ISOCHRONOUS: - ret = process_iso (s, urb, PROCESS_ISO_REGULAR); - break; - case PIPE_INTERRUPT: - ret = process_interrupt (s, urb); - break; - } - - if (urb->status != -EINPROGRESS) { - urb_priv_t *urb_priv; - struct usb_device *usb_dev; - - usb_dev=urb->dev; - - /* Release bandwidth for Interrupt or Iso transfers */ - if (urb->bandwidth) { - if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS) - usb_release_bandwidth (urb->dev, urb, 1); - else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval) - usb_release_bandwidth (urb->dev, urb, 0); - } - - dbg("dequeued urb: %p", urb); - dequeue_urb (s, urb); - - urb_priv = urb->hcpriv; - - uhci_urb_dma_unmap(s, urb, urb_priv); - -#ifdef DEBUG_SLAB - kmem_cache_free(urb_priv_kmem, urb_priv); -#else - kfree (urb_priv); -#endif - - if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) { // process_interrupt does completion on its own - struct urb *next_urb = urb->next; - int is_ring = 0; - int contains_killed = 0; - int loop_count=0; - - if (next_urb) { - // Find out if the URBs are linked to a ring - while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) { - if (next_urb->status == -ENOENT) {// killed URBs break ring structure & resubmission - contains_killed = 1; - break; - } - next_urb = next_urb->next; - loop_count++; - } - - if (loop_count == MAX_NEXT_COUNT) - err("process_urb: Too much linked URBs in ring detection!"); - - if (next_urb == urb) - is_ring=1; - } - - // Submit idle/non-killed URBs linked with urb->next - // Stop before the current URB - - next_urb = urb->next; - if (next_urb && !contains_killed) { - int ret_submit; - next_urb = urb->next; - - loop_count=0; - while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) { - if (next_urb->status != -EINPROGRESS) { - - if (next_urb->status == -ENOENT) - break; - - spin_unlock(&s->urb_list_lock); - - ret_submit=uhci_submit_urb(next_urb); - spin_lock(&s->urb_list_lock); - - if (ret_submit) - break; - } - loop_count++; - next_urb = next_urb->next; - } - if (loop_count == MAX_NEXT_COUNT) - err("process_urb: Too much linked URBs in resubmission!"); - } - - // Completion - if (urb->complete) { - int was_unlinked = (urb->status == -ENOENT); - urb->dev = NULL; - spin_unlock(&s->urb_list_lock); - - urb->complete ((struct urb *) urb); - - // Re-submit the URB if ring-linked - if (is_ring && !was_unlinked && !contains_killed) { - urb->dev=usb_dev; - uhci_submit_urb (urb); - } - spin_lock(&s->urb_list_lock); - } - - usb_dec_dev_use (usb_dev); - } - } - - return ret; -} - -_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs) -{ - uhci_t *s = __uhci; - unsigned int io_addr = s->io_addr; - unsigned short status; - struct list_head *p, *p2; - int restarts, work_done; - - /* - * Read the interrupt status, and write it back to clear the - * interrupt cause - */ - - status = inw (io_addr + USBSTS); - - if (!status) /* shared interrupt, not mine */ - return; - - dbg("interrupt"); - - if (status != 1) { - // Avoid too much error messages at a time - if (time_after(jiffies, s->last_error_time + ERROR_SUPPRESSION_TIME)) { - warn("interrupt, status %x, frame# %i", status, - UHCI_GET_CURRENT_FRAME(s)); - s->last_error_time = jiffies; - } - - // remove host controller halted state - if ((status&0x20) && (s->running)) { - err("Host controller halted, trying to restart."); - outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD); - } - //uhci_show_status (s); - } - /* - * traverse the list in *reverse* direction, because new entries - * may be added at the end. - * also, because process_urb may unlink the current urb, - * we need to advance the list before - * New: check for max. workload and restart count - */ - - spin_lock (&s->urb_list_lock); - - restarts=0; - work_done=0; - -restart: - s->unlink_urb_done=0; - p = s->urb_list.prev; - - while (p != &s->urb_list && (work_done < 1024)) { - p2 = p; - p = p->prev; - - process_urb (s, p2); - - work_done++; - - if (s->unlink_urb_done) { - s->unlink_urb_done=0; - restarts++; - - if (restarts<16) // avoid endless restarts - goto restart; - else - break; - } - } - if (time_after(jiffies, s->timeout_check + (HZ/30))) - uhci_check_timeouts(s); - - clean_descs(s, CLEAN_NOT_FORCED); - uhci_cleanup_unlink(s, CLEAN_NOT_FORCED); - uhci_switch_timer_int(s); - - spin_unlock (&s->urb_list_lock); - - outw (status, io_addr + USBSTS); - - //dbg("uhci_interrupt: done"); -} - -_static void reset_hc (uhci_t *s) -{ - unsigned int io_addr = s->io_addr; - - s->apm_state = 0; - /* Global reset for 50ms */ - outw (USBCMD_GRESET, io_addr + USBCMD); - uhci_wait_ms (50); - outw (0, io_addr + USBCMD); - uhci_wait_ms (10); -} - -_static void start_hc (uhci_t *s) -{ - unsigned int io_addr = s->io_addr; - int timeout = 10; - - /* - * Reset the HC - this will force us to get a - * new notification of any already connected - * ports due to the virtual disconnect that it - * implies. - */ - outw (USBCMD_HCRESET, io_addr + USBCMD); - - while (inw (io_addr + USBCMD) & USBCMD_HCRESET) { - if (!--timeout) { - err("USBCMD_HCRESET timed out!"); - break; - } - udelay(1); - } - - /* Turn on all interrupts */ - outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); - - /* Start at frame 0 */ - outw (0, io_addr + USBFRNUM); - outl (s->framelist_dma, io_addr + USBFLBASEADD); - - /* Run and mark it configured with a 64-byte max packet */ - outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); - s->apm_state = 1; - s->running = 1; -} - -/* No __devexit, since it maybe called from alloc_uhci() */ -_static void -uhci_pci_remove (struct pci_dev *dev) -{ - uhci_t *s = pci_get_drvdata(dev); - struct usb_device *root_hub = s->bus->root_hub; - - s->running = 0; // Don't allow submit_urb - - if (root_hub) - usb_disconnect (&root_hub); - - reset_hc (s); - wait_ms (1); - - uhci_unlink_urbs (s, 0, CLEAN_FORCED); // Forced unlink of remaining URBs - uhci_cleanup_unlink (s, CLEAN_FORCED); // force cleanup of async killed URBs - - usb_deregister_bus (s->bus); - - release_region (s->io_addr, s->io_size); - free_irq (s->irq, s); - usb_free_bus (s->bus); - cleanup_skel (s); - kfree (s); -} - -_static int __init uhci_start_usb (uhci_t *s) -{ /* start it up */ - /* connect the virtual root hub */ - struct usb_device *usb_dev; - - usb_dev = usb_alloc_dev (NULL, s->bus); - if (!usb_dev) - return -1; - - s->bus->root_hub = usb_dev; - usb_connect (usb_dev); - - if (usb_new_device (usb_dev) != 0) { - usb_free_dev (usb_dev); - return -1; - } - - return 0; -} - -#ifdef CONFIG_PM -_static int -uhci_pci_suspend (struct pci_dev *dev, u32 state) -{ - reset_hc((uhci_t *) pci_get_drvdata(dev)); - return 0; -} - -_static int -uhci_pci_resume (struct pci_dev *dev) -{ - start_hc((uhci_t *) pci_get_drvdata(dev)); - return 0; -} -#endif - -_static int __devinit alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size) -{ - uhci_t *s; - struct usb_bus *bus; - char buf[8], *bufp = buf; - -#ifndef __sparc__ - sprintf(buf, "%d", irq); -#else - bufp = __irq_itoa(irq); -#endif - printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", - io_addr, bufp); - - s = kmalloc (sizeof (uhci_t), GFP_KERNEL); - if (!s) - return -1; - - memset (s, 0, sizeof (uhci_t)); - INIT_LIST_HEAD (&s->free_desc); - INIT_LIST_HEAD (&s->urb_list); - INIT_LIST_HEAD (&s->urb_unlinked); - spin_lock_init (&s->urb_list_lock); - spin_lock_init (&s->qh_lock); - spin_lock_init (&s->td_lock); - atomic_set(&s->avoid_bulk, 0); - s->irq = -1; - s->io_addr = io_addr; - s->io_size = io_size; - s->uhci_pci=dev; - - bus = usb_alloc_bus (&uhci_device_operations); - if (!bus) { - kfree (s); - return -1; - } - - s->bus = bus; - bus->bus_name = dev->slot_name; - bus->hcpriv = s; - - /* UHCI specs says devices must have 2 ports, but goes on to say */ - /* they may have more but give no way to determine how many they */ - /* have, so default to 2 */ - /* According to the UHCI spec, Bit 7 is always set to 1. So we try */ - /* to use this to our advantage */ - - for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) { - unsigned int portstatus; - - portstatus = inw (io_addr + 0x10 + (s->maxports * 2)); - dbg("port %i, adr %x status %x", s->maxports, - io_addr + 0x10 + (s->maxports * 2), portstatus); - if (!(portstatus & 0x0080)) - break; - } - warn("Detected %d ports", s->maxports); - - /* This is experimental so anything less than 2 or greater than 8 is */ - /* something weird and we'll ignore it */ - if (s->maxports < 2 || s->maxports > 8) { - dbg("Port count misdetected, forcing to 2 ports"); - s->maxports = 2; - } - - s->rh.numports = s->maxports; - s->loop_usage=0; - if (init_skel (s)) { - usb_free_bus (bus); - kfree(s); - return -1; - } - - request_region (s->io_addr, io_size, MODNAME); - reset_hc (s); - usb_register_bus (s->bus); - - start_hc (s); - - if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) { - err("request_irq %d failed!",irq); - usb_free_bus (bus); - reset_hc (s); - release_region (s->io_addr, s->io_size); - cleanup_skel(s); - kfree(s); - return -1; - } - - /* Enable PIRQ */ - pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT); - - s->irq = irq; - - if(uhci_start_usb (s) < 0) { - uhci_pci_remove(dev); - return -1; - } - - //chain new uhci device into global list - pci_set_drvdata(dev, s); - devs=s; - - return 0; -} - -_static int __devinit -uhci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) -{ - int i; - - if (pci_enable_device(dev) < 0) - return -ENODEV; - - if (!dev->irq) { - err("found UHCI device with no IRQ assigned. check BIOS settings!"); - return -ENODEV; - } - - pci_set_master(dev); - - /* Search for the IO base address.. */ - for (i = 0; i < 6; i++) { - - unsigned int io_addr = pci_resource_start(dev, i); - unsigned int io_size = pci_resource_len(dev, i); - if (!(pci_resource_flags(dev,i) & IORESOURCE_IO)) - continue; - - /* Is it already in use? */ - if (check_region (io_addr, io_size)) - break; - /* disable legacy emulation */ - pci_write_config_word (dev, USBLEGSUP, 0); - - return alloc_uhci(dev, dev->irq, io_addr, io_size); - } - return -ENODEV; -} - -/*-------------------------------------------------------------------------*/ - -static const struct pci_device_id __devinitdata uhci_pci_ids [] = { { - - /* handle any USB UHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x00), - class_mask: ~0, - - /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - - }, { /* end: all zeroes */ } -}; - -MODULE_DEVICE_TABLE (pci, uhci_pci_ids); - -static struct pci_driver uhci_pci_driver = { - name: "usb-uhci", - id_table: &uhci_pci_ids [0], - - probe: uhci_pci_probe, - remove: uhci_pci_remove, - -#ifdef CONFIG_PM - suspend: uhci_pci_suspend, - resume: uhci_pci_resume, -#endif /* PM */ - -}; - -/*-------------------------------------------------------------------------*/ - -static int __init uhci_hcd_init (void) -{ - int retval; - -#ifdef DEBUG_SLAB - urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); - - if(!urb_priv_kmem) { - err("kmem_cache_create for urb_priv_t failed (out of memory)"); - return -ENOMEM; - } -#endif - info(VERSTR); - -#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH - info("High bandwidth mode enabled"); -#endif - - retval = pci_module_init (&uhci_pci_driver); - -#ifdef DEBUG_SLAB - if (retval < 0 ) { - if (kmem_cache_destroy(urb_priv_kmem)) - err("urb_priv_kmem remained"); - } -#endif - - info(DRIVER_VERSION ":" DRIVER_DESC); - - return retval; -} - -static void __exit uhci_hcd_cleanup (void) -{ - pci_unregister_driver (&uhci_pci_driver); - -#ifdef DEBUG_SLAB - if(kmem_cache_destroy(urb_priv_kmem)) - err("urb_priv_kmem remained"); -#endif -} - -module_init (uhci_hcd_init); -module_exit (uhci_hcd_cleanup); - - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h --- a/drivers/usb/usb-uhci.h Thu Mar 6 14:22:16 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,308 +0,0 @@ -#ifndef __LINUX_UHCI_H -#define __LINUX_UHCI_H - -/* - $Id: usb-uhci.h,v 1.58 2001/08/28 16:45:00 acher Exp $ - */ -#define MODNAME "usb-uhci" -#define UHCI_LATENCY_TIMER 0 - -static __inline__ void uhci_wait_ms(unsigned int ms) -{ - if(!in_interrupt()) - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1 + ms * HZ / 1000); - } - else - mdelay(ms); -} - -/* Command register */ -#define USBCMD 0 -#define USBCMD_RS 0x0001 /* Run/Stop */ -#define USBCMD_HCRESET 0x0002 /* Host reset */ -#define USBCMD_GRESET 0x0004 /* Global reset */ -#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ -#define USBCMD_FGR 0x0010 /* Force Global Resume */ -#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ -#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ -#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ - -/* Status register */ -#define USBSTS 2 -#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ -#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ -#define USBSTS_RD 0x0004 /* Resume Detect */ -#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ -#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ -#define USBSTS_HCH 0x0020 /* HC Halted */ - -/* Interrupt enable register */ -#define USBINTR 4 -#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ -#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ -#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ -#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ - -#define USBFRNUM 6 -#define USBFLBASEADD 8 -#define USBSOF 12 - -/* USB port status and control registers */ -#define USBPORTSC1 16 -#define USBPORTSC2 18 -#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ -#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ -#define USBPORTSC_PE 0x0004 /* Port Enable */ -#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ -#define USBPORTSC_LS 0x0030 /* Line Status */ -#define USBPORTSC_RD 0x0040 /* Resume Detect */ -#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ -#define USBPORTSC_PR 0x0200 /* Port Reset */ -#define USBPORTSC_SUSP 0x1000 /* Suspend */ - -/* Legacy support register */ -#define USBLEGSUP 0xc0 -#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ - -#define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */ -#define UHCI_PID 0xff /* PID MASK */ - -#define UHCI_PTR_BITS 0x000F -#define UHCI_PTR_TERM 0x0001 -#define UHCI_PTR_QH 0x0002 -#define UHCI_PTR_DEPTH 0x0004 - -#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ -#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ -#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ - -/* - * for TD : - */ -#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ -#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ -#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ -#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ -#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ -#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ -#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ -#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ -#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ -#define TD_CTRL_NAK (1 << 19) /* NAK Received */ -#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ -#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ -#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 */ - -#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ - TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) - -#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) -#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ -#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) - -/* - * for TD : - */ -#define UHCI_TD_REMOVE 0x0001 /* Remove when done */ - -/* - * for TD : (a.k.a. Token) - */ -#define TD_TOKEN_TOGGLE 19 - -#define uhci_maxlen(token) ((token) >> 21) -#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) -#define uhci_endpoint(token) (((token) >> 15) & 0xf) -#define uhci_devaddr(token) (((token) >> 8) & 0x7f) -#define uhci_devep(token) (((token) >> 8) & 0x7ff) -#define uhci_packetid(token) ((token) & 0xff) -#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) -#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) - -/* ------------------------------------------------------------------------------------ - New TD/QH-structures - ------------------------------------------------------------------------------------ */ -typedef enum { - TD_TYPE, QH_TYPE -} uhci_desc_type_t; - -typedef struct { - __u32 link; - __u32 status; - __u32 info; - __u32 buffer; -} uhci_td_t, *puhci_td_t; - -typedef struct { - __u32 head; - __u32 element; /* Queue element pointer */ -} uhci_qh_t, *puhci_qh_t; - -typedef struct { - union { - uhci_td_t td; - uhci_qh_t qh; - } hw; - uhci_desc_type_t type; - dma_addr_t dma_addr; - struct list_head horizontal; - struct list_head vertical; - struct list_head desc_list; - int last_used; -} uhci_desc_t, *puhci_desc_t; - -typedef struct { - struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request - dma_addr_t setup_packet_dma; - dma_addr_t transfer_buffer_dma; - unsigned long started; - struct urb *next_queued_urb; // next queued urb for this EP - struct urb *prev_queued_urb; - uhci_desc_t *bottom_qh; - uhci_desc_t *next_qh; // next helper QH - char use_loop; - char flags; -} urb_priv_t, *purb_priv_t; - -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void *urb; - void *int_addr; - int send; - int interval; - int numports; - int c_p_r[8]; - struct timer_list rh_int_timer; -}; - -typedef struct uhci { - int irq; - unsigned int io_addr; - unsigned int io_size; - unsigned int maxports; - int running; - - int apm_state; - - struct uhci *next; // chain of uhci device contexts - - struct list_head urb_list; // list of all pending urbs - - spinlock_t urb_list_lock; // lock to keep consistency - - int unlink_urb_done; - atomic_t avoid_bulk; - - struct usb_bus *bus; // our bus - - __u32 *framelist; - dma_addr_t framelist_dma; - uhci_desc_t **iso_td; - uhci_desc_t *int_chain[8]; - uhci_desc_t *ls_control_chain; - uhci_desc_t *control_chain; - uhci_desc_t *bulk_chain; - uhci_desc_t *chain_end; - uhci_desc_t *td1ms; - uhci_desc_t *td32ms; - struct list_head free_desc; - spinlock_t qh_lock; - spinlock_t td_lock; - struct virt_root_hub rh; //private data of the virtual root hub - int loop_usage; // URBs using bandwidth reclamation - - struct list_head urb_unlinked; // list of all unlinked urbs - long timeout_check; - int timeout_urbs; - struct pci_dev *uhci_pci; - struct pci_pool *desc_pool; - long last_error_time; // last error output in uhci_interrupt() -} uhci_t, *puhci_t; - - -#define MAKE_TD_ADDR(a) ((a)->dma_addr&~UHCI_PTR_QH) -#define MAKE_QH_ADDR(a) ((a)->dma_addr|UHCI_PTR_QH) -#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM)) - -#define CLEAN_TRANSFER_NO_DELETION 0 -#define CLEAN_TRANSFER_REGULAR 1 -#define CLEAN_TRANSFER_DELETION_MARK 2 - -#define CLEAN_NOT_FORCED 0 -#define CLEAN_FORCED 1 - -#define PROCESS_ISO_REGULAR 0 -#define PROCESS_ISO_FORCE 1 - -#define UNLINK_ASYNC_STORE_URB 0 -#define UNLINK_ASYNC_DONT_STORE 1 - -#define is_td_active(desc) (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE)) - -#define set_qh_head(desc,val) (desc)->hw.qh.head=cpu_to_le32(val) -#define set_qh_element(desc,val) (desc)->hw.qh.element=cpu_to_le32(val) -#define set_td_link(desc,val) (desc)->hw.td.link=cpu_to_le32(val) -#define set_td_ioc(desc) (desc)->hw.td.status |= cpu_to_le32(TD_CTRL_IOC) -#define clr_td_ioc(desc) (desc)->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC) - - -/* ------------------------------------------------------------------------------------ - Virtual Root HUB - ------------------------------------------------------------------------------------ */ -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -/* Our Vendor Specific feature */ -#define RH_REMOVE_EP 0x00 - - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -#endif