/* * SL811 Host Controller Interface driver for USB. * * Copyright (c) 2003/06, Courage Co., Ltd. * * Based on: * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, * Adam Richter, Gregory P. Smith; * 2.Original SL811 driver (hc_sl811.o) by Pei Liu * 3.Rewrited as sl811.o by Yin Aihua * * It's now support isochornous mode and more effective than hc_sl811.o * Support x86 architecture now. * * 19.09.2003 (05.06.2003) HNE * sl811_alloc_hc: Set "bus->bus_name" at init. * sl811_reg_test (hc_reset,regTest): * Stop output at first failed pattern. * Down-Grade for Kernel 2.4.20 and from 2.4.22 * Splitt hardware depens into file sl811-x86.h and sl811-arm.h. * * 22.09.2003 HNE * sl811_found_hc: First patterntest, than interrupt enable. * Do nothing, if patterntest failed. Release io, if failed. * Stop Interrupts first, than remove handle. (Old blocked Shred IRQ) * Alternate IO-Base for second Controller (CF/USB1). * * 24.09.2003 HNE * Remove all arm specific source (moved into include/asm/sl811-hw.h). * * 03.10.2003 HNE * Low level only for port io into hardware-include. * * To do: * 1.Modify the timeout part, it's some messy * 2.Use usb-a and usb-b set in Ping-Pong mode * o Floppy do not work. * o driver crash, if io region can't register * o Only as module tested! Compiled in Version not tested! * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../hcd.h" #include "../hub.h" #include "sl811.h" #define DRIVER_VERSION "v0.30" #define MODNAME "SL811" #define DRIVER_AUTHOR "Yin Aihua , Henry Nestler " #define DRIVER_DESC "Sl811 USB Host Controller Alternate Driver" static LIST_HEAD(sl811_hcd_list); /* * 0, normal prompt and information * 1, error should not occur in normal * 2, error maybe occur in normal * 3, useful and detail debug information * 4, function level enter and level inforamtion * 5, endless information will output because of timer function or interrupt */ static int debug = 0; MODULE_PARM(debug,"i"); MODULE_PARM_DESC(debug,"debug level"); #include /* Include hardware and board depens */ static void sl811_rh_int_timer_do(unsigned long ptr); static void sl811_transfer_done(struct sl811_hc *hc, int sof); /* * Read a byte of data from the SL811H/SL11H */ static __u8 inline sl811_read(struct sl811_hc *hc, __u8 offset) { sl811_write_index (hc, offset); return (sl811_read_data (hc)); } /* * Write a byte of data to the SL811H/SL11H */ static void inline sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data) { sl811_write_index_data (hc, offset, data); } /* * Read consecutive bytes of data from the SL811H/SL11H buffer */ static void inline sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) { sl811_write_index (hc, offset); while (size--) { *buf++ = sl811_read_data(hc); } } /* * Write consecutive bytes of data to the SL811H/SL11H buffer */ static void inline sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) { sl811_write_index (hc, offset); while (size--) { sl811_write_data (hc, *buf); buf++; } } /* * This routine test the Read/Write functionality of SL811HS registers */ static int sl811_reg_test(struct sl811_hc *hc) { int i, data, result = 0; __u8 buf[256]; for (i = 0x10; i < 256; i++) { /* save the original buffer */ buf[i] = sl811_read(hc, i); /* Write the new data to the buffer */ sl811_write(hc, i, i); } /* compare the written data */ for (i = 0x10; i < 256; i++) { data = sl811_read(hc, i); if (data != i) { PDEBUG(1, "Pattern test failed!! value = 0x%x, s/b 0x%x", data, i); result = -1; /* If no Debug, show only first failed Address */ if (!debug) break; } } /* restore the data */ for (i = 0x10; i < 256; i++) sl811_write(hc, i, buf[i]); return result; } /* * Display all SL811HS register values */ #if 0 /* unused (hne) */ static void sl811_reg_show(struct sl811_hc *hc) { int i; for (i = 0; i < 256; i++) PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i)); } #endif /* * This function enables SL811HS interrupts */ static void sl811_enable_interrupt(struct sl811_hc *hc) { PDEBUG(4, "enter"); sl811_write(hc, SL811_INTR, SL811_INTR_DONE_A | SL811_INTR_SOF | SL811_INTR_INSRMV); } /* * This function disables SL811HS interrupts */ static void sl811_disable_interrupt(struct sl811_hc *hc) { PDEBUG(4, "enter"); // Disable all other interrupt except for insert/remove. sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); } /* * SL811 Virtual Root Hub */ /* Device descriptor */ static __u8 sl811_rh_dev_des[] = { 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ 0x10, /* __u16 bcdUSB; v1.1 */ 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 sl811_rh_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; */ 0x00, 0xff /* __u8 ep_bInterval; 255 ms */ }; /* root hub class descriptor*/ static __u8 sl811_rh_hub_des[] = { 0x09, /* __u8 bLength; */ 0x29, /* __u8 bDescriptorType; Hub-descriptor */ 0x01, /* __u8 bNbrPorts; */ 0x00, /* __u16 wHubCharacteristics; */ 0x00, 0x50, /* __u8 bPwrOn2pwrGood; 2ms */ 0x00, /* __u8 bHubContrCurrent; 0 mA */ 0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ }; /* * This function examine the port change in the virtual root hub. HUB INTERRUPT ENDPOINT. */ static int sl811_rh_send_irq(struct sl811_hc *hc, __u8 *rh_change, int rh_len) { __u8 data = 0; PDEBUG(5, "enter"); /* * Right now, It is assume the power is good and no changes and only one port. */ if (hc->rh_status.wPortChange & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) { data = 1<<1; *(__u8 *)rh_change = data; return 1; } else return 0; } /* * This function creates a timer that act as interrupt pipe in the virtual hub. * * Note: The virtual root hub's interrupt pipe are polled by the timer * every "interval" ms */ static void sl811_rh_init_int_timer(struct urb * urb) { struct sl811_hc *hc = urb->dev->bus->hcpriv; hc->rh.interval = urb->interval; init_timer(&hc->rh.rh_int_timer); hc->rh.rh_int_timer.function = sl811_rh_int_timer_do; hc->rh.rh_int_timer.data = (unsigned long)urb; hc->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000; add_timer (&hc->rh.rh_int_timer); } /* * This function is called when the timer expires. It gets the the port * change data and pass along to the upper protocol. */ static void sl811_rh_int_timer_do(unsigned long ptr) { int len; struct urb *urb = (struct urb *)ptr; struct sl811_hc *hc = urb->dev->bus->hcpriv; PDEBUG (5, "enter"); if(hc->rh.send) { len = sl811_rh_send_irq(hc, urb->transfer_buffer, urb->transfer_buffer_length); if (len > 0) { urb->actual_length = len; if (urb->complete) urb->complete(urb); } } #ifdef SL811_TIMEOUT { struct list_head *head, *tmp; struct sl811_urb_priv *urbp; struct urb *u; int i; static int timeout_count = 0; // check time out every second if (++timeout_count > 4) { int max_scan = hc->active_urbs; timeout_count = 0; for (i = 0; i < 6; ++i) { head = &hc->urb_list[i]; tmp = head->next; while (tmp != head && max_scan--) { u = list_entry(tmp, struct urb, urb_list); urbp = (struct sl811_urb_priv *)u->hcpriv; tmp = tmp->next; // Check if the URB timed out if (u->timeout && time_after_eq(jiffies, urbp->inserttime + u->timeout)) { PDEBUG(3, "urb = %p time out, we kill it", urb); u->transfer_flags |= USB_TIMEOUT_KILLED; } } } } } #endif // re-activate the timer sl811_rh_init_int_timer(urb); } /* helper macro */ #define OK(x) len = (x); break /* * This function handles all USB request to the the virtual root hub */ static int sl811_rh_submit_urb(struct urb *urb) { struct usb_device *usb_dev = urb->dev; struct sl811_hc *hc = usb_dev->bus->hcpriv; struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet; void *data = urb->transfer_buffer; int buf_len = urb->transfer_buffer_length; unsigned int pipe = urb->pipe; __u8 data_buf[16]; __u8 *bufp = data_buf; int len = 0; int status = 0; __u16 bmRType_bReq; __u16 wValue; __u16 wIndex; __u16 wLength; if (usb_pipeint(pipe)) { hc->rh.urb = urb; hc->rh.send = 1; hc->rh.interval = urb->interval; sl811_rh_init_int_timer(urb); urb->status = 0; 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); PDEBUG(5, "submit rh urb, req = %d(%x) len=%d", bmRType_bReq, bmRType_bReq, wLength); /* Request Destination: without flags: Device, USB_RECIP_INTERFACE: interface, USB_RECIP_ENDPOINT: endpoint, USB_TYPE_CLASS means HUB here, USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here */ switch (bmRType_bReq) { case RH_GET_STATUS: *(__u16 *)bufp = cpu_to_le16(1); OK(2); case RH_GET_STATUS | USB_RECIP_INTERFACE: *(__u16 *)bufp = cpu_to_le16(0); OK(2); case RH_GET_STATUS | USB_RECIP_ENDPOINT: *(__u16 *)bufp = cpu_to_le16(0); OK(2); case RH_GET_STATUS | USB_TYPE_CLASS: *(__u32 *)bufp = cpu_to_le32(0); OK(4); case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS: *(__u32 *)bufp = cpu_to_le32(hc->rh_status.wPortChange<<16 | hc->rh_status.wPortStatus); OK(4); case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT: switch (wValue) { case 1: OK(0); } break; case RH_CLEAR_FEATURE | USB_TYPE_CLASS: switch (wValue) { case C_HUB_LOCAL_POWER: OK(0); case C_HUB_OVER_CURRENT: OK(0); } break; case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: switch (wValue) { case USB_PORT_FEAT_ENABLE: hc->rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; OK(0); case USB_PORT_FEAT_SUSPEND: hc->rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; OK(0); case USB_PORT_FEAT_POWER: hc->rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; OK(0); case USB_PORT_FEAT_C_CONNECTION: hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; OK(0); case USB_PORT_FEAT_C_ENABLE: hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; OK(0); case USB_PORT_FEAT_C_SUSPEND: hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; OK(0); case USB_PORT_FEAT_C_OVER_CURRENT: hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; OK(0); case USB_PORT_FEAT_C_RESET: hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET; OK(0); } break; case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: switch (wValue) { case USB_PORT_FEAT_SUSPEND: hc->rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; OK(0); case USB_PORT_FEAT_RESET: hc->rh_status.wPortStatus |= USB_PORT_STAT_RESET; hc->rh_status.wPortChange = 0; hc->rh_status.wPortChange |= USB_PORT_STAT_C_RESET; hc->rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; OK(0); case USB_PORT_FEAT_POWER: hc->rh_status.wPortStatus |= USB_PORT_STAT_POWER; OK(0); case USB_PORT_FEAT_ENABLE: hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; OK(0); } break; case RH_SET_ADDRESS: hc->rh.devnum = wValue; OK(0); case RH_GET_DESCRIPTOR: switch ((wValue & 0xff00) >> 8) { case USB_DT_DEVICE: len = sizeof(sl811_rh_dev_des); bufp = sl811_rh_dev_des; OK(len); case USB_DT_CONFIG: len = sizeof(sl811_rh_config_des); bufp = sl811_rh_config_des; OK(len); case USB_DT_STRING: len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength); if (len > 0) { bufp = data; OK(len); } default: status = -EPIPE; } break; case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: len = sizeof(sl811_rh_hub_des); bufp = sl811_rh_hub_des; OK(len); case RH_GET_CONFIGURATION: bufp[0] = 0x01; OK(1); case RH_SET_CONFIGURATION: OK(0); default: PDEBUG(1, "unsupported root hub command"); status = -EPIPE; } len = min(len, buf_len); if (data != bufp) memcpy(data, bufp, len); urb->actual_length = len; urb->status = status; PDEBUG(5, "len = %d, status = %d", len, status); urb->hcpriv = NULL; urb->dev = NULL; if (urb->complete) urb->complete(urb); return 0; } /* * This function unlinks the URB */ static int sl811_rh_unlink_urb(struct urb *urb) { struct sl811_hc *hc = urb->dev->bus->hcpriv; PDEBUG(5, "enter"); if (hc->rh.urb == urb) { hc->rh.send = 0; del_timer(&hc->rh.rh_int_timer); hc->rh.urb = NULL; urb->hcpriv = NULL; usb_dec_dev_use(urb->dev); urb->dev = NULL; if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; if (urb->complete) urb->complete(urb); } else urb->status = -ENOENT; } return 0; } /* * This function connect the virtual root hub to the USB stack */ static int sl811_connect_rh(struct sl811_hc * hc) { struct usb_device *usb_dev; hc->rh.devnum = 0; usb_dev = usb_alloc_dev(NULL, hc->bus); if (!usb_dev) return -ENOMEM; hc->bus->root_hub = usb_dev; usb_connect(usb_dev); if (usb_new_device(usb_dev)) { usb_free_dev(usb_dev); return -ENODEV; } PDEBUG(5, "leave success"); return 0; } /* * This function allocates private data space for the usb device */ static int sl811_alloc_dev_priv(struct usb_device *usb_dev) { return 0; } /* * This function de-allocates private data space for the usb devic */ static int sl811_free_dev_priv (struct usb_device *usb_dev) { return 0; } /* * This function allocates private data space for the urb */ static struct sl811_urb_priv* sl811_alloc_urb_priv(struct urb *urb) { struct sl811_urb_priv *urbp; urbp = kmalloc(sizeof(*urbp), GFP_ATOMIC); if (!urbp) return NULL; memset(urbp, 0, sizeof(*urbp)); INIT_LIST_HEAD(&urbp->td_list); urbp->urb = urb; urb->hcpriv = urbp; return urbp; } /* * This function free private data space for the urb */ static void sl811_free_urb_priv(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td; struct list_head *head, *tmp; if (!urbp) return ; head = &urbp->td_list; tmp = head->next; while (tmp != head) { td = list_entry(tmp, struct sl811_td, td_list); tmp = tmp->next; kfree(td); } kfree(urbp); urb->hcpriv = NULL; return ; } /* * This function calculate the bus time need by this td. * Fix me! Can this use usb_calc_bus_time()? */ static void sl811_calc_td_time(struct sl811_td *td) { #if 1 int time; int len = td->len; struct sl811_hc *hc = td->urb->dev->bus->hcpriv; if (hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) time = 8*8*len + 1024; else { if (td->ctrl & SL811_USB_CTRL_PREAMBLE) time = 8*8*len + 2048; else time = 8*len + 256; } time += 2*10 * len; td->bustime = time; #else unsigned long tmp; int time; int low_speed = usb_pipeslow(td->urb->pipe); int input_dir = usb_pipein(td->urb->pipe); int bytecount = td->len; int isoc = usb_pipeisoc(td->urb->pipe); if (low_speed) { /* no isoc. here */ if (input_dir) { tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; time = (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); } else { tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; time = (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); } } else if (!isoc){ /* for full-speed: */ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; time = (9107L + BW_HOST_DELAY + tmp); } else { /* for isoc: */ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; time = (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); } td->bustime = time / 84; #endif } /* * This function calculate the remainder bus time in current frame. */ static inline int sl811_calc_bus_remainder(struct sl811_hc *hc) { return (sl811_read(hc, SL811_SOFCNTDIV) * 64); } /* * This function allocates td for the urb */ static struct sl811_td* sl811_alloc_td(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td; td = kmalloc(sizeof (*td), GFP_ATOMIC); if (!td) return NULL; memset(td, 0, sizeof(*td)); INIT_LIST_HEAD(&td->td_list); td->urb = urb; list_add_tail(&td->td_list, &urbp->td_list); return td; } /* * Fill the td. */ static inline void sl811_fill_td(struct sl811_td *td, __u8 ctrl, __u8 addr, __u8 len, __u8 pidep, __u8 dev, __u8 *buf) { td->ctrl = ctrl; td->addr = addr; td->len = len; td->pidep = pidep; td->dev = dev; td->buf = buf; td->left = len; td->errcnt = 3; } /* * Fill the td. */ static inline void sl811_reset_td(struct sl811_td *td) { td->status = 0; td->left = td->len; td->done = 0; td->errcnt = 3; td->nakcnt = 0; td->td_status = 0; } static void sl811_print_td(int level, struct sl811_td *td) { PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n " "dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n " "buf = %p, bustime = %d, td_status = %d\n", td, td->ctrl, td->addr, td->len, td->pidep, td->dev, td->status, td->left, td->errcnt, td->done, td->buf, td->bustime, td->td_status); } /* * Isochronous transfers */ static int sl811_submit_isochronous(struct urb *urb) { __u8 dev = usb_pipedevice(urb->pipe); __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); __u8 ctrl = 0; struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = NULL; int i; PDEBUG(4, "enter, urb = %p, urbp = %p", urb, urbp); /* Can't have low speed bulk transfers */ if (usb_pipeslow(urb->pipe)) { PDEBUG(1, "error, urb = %p, low speed device", urb); return -EINVAL; } if (usb_pipeout(urb->pipe)) ctrl |= SL811_USB_CTRL_DIR_OUT; ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_ISO; for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = -EXDEV; td = sl811_alloc_td(urb); if (!td) return -ENOMEM; sl811_fill_td(td, ctrl, SL811_DATA_START, urb->iso_frame_desc[i].length, pidep, dev, urb->transfer_buffer + urb->iso_frame_desc[i].offset); sl811_calc_td_time(td); if (urbp->cur_td == NULL) urbp->cur_td = urbp->first_td = td; } urbp->last_td = td; PDEBUG(4, "leave success"); /* // for debug { struct list_head *head, *tmp; struct sl811_td *td; int i = 0; head = &urbp->td_list; tmp = head->next; if (list_empty(&urbp->td_list)) { PDEBUG(1, "bug!!! td list is empty!"); return -ENODEV; } while (tmp != head) { ++i; td = list_entry(tmp, struct sl811_td, td_list); PDEBUG(2, "td = %p, i = %d", td, i); tmp = tmp->next; } } */ return 0; } /* * Reset isochronous transfers */ static void sl811_reset_isochronous(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = NULL; struct list_head *head, *tmp; int i; PDEBUG(4, "enter, urb = %p", urb); for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = -EXDEV; } head = &urbp->td_list; tmp = head->next; while (tmp != head) { td = list_entry(tmp, struct sl811_td, td_list); tmp = tmp->next; sl811_reset_td(td); } urbp->cur_td = urbp->first_td; urb->status = -EINPROGRESS; urb->actual_length = 0; urb->error_count = 0; } /* * Result the iso urb. */ static void sl811_result_isochronous(struct urb *urb) { struct list_head *tmp, *head; struct sl811_urb_priv *urbp = urb->hcpriv; int status = 0; struct sl811_td *td; int i; PDEBUG(4, "enter, urb = %p", urb); urb->actual_length = 0; i = 0; head = &urbp->td_list; tmp = head->next; while (tmp != head) { td = list_entry(tmp, struct sl811_td, td_list); tmp = tmp->next; if (!td->done) { if (urbp->unlink) urb->status = -ENOENT; else { PDEBUG(1, "we should not get here!"); urb->status = -EXDEV; } return ; } if (td->td_status) { status = td->td_status; urb->error_count++; PDEBUG(1, "error: td = %p, td status = %d", td, td->td_status); } urb->iso_frame_desc[i].actual_length = td->len - td->left; urb->actual_length += td->len - td->left; urb->iso_frame_desc[i].status = td->td_status; ++i; if (td->left) PDEBUG(3, "short packet, td = %p, len = %d, left = %d", td, td->len, td->left); } urb->status = status; /* // for debug PDEBUG(2, "iso urb complete, len = %d, status =%d ", urb->actual_length, urb->status); */ PDEBUG(4, "leave success"); } /* * Interrupt transfers */ static int sl811_submit_interrupt(struct urb *urb) { int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); int len = urb->transfer_buffer_length; __u8 *data = urb->transfer_buffer; __u8 dev = usb_pipedevice(urb->pipe); __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); __u8 ctrl = 0; struct sl811_hc *hc = urb->dev->bus->hcpriv; struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = NULL; PDEBUG(4, "enter, urb = %p", urb); if (len > maxsze) { PDEBUG(1, "length is big than max packet size, len = %d, max packet = %d", len, maxsze); return -EINVAL; } if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)) ctrl |= SL811_USB_CTRL_PREAMBLE; ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ctrl |= SL811_USB_CTRL_TOGGLE_1; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); td = sl811_alloc_td(urb); if (!td) return -ENOMEM; sl811_fill_td(td, ctrl, SL811_DATA_START, len, pidep, dev, data); sl811_calc_td_time(td); urbp->cur_td = urbp->first_td = urbp->last_td = td; urbp->interval = 0; PDEBUG(4, "leave success"); return 0; } /* * Reset interrupt transfers */ static void sl811_reset_interrupt(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = urbp->cur_td; PDEBUG(4, "enter, interval = %d", urb->interval); td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1; if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) td->ctrl |= SL811_USB_CTRL_TOGGLE_1; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); sl811_reset_td(td); urbp->interval = urb->interval; urb->status = -EINPROGRESS; urb->actual_length = 0; } /* * Result the interrupt urb. */ static void sl811_result_interrupt(struct urb *urb) { struct list_head *tmp; struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td; int toggle; PDEBUG(4, "enter, urb = %p", urb); urb->actual_length = 0; tmp = &urbp->td_list; tmp = tmp->next; td = list_entry(tmp, struct sl811_td, td_list); // success. if (td->done && td->td_status == 0) { urb->actual_length += td->len - td->left; urb->status = 0; return ; } // tranfer is done but fail, reset the toggle. else if (td->done && td->td_status) { urb->status = td->td_status; reset_toggle: toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0; usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status); return ; } // unlink, and not do transfer yet else if (td->done == 0 && urbp->unlink && td->td_status == 0) { urb->status = -ENOENT; PDEBUG(3, "unlink and not transfer!"); return ; } // unlink, and transfer not complete yet. else if (td->done == 0 && urbp->unlink && td->td_status) { urb->status = -ENOENT; PDEBUG(3, "unlink and not complete!"); goto reset_toggle; } // must be bug!!! else {// (td->done == 0 && urbp->unlink == 0) PDEBUG(1, "we should not get here!"); urb->status = -EPIPE; return ; } } /* * Control transfers */ static int sl811_submit_control(struct urb *urb) { int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); int len = urb->transfer_buffer_length; __u8 *data = urb->transfer_buffer; __u8 dev = usb_pipedevice(urb->pipe); __u8 pidep = 0; __u8 ctrl = 0; struct sl811_hc *hc = urb->dev->bus->hcpriv; struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = NULL; PDEBUG(4, "enter, urb = %p", urb); if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)) ctrl |= SL811_USB_CTRL_PREAMBLE; /* Build SETUP TD */ pidep = PIDEP(USB_PID_SETUP, usb_pipeendpoint(urb->pipe)); ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_DIR_OUT; td = sl811_alloc_td(urb); if (!td) return -ENOMEM; sl811_fill_td(td, ctrl, SL811_DATA_START, 8, pidep, dev, urb->setup_packet); sl811_calc_td_time(td); urbp->cur_td = urbp->first_td = td; /* * If direction is "send", change the frame from SETUP (0x2D) * to OUT (0xE1). Else change it from SETUP to IN (0x69). */ pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); if (usb_pipeout(urb->pipe)) ctrl |= SL811_USB_CTRL_DIR_OUT; else ctrl &= ~SL811_USB_CTRL_DIR_OUT; /* Build the DATA TD's */ while (len > 0) { int pktsze = len; if (pktsze > maxsze) pktsze = maxsze; /* Alternate Data0/1 (start with Data1) */ ctrl ^= SL811_USB_CTRL_TOGGLE_1; td = sl811_alloc_td(urb); if (!td) return -ENOMEM; sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data); sl811_calc_td_time(td); data += pktsze; len -= pktsze; } /* Build the final TD for control status */ td = sl811_alloc_td(urb); if (!td) return -ENOMEM; /* It's IN if the pipe is an output pipe or we're not expecting data back */ if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) { pidep = PIDEP(USB_PID_IN, usb_pipeendpoint(urb->pipe)); ctrl &= ~SL811_USB_CTRL_DIR_OUT; } else { pidep = PIDEP(USB_PID_OUT, usb_pipeendpoint(urb->pipe)); ctrl |= SL811_USB_CTRL_DIR_OUT; } /* End in Data1 */ ctrl |= SL811_USB_CTRL_TOGGLE_1; sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0); sl811_calc_td_time(td); urbp->last_td = td; /* // for debug { struct list_head *head, *tmp; struct sl811_td *td; int i = 0; head = &urbp->td_list; tmp = head->next; if (list_empty(&urbp->td_list)) { PDEBUG(1, "bug!!! td list is empty!"); return -ENODEV; } while (tmp != head) { ++i; td = list_entry(tmp, struct sl811_td, td_list); PDEBUG(3, "td = %p, i = %d", td, i); tmp = tmp->next; } } */ PDEBUG(4, "leave success"); return 0; } /* * Result the control urb. */ static void sl811_result_control(struct urb *urb) { struct list_head *tmp, *head; struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td; PDEBUG(4, "enter, urb = %p", urb); if (list_empty(&urbp->td_list)) { PDEBUG(1, "td list is empty"); return ; } head = &urbp->td_list; tmp = head->next; td = list_entry(tmp, struct sl811_td, td_list); /* The first TD is the SETUP phase, check the status, but skip the count */ if (!td->done) { PDEBUG(3, "setup phase error, td = %p, done = %d", td, td->done); goto err_done; } if (td->td_status) { PDEBUG(3, "setup phase error, td = %p, td status = %d", td, td->td_status); goto err_status; } 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 sl811_td, td_list); tmp = tmp->next; if (!td->done) { PDEBUG(3, "data phase error, td = %p, done = %d", td, td->done); goto err_done; } if (td->td_status) { PDEBUG(3, "data phase error, td = %p, td status = %d", td, td->td_status); goto err_status; } urb->actual_length += td->len - td->left; // short packet. if (td->left) { PDEBUG(3, "data phase short packet, td = %p, count = %d", td, td->len - td->left); break; } } /* The last td is status phase */ td = urbp->last_td; if (!td->done) { PDEBUG(3, "status phase error, td = %p, done = %d", td, td->done); goto err_done; } if (td->td_status) { PDEBUG(3, "status phase error, td = %p, td status = %d", td, td->td_status); goto err_status; } PDEBUG(4, "leave success"); urb->status = 0; return ; err_done: if (urbp->unlink) urb->status = -ENOENT; else { PDEBUG(1, "we should not get here! td = %p", td); urb->status = -EPIPE; } return ; err_status: urb->status = td->td_status; return ; } /* * Bulk transfers */ static int sl811_submit_bulk(struct urb *urb) { int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); int len = urb->transfer_buffer_length; __u8 *data = urb->transfer_buffer; __u8 dev = usb_pipedevice(urb->pipe); __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); __u8 ctrl = 0; struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = NULL; PDEBUG(4, "enter, urb = %p", urb); if (len < 0) { PDEBUG(1, "error, urb = %p, len = %d", urb, len); return -EINVAL; } /* Can't have low speed bulk transfers */ if (usb_pipeslow(urb->pipe)) { PDEBUG(1, "error, urb = %p, low speed device", urb); return -EINVAL; } if (usb_pipeout(urb->pipe)) ctrl |= SL811_USB_CTRL_DIR_OUT; ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; /* Build the DATA TD's */ do { /* Allow zero length packets */ int pktsze = len; if (pktsze > maxsze) pktsze = maxsze; td = sl811_alloc_td(urb); if (!td) return -ENOMEM; /* Alternate Data0/1 (start with Data1) */ ctrl &= ~SL811_USB_CTRL_TOGGLE_1; if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ctrl |= SL811_USB_CTRL_TOGGLE_1; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data); sl811_calc_td_time(td); if (urbp->cur_td == NULL) urbp->cur_td = urbp->first_td = td; data += pktsze; len -= maxsze; } 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 = sl811_alloc_td(urb); if (!td) return -ENOMEM; /* Alternate Data0/1 (start with Data1) */ ctrl &= ~SL811_USB_CTRL_TOGGLE_1; if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) ctrl |= SL811_USB_CTRL_TOGGLE_1; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0); sl811_calc_td_time(td); } urbp->last_td = td; PDEBUG(4, "leave success"); return 0; } /* * Reset bulk transfers */ static int sl811_reset_bulk(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td; struct list_head *head, *tmp; PDEBUG(4, "enter, urb = %p", urb); head = &urbp->td_list; tmp = head->next; while (tmp != head) { td = list_entry(tmp, struct sl811_td, td_list); /* Alternate Data0/1 (start with Data1) */ td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1; if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) td->ctrl |= SL811_USB_CTRL_TOGGLE_1; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); sl811_reset_td(td); } urb->status = -EINPROGRESS; urb->actual_length = 0; urbp->cur_td = urbp->first_td; PDEBUG(4, "leave success"); return 0; } /* * Result the bulk urb. */ static void sl811_result_bulk(struct urb *urb) { struct list_head *tmp, *head; struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = NULL; int toggle; PDEBUG(4, "enter, urb = %p", urb); urb->actual_length = 0; head = &urbp->td_list; tmp = head->next; while (tmp != head) { td = list_entry(tmp, struct sl811_td, td_list); tmp = tmp->next; // success. if (td->done && td->td_status == 0) { urb->actual_length += td->len - td->left; // short packet if (td->left) { urb->status = 0; PDEBUG(3, "short packet, td = %p, count = %d", td, td->len - td->left); goto reset_toggle; } } // tranfer is done but fail, reset the toggle. else if (td->done && td->td_status) { urb->status = td->td_status; PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status); goto reset_toggle; } // unlink, and not do transfer yet else if (td->done == 0 && urbp->unlink && td->td_status == 0) { urb->status = -ENOENT; PDEBUG(3, "unlink and not transfer!"); return ; } // unlink, and transfer not complete yet. else if (td->done == 0 && urbp->unlink && td->td_status) { PDEBUG(3, "unlink and not complete!"); urb->status = -ENOENT; goto reset_toggle; } // must be bug!!! else {// (td->done == 0 && urbp->unlink == 0) urb->status = -EPIPE; PDEBUG(1, "we should not get here!"); return ; } } PDEBUG(4, "leave success"); urb->status = 0; return ; reset_toggle: toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0; usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); } /* * Find the first urb have the same dev and endpoint. */ static inline int sl811_find_same_urb(struct list_head *head, struct urb *urb) { struct list_head *tmp; struct urb *u; if (!head || !urb) return 0; tmp = head->next; while (tmp != head) { u = list_entry(tmp, struct urb, urb_list); if (u == urb) return 1; tmp = tmp->next; } return 0; } /* * Find the first urb have the same dev and endpoint. */ static inline struct urb* sl811_find_same_devep(struct list_head *head, struct urb *urb) { struct list_head *tmp; struct urb *u; if (!head || !urb) return NULL; tmp = head->next; while (tmp != head) { u = list_entry(tmp, struct urb, urb_list); if ((usb_pipe_endpdev(u->pipe)) == (usb_pipe_endpdev(urb->pipe))) return u; tmp = tmp->next; } return NULL; } /* * This function is called by the USB core API when an URB is available to * process. */ static int sl811_submit_urb(struct urb *urb) { struct sl811_hc *hc = urb->dev->bus->hcpriv; unsigned int pipe = urb->pipe; struct list_head *head = NULL; unsigned long flags; int bustime; int ret = 0; if (!urb) { PDEBUG(1, "urb is null"); return -EINVAL; } if (urb->hcpriv) { PDEBUG(1, "urbp is not null, urb = %p, urbp = %p", urb, urb->hcpriv); return -EINVAL; } if (!urb->dev || !urb->dev->bus || !hc) { PDEBUG(1, "dev or bus or hc is null"); return -ENODEV; } if (usb_endpoint_halted(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { PDEBUG(2, "sl811_submit_urb: endpoint_halted"); return -EPIPE; } if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) > SL811_DATA_LIMIT) { printk(KERN_ERR "Packet size is big for SL811, should < %d!\n", SL811_DATA_LIMIT); return -EINVAL; } /* a request to the virtual root hub */ if (usb_pipedevice(pipe) == hc->rh.devnum) return sl811_rh_submit_urb(urb); spin_lock_irqsave(&hc->hc_lock, flags); spin_lock(&urb->lock); switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: head = &hc->iso_list; break; case PIPE_INTERRUPT: head = &hc->intr_list; break; case PIPE_CONTROL: head = &hc->ctrl_list; break; case PIPE_BULK: head = &hc->bulk_list; break; } if (sl811_find_same_devep(head, urb)) { list_add(&urb->urb_list, &hc->wait_list); PDEBUG(4, "add to wait list"); goto out_unlock; } if (!sl811_alloc_urb_priv(urb)) { ret = -ENOMEM; goto out_unlock; } switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: if (urb->number_of_packets <= 0) { ret = -EINVAL; break; } bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { ret = bustime; break; } if (!(ret = sl811_submit_isochronous(urb))) usb_claim_bandwidth(urb->dev, urb, bustime, 1); break; case PIPE_INTERRUPT: bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) ret = bustime; else if (!(ret = sl811_submit_interrupt(urb))) usb_claim_bandwidth(urb->dev, urb, bustime, 0); break; case PIPE_CONTROL: ret = sl811_submit_control(urb); break; case PIPE_BULK: ret = sl811_submit_bulk(urb); break; } if (!ret) { ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies; list_add(&urb->urb_list, head); PDEBUG(4, "add to type list"); urb->status = -EINPROGRESS; if (++hc->active_urbs == 1) sl811_enable_interrupt(hc); goto out_unlock; } else { PDEBUG(2, "submit urb fail! error = %d", ret); sl811_free_urb_priv(urb); } out_unlock: spin_unlock(&urb->lock); spin_unlock_irqrestore(&hc->hc_lock, flags); return ret; } /* * Submit the urb the wait list. */ static int sl811_submit_urb_with_lock(struct urb *urb) { struct sl811_hc *hc = urb->dev->bus->hcpriv; struct list_head *head = NULL; int bustime; int ret = 0; spin_lock(&urb->lock); switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: head = &hc->iso_list; break; case PIPE_INTERRUPT: head = &hc->intr_list; break; case PIPE_CONTROL: head = &hc->ctrl_list; break; case PIPE_BULK: head = &hc->bulk_list; break; } if (!sl811_alloc_urb_priv(urb)) { ret = -ENOMEM; goto out_unlock; } switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: if (urb->number_of_packets <= 0) { ret = -EINVAL; break; } bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { ret = bustime; break; } if (!(ret = sl811_submit_isochronous(urb))) usb_claim_bandwidth(urb->dev, urb, bustime, 1); break; case PIPE_INTERRUPT: bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) ret = bustime; else if (!(ret = sl811_submit_interrupt(urb))) usb_claim_bandwidth(urb->dev, urb, bustime, 0); break; case PIPE_CONTROL: ret = sl811_submit_control(urb); break; case PIPE_BULK: ret = sl811_submit_bulk(urb); break; } if (ret == 0) { ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies; list_add(&urb->urb_list, head); PDEBUG(4, "add to type list"); urb->status = -EINPROGRESS; if (++hc->active_urbs == 1) sl811_enable_interrupt(hc); goto out_unlock; } else { PDEBUG(2, "submit urb fail! error = %d", ret); sl811_free_urb_priv(urb); } out_unlock: spin_unlock(&urb->lock); return ret; } /* * Reset the urb */ static void sl811_reset_urb(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: sl811_reset_isochronous(urb); break; case PIPE_INTERRUPT: sl811_reset_interrupt(urb); break; case PIPE_CONTROL: return; case PIPE_BULK: sl811_reset_bulk(urb); break; } urbp->inserttime = jiffies; } /* * Return the result of a transfer */ static void sl811_result_urb(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_hc *hc = urb->dev->bus->hcpriv; struct list_head *head = NULL; struct urb *u = NULL; int reset = 0; int ring = 0; if (urb->status != -EINPROGRESS) { PDEBUG(1, "urb status is not EINPROGRESS!"); return ; } spin_lock(&urb->lock); switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: head = &hc->iso_list; sl811_result_isochronous(urb); // if the urb is not unlink and is in a urb "ring", we reset it if (!urbp->unlink && urb->next) ring = 1; break; case PIPE_INTERRUPT: head = &hc->intr_list; sl811_result_interrupt(urb); // if the urb is not unlink and not "once" query, we reset. if (!urbp->unlink && urb->interval) reset = 1; break; case PIPE_CONTROL: head = &hc->ctrl_list; sl811_result_control(urb); break; case PIPE_BULK: head = &hc->bulk_list; sl811_result_bulk(urb); // if the urb is not unlink and is in a urb "ring", we reset it if (!urbp->unlink && urb->next) ring = 1; break; } PDEBUG(4, "result urb status = %d", urb->status); if (ring && urb->next == urb) reset = 1; if (!reset) { switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: usb_release_bandwidth(urb->dev, urb, 1); break; case PIPE_INTERRUPT: usb_release_bandwidth(urb->dev, urb, 0); break; } sl811_free_urb_priv(urb); } spin_unlock(&urb->lock); if (urb->complete) urb->complete(urb); if (reset) { spin_lock(&urb->lock); sl811_reset_urb(urb); if (usb_pipeint(urb->pipe)) list_add(&urb->urb_list, &hc->idle_intr_list); else list_add(&urb->urb_list, head); spin_unlock(&urb->lock); } else { if (--hc->active_urbs <= 0) { hc->active_urbs = 0; sl811_disable_interrupt(hc); } if (ring) u = urb->next; else u = sl811_find_same_devep(&hc->wait_list, urb); if (u) { if (!list_empty(&u->urb_list)) list_del(&u->urb_list); if (sl811_submit_urb_with_lock(u)) list_add(&u->urb_list, &hc->wait_list); } } } #ifdef SL811_TIMEOUT /* * Unlink the urb from the urb list */ static int sl811_unlink_urb(struct urb *urb) { unsigned long flags; struct sl811_hc *hc; struct sl811_urb_priv *urbp; int call = 0; int schedule = 0; int count = 0; if (!urb) { PDEBUG(1, "urb is null"); return -EINVAL; } if (!urb->dev || !urb->dev->bus) { PDEBUG(1, "dev or bus is null"); return -ENODEV; } hc = urb->dev->bus->hcpriv; urbp = urb->hcpriv; /* a request to the virtual root hub */ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) return sl811_rh_unlink_urb(urb); spin_lock_irqsave(&hc->hc_lock, flags); spin_lock(&urb->lock); // in wait list if (sl811_find_same_urb(&hc->wait_list, urb)) { PDEBUG(4, "unlink urb in wait list"); list_del_init(&urb->urb_list); urb->status = -ENOENT; call = 1; goto out; } // in intr idle list. if (sl811_find_same_urb(&hc->idle_intr_list, urb)) { PDEBUG(4, "unlink urb in idle intr list"); list_del_init(&urb->urb_list); urb->status = -ENOENT; sl811_free_urb_priv(urb); usb_release_bandwidth(urb->dev, urb, 0); if (--hc->active_urbs <= 0) { hc->active_urbs = 0; sl811_disable_interrupt(hc); } call = 1; goto out; } if (urb->status == -EINPROGRESS) { PDEBUG(3, "urb is still in progress"); urbp->unlink = 1; re_unlink: // Is it in progress? urbp = urb->hcpriv; if (urbp && hc->cur_td == urbp->cur_td) { ++count; if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) { PDEBUG(3, "unlink: cur td is still in progress! count = %d", count); re_schedule: schedule = 1; spin_unlock(&urb->lock); spin_unlock_irqrestore(&hc->hc_lock, flags); schedule_timeout(HZ/50); spin_lock_irqsave(&hc->hc_lock, flags); spin_lock(&urb->lock); } else { PDEBUG(3, "unlink: lost of interrupt? do parse! count = %d", count); spin_unlock(&urb->lock); sl811_transfer_done(hc, 0); spin_lock(&urb->lock); } goto re_unlink; } if (list_empty(&urb->urb_list)) { PDEBUG(3, "unlink: list empty!"); goto out; } if (urb->transfer_flags & USB_TIMEOUT_KILLED) { PDEBUG(3, "unlink: time out killed"); // it is timeout killed by us goto result; } else if (urb->transfer_flags & USB_ASYNC_UNLINK) { // we do nothing, just let it be processing later PDEBUG(3, "unlink async, do nothing"); goto out; } else { // synchron without callback PDEBUG(3, "unlink synchron, we wait the urb complete or timeout"); if (schedule == 0) { PDEBUG(3, "goto re_schedule"); goto re_schedule; } else { PDEBUG(3, "already scheduled"); goto result; } } } else if (!list_empty(&urb->urb_list)) { PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); //list_del_init(&urb->urb_list); //call = 1; } out: spin_unlock(&urb->lock); spin_unlock_irqrestore(&hc->hc_lock, flags); if (call && urb->complete) urb->complete(urb); return 0; result: spin_unlock(&urb->lock); list_del_init(&urb->urb_list); sl811_result_urb(urb); spin_unlock_irqrestore(&hc->hc_lock, flags); return 0; } #else /* * Unlink the urb from the urb list */ static int sl811_unlink_urb(struct urb *urb) { unsigned long flags; struct sl811_hc *hc; struct sl811_urb_priv *urbp; int call = 0; if (!urb) { PDEBUG(1, "urb is null"); return -EINVAL; } if (!urb->dev || !urb->dev->bus) { PDEBUG(1, "dev or bus is null"); return -ENODEV; } hc = urb->dev->bus->hcpriv; urbp = urb->hcpriv; /* a request to the virtual root hub */ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) return sl811_rh_unlink_urb(urb); spin_lock_irqsave(&hc->hc_lock, flags); spin_lock(&urb->lock); // in wait list if (sl811_find_same_urb(&hc->wait_list, urb)) { PDEBUG(2, "unlink urb in wait list"); list_del_init(&urb->urb_list); urb->status = -ENOENT; call = 1; goto out; } if (urb->status == -EINPROGRESS) { PDEBUG(2, "urb is still in progress"); urbp->unlink = 1; // Is it in progress? urbp = urb->hcpriv; if (urbp && hc->cur_td == urbp->cur_td) { // simple, let it out PDEBUG(2, "unlink: cur td is still in progress!"); hc->cur_td = NULL; } goto result; } else if (!list_empty(&urb->urb_list)) { PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); list_del_init(&urb->urb_list); if (urbp) goto result; else call = 1; } out: spin_unlock(&urb->lock); spin_unlock_irqrestore(&hc->hc_lock, flags); if (call && urb->complete) urb->complete(urb); return 0; result: spin_unlock(&urb->lock); list_del_init(&urb->urb_list); sl811_result_urb(urb); spin_unlock_irqrestore(&hc->hc_lock, flags); return 0; } #endif static int sl811_get_current_frame_number(struct usb_device *usb_dev) { return ((struct sl811_hc *)(usb_dev->bus->hcpriv))->frame_number; } static struct usb_operations sl811_device_operations = { sl811_alloc_dev_priv, sl811_free_dev_priv, sl811_get_current_frame_number, sl811_submit_urb, sl811_unlink_urb }; /* * This functions transmit a td. */ static inline void sl811_trans_cur_td(struct sl811_hc *hc, struct sl811_td *td) { sl811_print_td(4, td); sl811_write_buf(hc, SL811_ADDR_A, &td->addr, 4); if (td->len && (td->ctrl & SL811_USB_CTRL_DIR_OUT)) sl811_write_buf(hc, td->addr, td->buf, td->len); sl811_write(hc, SL811_CTRL_A, td->ctrl); } /* * This function checks the status of the transmitted or received packet * and copy the data from the SL811HS register into a buffer. */ static void sl811_parse_cur_td(struct sl811_hc *hc, struct sl811_td *td) { struct urb *urb = td->urb; #ifdef SL811_DEBUG int dev = usb_pipedevice(td->urb->pipe); int ep = usb_pipeendpoint(td->urb->pipe); #endif sl811_read_buf(hc, SL811_STS_A, &td->status, 2); if (td->status & SL811_USB_STS_ACK) { td->done = 1; /* if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) { PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep); td->td_status = -EILSEQ; } */ if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0) sl811_read_buf(hc, td->addr, td->buf, td->len - td->left); if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) { PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td); td->td_status = -EREMOTEIO; } else td->td_status = 0; } else if (td->status & SL811_USB_STS_STALL) { PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td); td->td_status = -EPIPE; if (urb->dev) usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe)); td->done = 1; } else if (td->status & SL811_USB_STS_OVERFLOW) { PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT); td->td_status = -EOVERFLOW; td->done = 1; } else if (td->status & SL811_USB_STS_TIMEOUT ) { PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td); td->td_status = -ETIMEDOUT; if (--td->errcnt == 0) td->done = 1; } else if (td->status & SL811_USB_STS_ERROR) { PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td); td->td_status = -EILSEQ; if (--td->errcnt == 0) td->done = 1; } else if (td->status & SL811_USB_STS_NAK) { ++td->nakcnt; PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt); td->td_status = -EINPROGRESS; if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) { PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt); td->td_status = -ETIMEDOUT; td->done = 1; } } sl811_print_td(4, td); } /* * This function checks the status of current urb. */ static int sl811_parse_cur_urb(struct urb *urb) { struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = urbp->cur_td; struct list_head *tmp; sl811_print_td(5, td); // this td not done yet. if (!td->done) return 0; // the last ld, so the urb is done. if (td == urbp->last_td) { PDEBUG(4, "urb = %p is done success", td->urb); if (usb_pipeisoc(td->urb->pipe)) PDEBUG(4, "ISO URB DONE, td = %p", td); return 1; } // iso transfer, we always advance to next td if (usb_pipeisoc(td->urb->pipe)) { tmp = &td->td_list; tmp = tmp->next; urbp->cur_td = list_entry(tmp, struct sl811_td, td_list); PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td); return 0; } // some error occur, so the urb is done. if (td->td_status) { PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status); return 1; } // short packet. if (td->left) { if (usb_pipecontrol(td->urb->pipe)) { // control packet, we advance to the last td PDEBUG(3, "ctrl short packet, advance to last td"); urbp->cur_td = urbp->last_td; return 0; } else { // interrut and bulk packet, urb is over. PDEBUG(3, "bulk or intr short packet, urb is over"); return 1; } } // we advance to next td. tmp = &td->td_list; tmp = tmp->next; urbp->cur_td = list_entry(tmp, struct sl811_td, td_list); #ifdef SL811_DEBUG PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td); sl811_print_td(5, urbp->cur_td); if (td == urbp->cur_td) PDEBUG(1, "bug!!!"); #endif return 0; } /* * Find the next td to transfer. */ static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct sl811_td *cur_td) { struct sl811_urb_priv *urbp = urb->hcpriv; PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td); // iso don't schedule the td in the same frame. if (usb_pipeisoc(cur_td->urb->pipe)) return NULL; // cur td is not complete if (!cur_td->done) return NULL; // here, urbp->cur_td is already the next td; return urbp->cur_td; } /* * Scan the list to find a active urb */ static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct list_head *next) { struct urb *urb; int i; if (list_empty(next)) return NULL; if (next == hc->cur_list) return NULL; for (i = 0; i < 4; ++i) if (next == &hc->urb_list[i]) return NULL; urb = list_entry(next, struct urb, urb_list); PDEBUG(4, "next urb in list is at %p", urb); return urb; } /* * Find the next td to transfer. */ static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head *next) { struct urb *urb = NULL; int back_loop = 1; struct list_head *old_list = hc->cur_list; // try to get next urb in the same list. if (next) { urb = sl811_get_list_next_urb(hc, next); if (!urb) ++hc->cur_list; } // try other list. if (!urb) { re_loop: // try all the list. while (hc->cur_list < &hc->urb_list[4]) { if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next))) return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td; ++hc->cur_list; } // the last list is try if (back_loop && (old_list >= &hc->ctrl_list)) { hc->cur_list = &hc->ctrl_list; back_loop = 0; goto re_loop; } } if (hc->cur_list > &hc->urb_list[3]) hc->cur_list = &hc->ctrl_list; return NULL; } /* * This function process the transfer rusult. */ static void sl811_transfer_done(struct sl811_hc *hc, int sof) { struct sl811_td *cur_td = hc->cur_td, *next_td = NULL; struct urb *cur_urb = NULL; struct list_head *next = NULL; int done; PDEBUG(5, "enter"); if (cur_td == NULL) { PDEBUG(1, "in done interrupt, but td is null, be already parsed?"); return ; } cur_urb = cur_td->urb; hc->cur_td = NULL; next = &cur_urb->urb_list; next = next->next; spin_lock(&cur_urb->lock); sl811_parse_cur_td(hc, cur_td); done = sl811_parse_cur_urb(cur_urb); spin_unlock(&cur_urb->lock); if (done) { list_del_init(&cur_urb->urb_list); cur_td = NULL; sl811_result_urb(cur_urb); } if (sof) return ; if (!done) { next_td = sl811_schedule_next_td(cur_urb, cur_td); if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > next_td->bustime)) { hc->cur_td = next_td; PDEBUG(5, "ADD TD"); sl811_trans_cur_td(hc, next_td); return ; } } while (1) { next_td = sl811_schedule_next_urb(hc, next); if (!next_td) return; if (next_td == cur_td) return; next = &next_td->urb->urb_list; next = next->next; if (sl811_calc_bus_remainder(hc) > next_td->bustime) { hc->cur_td = next_td; PDEBUG(5, "ADD TD"); sl811_trans_cur_td(hc, next_td); return ; } } } /* * */ static void inline sl811_dec_intr_interval(struct sl811_hc *hc) { struct list_head *head, *tmp; struct urb *urb; struct sl811_urb_priv *urbp; if (list_empty(&hc->idle_intr_list)) return ; head = &hc->idle_intr_list; tmp = head->next; while (tmp != head) { urb = list_entry(tmp, struct urb, urb_list); tmp = tmp->next; spin_lock(&urb->lock); urbp = urb->hcpriv; if (--urbp->interval == 0) { list_del(&urb->urb_list); list_add(&urb->urb_list, &hc->intr_list); PDEBUG(4, "intr urb active"); } spin_unlock(&urb->lock); } } /* * The sof interrupt is happen. */ static void sl811_start_sof(struct sl811_hc *hc) { struct sl811_td *next_td; #ifdef SL811_DEBUG static struct sl811_td *repeat_td = NULL; static int repeat_cnt = 1; #endif if (++hc->frame_number > 1024) hc->frame_number = 0; if (hc->active_urbs == 0) return ; sl811_dec_intr_interval(hc); if (hc->cur_td) { if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) { #ifdef SL811_DEBUG if (repeat_td == hc->cur_td) ++repeat_cnt; else { if (repeat_cnt >= 2) PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, repeat_cnt); repeat_cnt = 1; repeat_td = hc->cur_td; } #endif return ; } else { PDEBUG(2, "lost of interrupt in sof? do parse!"); sl811_transfer_done(hc, 1); // let this frame idle return; } } hc->cur_list = &hc->iso_list; if (hc->active_urbs == 0) return ; next_td = sl811_schedule_next_urb(hc, NULL); if (!next_td) { #ifdef SL811_DEBUG if (list_empty(&hc->idle_intr_list)) PDEBUG(2, "not schedule a td, why? urbs = %d", hc->active_urbs); #endif return; } if (sl811_calc_bus_remainder(hc) > next_td->bustime) { hc->cur_td = next_td; sl811_trans_cur_td(hc, next_td); } else PDEBUG(2, "bus time if not enough, why?"); } /* * This function resets SL811HS controller and detects the speed of * the connecting device * * Return: 0 = no device attached; 1 = USB device attached */ static int sl811_hc_reset(struct sl811_hc *hc) { int status ; sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); // Disable hardware SOF generation, clear all irq status. sl811_write(hc, SL811_CTRL1, 0); mdelay(2); sl811_write(hc, SL811_INTRSTS, 0xff); status = sl811_read(hc, SL811_INTRSTS); if (status & SL811_INTR_NOTPRESENT) { // Device is not present PDEBUG(0, "Device not present"); hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); return 0; } // Send SOF to address 0, endpoint 0. sl811_write(hc, SL811_LEN_B, 0); sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); sl811_write(hc, SL811_DEV_B, 0x00); sl811_write (hc, SL811_SOFLOW, SL811_12M_HI); if (status & SL811_INTR_SPEED_FULL) { /* full speed device connect directly to root hub */ PDEBUG (0, "Full speed Device attached"); sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF); /* start the SOF or EOP */ sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM); hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; mdelay(2); sl811_write (hc, SL811_INTRSTS, 0xff); } else { /* slow speed device connect directly to root-hub */ PDEBUG(0, "Low speed Device attached"); sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); /* start the SOF or EOP */ sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM); hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; mdelay(2); sl811_write(hc, SL811_INTRSTS, 0xff); } hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); return 1; } /* * Interrupt service routine. */ static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r) { __u8 status; struct sl811_hc *hc = __hc; status = sl811_read(hc, SL811_INTRSTS); if (status == 0) return ; /* Not me */ sl811_write(hc, SL811_INTRSTS, 0xff); if (status & SL811_INTR_INSRMV) { sl811_write(hc, SL811_INTR, 0); sl811_write(hc, SL811_CTRL1, 0); // wait for device stable mdelay(100); sl811_hc_reset(hc); return ; } spin_lock(&hc->hc_lock); if (status & SL811_INTR_DONE_A) { if (status & SL811_INTR_SOF) { sl811_transfer_done(hc, 1); PDEBUG(4, "sof in done!"); sl811_start_sof(hc); } else sl811_transfer_done(hc, 0); } else if (status & SL811_INTR_SOF) sl811_start_sof(hc); spin_unlock(&hc->hc_lock); return ; } /* * This function allocates all data structure and store in the * private data structure. * * Return value : data structure for the host controller */ static struct sl811_hc* __devinit sl811_alloc_hc(void) { struct sl811_hc *hc; struct usb_bus *bus; int i; PDEBUG(5, "enter"); hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL); if (!hc) return NULL; memset(hc, 0, sizeof(struct sl811_hc)); hc->rh_status.wPortStatus = USB_PORT_STAT_POWER; hc->rh_status.wPortChange = 0; hc->active_urbs = 0; INIT_LIST_HEAD(&hc->hc_hcd_list); list_add(&hc->hc_hcd_list, &sl811_hcd_list); init_waitqueue_head(&hc->waitq); for (i = 0; i < 6; ++i) INIT_LIST_HEAD(&hc->urb_list[i]); hc->cur_list = &hc->iso_list; bus = usb_alloc_bus(&sl811_device_operations); if (!bus) { kfree (hc); return NULL; } hc->bus = bus; bus->bus_name = MODNAME; bus->hcpriv = hc; return hc; } /* * This function De-allocate all resources */ static void sl811_release_hc(struct sl811_hc *hc) { PDEBUG(5, "enter"); /* disconnect all devices */ if (hc->bus->root_hub) usb_disconnect(&hc->bus->root_hub); // Stop interrupt handle if (hc->irq) free_irq(hc->irq, hc); hc->irq = 0; /* Stop interrupt for sharing, but only, if PatternTest ok */ if (hc->addr_io) { /* Disable Interrupts */ sl811_write(hc, SL811_INTR, 0); /* Remove all Interrupt events */ mdelay(2); sl811_write(hc, SL811_INTRSTS, 0xff); } /* free io regions */ sl811_release_regions(hc); usb_deregister_bus(hc->bus); usb_free_bus(hc->bus); list_del(&hc->hc_hcd_list); INIT_LIST_HEAD(&hc->hc_hcd_list); kfree (hc); } /* * This function request IO memory regions, request IRQ, and * allocate all other resources. * * Input: addr_io = first IO address * data_io = second IO address * irq = interrupt number * * Return: 0 = success or error condition */ static int __devinit sl811_found_hc(int addr_io, int data_io, int irq) { struct sl811_hc *hc; PDEBUG(5, "enter"); hc = sl811_alloc_hc(); if (!hc) return -ENOMEM; if (sl811_request_regions (hc, addr_io, data_io, MODNAME)) { PDEBUG(1, "ioport %X,%X is in use!", addr_io, data_io); sl811_release_hc(hc); return -EBUSY; } if (sl811_reg_test(hc)) { PDEBUG(1, "SL811 register test failed!"); sl811_release_hc(hc); return -ENODEV; } #ifdef SL811_DEBUG_VERBOSE { __u8 u = SL811Read (hci, SL11H_HWREVREG); // Show the hardware revision of chip PDEBUG(1, "SL811 HW: %02Xh", u); switch (u & 0xF0) { case 0x00: PDEBUG(1, "SL11H"); break; case 0x10: PDEBUG(1, "SL811HS rev1.2"); break; case 0x20: PDEBUG(1, "SL811HS rev1.5"); break; default: PDEBUG(1, "Revision unknown!"); } } #endif // SL811_DEBUG_VERBOSE sl811_init_irq(); usb_register_bus(hc->bus); if (request_irq(irq, sl811_interrupt, SA_SHIRQ, MODNAME, hc)) { PDEBUG(1, "request interrupt %d failed", irq); sl811_release_hc(hc); return -EBUSY; } hc->irq = irq; printk(KERN_INFO __FILE__ ": USB SL811 at %x,%x, IRQ %d\n", addr_io, data_io, irq); sl811_hc_reset(hc); sl811_connect_rh(hc); return 0; } /* * This is an init function, and it is the first function being called * * Return: 0 = success or error condition */ static int __init sl811_hcd_init(void) { int ret = -ENODEV; int count; PDEBUG(5, "enter"); info(DRIVER_VERSION " : " DRIVER_DESC); // registering some instance for (count = 0; count < MAX_CONTROLERS; count++) { if (io[count]) { ret = sl811_found_hc(io[count], io[count]+OFFSET_DATA_REG, irq[count]); if (ret) return (ret); } } return ret; } /* * This is a cleanup function, and it is called when module is unloaded. */ static void __exit sl811_hcd_cleanup(void) { struct list_head *list = sl811_hcd_list.next; struct sl811_hc *hc; PDEBUG(5, "enter"); for (; list != &sl811_hcd_list; ) { hc = list_entry(list, struct sl811_hc, hc_hcd_list); list = list->next; sl811_release_hc(hc); } } module_init(sl811_hcd_init); module_exit(sl811_hcd_cleanup); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC);