# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.601 -> 1.602 # (new) -> 1.1 drivers/usb/hc_simple.c # (new) -> 1.1 drivers/usb/hc_sl811_rh.c # (new) -> 1.1 drivers/usb/hc_sl811.h # (new) -> 1.1 drivers/usb/hc_simple.h # (new) -> 1.1 drivers/usb/hc_sl811.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/08/05 greg@kroah.com 1.602 # USB: added new host controller driver for HC_SL811 devices. # -------------------------------------------- # diff -Nru a/drivers/usb/hc_simple.c b/drivers/usb/hc_simple.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/hc_simple.c Mon Aug 5 14:46:51 2002 @@ -0,0 +1,1072 @@ +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * simple generic USB HCD frontend Version 0.9.5 (10/28/2001) + * for embedded HCs (SL811HS) + * + * USB URB handling, hci_ hcs_ + * URB queueing, qu_ + * Transfer scheduling, sh_ + * + * + *-------------------------------------------------------------------------* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +/* main lock for urb access */ +static spinlock_t usb_urb_lock = SPIN_LOCK_UNLOCKED; + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/* URB HCD API function layer + * * * */ + +/*************************************************************************** + * Function Name : hcs_urb_queue + * + * This function initializes the urb status and length before queueing the + * urb. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: 0 + **************************************************************************/ +static inline int hcs_urb_queue (hci_t * hci, struct urb * urb) +{ + int i; + + DBGFUNC ("enter hcs_urb_queue\n"); + if (usb_pipeisoc (urb->pipe)) { + DBGVERBOSE ("hcs_urb_queue: isoc pipe\n"); + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + + /* urb->next hack : 1 .. resub, 0 .. single shot */ + /* urb->interval = urb->next ? 1 : 0; */ + } + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->error_count = 0; + + if (usb_pipecontrol (urb->pipe)) + hc_flush_data_cache (hci, urb->setup_packet, 8); + if (usb_pipeout (urb->pipe)) + hc_flush_data_cache (hci, urb->transfer_buffer, + urb->transfer_buffer_length); + + qu_queue_urb (hci, urb); + + return 0; +} + +/*************************************************************************** + * Function Name : hcs_return_urb + * + * This function the return path of URB back to the USB core. It calls the + * the urb complete function if exist, and also handles the resubmition of + * interrupt URBs. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit + * + * Return: 0 + **************************************************************************/ +static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct usb_device *dev = urb->dev; + int resubmit = 0; + + DBGFUNC ("enter hcs_return_urb, urb pointer = 0x%x, " + "transferbuffer point = 0x%x, " + " setup packet pointer = 0x%x, context pointer = 0x%x \n", + (__u32 *) urb, (__u32 *) urb->transfer_buffer, + (__u32 *) urb->setup_packet, (__u32 *) urb->context); + if (urb_debug) + urb_print (urb, "RET", usb_pipeout (urb->pipe)); + + resubmit = urb->interval && resub_ok; + + urb->dev = urb->hcpriv = NULL; + + if (urb->complete) { + urb->complete (urb); /* call complete */ + } + + if (resubmit) { + /* requeue the URB */ + urb->dev = dev; + hcs_urb_queue (hci, urb); + } + + return 0; +} + +/*************************************************************************** + * Function Name : hci_submit_urb + * + * This function is called by the USB core API when an URB is available to + * process. This function does the following + * + * 1) Check the validity of the URB + * 2) Parse the device number from the URB + * 3) Pass the URB to the root hub routine if its intended for the hub, else + * queue the urb for the attached device. + * + * Input: urb = USB request block data structure + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_submit_urb (struct urb * urb) +{ + hci_t *hci; + unsigned int pipe = urb->pipe; + unsigned long flags; + int ret; + + DBGFUNC ("enter hci_submit_urb, pipe = 0x%x\n", urb->pipe); + if (!urb->dev || !urb->dev->bus || urb->hcpriv) + return -EINVAL; + + if (usb_endpoint_halted + (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) { + printk ("hci_submit_urb: endpoint_halted\n"); + return -EPIPE; + } + hci = (hci_t *) urb->dev->bus->hcpriv; + + /* a request to the virtual root hub */ + + if (usb_pipedevice (pipe) == hci->rh.devnum) { + if (urb_debug > 1) + urb_print (urb, "SUB-RH", usb_pipein (pipe)); + + return rh_submit_urb (urb); + } + + /* queue the URB to its endpoint-queue */ + + spin_lock_irqsave (&usb_urb_lock, flags); + ret = hcs_urb_queue (hci, urb); + if (ret != 0) { + /* error on return */ + DBGERR + ("hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x\n", + ret, urb->status); + } + + spin_unlock_irqrestore (&usb_urb_lock, flags); + + return ret; + +} + +/*************************************************************************** + * Function Name : hci_unlink_urb + * + * This function mark the URB to unlink + * + * Input: urb = USB request block data structure + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_unlink_urb (struct urb * urb) +{ + unsigned long flags; + hci_t *hci; + DECLARE_WAITQUEUE (wait, current); + void *comp = NULL; + + DBGFUNC ("enter hci_unlink_urb\n"); + + if (!urb) /* just to be sure */ + return -EINVAL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + hci = (hci_t *) urb->dev->bus->hcpriv; + + /* a request to the virtual root hub */ + if (usb_pipedevice (urb->pipe) == hci->rh.devnum) { + return rh_unlink_urb (urb); + } + + if (urb_debug) + urb_print (urb, "UNLINK", 1); + + spin_lock_irqsave (&usb_urb_lock, flags); + + if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) { + /* URB active? */ + + if (urb-> + transfer_flags & (USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED)) { + /* asynchron with callback */ + + list_del (&urb->urb_list); /* relink the urb to the del list */ + list_add (&urb->urb_list, &hci->del_list); + spin_unlock_irqrestore (&usb_urb_lock, flags); + + } else { + /* synchron without callback */ + + add_wait_queue (&hci->waitq, &wait); + + set_current_state (TASK_UNINTERRUPTIBLE); + comp = urb->complete; + urb->complete = NULL; + + list_del (&urb->urb_list); /* relink the urb to the del list */ + list_add (&urb->urb_list, &hci->del_list); + + spin_unlock_irqrestore (&usb_urb_lock, flags); + + schedule_timeout (HZ / 50); + + if (!list_empty (&urb->urb_list)) + list_del (&urb->urb_list); + + urb->complete = comp; + urb->hcpriv = NULL; + remove_wait_queue (&hci->waitq, &wait); + } + } else { + /* hcd does not own URB but we keep the driver happy anyway */ + spin_unlock_irqrestore (&usb_urb_lock, flags); + + if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { + urb->status = -ENOENT; + urb->actual_length = 0; + urb->complete (urb); + urb->status = 0; + } else { + urb->status = -ENOENT; + } + } + + return 0; +} + +/*************************************************************************** + * Function Name : hci_alloc_dev + * + * This function allocates private data space for the usb device and + * initialize the endpoint descriptor heads. + * + * Input: usb_dev = pointer to the usb device + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_alloc_dev (struct usb_device *usb_dev) +{ + struct hci_device *dev; + int i; + + DBGFUNC ("enter hci_alloc_dev\n"); + dev = kmalloc (sizeof (*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + memset (dev, 0, sizeof (*dev)); + + for (i = 0; i < 32; i++) { + INIT_LIST_HEAD (&(dev->ed[i].urb_queue)); + dev->ed[i].pipe_head = NULL; + } + + usb_dev->hcpriv = dev; + + DBGVERBOSE ("USB HC dev alloc %d bytes\n", sizeof (*dev)); + + return 0; + +} + +/*************************************************************************** + * Function Name : hci_free_dev + * + * This function de-allocates private data space for the usb devic + * + * Input: usb_dev = pointer to the usb device + * + * Return: 0 + **************************************************************************/ +static int hci_free_dev (struct usb_device *usb_dev) +{ + DBGFUNC ("enter hci_free_dev\n"); + + if (usb_dev->hcpriv) + kfree (usb_dev->hcpriv); + + usb_dev->hcpriv = NULL; + + return 0; +} + +/*************************************************************************** + * Function Name : hci_get_current_frame_number + * + * This function get the current USB frame number + * + * Input: usb_dev = pointer to the usb device + * + * Return: frame number + **************************************************************************/ +static int hci_get_current_frame_number (struct usb_device *usb_dev) +{ + hci_t *hci = usb_dev->bus->hcpriv; + DBGFUNC ("enter hci_get_current_frame_number, frame = 0x%x \r\n", + hci->frame_number); + + return (hci->frame_number); +} + +/*************************************************************************** + * List of all io-functions + **************************************************************************/ + +static struct usb_operations hci_device_operations = { + allocate: hci_alloc_dev, + deallocate: hci_free_dev, + get_frame_number: hci_get_current_frame_number, + submit_urb: hci_submit_urb, + unlink_urb: hci_unlink_urb, +}; + +/*************************************************************************** + * URB queueing: + * + * For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of + * active URBs. + * (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list) + * For every endpoint the head URB of the queued URBs is linked to one of + * those lists. + * + * The rest of the queued URBs of an endpoint are linked into a + * private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue) + * hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is + * in one of the active URB lists. + * + * The index of an endpoint consists of its number and its direction. + * + * The state of an intr and iso URB is 0. + * For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK + * Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet) + * + **************************************************************************/ + +/*************************************************************************** + * Function Name : qu_urb_timeout + * + * This function is called when the URB timeout. The function unlinks the + * URB. + * + * Input: lurb: URB + * + * Return: none + **************************************************************************/ +#ifdef HC_URB_TIMEOUT +static void qu_urb_timeout (unsigned long lurb) +{ + struct urb *urb = (struct urb *) lurb; + + DBGFUNC ("enter qu_urb_timeout\n"); + urb->transfer_flags |= USB_TIMEOUT_KILLED; + hci_unlink_urb (urb); +} +#endif + +/*************************************************************************** + * Function Name : qu_pipeindex + * + * This function gets the index of the pipe. + * + * Input: pipe: the urb pipe + * + * Return: index + **************************************************************************/ +static inline int qu_pipeindex (__u32 pipe) +{ + DBGFUNC ("enter qu_pipeindex\n"); + return (usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe) ? 0 : usb_pipeout (pipe)); +} + +/*************************************************************************** + * Function Name : qu_seturbstate + * + * This function set the state of the URB. + * + * control pipe: 3 states -- Setup, data, status + * interrupt and bulk pipe: 1 state -- data + * + * Input: urb = USB request block data structure + * state = the urb state + * + * Return: none + **************************************************************************/ +static inline void qu_seturbstate (struct urb * urb, int state) +{ + DBGFUNC ("enter qu_seturbstate\n"); + urb->pipe &= ~0x1f; + urb->pipe |= state & 0x1f; +} + +/*************************************************************************** + * Function Name : qu_urbstate + * + * This function get the current state of the URB. + * + * Input: urb = USB request block data structure + * + * Return: none + **************************************************************************/ +static inline int qu_urbstate (struct urb * urb) +{ + + DBGFUNC ("enter qu_urbstate\n"); + + return urb->pipe & 0x1f; +} + +/*************************************************************************** + * Function Name : qu_queue_active_urb + * + * This function adds the urb to the appropriate active urb list and set + * the urb state. + * + * There are four active lists: isochoronous list, interrupt list, + * control list, and bulk list. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * ed = endpoint descriptor + * + * Return: none + **************************************************************************/ +static inline void qu_queue_active_urb (hci_t * hci, struct urb * urb, epd_t * ed) +{ + int urb_state = 0; + DBGFUNC ("enter qu_queue_active_urb\n"); + switch (usb_pipetype (urb->pipe)) { + case PIPE_CONTROL: + list_add (&urb->urb_list, &hci->ctrl_list); + urb_state = US_CTRL_SETUP; + break; + + case PIPE_BULK: + list_add (&urb->urb_list, &hci->bulk_list); + if ((urb->transfer_flags & USB_ZERO_PACKET) + && urb->transfer_buffer_length > 0 + && + ((urb->transfer_buffer_length % + usb_maxpacket (urb->dev, urb->pipe, + usb_pipeout (urb->pipe))) == 0)) { + urb_state = US_BULK0; + } + break; + + case PIPE_INTERRUPT: + urb->start_frame = hci->frame_number; + list_add (&urb->urb_list, &hci->intr_list); + break; + + case PIPE_ISOCHRONOUS: + list_add (&urb->urb_list, &hci->iso_list); + break; + } + +#ifdef HC_URB_TIMEOUT + if (urb->timeout) { + ed->timeout.data = (unsigned long) urb; + ed->timeout.expires = urb->timeout + jiffies; + ed->timeout.function = qu_urb_timeout; + add_timer (&ed->timeout); + } +#endif + + qu_seturbstate (urb, urb_state); +} + +/*************************************************************************** + * Function Name : qu_queue_urb + * + * This function adds the urb to the endpoint descriptor list + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: none + **************************************************************************/ +static int qu_queue_urb (hci_t * hci, struct urb * urb) +{ + struct hci_device *hci_dev = usb_to_hci (urb->dev); + epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; + + DBGFUNC ("Enter qu_queue_urb\n"); + + /* for ISOC transfers calculate start frame index */ + + if (usb_pipeisoc (urb->pipe) && urb->transfer_flags & USB_ISO_ASAP) { + urb->start_frame = ((ed->pipe_head) ? (ed->last_iso + 1) : hci_get_current_frame_number (urb-> dev) + 1) & 0xffff; + } + + if (ed->pipe_head) { + __list_add (&urb->urb_list, ed->urb_queue.prev, + &(ed->urb_queue)); + } else { + ed->pipe_head = urb; + qu_queue_active_urb (hci, urb, ed); + if (++hci->active_urbs == 1) + hc_start_int (hci); + } + + return 0; +} + +/*************************************************************************** + * Function Name : qu_next_urb + * + * This function removes the URB from the queue and add the next URB to + * active list. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag + * + * Return: pointer to the next urb + **************************************************************************/ +static struct urb *qu_next_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct hci_device *hci_dev = usb_to_hci (urb->dev); + epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; + + DBGFUNC ("enter qu_next_urb\n"); + list_del (&urb->urb_list); + INIT_LIST_HEAD (&urb->urb_list); + if (ed->pipe_head == urb) { + +#ifdef HC_URB_TIMEOUT + if (urb->timeout) + del_timer (&ed->timeout); +#endif + + if (!--hci->active_urbs) + hc_stop_int (hci); + + if (!list_empty (&ed->urb_queue)) { + urb = list_entry (ed->urb_queue.next, struct urb, urb_list); + list_del (&urb->urb_list); + INIT_LIST_HEAD (&urb->urb_list); + ed->pipe_head = urb; + qu_queue_active_urb (hci, urb, ed); + } else { + ed->pipe_head = NULL; + urb = NULL; + } + } + return urb; +} + +/*************************************************************************** + * Function Name : qu_return_urb + * + * This function is part of the return path. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag + * + * Return: pointer to the next urb + **************************************************************************/ +static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct urb *next_urb; + + DBGFUNC ("enter qu_return_rub\n"); + next_urb = qu_next_urb (hci, urb, resub_ok); + hcs_return_urb (hci, urb, resub_ok); + return next_urb; +} + +/*************************************************************************** + * Function Name : sh_scan_iso_urb_list + * + * This function goes throught the isochronous urb list and schedule the + * the transfer. + * + * Note: This function has not tested yet + * + * Input: hci = data structure for the host controller + * list_lh = pointer to the isochronous list + * frame_number = the frame number + * + * Return: 0 = unsuccessful; 1 = successful + **************************************************************************/ +static int sh_scan_iso_urb_list (hci_t * hci, struct list_head *list_lh, + int frame_number) +{ + struct list_head *lh = list_lh->next; + struct urb *urb; + + DBGFUNC ("enter sh_scan_iso_urb_list\n"); + hci->td_array->len = 0; + + while (lh != list_lh) { + urb = list_entry (lh, struct urb, urb_list); + lh = lh->next; + if (((frame_number - urb->start_frame) & 0x7ff) < + urb->number_of_packets) { + if (!sh_add_packet (hci, urb)) { + return 0; + } else { + if (((frame_number - + urb->start_frame) & 0x7ff) > 0x400) { + if (qu_urbstate (urb) > 0) + urb = qu_return_urb (hci, urb, 1); + else + urb = qu_next_urb (hci, urb, 1); + + if (lh == list_lh && urb) + lh = &urb->urb_list; + } + } + } + } + return 1; +} + +/*************************************************************************** + * Function Name : sh_scan_urb_list + * + * This function goes through the urb list and schedule the + * the transaction. + * + * Input: hci = data structure for the host controller + * list_lh = pointer to the isochronous list + * + * Return: 0 = unsuccessful; 1 = successful + **************************************************************************/ +static int sh_scan_urb_list (hci_t * hci, struct list_head *list_lh) +{ + struct list_head *lh = NULL; + struct urb *urb; + + if (list_lh == NULL) { + DBGERR ("sh_scan_urb_list: error, list_lh == NULL\n"); + } + + DBGFUNC ("enter sh_scan_urb_list: frame# \n"); + + list_for_each (lh, list_lh) { + urb = list_entry (lh, struct urb, urb_list); + if (urb == NULL) + return 1; + if (!usb_pipeint (urb->pipe) + || (((hci->frame_number - urb->start_frame) + & 0x7ff) >= urb->interval)) { + DBGVERBOSE ("sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d\n", + urb->start_frame, hci->frame_number, urb->interval, + usb_pipeint (urb->pipe)); + if (!sh_add_packet (hci, urb)) { + return 0; + } else { + DBGVERBOSE ("INT: start: %d fr_no: %d int: %d pint: %d\n", + urb->start_frame, hci->frame_number, + urb->interval, usb_pipeint (urb->pipe)); + urb->start_frame = hci->frame_number; + return 0; + + } + } + } + return 1; +} + +/*************************************************************************** + * Function Name : sh_shedule_trans + * + * This function schedule the USB transaction. + * This function will process the endpoint in the following order: + * interrupt, control, and bulk. + * + * Input: hci = data structure for the host controller + * isSOF = flag indicate if Start Of Frame has occurred + * + * Return: 0 + **************************************************************************/ +static int sh_schedule_trans (hci_t * hci, int isSOF) +{ + int units_left = 1; + struct list_head *lh; + + if (hci == NULL) { + DBGERR ("sh_schedule_trans: hci == NULL\n"); + return 0; + } + if (hci->td_array == NULL) { + DBGERR ("sh_schedule_trans: hci->td_array == NULL\n"); + return 0; + } + + if (hci->td_array->len != 0) { + DBGERR ("ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0\n", + hci->td_array->len); + } + + /* schedule the next available interrupt transfer or the next + * stage of the interrupt transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->intr_list)) { + units_left = sh_scan_urb_list (hci, &hci->intr_list); + } + + /* schedule the next available control transfer or the next + * stage of the control transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->ctrl_list) && units_left > 0) { + units_left = sh_scan_urb_list (hci, &hci->ctrl_list); + } + + /* schedule the next available bulk transfer or the next + * stage of the bulk transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->bulk_list) && units_left > 0) { + sh_scan_urb_list (hci, &hci->bulk_list); + + /* be fair to each BULK URB (move list head around) + * only when the new SOF happens */ + + lh = hci->bulk_list.next; + list_del (&hci->bulk_list); + list_add (&hci->bulk_list, lh); + } + return 0; +} + +/*************************************************************************** + * Function Name : sh_add_packet + * + * This function forms the packet and transmit the packet. This function + * will handle all endpoint type: isochoronus, interrupt, control, and + * bulk. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: 0 = unsucessful; 1 = successful + **************************************************************************/ +static int sh_add_packet (hci_t * hci, struct urb * urb) +{ + __u8 *data = NULL; + int len = 0; + int toggle = 0; + int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + int endpoint = usb_pipeendpoint (urb->pipe); + int address = usb_pipedevice (urb->pipe); + int slow = (((urb->pipe) >> 26) & 1); + int out = usb_pipeout (urb->pipe); + int pid = 0; + int ret; + int i = 0; + int iso = 0; + + DBGFUNC ("enter sh_add_packet\n"); + if (maxps == 0) + maxps = 8; + + /* calculate len, toggle bit and add the transaction */ + switch (usb_pipetype (urb->pipe)) { + case PIPE_ISOCHRONOUS: + pid = out ? PID_OUT : PID_IN; + iso = 1; + i = hci->frame_number - urb->start_frame; + data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + len = urb->iso_frame_desc[i].length; + break; + + case PIPE_BULK: /* BULK and BULK0 */ + case PIPE_INTERRUPT: + pid = out ? PID_OUT : PID_IN; + len = urb->transfer_buffer_length - urb->actual_length; + data = urb->transfer_buffer + urb->actual_length; + toggle = usb_gettoggle (urb->dev, endpoint, out); + break; + + case PIPE_CONTROL: + switch (qu_urbstate (urb)) { + case US_CTRL_SETUP: + len = 8; + pid = PID_SETUP; + data = urb->setup_packet; + toggle = 0; + break; + + case US_CTRL_DATA: + if (!hci->last_packet_nak) { + /* The last packet received is not a nak: + * reset the nak count + */ + + hci->nakCnt = 0; + } + if (urb->transfer_buffer_length != 0) { + pid = out ? PID_OUT : PID_IN; + len = urb->transfer_buffer_length - urb->actual_length; + data = urb->transfer_buffer + urb->actual_length; + toggle = (urb->actual_length & maxps) ? 0 : 1; + usb_settoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + break; + } else { + /* correct state and fall through */ + qu_seturbstate (urb, US_CTRL_ACK); + } + + case US_CTRL_ACK: + len = 0; + + /* reply in opposite direction */ + pid = !out ? PID_OUT : PID_IN; + toggle = 1; + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + break; + } + } + + ret = + hc_add_trans (hci, len, data, toggle, maxps, slow, endpoint, + address, pid, iso, qu_urbstate (urb)); + + DBGVERBOSE ("transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x " + "max:%d\n len:%d ret:%d data:%p left:%d\n", + address, endpoint, pid, toggle, iso, slow, + maxps, len, ret, data, hci->hp.units_left); + + if (ret >= 0) { + hci->td_array->td[hci->td_array->len].urb = urb; + hci->td_array->td[hci->td_array->len].len = ret; + hci->td_array->td[hci->td_array->len].iso_index = i; + hci->td_array->len++; + hci->active_trans = 1; + return 1; + } + return 0; +} + +/*************************************************************************** + * Function Name : cc_to_error + * + * This function maps the SL811HS hardware error code to the linux USB error + * code. + * + * Input: cc = hardware error code + * + * Return: USB error code + **************************************************************************/ +static int cc_to_error (int cc) +{ + int errCode = 0; + if (cc & SL11H_STATMASK_ERROR) { + errCode |= -EILSEQ; + } else if (cc & SL11H_STATMASK_OVF) { + errCode |= -EOVERFLOW; + } else if (cc & SL11H_STATMASK_STALL) { + errCode |= -EPIPE; + } + return errCode; +} + +/*************************************************************************** + * Function Name : sh_done_list + * + * This function process the packet when it has done finish transfer. + * + * 1) It handles hardware error + * 2) It updates the URB state + * 3) If the USB transaction is complete, it start the return stack path. + * + * Input: hci = data structure for the host controller + * isExcessNak = flag tells if there excess NAK condition occurred + * + * Return: urb_state or -1 if the transaction has complete + **************************************************************************/ +static int sh_done_list (hci_t * hci, int *isExcessNak) +{ + int actbytes = 0; + int active = 0; + void *data = NULL; + int cc; + int maxps; + int toggle; + struct urb *urb; + int urb_state = 0; + int ret = 1; /* -1 parse abbort, 1 parse ok, 0 last element */ + int trans = 0; + int len; + int iso_index = 0; + int out; + int pid = 0; + int debugLen = 0; + + *isExcessNak = 0; + + DBGFUNC ("enter sh_done_list: td_array->len = 0x%x\n", + hci->td_array->len); + + debugLen = hci->td_array->len; + if (debugLen > 1) + DBGERR ("sh_done_list: td_array->len = 0x%x > 1\n", + hci->td_array->len); + + for (trans = 0; ret && trans < hci->td_array->len && trans < MAX_TRANS; + trans++) { + urb = hci->td_array->td[trans].urb; + len = hci->td_array->td[trans].len; + out = usb_pipeout (urb->pipe); + + if (usb_pipeisoc (urb->pipe)) { + iso_index = hci->td_array->td[trans].iso_index; + data = urb->transfer_buffer + urb->iso_frame_desc[iso_index].offset; + toggle = 0; + } else { + data = urb->transfer_buffer + urb->actual_length; + toggle = usb_gettoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)); + + } + urb_state = qu_urbstate (urb); + pid = out ? PID_OUT : PID_IN; + ret = hc_parse_trans (hci, &actbytes, data, &cc, &toggle, len, + pid, urb_state); + maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + + if (maxps == 0) + maxps = 8; + + active = (urb_state != US_CTRL_SETUP) && (actbytes && !(actbytes & (maxps - 1))); + + /* If the transfer is not bulk in, then it is necessary to get all + * data specify by the urb->transfer_len. + */ + + if (!(usb_pipebulk (urb->pipe) && usb_pipein (urb->pipe))) + active = active && (urb->transfer_buffer_length != urb->actual_length + actbytes); + + if (urb->transfer_buffer_length == urb->actual_length + actbytes) + active = 0; + + if ((cc & + (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | + SL11H_STATMASK_OVF | SL11H_STATMASK_STALL)) + && !(cc & SL11H_STATMASK_NAK)) { + if (++urb->error_count > 3) { + DBGERR ("done_list: excessive error: errcount = 0x%x, cc = 0x%x\n", + urb->error_count, cc); + urb_state = 0; + active = 0; + } else { + DBGERR ("done_list: packet err, cc = 0x%x, " + " urb->length = 0x%x, actual_len = 0x%x," + " urb_state =0x%x\n", + cc, urb->transfer_buffer_length, + urb->actual_length, urb_state); +// if (cc & SL11H_STATMASK_STALL) { + /* The USB function is STALLED on a control pipe (0), + * then it needs to send the SETUP command again to + * clear the STALL condition + */ + +// if (usb_pipeendpoint (urb->pipe) == 0) { +// urb_state = 2; +// active = 0; +// } +// } else + active = 1; + } + } else { + if (cc & SL11H_STATMASK_NAK) { + if (hci->nakCnt < 0x10000) { + hci->nakCnt++; + hci->last_packet_nak = 1; + active = 1; + *isExcessNak = 0; + } else { + DBGERR ("done_list: nak count exceed limit\n"); + active = 0; + *isExcessNak = 1; + hci->nakCnt = 0; + } + } else { + hci->nakCnt = 0; + hci->last_packet_nak = 0; + } + + if (urb_state != US_CTRL_SETUP) { + /* no error */ + urb->actual_length += actbytes; + usb_settoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + } + if (usb_pipeisoc (urb->pipe)) { + urb->iso_frame_desc[iso_index].actual_length = actbytes; + urb->iso_frame_desc[iso_index].status = cc_to_error (cc); + active = (iso_index < urb->number_of_packets); + } + } + if (!active) { + if (!urb_state) { + urb->status = cc_to_error (cc); + if (urb->status) { + DBGERR ("error on received packet: urb->status = 0x%x\n", + urb->status); + } + hci->td_array->len = 0; + qu_return_urb (hci, urb, 1); + return -1; + } else { + /* We do not want to decrement the urb_state if exceeded nak, + * because we need to finish the data stage of the control + * packet + */ + + if (!(*isExcessNak)) + urb_state--; + qu_seturbstate (urb, urb_state); + } + } + } + + if (urb_state < 0) + DBGERR ("ERROR: done_list, urb_state = %d, suppose > 0\n", + urb_state); + if (debugLen != hci->td_array->len) { + DBGERR ("ERROR: done_list, debugLen!= td_array->len," + "debugLen = 0x%x, hci->td_array->len = 0x%x\n", + debugLen, hci->td_array->len); + } + + hci->td_array->len = 0; + + return urb_state; +} diff -Nru a/drivers/usb/hc_simple.h b/drivers/usb/hc_simple.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/hc_simple.h Mon Aug 5 14:46:51 2002 @@ -0,0 +1,231 @@ +/*-------------------------------------------------------------------------*/ +/* list of all controllers using this driver + * */ + +static LIST_HEAD (hci_hcd_list); + +/* URB states (urb_state) */ +/* isoc, interrupt single state */ + +/* bulk transfer main state and 0-length packet */ +#define US_BULK 0 +#define US_BULK0 1 +/* three setup states */ +#define US_CTRL_SETUP 2 +#define US_CTRL_DATA 1 +#define US_CTRL_ACK 0 + +/*-------------------------------------------------------------------------*/ +/* HC private part of a device descriptor + * */ + +#define NUM_EDS 32 + +typedef struct epd { + struct urb *pipe_head; + struct list_head urb_queue; +// int urb_state; + struct timer_list timeout; + int last_iso; /* timestamp of last queued ISOC transfer */ + +} epd_t; + +struct hci_device { + epd_t ed[NUM_EDS]; +}; + +/*-------------------------------------------------------------------------*/ +/* Virtual Root HUB + */ + +#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv) + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; /* interrupt URB of root hub */ + int send; /* active flag */ + int interval; /* intervall of roothub interrupt transfers */ + struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ +}; + +#if 1 +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */ + +/* 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 + +#endif + +/*-------------------------------------------------------------------------*/ +/* struct for each HC + */ + +#define MAX_TRANS 32 + +typedef struct td { + struct urb *urb; + __u16 len; + __u16 iso_index; +} td_t; + +typedef struct td_array { + int len; + td_t td[MAX_TRANS]; +} td_array_t; + +typedef struct hci { + struct virt_root_hub rh; /* roothub */ + wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ + int active; /* HC is operating */ + + struct list_head ctrl_list; /* set of ctrl endpoints */ + struct list_head bulk_list; /* set of bulk endpoints */ + struct list_head iso_list; /* set of isoc endpoints */ + struct list_head intr_list; /* ordered (tree) set of int endpoints */ + struct list_head del_list; /* set of entpoints to be deleted */ + + td_array_t *td_array; + td_array_t a_td_array; + td_array_t i_td_array[2]; + + struct list_head hci_hcd_list; /* list of all hci_hcd */ + struct usb_bus *bus; /* our bus */ + +// int trans; /* number of transactions pending */ + int active_urbs; + int active_trans; + int frame_number; /* frame number */ + hcipriv_t hp; /* individual part of hc type */ + int nakCnt; + int last_packet_nak; + +} hci_t; + +/*-------------------------------------------------------------------------*/ +/* condition (error) CC codes and mapping OHCI like + */ + +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +/* urb interface functions */ +static int hci_get_current_frame_number (struct usb_device *usb_dev); +static int hci_unlink_urb (struct urb * urb); + +static int qu_queue_urb (hci_t * hci, struct urb * urb); + +/* root hub */ +static int rh_init_int_timer (struct urb * urb); +static int rh_submit_urb (struct urb * urb); +static int rh_unlink_urb (struct urb * urb); + +/* schedule functions */ +static int sh_add_packet (hci_t * hci, struct urb * urb); + +/* hc specific functions */ +static inline void hc_flush_data_cache (hci_t * hci, void *data, int len); +static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, + int *cc, int *toggle, int length, int pid, + int urb_state); +static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, + int maxps, int slow, int endpoint, int address, + int pid, int format, int urb_state); + +static void hc_start_int (hci_t * hci); +static void hc_stop_int (hci_t * hci); +static void SL811Write (hci_t * hci, char offset, char data); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void urb_print (struct urb * urb, char *str, int small) +{ + unsigned int pipe = urb->pipe; + int i, len; + + if (!urb->dev || !urb->dev->bus) { + dbg ("%s URB: no dev", str); + return; + } + + printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n", + str, hci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), usb_pipeendpoint (pipe), + usb_pipeout (pipe) ? 'O' : 'I', + usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC") + : (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags, + urb->actual_length, urb->transfer_buffer_length, urb->status, + urb->status); + if (!small) { + if (usb_pipecontrol (pipe)) { + printk (__FILE__ ": cmd(8):"); + for (i = 0; i < 8; i++) + printk (" %02x", ((__u8 *) urb->setup_packet)[i]); + printk ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printk (__FILE__ ": data(%d/%d):", urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length; + for (i = 0; i < 2096 && i < len; i++) + printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]); + printk ("%s stat:%d\n", i < len ? "..." : "", + urb->status); + } + } +} diff -Nru a/drivers/usb/hc_sl811.c b/drivers/usb/hc_sl811.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/hc_sl811.c Mon Aug 5 14:46:51 2002 @@ -0,0 +1,1358 @@ +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * SL811HS USB HCD for Linux Version 0.1 (10/28/2001) + * + * requires (includes) hc_simple.[hc] simple generic HCD frontend + * + * COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR INC. + * + *-------------------------------------------------------------------------* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../core/hcd.h" + +#undef HC_URB_TIMEOUT +#undef HC_SWITCH_INT +#undef HC_ENABLE_ISOC + +#define SL811_DEBUG_ERR + +#ifdef SL811_DEBUG_ERR +#define DBGERR(fmt, args...) printk(fmt,## args) +#else +#define DBGERR(fmt, args...) +#endif + +#ifdef SL811_DEBUG +#define DBG(fmt, args...) printk(fmt,## args) +#else +#define DBG(fmt, args...) +#endif + +#ifdef SL811_DEBUG_FUNC +#define DBGFUNC(fmt, args...) printk(fmt,## args) +#else +#define DBGFUNC(fmt, args...) +#endif + +#ifdef SL811_DEBUG_DATA +#define DBGDATAR(fmt, args...) printk(fmt,## args) +#define DBGDATAW(fmt, args...) printk(fmt,## args) +#else +#define DBGDATAR(fmt, args...) +#define DBGDATAW(fmt, args...) +#endif + +#ifdef SL811_DEBUG_VERBOSE +#define DBGVERBOSE(fmt, args...) printk(fmt,## args) +#else +#define DBGVERBOSE(fmt, args...) +#endif + +#define TRUE 1 +#define FALSE 0 + +#define HC_SWITCH_INT +#include "hc_sl811.h" +#include "hc_simple.h" + +static int urb_debug = 0; + +#include "hc_simple.c" +#include "hc_sl811_rh.c" + +/* The base_addr, data_reg_addr, and irq number are board specific. + * The current values are design to run on the Accelent SA1110 IDP + * NOTE: values need to modify for different development boards + */ + +static int base_addr = 0xd3800000; +static int data_reg_addr = 0xd3810000; +static int irq = 34; + +/* forware declaration */ + +int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, + int toggle, int slow, int urb_state); + +static int sofWaitCnt = 0; + +MODULE_PARM (urb_debug, "i"); +MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)"); + +MODULE_PARM (base_addr, "i"); +MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000"); +MODULE_PARM (data_reg_addr, "i"); +MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000"); +MODULE_PARM (irq, "i"); +MODULE_PARM_DESC (irq, "IRQ 34 (default)"); + +static int hc_reset (hci_t * hci); + +/*************************************************************************** + * Function Name : SL811Read + * + * Read a byte of data from the SL811H/SL11H + * + * Input: hci = data structure for the host controller + * offset = address of SL811/SL11H register or memory + * + * Return: data + **************************************************************************/ +char SL811Read (hci_t * hci, char offset) +{ + hcipriv_t *hp = &hci->hp; + char data; + writeb (offset, hp->hcport); + wmb (); + data = readb (hp->hcport2); + rmb (); + return (data); +} + +/*************************************************************************** + * Function Name : SL811Write + * + * Write a byte of data to the SL811H/SL11H + * + * Input: hci = data structure for the host controller + * offset = address of SL811/SL11H register or memory + * data = the data going to write to SL811H + * + * Return: none + **************************************************************************/ +void SL811Write (hci_t * hci, char offset, char data) +{ + hcipriv_t *hp = &hci->hp; + writeb (offset, hp->hcport); + writeb (data, hp->hcport2); + wmb (); +} + +/*************************************************************************** + * Function Name : SL811BufRead + * + * Read consecutive bytes of data from the SL811H/SL11H buffer + * + * Input: hci = data structure for the host controller + * offset = SL811/SL11H register offset + * buf = the buffer where the data will store + * size = number of bytes to read + * + * Return: none + **************************************************************************/ +void SL811BufRead (hci_t * hci, short offset, char *buf, short size) +{ + hcipriv_t *hp = &hci->hp; + if (size <= 0) + return; + writeb ((char) offset, hp->hcport); + wmb (); + DBGDATAR ("SL811BufRead: offset = 0x%x, data = ", offset); + while (size--) { + *buf++ = (char) readb (hp->hcport2); + DBGDATAR ("0x%x ", *(buf - 1)); + rmb (); + } + DBGDATAR ("\n"); +} + +/*************************************************************************** + * Function Name : SL811BufWrite + * + * Write consecutive bytes of data to the SL811H/SL11H buffer + * + * Input: hci = data structure for the host controller + * offset = SL811/SL11H register offset + * buf = the data buffer + * size = number of bytes to write + * + * Return: none + **************************************************************************/ +void SL811BufWrite (hci_t * hci, short offset, char *buf, short size) +{ + hcipriv_t *hp = &hci->hp; + if (size <= 0) + return; + writeb ((char) offset, hp->hcport); + wmb (); + DBGDATAW ("SL811BufWrite: offset = 0x%x, data = ", offset); + while (size--) { + DBGDATAW ("0x%x ", *buf); + writeb (*buf, hp->hcport2); + wmb (); + buf++; + } + DBGDATAW ("\n"); +} + +/*************************************************************************** + * Function Name : regTest + * + * This routine test the Read/Write functionality of SL811HS registers + * + * 1) Store original register value into a buffer + * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255) + * 3) Read from register + * 4) Compare the written value with the read value and make sure they are + * equivalent + * 5) Restore the original register value + * + * Input: hci = data structure for the host controller + * + * + * Return: TRUE = passed; FALSE = failed + **************************************************************************/ +int regTest (hci_t * hci) +{ + int i, data, result = TRUE; + char buf[256]; + + DBGFUNC ("Enter regTest\n"); + for (i = 0x10; i < 256; i++) { + /* save the original buffer */ + buf[i] = (char) SL811Read (hci, i); + + /* Write the new data to the buffer */ + SL811Write (hci, i, i); + } + + /* compare the written data */ + for (i = 0x10; i < 256; i++) { + data = SL811Read (hci, i); + if (data != i) { + DBGERR ("Pattern test failed!! value = 0x%x, s/b 0x%x\n", + data, i); + result = FALSE; + } + } + + /* restore the data */ + for (i = 0x10; i < 256; i++) { + SL811Write (hci, i, buf[i]); + } + + return (result); +} + +/*************************************************************************** + * Function Name : regShow + * + * Display all SL811HS register values + * + * Input: hci = data structure for the host controller + * + * Return: none + **************************************************************************/ +void regShow (hci_t * hci) +{ + int i; + for (i = 0; i < 256; i++) { + printk ("offset %d: 0x%x\n", i, SL811Read (hci, i)); + } +} + +/************************************************************************ + * Function Name : USBReset + * + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Input: hci = data structure for the host controller + * + * Return: 0 = no device attached; 1 = USB device attached + * + ***********************************************************************/ +static int USBReset (hci_t * hci) +{ + int status; + hcipriv_t *hp = &hci->hp; + + DBGFUNC ("enter USBReset\n"); + + SL811Write (hci, SL11H_CTLREG2, 0xae); + + // setup master and full speed + + SL811Write (hci, SL11H_CTLREG1, 0x08); // reset USB + mdelay (20); // 20ms + SL811Write (hci, SL11H_CTLREG1, 0); // remove SE0 + + for (status = 0; status < 100; status++) + SL811Write (hci, SL11H_INTSTATREG, 0xff); // clear all interrupt bits + + status = SL811Read (hci, SL11H_INTSTATREG); + + if (status & 0x40) // Check if device is removed + { + DBG ("USBReset: Device removed\n"); + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV); + hp->RHportStatus->portStatus &= + ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT); + + return 0; + } + + SL811Write (hci, SL11H_BUFLNTHREG_B, 0); //zero lenth + SL811Write (hci, SL11H_PIDEPREG_B, 0x50); //send SOF to EP0 + SL811Write (hci, SL11H_DEVADDRREG_B, 0x01); //address0 + SL811Write (hci, SL11H_SOFLOWREG, 0xe0); + + if (!(status & 0x80)) { + /* slow speed device connect directly to root-hub */ + + DBG ("USBReset: low speed Device attached\n"); + SL811Write (hci, SL11H_CTLREG1, 0x8); + mdelay (20); + SL811Write (hci, SL11H_SOFTMRREG, 0xee); + SL811Write (hci, SL11H_CTLREG1, 0x21); + + /* start the SOF or EOP */ + + SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); + hp->RHportStatus->portStatus |= + (PORT_CONNECT_STAT | PORT_LOW_SPEED_DEV_ATTACH_STAT); + + /* clear all interrupt bits */ + + for (status = 0; status < 20; status++) + SL811Write (hci, SL11H_INTSTATREG, 0xff); + } else { + /* full speed device connect directly to root hub */ + + DBG ("USBReset: full speed Device attached\n"); + SL811Write (hci, SL11H_CTLREG1, 0x8); + mdelay (20); + SL811Write (hci, SL11H_SOFTMRREG, 0xae); + SL811Write (hci, SL11H_CTLREG1, 0x01); + + /* start the SOF or EOP */ + + SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); + hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT); + hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT; + + /* clear all interrupt bits */ + + SL811Write (hci, SL11H_INTSTATREG, 0xff); + + } + + /* enable all interrupts */ + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV); + + return 1; +} + +/*-------------------------------------------------------------------------*/ +/* tl functions */ +static inline void hc_mark_last_trans (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + __u8 *ptd = hp->tl; + + dbg ("enter hc_mark_last_trans\n"); + if (ptd == NULL) { + printk ("hc_mark_last_trans: ptd = null\n"); + return; + } + if (hp->xferPktLen > 0) + *(ptd + hp->tl_last) |= (1 << 3); +} + +static inline void hc_flush_data_cache (hci_t * hci, void *data, int len) +{ +} + +/************************************************************************ + * Function Name : hc_add_trans + * + * This function sets up the SL811HS register and transmit the USB packets. + * + * 1) Determine if enough time within the current frame to send the packet + * 2) Load the data into the SL811HS register + * 3) Set the appropriate command to the register and trigger the transmit + * + * Input: hci = data structure for the host controller + * len = data length + * data = transmitting data + * toggle = USB toggle bit, either 0 or 1 + * maxps = maximum packet size for this endpoint + * slow = speed of the device + * endpoint = endpoint number + * address = USB address of the device + * pid = packet ID + * format = + * urb_state = the current stage of USB transaction + * + * Return: 0 = no time left to schedule the transfer + * 1 = success + * + ***********************************************************************/ +static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, + int maxps, int slow, int endpoint, int address, + int pid, int format, int urb_state) +{ + hcipriv_t *hp = &hci->hp; + __u16 speed; + int ii, jj, kk; + + DBGFUNC ("enter hc_addr_trans: len =0x%x, toggle:0x%x, endpoing:0x%x," + " addr:0x%x, pid:0x%x,format:0x%x\n", len, toggle, endpoint, + i address, pid, format); + + if (len > maxps) { + len = maxps; + } + + speed = hp->RHportStatus->portStatus; + if (speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) { +// ii = (8*7*8 + 6*3) * len + 800; + ii = 8 * 8 * len + 1024; + } else { + if (slow) { +// ii = (8*7*8 + 6*3) * len + 800; + ii = 8 * 8 * len + 2048; + } else +// ii = (8*7 + 6*3)*len + 110; + ii = 8 * len + 256; + } + + ii += 2 * 10 * len; + + jj = SL811Read (hci, SL11H_SOFTMRREG); + kk = (jj & 0xFF) * 64 - ii; + + if (kk < 0) { + DBGVERBOSE + ("hc_add_trans: no bandwidth for schedule, ii = 0x%x," + "jj = 0x%x, len =0x%x, active_trans = 0x%x\n", ii, jj, len, + hci->active_trans); + return (-1); + } + + if (pid != PID_IN) { + /* Load data into hc */ + + SL811BufWrite (hci, SL11H_DATA_START, (__u8 *) data, len); + } + + /* transmit */ + + SL11StartXaction (hci, (__u8) address, (__u8) endpoint, (__u8) pid, len, + toggle, slow, urb_state); + + return len; +} + +/************************************************************************ + * Function Name : hc_parse_trans + * + * This function checks the status of the transmitted or received packet + * and copy the data from the SL811HS register into a buffer. + * + * 1) Check the status of the packet + * 2) If successful, and IN packet then copy the data from the SL811HS register + * into a buffer + * + * Input: hci = data structure for the host controller + * actbytes = pointer to actual number of bytes + * data = data buffer + * cc = packet status + * length = the urb transmit length + * pid = packet ID + * urb_state = the current stage of USB transaction + * + * Return: 0 + ***********************************************************************/ +static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, + int *cc, int *toggle, int length, int pid, + int urb_state) +{ + __u8 addr; + __u8 len; + + DBGFUNC ("enter hc_parse_trans\n"); + + /* get packet status; convert ack rcvd to ack-not-rcvd */ + + *cc = (int) SL811Read (hci, SL11H_PKTSTATREG); + + if (*cc & + (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | SL11H_STATMASK_OVF | + SL11H_STATMASK_NAK | SL11H_STATMASK_STALL)) { + if (*cc & SL11H_STATMASK_OVF) + DBGERR ("parse trans: error recv ack, cc = 0x%x, TX_BASE_Len = " + "0x%x, TX_count=0x%x\n", *cc, + SL811Read (hci, SL11H_BUFLNTHREG), + SL811Read (hci, SL11H_XFERCNTREG)); + + } else { + DBGVERBOSE ("parse trans: recv ack, cc = 0x%x, len = 0x%x, \n", + *cc, length); + + /* Successful data */ + if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP)) { + + /* Find the base address */ + addr = SL811Read (hci, SL11H_BUFADDRREG); + + /* Find the Transmit Length */ + len = SL811Read (hci, SL11H_BUFLNTHREG); + + /* The actual data length = xmit length reg - xfer count reg */ + *actbytes = len - SL811Read (hci, SL11H_XFERCNTREG); + + if ((data != NULL) && (*actbytes > 0)) { + SL811BufRead (hci, addr, data, *actbytes); + + } else if ((data == NULL) && (*actbytes <= 0)) { + DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n", + *actbytes); + return 0; + } + } else if (pid == PID_OUT) { + *actbytes = length; + } else { + // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid); + } + *toggle = !*toggle; + } + + return 0; +} + +/************************************************************************ + * Function Name : hc_start_int + * + * This function enables SL811HS interrupts + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +static void hc_start_int (hci_t * hci) +{ +#ifdef HC_SWITCH_INT + int mask = + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET; + SL811Write (hci, IntEna, mask); +#endif +} + +/************************************************************************ + * Function Name : hc_stop_int + * + * This function disables SL811HS interrupts + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +static void hc_stop_int (hci_t * hci) +{ +#ifdef HC_SWITCH_INT + SL811Write (hci, SL11H_INTSTATREG, 0xff); +// SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV); + +#endif +} + +/************************************************************************ + * Function Name : handleInsRmvIntr + * + * This function handles the insertion or removal of device on SL811HS. + * It resets the controller and updates the port status + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +void handleInsRmvIntr (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + + USBReset (hci); + + /* Changes in connection status */ + + hp->RHportStatus->portChange |= PORT_CONNECT_CHANGE; + + /* Port Enable or Disable */ + + if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT) { + /* device is connected to the port: + * 1) Enable port + * 2) Resume ?? + */ +// hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; + + /* Over Current is not supported by the SL811 HW ?? */ + + /* How about the Port Power ?? */ + + } else { + /* Device has disconnect: + * 1) Disable port + */ + + hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT); + hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; + + } +} + +/***************************************************************** + * + * Function Name: SL11StartXaction + * + * This functions load the registers with appropriate value and + * transmit the packet. + * + * Input: hci = data structure for the host controller + * addr = USB address of the device + * epaddr = endpoint number + * pid = packet ID + * len = data length + * toggle = USB toggle bit, either 0 or 1 + * slow = speed of the device + * urb_state = the current stage of USB transaction + * + * Return: 0 = error; 1 = successful + * + *****************************************************************/ +int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, + int toggle, int slow, int urb_state) +{ + + hcipriv_t *hp = &hci->hp; + __u8 cmd = 0; + __u8 setup_data[4]; + __u16 speed; + + speed = hp->RHportStatus->portStatus; + if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow) { + cmd |= SL11H_HCTLMASK_PREAMBLE; + } + switch (pid) { + case PID_SETUP: + cmd &= SL11H_HCTLMASK_PREAMBLE; + cmd |= + (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | + SL11H_HCTLMASK_WRITE); + break; + + case PID_OUT: + cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); + cmd |= + (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | + SL11H_HCTLMASK_WRITE); + if (toggle) { + cmd |= SL11H_HCTLMASK_SEQ; + } + break; + + case PID_IN: + cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); + cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP); + break; + + default: + DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid); + return 0; + } + setup_data[0] = SL11H_DATA_START; + setup_data[1] = len; + setup_data[2] = (((pid & 0x0F) << 4) | (epaddr & 0xF)); + setup_data[3] = addr & 0x7F; + + SL811BufWrite (hci, SL11H_BUFADDRREG, (__u8 *) & setup_data[0], 4); + + SL811Write (hci, SL11H_HOSTCTLREG, cmd); + +#if 0 + /* The SL811 has a hardware flaw when hub devices sends out + * SE0 between packets. It has been found in a TI chipset and + * cypress hub chipset. It causes the SL811 to hang + * The workaround is to re-issue the preample again. + */ + + if ((cmd & SL11H_HCTLMASK_PREAMBLE)) { + SL811Write (hci, SL11H_PIDEPREG_B, 0xc0); + SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable + } +#endif + return 1; +} + +/***************************************************************** + * + * Function Name: hc_interrupt + * + * Interrupt service routine. + * + * 1) determine the causes of interrupt + * 2) clears all interrupts + * 3) calls appropriate function to service the interrupt + * + * Input: irq = interrupt line associated with the controller + * hci = data structure for the host controller + * r = holds the snapshot of the processor's context before + * the processor entered interrupt code. (not used here) + * + * Return value : None. + * + *****************************************************************/ +static void hc_interrupt (int irq, void *__hci, struct pt_regs *r) +{ + char ii; + hci_t *hci = __hci; + int isExcessNak = 0; + int urb_state = 0; + char tmpIrq = 0; + + /* Get value from interrupt status register */ + + ii = SL811Read (hci, SL11H_INTSTATREG); + + if (ii & SL11H_INTMASK_INSRMV) { + /* Device insertion or removal detected for the USB port */ + + SL811Write (hci, SL11H_INTENBLREG, 0); + SL811Write (hci, SL11H_CTLREG1, 0); + mdelay (100); // wait for device stable + handleInsRmvIntr (hci); + return; + } + + /* Clear all interrupts */ + + SL811Write (hci, SL11H_INTSTATREG, 0xff); + + if (ii & SL11H_INTMASK_XFERDONE) { + /* USB Done interrupt occurred */ + + urb_state = sh_done_list (hci, &isExcessNak); +#ifdef WARNING + if (hci->td_array->len > 0) + printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n", + hci->td_array->len); +#endif + if (hci->td_array->len == 0 && !isExcessNak + && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) { + if (urb_state == 0) { + /* All urb_state has not been finished yet! + * continue with the current urb transaction + */ + + if (hci->last_packet_nak == 0) { + if (!usb_pipecontrol + (hci->td_array->td[0].urb->pipe)) + sh_add_packet (hci, hci->td_array-> td[0].urb); + } + } else { + /* The last transaction has completed: + * schedule the next transaction + */ + + sh_schedule_trans (hci, 0); + } + } + SL811Write (hci, SL11H_INTSTATREG, 0xff); + return; + } + + if (ii & SL11H_INTMASK_SOFINTR) { + hci->frame_number = (hci->frame_number + 1) % 2048; + if (hci->td_array->len == 0) + sh_schedule_trans (hci, 1); + else { + if (sofWaitCnt++ > 100) { + /* The last transaction has not completed. + * Need to retire the current td, and let + * it transmit again later on. + * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER + * GET TO THIS POINT) + */ + + DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b: 0\n", + hci->td_array->len); + urb_print (hci->td_array->td[hci->td_array->len - 1].urb, + "INTERRUPT", 0); + sh_done_list (hci, &isExcessNak); + SL811Write (hci, SL11H_INTSTATREG, 0xff); + hci->td_array->len = 0; + sofWaitCnt = 0; + } + } + tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG); + if (tmpIrq) { + DBG ("IRQ occurred while service SOF: irq = 0x%x\n", + tmpIrq); + + /* If we receive a DONE IRQ after schedule, need to + * handle DONE IRQ again + */ + + if (tmpIrq & SL11H_INTMASK_XFERDONE) { + DBGERR ("IRQ occurred while service SOF: irq = 0x%x\n", + tmpIrq); + urb_state = sh_done_list (hci, &isExcessNak); + } + SL811Write (hci, SL11H_INTSTATREG, 0xff); + } + } else { + DBG ("SL811 ISR: unknown, int = 0x%x \n", ii); + } + + SL811Write (hci, SL11H_INTSTATREG, 0xff); + return; +} + +/***************************************************************** + * + * Function Name: hc_reset + * + * This function does register test and resets the SL811HS + * controller. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_reset (hci_t * hci) +{ + int attachFlag = 0; + + DBGFUNC ("Enter hc_reset\n"); + regTest (hci); + attachFlag = USBReset (hci); + if (attachFlag) { + setPortChange (hci, PORT_CONNECT_CHANGE); + } + return (0); +} + +/***************************************************************** + * + * Function Name: hc_alloc_trans_buffer + * + * This function allocates all transfer buffer + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_alloc_trans_buffer (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + int maxlen; + + hp->itl0_len = 0; + hp->itl1_len = 0; + hp->atl_len = 0; + + hp->itl_buffer_len = 1024; + hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len; /* 2048 */ + + maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len; + + hp->tl = kmalloc (maxlen, GFP_KERNEL); + + if (!hp->tl) + return -ENOMEM; + + memset (hp->tl, 0, maxlen); + return 0; +} + +/***************************************************************** + * + * Function Name: getPortStatusAndChange + * + * This function gets the ports status from SL811 and format it + * to a USB request format + * + * Input: hci = data structure for the host controller + * + * Return value : port status and change + * + *****************************************************************/ +static __u32 getPortStatusAndChange (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + __u32 portstatus; + + DBGFUNC ("enter getPorStatusAndChange\n"); + + portstatus = hp->RHportStatus->portChange << 16 | hp->RHportStatus->portStatus; + + return (portstatus); +} + +/***************************************************************** + * + * Function Name: setPortChange + * + * This function set the bit position of portChange. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void setPortChange (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + + switch (bitPos) { + case PORT_CONNECT_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_ENABLE_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_OVER_CURRENT_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: clrPortChange + * + * This function clear the bit position of portChange. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void clrPortChange (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_CONNECT_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_ENABLE_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_RESET_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_SUSPEND_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_OVER_CURRENT_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: clrPortStatus + * + * This function clear the bit position of portStatus. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void clrPortStatus (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_ENABLE_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: setPortStatus + * + * This function set the bit position of portStatus. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void setPortStatus (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_ENABLE_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: hc_start + * + * This function starts the root hub functionality. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_start (hci_t * hci) +{ + DBGFUNC ("Enter hc_start\n"); + + rh_connect_rh (hci); + + return 0; +} + +/***************************************************************** + * + * Function Name: hc_alloc_hci + * + * This function allocates all data structure and store in the + * private data structure. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static hci_t *__devinit hc_alloc_hci (void) +{ + hci_t *hci; + hcipriv_t *hp; + portstat_t *ps; + struct usb_bus *bus; + + DBGFUNC ("Enter hc_alloc_hci\n"); + hci = (hci_t *) kmalloc (sizeof (hci_t), GFP_KERNEL); + if (!hci) + return NULL; + + memset (hci, 0, sizeof (hci_t)); + + hp = &hci->hp; + + hp->irq = -1; + hp->hcport = -1; + + /* setup root hub port status */ + + ps = (portstat_t *) kmalloc (sizeof (portstat_t), GFP_KERNEL); + + if (!ps) + return NULL; + ps->portStatus = PORT_STAT_DEFAULT; + ps->portChange = PORT_CHANGE_DEFAULT; + hp->RHportStatus = ps; + + hci->nakCnt = 0; + hci->last_packet_nak = 0; + + hci->a_td_array.len = 0; + hci->i_td_array[0].len = 0; + hci->i_td_array[1].len = 0; + hci->td_array = &hci->a_td_array; + hci->active_urbs = 0; + hci->active_trans = 0; + INIT_LIST_HEAD (&hci->hci_hcd_list); + list_add (&hci->hci_hcd_list, &hci_hcd_list); + init_waitqueue_head (&hci->waitq); + + INIT_LIST_HEAD (&hci->ctrl_list); + INIT_LIST_HEAD (&hci->bulk_list); + INIT_LIST_HEAD (&hci->iso_list); + INIT_LIST_HEAD (&hci->intr_list); + INIT_LIST_HEAD (&hci->del_list); + + bus = usb_alloc_bus (&hci_device_operations); + if (!bus) { + kfree (hci); + return NULL; + } + + hci->bus = bus; + bus->hcpriv = (void *) hci; + + return hci; +} + +/***************************************************************** + * + * Function Name: hc_release_hci + * + * This function De-allocate all resources + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static void hc_release_hci (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + + DBGFUNC ("Enter hc_release_hci\n"); + + /* disconnect all devices */ + if (hci->bus->root_hub) + usb_disconnect (&hci->bus->root_hub); + + hc_reset (hci); + + if (hp->tl) + kfree (hp->tl); + + if (hp->hcport > 0) { + release_region (hp->hcport, 2); + hp->hcport = 0; + } + + if (hp->irq >= 0) { + free_irq (hp->irq, hci); + hp->irq = -1; + } + + usb_deregister_bus (hci->bus); + usb_free_bus (hci->bus); + + list_del (&hci->hci_hcd_list); + INIT_LIST_HEAD (&hci->hci_hcd_list); + + kfree (hci); +} + +/***************************************************************** + * + * Function Name: init_irq + * + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + * + * Input: none + * + * Return value : none + * + *****************************************************************/ +void init_irq (void) +{ + GPDR &= ~(1 << 13); + set_GPIO_IRQ_edge (1 << 13, GPIO_RISING_EDGE); +} + +/***************************************************************** + * + * Function Name: hc_found_hci + * + * This function request IO memory regions, request IRQ, and + * allocate all other resources. + * + * Input: addr = first IO address + * addr2 = second IO address + * irq = interrupt number + * + * Return: 0 = success or error condition + * + *****************************************************************/ +static int __devinit hc_found_hci (int addr, int addr2, int irq) +{ + hci_t *hci; + hcipriv_t *hp; + + DBGFUNC ("Enter hc_found_hci\n"); + hci = hc_alloc_hci (); + if (!hci) { + return -ENOMEM; + } + + init_irq (); + hp = &hci->hp; + + if (!request_region (addr, 256, "SL811 USB HOST")) { + DBGERR ("request address %d failed", addr); + hc_release_hci (hci); + return -EBUSY; + } + hp->hcport = addr; + if (!hp->hcport) { + DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport); + } + + if (!request_region (addr2, 256, "SL811 USB HOST")) { + DBGERR ("request address %d failed", addr2); + hc_release_hci (hci); + return -EBUSY; + } + hp->hcport2 = addr2; + if (!hp->hcport2) { + DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport2); + } + + if (hc_alloc_trans_buffer (hci)) { + hc_release_hci (hci); + return -ENOMEM; + } + + usb_register_bus (hci->bus); + + if (request_irq (irq, hc_interrupt, 0, "SL811", hci) != 0) { + DBGERR ("request interrupt %d failed", irq); + hc_release_hci (hci); + return -EBUSY; + } + hp->irq = irq; + + printk (KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n", + addr, addr2, irq); + hc_reset (hci); + + if (hc_start (hci) < 0) { + DBGERR ("can't start usb-%x", addr); + hc_release_hci (hci); + return -EBUSY; + } + + return 0; +} + +/***************************************************************** + * + * Function Name: hci_hcd_init + * + * This is an init function, and it is the first function being called + * + * Input: none + * + * Return: 0 = success or error condition + * + *****************************************************************/ +static int __init hci_hcd_init (void) +{ + int ret; + + DBGFUNC ("Enter hci_hcd_init\n"); + ret = hc_found_hci (base_addr, data_reg_addr, irq); + + return ret; +} + +/***************************************************************** + * + * Function Name: hci_hcd_cleanup + * + * This is a cleanup function, and it is called when module is + * unloaded. + * + * Input: none + * + * Return: none + * + *****************************************************************/ +static void __exit hci_hcd_cleanup (void) +{ + struct list_head *hci_l; + hci_t *hci; + + DBGFUNC ("Enter hci_hcd_cleanup\n"); + for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) { + hci = list_entry (hci_l, hci_t, hci_hcd_list); + hci_l = hci_l->next; + hc_release_hci (hci); + } +} + +module_init (hci_hcd_init); +module_exit (hci_hcd_cleanup); + +MODULE_AUTHOR ("Pei Liu "); +MODULE_DESCRIPTION ("USB SL811HS Host Controller Driver"); diff -Nru a/drivers/usb/hc_sl811.h b/drivers/usb/hc_sl811.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/hc_sl811.h Mon Aug 5 14:46:51 2002 @@ -0,0 +1,385 @@ +/* + * SL811HS HCD (Host Controller Driver) for USB. + * + * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC + * + * + */ + +#define GET_FRAME_NUMBER(hci) READ_REG32 (hci, HcFmNumber) + +/* + * Maximum number of root hub ports + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* control and status registers */ +#define HcRevision 0x00 +#define HcControl 0x01 +#define HcCommandStatus 0x02 +#define HcInterruptStatus 0x03 +#define HcInterruptEnable 0x04 +#define HcInterruptDisable 0x05 +#define HcFmInterval 0x0D +#define HcFmRemaining 0x0E +#define HcFmNumber 0x0F +#define HcLSThreshold 0x11 +#define HcRhDescriptorA 0x12 +#define HcRhDescriptorB 0x13 +#define HcRhStatus 0x14 +#define HcRhPortStatus 0x15 + +#define HcHardwareConfiguration 0x20 +#define HcDMAConfiguration 0x21 +#define HcTransferCounter 0x22 +#define HcuPInterrupt 0x24 +#define HcuPInterruptEnable 0x25 +#define HcChipID 0x27 +#define HcScratch 0x28 +#define HcSoftwareReset 0x29 +#define HcITLBufferLength 0x2A +#define HcATLBufferLength 0x2B +#define HcBufferStatus 0x2C +#define HcReadBackITL0Length 0x2D +#define HcReadBackITL1Length 0x2E +#define HcITLBufferPort 0x40 +#define HcATLBufferPort 0x41 + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_HCFS (3 << 6) /* BUS state mask */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +#define OHCI_USB_RESET (0 << 6) +#define OHCI_USB_RESUME (1 << 6) +#define OHCI_USB_OPER (2 << 6) +#define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_SO (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ + +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_ATD (1 << 7) /* scheduling overrun */ + +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* + * HcHardwareConfiguration + */ +#define InterruptPinEnable (1 << 0) +#define InterruptPinTrigger (1 << 1) +#define InterruptOutputPolarity (1 << 2) +#define DataBusWidth16 (1 << 3) +#define DREQOutputPolarity (1 << 5) +#define DACKInputPolarity (1 << 6) +#define EOTInputPolarity (1 << 7) +#define DACKMode (1 << 8) +#define AnalogOCEnable (1 << 10) +#define SuspendClkNotStop (1 << 11) +#define DownstreamPort15KRSel (1 << 12) + +/* + * HcDMAConfiguration + */ +#define DMAReadWriteSelect (1 << 0) +#define ITL_ATL_DataSelect (1 << 1) +#define DMACounterSelect (1 << 2) +#define DMAEnable (1 << 4) +#define BurstLen_1 0 +#define BurstLen_4 (1 << 5) +#define BurstLen_8 (2 << 5) + +/* + * HcuPInterrupt + */ +#define SOFITLInt (1 << 0) +#define ATLInt (1 << 1) +#define AllEOTInterrupt (1 << 2) +#define OPR_Reg (1 << 4) +#define HCSuspended (1 << 5) +#define ClkReady (1 << 6) + +/* + * HcBufferStatus + */ +#define ITL0BufferFull (1 << 0) +#define ITL1BufferFull (1 << 1) +#define ATLBufferFull (1 << 2) +#define ITL0BufferDone (1 << 3) +#define ITL1BufferDone (1 << 4) +#define ATLBufferDone (1 << 5) + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status */ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#define URB_DEL 1 + +#define PORT_STAT_DEFAULT 0x0100 +#define PORT_CONNECT_STAT 0x1 +#define PORT_ENABLE_STAT 0x2 +#define PORT_SUSPEND_STAT 0x4 +#define PORT_OVER_CURRENT_STAT 0x8 +#define PORT_RESET_STAT 0x10 +#define PORT_POWER_STAT 0x100 +#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200 + +#define PORT_CHANGE_DEFAULT 0x0 +#define PORT_CONNECT_CHANGE 0x1 +#define PORT_ENABLE_CHANGE 0x2 +#define PORT_SUSPEND_CHANGE 0x4 +#define PORT_OVER_CURRENT_CHANGE 0x8 +#define PORT_RESET_CHANGE 0x10 + +/* Port Status Request info */ + +typedef struct portstat { + __u16 portChange; + __u16 portStatus; +} portstat_t; + +typedef struct hcipriv { + int irq; + int disabled; /* e.g. got a UE, we're hung */ + atomic_t resume_count; /* defending against multiple resumes */ + struct ohci_regs *regs; /* OHCI controller's memory */ + int hcport; /* I/O base address */ + int hcport2; /* I/O data reg addr */ + + struct portstat *RHportStatus; /* root hub port status */ + + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + + int frame; + + __u8 *tl; + int xferPktLen; + int atl_len; + int atl_buffer_len; + int itl0_len; + int itl1_len; + int itl_buffer_len; + int itl_index; + int tl_last; + int units_left; + +} hcipriv_t; +struct hci; + +#define cClt 0 // Control +#define cISO 1 // ISO +#define cBULK 2 // BULK +#define cInt 3 // Interrupt +#define ISO_BIT 0x10 + +/*------------------------------------------------------------------------- + * EP0 use for configuration and Vendor Specific command interface + *------------------------------------------------------------------------*/ +#define cMemStart 0x10 +#define EP0Buf 0x40 /* SL11H/SL811H memory start at 0x40 */ +#define EP0Len 0x40 /* Length of config buffer EP0Buf */ +#define EP1Buf 0x60 +#define EP1Len 0x40 + +/*------------------------------------------------------------------------- + * SL11H/SL811H memory from 80h-ffh use as ping-pong buffer. + *------------------------------------------------------------------------*/ +#define uBufA 0x80 /* buffer A address for DATA0 */ +#define uBufB 0xc0 /* buffer B address for DATA1 */ +#define uXferLen 0x40 /* xfer length */ +#define sMemSize 0xc0 /* Total SL11 memory size */ +#define cMemEnd 256 + +/*------------------------------------------------------------------------- + * SL811H Register Control memory map + * --Note: + * --SL11H only has one control register set from 0x00-0x04 + * --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c + *------------------------------------------------------------------------*/ + +#define EP0Control 0x00 +#define EP0Address 0x01 +#define EP0XferLen 0x02 +#define EP0Status 0x03 +#define EP0Counter 0x04 + +#define EP1Control 0x08 +#define EP1Address 0x09 +#define EP1XferLen 0x0a +#define EP1Status 0x0b +#define EP1Counter 0x0c + +#define CtrlReg 0x05 +#define IntEna 0x06 + // 0x07 is reserved +#define IntStatus 0x0d +#define cDATASet 0x0e +#define cSOFcnt 0x0f +#define IntMask 0x57 /* Reset|DMA|EP0|EP2|EP1 for IntEna */ +#define HostMask 0x47 /* Host request command for IntStatus */ +#define ReadMask 0xd7 /* Read mask interrupt for IntStatus */ + +/*------------------------------------------------------------------------- + * Standard Chapter 9 definition + *------------------------------------------------------------------------- + */ +#define GET_STATUS 0x00 +#define CLEAR_FEATURE 0x01 +#define SET_FEATURE 0x03 +#define SET_ADDRESS 0x05 +#define GET_DESCRIPTOR 0x06 +#define SET_DESCRIPTOR 0x07 +#define GET_CONFIG 0x08 +#define SET_CONFIG 0x09 +#define GET_INTERFACE 0x0a +#define SET_INTERFACE 0x0b +#define SYNCH_FRAME 0x0c + +#define DEVICE 0x01 +#define CONFIGURATION 0x02 +#define STRING 0x03 +#define INTERFACE 0x04 +#define ENDPOINT 0x05 + +/*------------------------------------------------------------------------- + * SL11H/SL811H definition + *------------------------------------------------------------------------- + */ +#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0) +#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1) +#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0) +#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1) +#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0) +#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1) + +#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition +#define PID_SOF 0xA5 +#define PID_IN 0x69 +#define PID_OUT 0xe1 + +#define MAX_RETRY 0xffff +#define TIMEOUT 5 /* 2 mseconds */ + +#define SL11H_HOSTCTLREG 0 +#define SL11H_BUFADDRREG 1 +#define SL11H_BUFLNTHREG 2 +#define SL11H_PKTSTATREG 3 /* read */ +#define SL11H_PIDEPREG 3 /* write */ +#define SL11H_XFERCNTREG 4 /* read */ +#define SL11H_DEVADDRREG 4 /* write */ +#define SL11H_CTLREG1 5 +#define SL11H_INTENBLREG 6 + +#define SL11H_HOSTCTLREG_B 8 +#define SL11H_BUFADDRREG_B 9 +#define SL11H_BUFLNTHREG_B 0x0A +#define SL11H_PKTSTATREG_B 0x0B /* read */ +#define SL11H_PIDEPREG_B 0x0B /* write */ +#define SL11H_XFERCNTREG_B 0x0C /* read */ +#define SL11H_DEVADDRREG_B 0x0C /* write */ + +#define SL11H_INTSTATREG 0x0D /* write clears bitwise */ +#define SL11H_HWREVREG 0x0E /* read */ +#define SL11H_SOFLOWREG 0x0E /* write */ +#define SL11H_SOFTMRREG 0x0F /* read */ +#define SL11H_CTLREG2 0x0F /* write */ +#define SL11H_DATA_START 0x10 + +/* Host control register bits (addr 0) */ +#define SL11H_HCTLMASK_ARM 1 +#define SL11H_HCTLMASK_ENBLEP 2 +#define SL11H_HCTLMASK_WRITE 4 +#define SL11H_HCTLMASK_ISOCH 0x10 +#define SL11H_HCTLMASK_AFTERSOF 0x20 +#define SL11H_HCTLMASK_SEQ 0x40 +#define SL11H_HCTLMASK_PREAMBLE 0x80 + +/* Packet status register bits (addr 3) */ +#define SL11H_STATMASK_ACK 1 +#define SL11H_STATMASK_ERROR 2 +#define SL11H_STATMASK_TMOUT 4 +#define SL11H_STATMASK_SEQ 8 +#define SL11H_STATMASK_SETUP 0x10 +#define SL11H_STATMASK_OVF 0x20 +#define SL11H_STATMASK_NAK 0x40 +#define SL11H_STATMASK_STALL 0x80 + +/* Control register 1 bits (addr 5) */ +#define SL11H_CTL1MASK_DSBLSOF 1 +#define SL11H_CTL1MASK_NOTXEOF2 4 +#define SL11H_CTL1MASK_DSTATE 0x18 +#define SL11H_CTL1MASK_NSPD 0x20 +#define SL11H_CTL1MASK_SUSPEND 0x40 +#define SL11H_CTL1MASK_CLK12 0x80 + +#define SL11H_CTL1VAL_RESET 8 + +/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */ +#define SL11H_INTMASK_XFERDONE 1 +#define SL11H_INTMASK_SOFINTR 0x10 +#define SL11H_INTMASK_INSRMV 0x20 +#define SL11H_INTMASK_USBRESET 0x40 +#define SL11H_INTMASK_DSTATE 0x80 /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0xE) */ +#define SL11H_HWRMASK_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0xF) */ +#define SL11H_CTL2MASK_SOFHI 0x3F +#define SL11H_CTL2MASK_DSWAP 0x40 +#define SL11H_CTL2MASK_HOSTMODE 0xae + diff -Nru a/drivers/usb/hc_sl811_rh.c b/drivers/usb/hc_sl811_rh.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/hc_sl811_rh.c Mon Aug 5 14:46:51 2002 @@ -0,0 +1,526 @@ + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * SL811HS virtual root hub + * + * based on usb-ohci.c by R. Weissgaerber et al. + *-------------------------------------------------------------------------* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#undef DEBUG +#endif +static __u32 getPortStatusAndChange (hci_t * hci); +static void setPortStatus (hci_t * hci, __u16 bitPos); +static void setPortChange (hci_t * hci, __u16 bitPos); +static void clrPortStatus (hci_t * hci, __u16 bitPos); +static void clrPortChange (hci_t * hci, __u16 bitPos); +static int USBReset (hci_t * hci); +static int cc_to_error (int cc); + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_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 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 */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + +/*************************************************************************** + * Function Name : rh_send_irq + * + * This function examine the port change in the virtual root hub. + * + * Note: This function assumes only one port exist in the root hub. + * + * Input: hci = data structure for the host controller + * rh_data = The pointer to port change data + * rh_len = length of the data in bytes + * + * Return: length of data + **************************************************************************/ +static int rh_send_irq (hci_t * hci, void *rh_data, int rh_len) +{ + int num_ports; + int i; + int ret; + int len; + __u8 data[8]; + + DBGFUNC ("enter rh_send_irq: \n"); + + /* Assuming the root hub has one port. This value need to change if + * there are more than one port for the root hub + */ + + num_ports = 1; + + /* The root hub status is not implemented, it basically has two fields: + * -- Local Power Status + * -- Over Current Indicator + * -- Local Power Change + * -- Over Current Indicator + * + * Right now, It is assume the power is good and no changes + */ + + *(__u8 *) data = 0; + + ret = *(__u8 *) data; + + /* Has the port status change within the root hub: It checks for + * -- Port Connect Status change + * -- Port Enable Change + */ + + for (i = 0; i < num_ports; i++) { + *(__u8 *) (data + (i + 1) / 8) |= + (((getPortStatusAndChange (hci) >> 16) & (PORT_CONNECT_STAT | PORT_ENABLE_STAT)) ? 1 : 0) << ((i + 1) % 8); + ret += *(__u8 *) (data + (i + 1) / 8); + + /* After the port change is read, it should be reset so the next time + * is it doesn't trigger a change again */ + + } + len = i / 8 + 1; + + if (ret > 0) { + memcpy (rh_data, data, min (len, min (rh_len, (int)sizeof (data)))); + return len; + } + return 0; +} + +/*************************************************************************** + * Function Name : rh_int_timer_do + * + * This function is called when the timer expires. It gets the the port + * change data and pass along to the upper protocol. + * + * Note: The virtual root hub interrupt pipe are polled by the timer + * every "interval" ms + * + * Input: ptr = ptr to the urb + * + * Return: none + **************************************************************************/ +static void rh_int_timer_do (unsigned long ptr) +{ + int len; + struct urb *urb = (struct urb *) ptr; + hci_t *hci = urb->dev->bus->hcpriv; + + DBGFUNC ("enter rh_int_timer_do\n"); + + if (hci->rh.send) { + len = rh_send_irq (hci, urb->transfer_buffer, + urb->transfer_buffer_length); + if (len > 0) { + urb->actual_length = len; + if (urb_debug == 2) + urb_print (urb, "RET-t(rh)", + usb_pipeout (urb->pipe)); + + if (urb->complete) { + urb->complete (urb); + } + } + } + + /* re-activate the timer */ + rh_init_int_timer (urb); +} + +/*************************************************************************** + * Function Name : rh_init_int_timer + * + * 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 + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_init_int_timer (struct urb * urb) +{ + hci_t *hci = urb->dev->bus->hcpriv; + hci->rh.interval = urb->interval; + + init_timer (&hci->rh.rh_int_timer); + hci->rh.rh_int_timer.function = rh_int_timer_do; + hci->rh.rh_int_timer.data = (unsigned long) urb; + hci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + add_timer (&hci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* helper macro */ +#define OK(x) len = (x); break + +/*************************************************************************** + * Function Name : rh_submit_urb + * + * This function handles all USB request to the the virtual root hub + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_submit_urb (struct urb * urb) +{ + struct usb_device *usb_dev = urb->dev; + hci_t *hci = 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 = TD_CC_NOERROR; + __u32 datab[4]; + __u8 *data_buf = (__u8 *) datab; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + DBGFUNC ("enter rh_submit_urb\n"); + if (usb_pipeint (pipe)) { + hci->rh.urb = urb; + hci->rh.send = 1; + hci->rh.interval = urb->interval; + rh_init_int_timer (urb); + urb->status = cc_to_error (TD_CC_NOERROR); + + 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); + + DBG ("rh_submit_urb, req = %d(%x) len=%d", + bmRType_bReq, bmRType_bReq, 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_buf = cpu_to_le16 (1); + OK (2); + + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = cpu_to_le16 (0); + OK (2); + + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = cpu_to_le16 (0); + OK (2); + + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32 (0); + OK (4); + + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = + cpu_to_le32 (getPortStatusAndChange (hci)); + 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_LOCAL_POWER: + OK (0); + + case (RH_C_HUB_OVER_CURRENT): + /* Over Current Not Implemented */ + OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + clrPortStatus (hci, PORT_ENABLE_STAT); + OK (0); + + case (RH_PORT_SUSPEND): + clrPortStatus (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_PORT_POWER): + clrPortStatus (hci, PORT_POWER_STAT); + OK (0); + + case (RH_C_PORT_CONNECTION): + clrPortChange (hci, PORT_CONNECT_STAT); + OK (0); + + case (RH_C_PORT_ENABLE): + clrPortChange (hci, PORT_ENABLE_STAT); + OK (0); + + case (RH_C_PORT_SUSPEND): + clrPortChange (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_C_PORT_OVER_CURRENT): + clrPortChange (hci, PORT_OVER_CURRENT_STAT); + OK (0); + + case (RH_C_PORT_RESET): + clrPortChange (hci, PORT_RESET_STAT); + OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + setPortStatus (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_PORT_RESET): + setPortStatus (hci, PORT_RESET_STAT); + // USBReset(hci); + clrPortChange (hci, + PORT_CONNECT_CHANGE | PORT_ENABLE_CHANGE + | PORT_SUSPEND_CHANGE | + PORT_OVER_CURRENT_CHANGE); + setPortChange (hci, PORT_RESET_CHANGE); + clrPortStatus (hci, PORT_RESET_STAT); + setPortStatus (hci, PORT_ENABLE_STAT); + + OK (0); + + case (RH_PORT_POWER): + setPortStatus (hci, PORT_POWER_STAT); + OK (0); + + case (RH_PORT_ENABLE): + setPortStatus (hci, PORT_ENABLE_STAT); + OK (0); + } + break; + + case RH_SET_ADDRESS: + hci->rh.devnum = wValue; + OK (0); + + case RH_GET_DESCRIPTOR: + DBGVERBOSE ("rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x\n", wValue); + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min (leni, min ((__u16)sizeof (root_hub_dev_des), wLength)); + data_buf = root_hub_dev_des; + OK (len); + + case (0x02): /* configuration descriptor */ + len = min (leni, min ((__u16)sizeof (root_hub_config_des), wLength)); + data_buf = root_hub_config_des; + OK (len); + + case (0x03): /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, (int) (long) 0, + "SL811HS", data, wLength); + if (len > 0) { + data_buf = data; + OK (min (leni, len)); + } + + default: + status = SL11H_STATMASK_STALL; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + data_buf[0] = 9; // min length; + data_buf[1] = 0x29; + data_buf[2] = 1; // # of downstream port + data_buf[3] = 0; + datab[1] = 0; + data_buf[5] = 50; // 100 ms for port reset + data_buf[7] = 0xfc; // which port is attachable + if (data_buf[2] < 7) { + data_buf[8] = 0xff; + } else { + } + + len = min (leni, min ((__u16)data_buf[0], wLength)); + OK (len); + + case RH_GET_CONFIGURATION: + *(__u8 *) data_buf = 0x01; + OK (1); + + case RH_SET_CONFIGURATION: + OK (0); + + default: + DBGERR ("unsupported root hub command"); + status = SL11H_STATMASK_STALL; + } + + len = min (len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + urb->actual_length = len; + urb->status = cc_to_error (status); + + urb->hcpriv = NULL; + urb->dev = NULL; + if (urb->complete) { + urb->complete (urb); + } + + return 0; +} + +/*************************************************************************** + * Function Name : rh_unlink_urb + * + * This function unlinks the URB + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_unlink_urb (struct urb * urb) +{ + hci_t *hci = urb->dev->bus->hcpriv; + + DBGFUNC ("enter rh_unlink_urb\n"); + if (hci->rh.urb == urb) { + hci->rh.send = 0; + del_timer (&hci->rh.rh_int_timer); + hci->rh.urb = NULL; + + urb->hcpriv = NULL; + usb_put_dev (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; +} + +/*************************************************************************** + * Function Name : rh_connect_rh + * + * This function connect the virtual root hub to the USB stack + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_connect_rh (hci_t * hci) +{ + struct usb_device *usb_dev; + + hci->rh.devnum = 0; + usb_dev = usb_alloc_dev (NULL, hci->bus); + if (!usb_dev) + return -ENOMEM; + + hci->bus->root_hub = usb_dev; + usb_connect (usb_dev); + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); + return -ENODEV; + } + + return 0; +}