|
|
我在网上找到了一个补丁,但是全文本的,不知道该怎么打上。
- ChangeSet 1.1074.1.15, 2003/07/09 22:17:36-07:00, yinah@couragetech.com.cn
- [PATCH] USB: patch for sl811 usb host controller driver
- Ok, I have get rid of the typedefs and make it build on 2.4.21.
- I'm not try to merge this with the existing sl811 driver, I modifed most
- of it for support isochronous transfer mode, especially the structrue
- and the urb schedule part, so it's almost a new one. Some one need this
- feature can use this one, others can still use the existing. So may be
- it makes sense to have two different drivers. Note: I wrote it on
- 2.4.18, at our board which use SA1110, and I test it with usb mouse,
- usb moving disk and a web camera which use ov511 chipset.
- drivers/usb/host/Config.in | 1
- drivers/usb/host/Makefile | 1
- drivers/usb/host/sl811.c | 2749 +++++++++++++++++++++++++++++++++++++++++++++
- drivers/usb/host/sl811.h | 177 ++
- 4 files changed, 2928 insertions(+)
- diff -Nru a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in
- --- a/drivers/usb/host/Config.in Mon Jul 14 10:04:12 2003
- +++ b/drivers/usb/host/Config.in Mon Jul 14 10:04:12 2003
- @@ -12,4 +12,5 @@
- define_bool CONFIG_USB_UHCI_ALT n
- fi
- dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
- +dep_tristate ' SL811HS Alternate (support isochornous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB
-
- diff -Nru a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
- --- a/drivers/usb/host/Makefile Mon Jul 14 10:04:12 2003
- +++ b/drivers/usb/host/Makefile Mon Jul 14 10:04:12 2003
- @@ -9,6 +9,7 @@
- obj-$(CONFIG_USB_UHCI_ALT) += uhci.o
- obj-$(CONFIG_USB_UHCI) += usb-uhci.o
- obj-$(CONFIG_USB_OHCI) += usb-ohci.o
- +obj-$(CONFIG_USB_SL811HS_ALT) += sl811.o
-
- # Extract lists of the multi-part drivers.
- # The 'int-*' lists are the intermediate files used to build the multi's.
- diff -Nru a/drivers/usb/host/sl811.c b/drivers/usb/host/sl811.c
- --- /dev/null Wed Dec 31 16:00:00 1969
- +++ b/drivers/usb/host/sl811.c Mon Jul 14 10:04:12 2003
- @@ -0,0 +1,2749 @@
- +/*
- + * SL811 Host Controller Interface driver for USB.
- + *
- + * Copyright (c) 2003/06, Courage Co., Ltd.
- + *
- + * Based on:
- + * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
- + * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
- + * Adam Richter, Gregory P. Smith;
- + 2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
- + *
- + * It's now support isochronous mode and more effective than hc_sl811.o
- + *
- + * To do:
- + * 1.Modify the timeout part, it's some messy
- + * 2.Use usb-a and usb-b set in Ping-Pong mode
- +
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License as published by
- + * the Free Software Foundation; either version 2 of the License, or
- + * (at your option) any later version.
- + *
- + */
- +
- +#include <linux/config.h>
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/delay.h>
- +#include <linux/sched.h>
- +#include <linux/slab.h>
- +#include <linux/errno.h>
- +#include <linux/init.h>
- +#include <linux/smp_lock.h>
- +#include <linux/list.h>
- +#include <linux/ioport.h>
- +#include <asm/io.h>
- +#include <asm/arch/irq.h>
- +#include <linux/usb.h>
- +
- +#include "hcd.h"
- +#include "hub.h"
- +#include "sl811.h"
- +
- +#define DRIVER_VERSION "v0.28"
- +#define DRIVER_AUTHOR "Yin Aihua <yinah@couragetech.com.cn>"
- +#define DRIVER_DESC "Sl811 USB Host Controller Driver"
- +
- +static int sl811_addr_io = 0xf100000e;
- +static int sl811_data_io = 0xf100000f;
- +static int sl811_irq = 44;
- +
- +static LIST_HEAD(sl811_hcd_list);
- +
- +/*
- + * 0, normal prompt and information
- + * 1, error should not occur in normal
- + * 2, error maybe occur in normal
- + * 3, useful and detail debug information
- + * 4, function level enter and level inforamtion
- + * 5, endless information will output because of timer function or interrupt
- + */
- +static int debug = 0;
- +
- +MODULE_PARM(sl811_addr_io,"i");
- +MODULE_PARM_DESC(sl811_addr_io,"sl811 address io port 0xf100000e");
- +MODULE_PARM(sl811_data_io,"i");
- +MODULE_PARM_DESC(sl811_data_io,"sl811 data io port 0xf100000f");
- +MODULE_PARM(sl811_irq,"i");
- +MODULE_PARM_DESC(sl811_irq,"sl811 irq 44(default)");
- +MODULE_PARM(debug,"i");
- +MODULE_PARM_DESC(debug,"debug level");
- +
- +static void sl811_rh_int_timer_do(unsigned long ptr);
- +static void sl811_transfer_done(struct sl811_hc *hc, int sof);
- +
- +/*
- + * Read a byte of data from the SL811H/SL11H
- + */
- +static __u8 sl811_read(struct sl811_hc *hc, __u8 offset)
- +{
- + __u8 data;
- +
- + writeb(offset, hc->addr_io);
- + wmb();
- + data = readb(hc->data_io);
- + rmb();
- +
- + return data;
- +}
- +
- +/*
- + * Write a byte of data to the SL811H/SL11H
- + */
- +static void sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data)
- +{
- + writeb(offset, hc->addr_io);
- + writeb(data, hc->data_io);
- + wmb();
- +}
- +
- +/*
- + * Read consecutive bytes of data from the SL811H/SL11H buffer
- + */
- +static void sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
- +{
- + writeb(offset, hc->addr_io);
- + wmb();
- + while (size--) {
- + *buf++ = readb(hc->data_io);
- + rmb();
- + }
- +}
- +
- +/*
- + * Write consecutive bytes of data to the SL811H/SL11H buffer
- + */
- +static void sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
- +{
- + writeb(offset, hc->addr_io);
- + wmb();
- + while (size--) {
- + writeb(*buf, hc->data_io);
- + wmb();
- + buf++;
- + }
- +}
- +
- +/*
- + * This routine test the Read/Write functionality of SL811HS registers
- + */
- +static int sl811_reg_test(struct sl811_hc *hc)
- +{
- + int i, data, result = 0;
- + __u8 buf[256];
- +
- + for (i = 0x10; i < 256; i++) {
- + /* save the original buffer */
- + buf[i] = sl811_read(hc, i);
- +
- + /* Write the new data to the buffer */
- + sl811_write(hc, i, i);
- + }
- +
- + /* compare the written data */
- + for (i = 0x10; i < 256; i++) {
- + data = sl811_read(hc, i);
- + if (data != i) {
- + PDEBUG(1, "Pattern test failed!! value = 0x%x, s/b 0x%x", data, i);
- + result = -1;
- + }
- + }
- +
- + /* restore the data */
- + for (i = 0x10; i < 256; i++)
- + sl811_write(hc, i, buf[i]);
- +
- + return result;
- +}
- +
- +/*
- + * Display all SL811HS register values
- + */
- +static void sl811_reg_show(struct sl811_hc *hc)
- +{
- + int i;
- +
- + for (i = 0; i < 256; i++)
- + PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i));
- +}
- +
- +/*
- + * This function enables SL811HS interrupts
- + */
- +static void sl811_enable_interrupt(struct sl811_hc *hc)
- +{
- + PDEBUG(4, "enter");
- + sl811_write(hc, SL811_INTR, SL811_INTR_DONE_A | SL811_INTR_SOF | SL811_INTR_INSRMV);
- +}
- +
- +/*
- + * This function disables SL811HS interrupts
- + */
- +static void sl811_disable_interrupt(struct sl811_hc *hc)
- +{
- + PDEBUG(4, "enter");
- + // Disable all other interrupt except for insert/remove.
- + sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV);
- +}
- +
- +/*
- + * SL811 Virtual Root Hub
- + */
- +
- +/* Device descriptor */
- +static __u8 sl811_rh_dev_des[] =
- +{
- + 0x12, /* __u8 bLength; */
- + 0x01, /* __u8 bDescriptorType; Device */
- + 0x10, /* __u16 bcdUSB; v1.1 */
- + 0x01,
- + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
- + 0x00, /* __u8 bDeviceSubClass; */
- + 0x00, /* __u8 bDeviceProtocol; */
- + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
- + 0x00, /* __u16 idVendor; */
- + 0x00,
- + 0x00, /* __u16 idProduct; */
- + 0x00,
- + 0x00, /* __u16 bcdDevice; */
- + 0x00,
- + 0x00, /* __u8 iManufacturer; */
- + 0x02, /* __u8 iProduct; */
- + 0x01, /* __u8 iSerialNumber; */
- + 0x01 /* __u8 bNumConfigurations; */
- +};
- +
- +/* Configuration descriptor */
- +static __u8 sl811_rh_config_des[] =
- +{
- + 0x09, /* __u8 bLength; */
- + 0x02, /* __u8 bDescriptorType; Configuration */
- + 0x19, /* __u16 wTotalLength; */
- + 0x00,
- + 0x01, /* __u8 bNumInterfaces; */
- + 0x01, /* __u8 bConfigurationValue; */
- + 0x00, /* __u8 iConfiguration; */
- + 0x40, /* __u8 bmAttributes;
- + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
- + 4..0: resvd */
- + 0x00, /* __u8 MaxPower; */
- +
- + /* interface */
- + 0x09, /* __u8 if_bLength; */
- + 0x04, /* __u8 if_bDescriptorType; Interface */
- + 0x00, /* __u8 if_bInterfaceNumber; */
- + 0x00, /* __u8 if_bAlternateSetting; */
- + 0x01, /* __u8 if_bNumEndpoints; */
- + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
- + 0x00, /* __u8 if_bInterfaceSubClass; */
- + 0x00, /* __u8 if_bInterfaceProtocol; */
- + 0x00, /* __u8 if_iInterface; */
- +
- + /* endpoint */
- + 0x07, /* __u8 ep_bLength; */
- + 0x05, /* __u8 ep_bDescriptorType; Endpoint */
- + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- + 0x03, /* __u8 ep_bmAttributes; Interrupt */
- + 0x08, /* __u16 ep_wMaxPacketSize; */
- + 0x00,
- + 0xff /* __u8 ep_bInterval; 255 ms */
- +};
- +
- +/* root hub class descriptor*/
- +static __u8 sl811_rh_hub_des[] =
- +{
- + 0x09, /* __u8 bLength; */
- + 0x29, /* __u8 bDescriptorType; Hub-descriptor */
- + 0x01, /* __u8 bNbrPorts; */
- + 0x00, /* __u16 wHubCharacteristics; */
- + 0x00,
- + 0x50, /* __u8 bPwrOn2pwrGood; 2ms */
- + 0x00, /* __u8 bHubContrCurrent; 0 mA */
- + 0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */
- + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
- +};
- +
- +/*
- + * This function examine the port change in the virtual root hub. HUB INTERRUPT ENDPOINT.
- + */
- +static int sl811_rh_send_irq(struct sl811_hc *hc, __u8 *rh_change, int rh_len)
- +{
- + __u8 data = 0;
- +
- + PDEBUG(5, "enter");
- +
- + /*
- + * Right now, It is assume the power is good and no changes and only one port.
- + */
- + if (hc->rh_status.wPortChange & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) {
- + data = 1<<1;
- + *(__u8 *)rh_change = data;
- + return 1;
- + } else
- + return 0;
- +}
- +
- +/*
- + * This function creates a timer that act as interrupt pipe in the virtual hub.
- + *
- + * Note: The virtual root hub's interrupt pipe are polled by the timer
- + * every "interval" ms
- + */
- +static void sl811_rh_init_int_timer(struct urb * urb)
- +{
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- + hc->rh.interval = urb->interval;
- +
- + init_timer(&hc->rh.rh_int_timer);
- + hc->rh.rh_int_timer.function = sl811_rh_int_timer_do;
- + hc->rh.rh_int_timer.data = (unsigned long)urb;
- + hc->rh.rh_int_timer.expires = jiffies +
- + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
- + add_timer (&hc->rh.rh_int_timer);
- +}
- +
- +/*
- + * This function is called when the timer expires. It gets the the port
- + * change data and pass along to the upper protocol.
- + */
- +static void sl811_rh_int_timer_do(unsigned long ptr)
- +{
- + int len;
- + struct urb *urb = (struct urb *)ptr;
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- + PDEBUG (5, "enter");
- +
- + if(hc->rh.send) {
- + len = sl811_rh_send_irq(hc, urb->transfer_buffer,
- + urb->transfer_buffer_length);
- + if (len > 0) {
- + urb->actual_length = len;
- + if (urb->complete)
- + urb->complete(urb);
- + }
- + }
- +
- +#ifdef SL811_TIMEOUT
- +
- +{
- + struct list_head *head, *tmp;
- + struct sl811_urb_priv *urbp;
- + struct urb *u;
- + int i;
- + static int timeout_count = 0;
- +
- +// check time out every second
- + if (++timeout_count > 4) {
- + int max_scan = hc->active_urbs;
- + timeout_count = 0;
- + for (i = 0; i < 6; ++i) {
- + head = &hc->urb_list[i];
- + tmp = head->next;
- + while (tmp != head && max_scan--) {
- + u = list_entry(tmp, struct urb, urb_list);
- + urbp = (struct sl811_urb_priv *)u->hcpriv;
- + tmp = tmp->next;
- + // Check if the URB timed out
- + if (u->timeout && time_after_eq(jiffies, urbp->inserttime + u->timeout)) {
- + PDEBUG(3, "urb = %p time out, we kill it", urb);
- + u->transfer_flags |= USB_TIMEOUT_KILLED;
- + }
- + }
- + }
- + }
- +}
- +
- +#endif
- + // re-activate the timer
- + sl811_rh_init_int_timer(urb);
- +}
- +
- +/* helper macro */
- +#define OK(x) len = (x); break
- +
- +/*
- + * This function handles all USB request to the the virtual root hub
- + */
- +static int sl811_rh_submit_urb(struct urb *urb)
- +{
- + struct usb_device *usb_dev = urb->dev;
- + struct sl811_hc *hc = usb_dev->bus->hcpriv;
- + struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
- + void *data = urb->transfer_buffer;
- + int buf_len = urb->transfer_buffer_length;
- + unsigned int pipe = urb->pipe;
- + __u8 data_buf[16];
- + __u8 *bufp = data_buf;
- + int len = 0;
- + int status = 0;
- +
- + __u16 bmRType_bReq;
- + __u16 wValue;
- + __u16 wIndex;
- + __u16 wLength;
- +
- + if (usb_pipeint(pipe)) {
- + hc->rh.urb = urb;
- + hc->rh.send = 1;
- + hc->rh.interval = urb->interval;
- + sl811_rh_init_int_timer(urb);
- + urb->status = 0;
- +
- + return 0;
- + }
- +
- + bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
- + wValue = le16_to_cpu (cmd->wValue);
- + wIndex = le16_to_cpu (cmd->wIndex);
- + wLength = le16_to_cpu (cmd->wLength);
- +
- + PDEBUG(5, "submit rh urb, req = %d(%x) len=%d", bmRType_bReq, bmRType_bReq, wLength);
- +
- + /* Request Destination:
- + without flags: Device,
- + USB_RECIP_INTERFACE: interface,
- + USB_RECIP_ENDPOINT: endpoint,
- + USB_TYPE_CLASS means HUB here,
- + USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here
- + */
- + switch (bmRType_bReq) {
- + case RH_GET_STATUS:
- + *(__u16 *)bufp = cpu_to_le16(1);
- + OK(2);
- +
- + case RH_GET_STATUS | USB_RECIP_INTERFACE:
- + *(__u16 *)bufp = cpu_to_le16(0);
- + OK(2);
- +
- + case RH_GET_STATUS | USB_RECIP_ENDPOINT:
- + *(__u16 *)bufp = cpu_to_le16(0);
- + OK(2);
- +
- + case RH_GET_STATUS | USB_TYPE_CLASS:
- + *(__u32 *)bufp = cpu_to_le32(0);
- + OK(4);
- +
- + case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
- + *(__u32 *)bufp = cpu_to_le32(hc->rh_status.wPortChange<<16 | hc->rh_status.wPortStatus);
- + OK(4);
- +
- + case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
- + switch (wValue) {
- + case 1:
- + OK(0);
- + }
- + break;
- +
- + case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
- + switch (wValue) {
- + case C_HUB_LOCAL_POWER:
- + OK(0);
- +
- + case C_HUB_OVER_CURRENT:
- + OK(0);
- + }
- + break;
- +
- + case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
- + switch (wValue) {
- + case USB_PORT_FEAT_ENABLE:
- + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
- + OK(0);
- +
- + case USB_PORT_FEAT_SUSPEND:
- + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
- + OK(0);
- +
- + case USB_PORT_FEAT_POWER:
- + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
- + OK(0);
- +
- + case USB_PORT_FEAT_C_CONNECTION:
- + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
- + OK(0);
- +
- + case USB_PORT_FEAT_C_ENABLE:
- + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
- + OK(0);
- +
- + case USB_PORT_FEAT_C_SUSPEND:
- + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
- + OK(0);
- +
- + case USB_PORT_FEAT_C_OVER_CURRENT:
- + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
- + OK(0);
- +
- + case USB_PORT_FEAT_C_RESET:
- + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
- + OK(0);
- + }
- + break;
- +
- + case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
- + switch (wValue) {
- + case USB_PORT_FEAT_SUSPEND:
- + hc->rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
- + OK(0);
- +
- + case USB_PORT_FEAT_RESET:
- + hc->rh_status.wPortStatus |= USB_PORT_STAT_RESET;
- + hc->rh_status.wPortChange = 0;
- + hc->rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
- + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
- + hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
- + OK(0);
- +
- + case USB_PORT_FEAT_POWER:
- + hc->rh_status.wPortStatus |= USB_PORT_STAT_POWER;
- + OK(0);
- +
- + case USB_PORT_FEAT_ENABLE:
- + hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
- + OK(0);
- + }
- + break;
- +
- + case RH_SET_ADDRESS:
- + hc->rh.devnum = wValue;
- + OK(0);
- +
- + case RH_GET_DESCRIPTOR:
- + switch ((wValue & 0xff00) >> 8) {
- + case USB_DT_DEVICE:
- + len = sizeof(sl811_rh_dev_des);
- + bufp = sl811_rh_dev_des;
- + OK(len);
- +
- + case USB_DT_CONFIG:
- + len = sizeof(sl811_rh_config_des);
- + bufp = sl811_rh_config_des;
- + OK(len);
- +
- + case USB_DT_STRING:
- + len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength);
- + if (len > 0) {
- + bufp = data;
- + OK(len);
- + }
- +
- + default:
- + status = -EPIPE;
- + }
- + break;
- +
- + case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
- + len = sizeof(sl811_rh_hub_des);
- + bufp = sl811_rh_hub_des;
- + OK(len);
- +
- + case RH_GET_CONFIGURATION:
- + bufp[0] = 0x01;
- + OK(1);
- +
- + case RH_SET_CONFIGURATION:
- + OK(0);
- +
- + default:
- + PDEBUG(1, "unsupported root hub command");
- + status = -EPIPE;
- + }
- +
- + len = min(len, buf_len);
- + if (data != bufp)
- + memcpy(data, bufp, len);
- + urb->actual_length = len;
- + urb->status = status;
- +
- + PDEBUG(5, "len = %d, status = %d", len, status);
- +
- + urb->hcpriv = NULL;
- + urb->dev = NULL;
- + if (urb->complete)
- + urb->complete(urb);
- +
- + return 0;
- +}
- +
- +/*
- + * This function unlinks the URB
- + */
- +static int sl811_rh_unlink_urb(struct urb *urb)
- +{
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- +
- + PDEBUG(5, "enter");
- +
- + if (hc->rh.urb == urb) {
- + hc->rh.send = 0;
- + del_timer(&hc->rh.rh_int_timer);
- + hc->rh.urb = NULL;
- + urb->hcpriv = NULL;
- + usb_dec_dev_use(urb->dev);
- + urb->dev = NULL;
- + if (urb->transfer_flags & USB_ASYNC_UNLINK) {
- + urb->status = -ECONNRESET;
- + if (urb->complete)
- + urb->complete(urb);
- + } else
- + urb->status = -ENOENT;
- + }
- +
- + return 0;
- +}
- +
- +/*
- + * This function connect the virtual root hub to the USB stack
- + */
- +static int sl811_connect_rh(struct sl811_hc * hc)
- +{
- + struct usb_device *usb_dev;
- +
- + hc->rh.devnum = 0;
- + usb_dev = usb_alloc_dev(NULL, hc->bus);
- + if (!usb_dev)
- + return -ENOMEM;
- +
- + hc->bus->root_hub = usb_dev;
- + usb_connect(usb_dev);
- +
- + if (usb_new_device(usb_dev)) {
- + usb_free_dev(usb_dev);
- + return -ENODEV;
- + }
- +
- + PDEBUG(5, "leave success");
- +
- + return 0;
- +}
- +
- +/*
- + * This function allocates private data space for the usb device
- + */
- +static int sl811_alloc_dev_priv(struct usb_device *usb_dev)
- +{
- + return 0;
- +}
- +
- +/*
- + * This function de-allocates private data space for the usb devic
- + */
- +static int sl811_free_dev_priv (struct usb_device *usb_dev)
- +{
- + return 0;
- +}
- +
- +/*
- + * This function allocates private data space for the urb
- + */
- +static struct sl811_urb_priv* sl811_alloc_urb_priv(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp;
- +
- + urbp = kmalloc(sizeof(*urbp), GFP_KERNEL);
- + if (!urbp)
- + return NULL;
- +
- + memset(urbp, 0, sizeof(*urbp));
- +
- + INIT_LIST_HEAD(&urbp->td_list);
- +
- + urbp->urb = urb;
- + urb->hcpriv = urbp;
- +
- + return urbp;
- +}
- +
- +/*
- + * This function free private data space for the urb
- + */
- +static void sl811_free_urb_priv(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td;
- + struct list_head *head, *tmp;
- +
- + if (!urbp)
- + return ;
- +
- + head = &urbp->td_list;
- + tmp = head->next;
- +
- + while (tmp != head) {
- + td = list_entry(tmp, struct sl811_td, td_list);
- + tmp = tmp->next;
- + kfree(td);
- + }
- +
- + kfree(urbp);
- + urb->hcpriv = NULL;
- +
- + return ;
- +}
- +
- +/*
- + * This function calculate the bus time need by this td.
- + * Fix me! Can this use usb_calc_bus_time()?
- + */
- +static void sl811_calc_td_time(struct sl811_td *td)
- +{
- +#if 1
- + int time;
- + int len = td->len;
- + struct sl811_hc *hc = td->urb->dev->bus->hcpriv;
- +
- + if (hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)
- + time = 8*8*len + 1024;
- + else {
- + if (td->ctrl & SL811_USB_CTRL_PREAMBLE)
- + time = 8*8*len + 2048;
- + else
- + time = 8*len + 256;
- + }
- +
- + time += 2*10 * len;
- +
- + td->bustime = time;
- +
- +#else
- +
- + unsigned long tmp;
- + int time;
- + int low_speed = usb_pipeslow(td->urb->pipe);
- + int input_dir = usb_pipein(td->urb->pipe);
- + int bytecount = td->len;
- + int isoc = usb_pipeisoc(td->urb->pipe);
- +
- + if (low_speed) { /* no isoc. here */
- + if (input_dir) {
- + tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
- + time = (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
- + } else {
- + tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
- + time = (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
- + }
- + } else if (!isoc){ /* for full-speed: */
- + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- + time = (9107L + BW_HOST_DELAY + tmp);
- + } else { /* for isoc: */
- + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- + time = (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
- + }
- +
- + td->bustime = time / 84;
- +
- +#endif
- +}
- +
- +/*
- + * This function calculate the remainder bus time in current frame.
- + */
- +static inline int sl811_calc_bus_remainder(struct sl811_hc *hc)
- +{
- + return (sl811_read(hc, SL811_SOFCNTDIV) * 64);
- +}
- +
- +/*
- + * This function allocates td for the urb
- + */
- +static struct sl811_td* sl811_alloc_td(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td;
- +
- + td = kmalloc(sizeof (*td), GFP_KERNEL);
- + if (!td)
- + return NULL;
- +
- + memset(td, 0, sizeof(*td));
- +
- + INIT_LIST_HEAD(&td->td_list);
- +
- + td->urb = urb;
- + list_add_tail(&td->td_list, &urbp->td_list);
- +
- + return td;
- +}
- +
- +/*
- + * Fill the td.
- + */
- +static inline void sl811_fill_td(struct sl811_td *td, __u8 ctrl, __u8 addr, __u8 len, __u8 pidep, __u8 dev, __u8 *buf)
- +{
- + td->ctrl = ctrl;
- + td->addr = addr;
- + td->len = len;
- + td->pidep = pidep;
- + td->dev = dev;
- + td->buf = buf;
- + td->left = len;
- + td->errcnt = 3;
- +}
- +
- +/*
- + * Fill the td.
- + */
- +static inline void sl811_reset_td(struct sl811_td *td)
- +{
- + td->status = 0;
- + td->left = td->len;
- + td->done = 0;
- + td->errcnt = 3;
- + td->nakcnt = 0;
- + td->td_status = 0;
- +}
- +
- +static void sl811_print_td(int level, struct sl811_td *td)
- +{
- + PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n
- + dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n
- + buf = %p, bustime = %d, td_status = %d\n",
- + td, td->ctrl, td->addr, td->len, td->pidep,
- + td->dev, td->status, td->left, td->errcnt, td->done,
- + td->buf, td->bustime, td->td_status);
- +}
- +
- +/*
- + * Isochronous transfers
- + */
- +static int sl811_submit_isochronous(struct urb *urb)
- +{
- + __u8 dev = usb_pipedevice(urb->pipe);
- + __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
- + __u8 ctrl = 0;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = NULL;
- + int i;
- +
- + PDEBUG(4, "enter, urb = %p, urbp = %p", urb, urbp);
- +
- + /* Can't have low speed bulk transfers */
- + if (usb_pipeslow(urb->pipe)) {
- + PDEBUG(1, "error, urb = %p, low speed device", urb);
- + return -EINVAL;
- + }
- +
- + if (usb_pipeout(urb->pipe))
- + ctrl |= SL811_USB_CTRL_DIR_OUT;
- +
- + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_ISO;
- +
- + for (i = 0; i < urb->number_of_packets; i++) {
- + urb->iso_frame_desc[i].actual_length = 0;
- + urb->iso_frame_desc[i].status = -EXDEV;
- +
- + td = sl811_alloc_td(urb);
- + if (!td)
- + return -ENOMEM;
- +
- + sl811_fill_td(td, ctrl, SL811_DATA_START,
- + urb->iso_frame_desc[i].length,
- + pidep, dev,
- + urb->transfer_buffer + urb->iso_frame_desc[i].offset);
- + sl811_calc_td_time(td);
- + if (urbp->cur_td == NULL)
- + urbp->cur_td = urbp->first_td = td;
- + }
- +
- + urbp->last_td = td;
- +
- + PDEBUG(4, "leave success");
- +
- +/*
- +// for debug
- + {
- + struct list_head *head, *tmp;
- + struct sl811_td *td;
- + int i = 0;
- + head = &urbp->td_list;
- + tmp = head->next;
- +
- + if (list_empty(&urbp->td_list)) {
- + PDEBUG(1, "bug!!! td list is empty!");
- + return -ENODEV;
- + }
- +
- + while (tmp != head) {
- + ++i;
- + td = list_entry(tmp, struct sl811_td, td_list);
- + PDEBUG(2, "td = %p, i = %d", td, i);
- + tmp = tmp->next;
- + }
- + }
- +*/
- + return 0;
- +}
- +
- +/*
- + * Reset isochronous transfers
- + */
- +static void sl811_reset_isochronous(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = NULL;
- + struct list_head *head, *tmp;
- + int i;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + for (i = 0; i < urb->number_of_packets; i++) {
- + urb->iso_frame_desc[i].actual_length = 0;
- + urb->iso_frame_desc[i].status = -EXDEV;
- + }
- +
- + head = &urbp->td_list;
- + tmp = head->next;
- + while (tmp != head) {
- + td = list_entry(tmp, struct sl811_td, td_list);
- + tmp = tmp->next;
- + sl811_reset_td(td);
- + }
- +
- + urbp->cur_td = urbp->first_td;
- +
- + urb->status = -EINPROGRESS;
- + urb->actual_length = 0;
- + urb->error_count = 0;
- +}
- +
- +/*
- + * Result the iso urb.
- + */
- +static void sl811_result_isochronous(struct urb *urb)
- +{
- + struct list_head *tmp, *head;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + int status = 0;
- + struct sl811_td *td;
- + int i;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + urb->actual_length = 0;
- +
- + i = 0;
- + head = &urbp->td_list;
- + tmp = head->next;
- + while (tmp != head) {
- + td = list_entry(tmp, struct sl811_td, td_list);
- + tmp = tmp->next;
- +
- + if (!td->done) {
- + if (urbp->unlink)
- + urb->status = -ENOENT;
- + else {
- + PDEBUG(1, "we should not get here!");
- + urb->status = -EXDEV;
- + }
- + return ;
- + }
- + if (td->td_status) {
- + status = td->td_status;
- + urb->error_count++;
- + PDEBUG(1, "error: td = %p, td status = %d", td, td->td_status);
- + }
- +
- + urb->iso_frame_desc[i].actual_length = td->len - td->left;
- + urb->actual_length += td->len - td->left;
- + urb->iso_frame_desc[i].status = td->td_status;
- + ++i;
- + if (td->left)
- + PDEBUG(3, "short packet, td = %p, len = %d, left = %d", td, td->len, td->left);
- + }
- +
- + urb->status = status;
- +/*
- +// for debug
- + PDEBUG(2, "iso urb complete, len = %d, status =%d ", urb->actual_length, urb->status);
- +*/
- + PDEBUG(4, "leave success");
- +}
- +
- +/*
- + * Interrupt transfers
- + */
- +static int sl811_submit_interrupt(struct urb *urb)
- +{
- + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- + int len = urb->transfer_buffer_length;
- + __u8 *data = urb->transfer_buffer;
- + __u8 dev = usb_pipedevice(urb->pipe);
- + __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
- + __u8 ctrl = 0;
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = NULL;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + if (len > maxsze) {
- + PDEBUG(1, "length is big than max packet size, len = %d, max packet = %d", len, maxsze);
- + return -EINVAL;
- + }
- + if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED))
- + ctrl |= SL811_USB_CTRL_PREAMBLE;
- +
- + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
- + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
- + ctrl |= SL811_USB_CTRL_TOGGLE_1;
- + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- + td = sl811_alloc_td(urb);
- + if (!td)
- + return -ENOMEM;
- +
- + sl811_fill_td(td, ctrl, SL811_DATA_START, len, pidep, dev, data);
- + sl811_calc_td_time(td);
- + urbp->cur_td = urbp->first_td = urbp->last_td = td;
- + urbp->interval = 0;
- +
- + PDEBUG(4, "leave success");
- +
- + return 0;
- +}
- +
- +/*
- + * Reset interrupt transfers
- + */
- +static void sl811_reset_interrupt(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = urbp->cur_td;
- +
- + PDEBUG(4, "enter, interval = %d", urb->interval);
- +
- + td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
- + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
- + td->ctrl |= SL811_USB_CTRL_TOGGLE_1;
- + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- +
- + sl811_reset_td(td);
- +
- + urbp->interval = urb->interval;
- +
- + urb->status = -EINPROGRESS;
- + urb->actual_length = 0;
- +}
- +
- +/*
- + * Result the interrupt urb.
- + */
- +static void sl811_result_interrupt(struct urb *urb)
- +{
- + struct list_head *tmp;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td;
- + int toggle;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + urb->actual_length = 0;
- +
- + tmp = &urbp->td_list;
- + tmp = tmp->next;
- + td = list_entry(tmp, struct sl811_td, td_list);
- +
- + // success.
- + if (td->done && td->td_status == 0) {
- + urb->actual_length += td->len - td->left;
- + urb->status = 0;
- + return ;
- + }
- + // tranfer is done but fail, reset the toggle.
- + else if (td->done && td->td_status) {
- + urb->status = td->td_status;
- +reset_toggle:
- + toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0;
- + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle);
- + PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status);
- + return ;
- + }
- + // unlink, and not do transfer yet
- + else if (td->done == 0 && urbp->unlink && td->td_status == 0) {
- + urb->status = -ENOENT;
- + PDEBUG(3, "unlink and not transfer!");
- + return ;
- + }
- + // unlink, and transfer not complete yet.
- + else if (td->done == 0 && urbp->unlink && td->td_status) {
- + urb->status = -ENOENT;
- + PDEBUG(3, "unlink and not complete!");
- + goto reset_toggle;
- + }
- + // must be bug!!!
- + else {// (td->done == 0 && urbp->unlink == 0)
- + PDEBUG(1, "we should not get here!");
- + urb->status = -EPIPE;
- + return ;
- + }
- +}
- +
- +/*
- + * Control transfers
- + */
- +static int sl811_submit_control(struct urb *urb)
- +{
- + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- + int len = urb->transfer_buffer_length;
- + __u8 *data = urb->transfer_buffer;
- + __u8 dev = usb_pipedevice(urb->pipe);
- + __u8 pidep = 0;
- + __u8 ctrl = 0;
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = NULL;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED))
- + ctrl |= SL811_USB_CTRL_PREAMBLE;
- +
- + /* Build SETUP TD */
- + pidep = PIDEP(USB_PID_SETUP, usb_pipeendpoint(urb->pipe));
- + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_DIR_OUT;
- + td = sl811_alloc_td(urb);
- + if (!td)
- + return -ENOMEM;
- +
- + sl811_fill_td(td, ctrl, SL811_DATA_START, 8, pidep, dev, urb->setup_packet);
- + sl811_calc_td_time(td);
- +
- + urbp->cur_td = urbp->first_td = td;
- +
- + /*
- + * If direction is "send", change the frame from SETUP (0x2D)
- + * to OUT (0xE1). Else change it from SETUP to IN (0x69).
- + */
- + pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
- + if (usb_pipeout(urb->pipe))
- + ctrl |= SL811_USB_CTRL_DIR_OUT;
- + else
- + ctrl &= ~SL811_USB_CTRL_DIR_OUT;
- +
- + /* Build the DATA TD's */
- + while (len > 0) {
- + int pktsze = len;
- +
- + if (pktsze > maxsze)
- + pktsze = maxsze;
- +
- + /* Alternate Data0/1 (start with Data1) */
- + ctrl ^= SL811_USB_CTRL_TOGGLE_1;
- +
- + td = sl811_alloc_td(urb);
- + if (!td)
- + return -ENOMEM;
- +
- + sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data);
- + sl811_calc_td_time(td);
- +
- + data += pktsze;
- + len -= pktsze;
- + }
- +
- + /* Build the final TD for control status */
- + td = sl811_alloc_td(urb);
- + if (!td)
- + return -ENOMEM;
- +
- + /* It's IN if the pipe is an output pipe or we're not expecting data back */
- + if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) {
- + pidep = PIDEP(USB_PID_IN, usb_pipeendpoint(urb->pipe));
- + ctrl &= ~SL811_USB_CTRL_DIR_OUT;
- + } else {
- + pidep = PIDEP(USB_PID_OUT, usb_pipeendpoint(urb->pipe));
- + ctrl |= SL811_USB_CTRL_DIR_OUT;
- + }
- +
- + /* End in Data1 */
- + ctrl |= SL811_USB_CTRL_TOGGLE_1;
- +
- + sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0);
- + sl811_calc_td_time(td);
- + urbp->last_td = td;
- +/*
- +// for debug
- + {
- + struct list_head *head, *tmp;
- + struct sl811_td *td;
- + int i = 0;
- + head = &urbp->td_list;
- + tmp = head->next;
- +
- + if (list_empty(&urbp->td_list)) {
- + PDEBUG(1, "bug!!! td list is empty!");
- + return -ENODEV;
- + }
- +
- + while (tmp != head) {
- + ++i;
- + td = list_entry(tmp, struct sl811_td, td_list);
- + PDEBUG(3, "td = %p, i = %d", td, i);
- + tmp = tmp->next;
- + }
- + }
- +*/
- + PDEBUG(4, "leave success");
- +
- + return 0;
- +}
- +
- +/*
- + * Result the control urb.
- + */
- +static void sl811_result_control(struct urb *urb)
- +{
- + struct list_head *tmp, *head;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + if (list_empty(&urbp->td_list)) {
- + PDEBUG(1, "td list is empty");
- + return ;
- + }
- +
- + head = &urbp->td_list;
- +
- + tmp = head->next;
- + td = list_entry(tmp, struct sl811_td, td_list);
- +
- + /* The first TD is the SETUP phase, check the status, but skip the count */
- + if (!td->done) {
- + PDEBUG(3, "setup phase error, td = %p, done = %d", td, td->done);
- + goto err_done;
- + }
- + if (td->td_status) {
- + PDEBUG(3, "setup phase error, td = %p, td status = %d", td, td->td_status);
- + goto err_status;
- + }
- +
- + urb->actual_length = 0;
- +
- + /* The rest of the TD's (but the last) are data */
- + tmp = tmp->next;
- + while (tmp != head && tmp->next != head) {
- + td = list_entry(tmp, struct sl811_td, td_list);
- + tmp = tmp->next;
- + if (!td->done) {
- + PDEBUG(3, "data phase error, td = %p, done = %d", td, td->done);
- + goto err_done;
- + }
- + if (td->td_status) {
- + PDEBUG(3, "data phase error, td = %p, td status = %d", td, td->td_status);
- + goto err_status;
- + }
- +
- + urb->actual_length += td->len - td->left;
- + // short packet.
- + if (td->left) {
- + PDEBUG(3, "data phase short packet, td = %p, count = %d", td, td->len - td->left);
- + break;
- + }
- + }
- +
- + /* The last td is status phase */
- + td = urbp->last_td;
- + if (!td->done) {
- + PDEBUG(3, "status phase error, td = %p, done = %d", td, td->done);
- + goto err_done;
- + }
- + if (td->td_status) {
- + PDEBUG(3, "status phase error, td = %p, td status = %d", td, td->td_status);
- + goto err_status;
- + }
- +
- + PDEBUG(4, "leave success");
- +
- + urb->status = 0;
- + return ;
- +
- +err_done:
- + if (urbp->unlink)
- + urb->status = -ENOENT;
- + else {
- + PDEBUG(1, "we should not get here! td = %p", td);
- + urb->status = -EPIPE;
- + }
- + return ;
- +
- +err_status:
- + urb->status = td->td_status;
- + return ;
- +}
- +
- +/*
- + * Bulk transfers
- + */
- +static int sl811_submit_bulk(struct urb *urb)
- +{
- + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- + int len = urb->transfer_buffer_length;
- + __u8 *data = urb->transfer_buffer;
- + __u8 dev = usb_pipedevice(urb->pipe);
- + __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
- + __u8 ctrl = 0;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = NULL;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + if (len < 0) {
- + PDEBUG(1, "error, urb = %p, len = %d", urb, len);
- + return -EINVAL;
- + }
- +
- + /* Can't have low speed bulk transfers */
- + if (usb_pipeslow(urb->pipe)) {
- + PDEBUG(1, "error, urb = %p, low speed device", urb);
- + return -EINVAL;
- + }
- +
- + if (usb_pipeout(urb->pipe))
- + ctrl |= SL811_USB_CTRL_DIR_OUT;
- +
- + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
- +
- + /* Build the DATA TD's */
- + do { /* Allow zero length packets */
- + int pktsze = len;
- +
- + if (pktsze > maxsze)
- + pktsze = maxsze;
- +
- + td = sl811_alloc_td(urb);
- + if (!td)
- + return -ENOMEM;
- +
- + /* Alternate Data0/1 (start with Data1) */
- + ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
- + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
- + ctrl |= SL811_USB_CTRL_TOGGLE_1;
- + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- +
- + sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data);
- + sl811_calc_td_time(td);
- +
- + if (urbp->cur_td == NULL)
- + urbp->cur_td = urbp->first_td = td;
- +
- + data += pktsze;
- + len -= maxsze;
- + } while (len > 0);
- +
- + /*
- + * USB_ZERO_PACKET means adding a 0-length packet, if
- + * direction is OUT and the transfer_length was an
- + * exact multiple of maxsze, hence
- + * (len = transfer_length - N * maxsze) == 0
- + * however, if transfer_length == 0, the zero packet
- + * was already prepared above.
- + */
- + if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
- + !len && urb->transfer_buffer_length) {
- +
- + td = sl811_alloc_td(urb);
- + if (!td)
- + return -ENOMEM;
- +
- + /* Alternate Data0/1 (start with Data1) */
- + ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
- + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
- + ctrl |= SL811_USB_CTRL_TOGGLE_1;
- + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- +
- + sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0);
- + sl811_calc_td_time(td);
- + }
- +
- + urbp->last_td = td;
- +
- + PDEBUG(4, "leave success");
- +
- + return 0;
- +}
- +
- +/*
- + * Reset bulk transfers
- + */
- +static int sl811_reset_bulk(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td;
- + struct list_head *head, *tmp;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- +
- + head = &urbp->td_list;
- + tmp = head->next;
- +
- + while (tmp != head) {
- + td = list_entry(tmp, struct sl811_td, td_list);
- +
- + /* Alternate Data0/1 (start with Data1) */
- + td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
- + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
- + td->ctrl |= SL811_USB_CTRL_TOGGLE_1;
- + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- +
- + sl811_reset_td(td);
- + }
- +
- + urb->status = -EINPROGRESS;
- + urb->actual_length = 0;
- + urbp->cur_td = urbp->first_td;
- +
- + PDEBUG(4, "leave success");
- +
- + return 0;
- +}
- +
- +/*
- + * Result the bulk urb.
- + */
- +static void sl811_result_bulk(struct urb *urb)
- +{
- + struct list_head *tmp, *head;
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = NULL;
- + int toggle;
- +
- + PDEBUG(4, "enter, urb = %p", urb);
- +
- + urb->actual_length = 0;
- +
- + head = &urbp->td_list;
- + tmp = head->next;
- + while (tmp != head) {
- + td = list_entry(tmp, struct sl811_td, td_list);
- + tmp = tmp->next;
- +
- + // success.
- + if (td->done && td->td_status == 0) {
- + urb->actual_length += td->len - td->left;
- +
- + // short packet
- + if (td->left) {
- + urb->status = 0;
- + PDEBUG(3, "short packet, td = %p, count = %d", td, td->len - td->left);
- + goto reset_toggle;
- + }
- + }
- + // tranfer is done but fail, reset the toggle.
- + else if (td->done && td->td_status) {
- + urb->status = td->td_status;
- + PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status);
- + goto reset_toggle;
- + }
- + // unlink, and not do transfer yet
- + else if (td->done == 0 && urbp->unlink && td->td_status == 0) {
- + urb->status = -ENOENT;
- + PDEBUG(3, "unlink and not transfer!");
- + return ;
- + }
- + // unlink, and transfer not complete yet.
- + else if (td->done == 0 && urbp->unlink && td->td_status) {
- + PDEBUG(3, "unlink and not complete!");
- + urb->status = -ENOENT;
- + goto reset_toggle;
- + }
- + // must be bug!!!
- + else {// (td->done == 0 && urbp->unlink == 0)
- + urb->status = -EPIPE;
- + PDEBUG(1, "we should not get here!");
- + return ;
- + }
- + }
- +
- + PDEBUG(4, "leave success");
- + urb->status = 0;
- + return ;
- +
- +reset_toggle:
- + toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0;
- + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle);
- +}
- +
- +/*
- + * Find the first urb have the same dev and endpoint.
- + */
- +static inline int sl811_find_same_urb(struct list_head *head, struct urb *urb)
- +{
- + struct list_head *tmp;
- + struct urb *u;
- +
- + if (!head || !urb)
- + return 0;
- +
- + tmp = head->next;
- +
- + while (tmp != head) {
- + u = list_entry(tmp, struct urb, urb_list);
- + if (u == urb)
- + return 1;
- + tmp = tmp->next;
- + }
- +
- + return 0;
- +}
- +
- +/*
- + * Find the first urb have the same dev and endpoint.
- + */
- +static inline struct urb* sl811_find_same_devep(struct list_head *head, struct urb *urb)
- +{
- + struct list_head *tmp;
- + struct urb *u;
- +
- + if (!head || !urb)
- + return NULL;
- +
- + tmp = head->next;
- +
- + while (tmp != head) {
- + u = list_entry(tmp, struct urb, urb_list);
- + if ((usb_pipe_endpdev(u->pipe)) == (usb_pipe_endpdev(urb->pipe)))
- + return u;
- + tmp = tmp->next;
- + }
- +
- + return NULL;
- +}
- +
- +/*
- + * This function is called by the USB core API when an URB is available to
- + * process.
- + */
- +static int sl811_submit_urb(struct urb *urb)
- +{
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- + unsigned int pipe = urb->pipe;
- + struct list_head *head = NULL;
- + unsigned long flags;
- + int bustime;
- + int ret = 0;
- +
- + if (!urb) {
- + PDEBUG(1, "urb is null");
- + return -EINVAL;
- + }
- +
- + if (urb->hcpriv) {
- + PDEBUG(1, "urbp is not null, urb = %p, urbp = %p", urb, urb->hcpriv);
- + return -EINVAL;
- + }
- +
- + if (!urb->dev || !urb->dev->bus || !hc) {
- + PDEBUG(1, "dev or bus or hc is null");
- + return -ENODEV;
- + }
- +
- + if (usb_endpoint_halted(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
- + PDEBUG(2, "sl811_submit_urb: endpoint_halted");
- + return -EPIPE;
- + }
- +
- + if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) > SL811_DATA_LIMIT) {
- + printk(KERN_ERR "Packet size is big for SL811, should < %d!\n", SL811_DATA_LIMIT);
- + return -EINVAL;
- + }
- +
- + /* a request to the virtual root hub */
- + if (usb_pipedevice(pipe) == hc->rh.devnum)
- + return sl811_rh_submit_urb(urb);
- +
- + spin_lock_irqsave(&hc->hc_lock, flags);
- + spin_lock(&urb->lock);
- +
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_ISOCHRONOUS:
- + head = &hc->iso_list;
- + break;
- + case PIPE_INTERRUPT:
- + head = &hc->intr_list;
- + break;
- + case PIPE_CONTROL:
- + head = &hc->ctrl_list;
- + break;
- + case PIPE_BULK:
- + head = &hc->bulk_list;
- + break;
- + }
- +
- + if (sl811_find_same_devep(head, urb)) {
- + list_add(&urb->urb_list, &hc->wait_list);
- + PDEBUG(4, "add to wait list");
- + goto out_unlock;
- + }
- +
- + if (!sl811_alloc_urb_priv(urb)) {
- + ret = -ENOMEM;
- + goto out_unlock;
- + }
- +
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_ISOCHRONOUS:
- + if (urb->number_of_packets <= 0) {
- + ret = -EINVAL;
- + break;
- + }
- + bustime = usb_check_bandwidth(urb->dev, urb);
- + if (bustime < 0) {
- + ret = bustime;
- + break;
- + }
- + if (!(ret = sl811_submit_isochronous(urb)))
- + usb_claim_bandwidth(urb->dev, urb, bustime, 1);
- + break;
- + case PIPE_INTERRUPT:
- + bustime = usb_check_bandwidth(urb->dev, urb);
- + if (bustime < 0)
- + ret = bustime;
- + else if (!(ret = sl811_submit_interrupt(urb)))
- + usb_claim_bandwidth(urb->dev, urb, bustime, 0);
- + break;
- + case PIPE_CONTROL:
- + ret = sl811_submit_control(urb);
- + break;
- + case PIPE_BULK:
- + ret = sl811_submit_bulk(urb);
- + break;
- + }
- +
- + if (!ret) {
- + ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies;
- + list_add(&urb->urb_list, head);
- + PDEBUG(4, "add to type list");
- + urb->status = -EINPROGRESS;
- + if (++hc->active_urbs == 1)
- + sl811_enable_interrupt(hc);
- + goto out_unlock;
- + } else {
- + PDEBUG(2, "submit urb fail! error = %d", ret);
- + sl811_free_urb_priv(urb);
- + }
- +
- +out_unlock:
- + spin_unlock(&urb->lock);
- + spin_unlock_irqrestore(&hc->hc_lock, flags);
- +
- + return ret;
- +}
- +
- +/*
- + * Submit the urb the wait list.
- + */
- +static int sl811_submit_urb_with_lock(struct urb *urb)
- +{
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- + struct list_head *head = NULL;
- + int bustime;
- + int ret = 0;
- +
- + spin_lock(&urb->lock);
- +
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_ISOCHRONOUS:
- + head = &hc->iso_list;
- + break;
- + case PIPE_INTERRUPT:
- + head = &hc->intr_list;
- + break;
- + case PIPE_CONTROL:
- + head = &hc->ctrl_list;
- + break;
- + case PIPE_BULK:
- + head = &hc->bulk_list;
- + break;
- + }
- +
- + if (!sl811_alloc_urb_priv(urb)) {
- + ret = -ENOMEM;
- + goto out_unlock;
- + }
- +
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_ISOCHRONOUS:
- + if (urb->number_of_packets <= 0) {
- + ret = -EINVAL;
- + break;
- + }
- + bustime = usb_check_bandwidth(urb->dev, urb);
- + if (bustime < 0) {
- + ret = bustime;
- + break;
- + }
- + if (!(ret = sl811_submit_isochronous(urb)))
- + usb_claim_bandwidth(urb->dev, urb, bustime, 1);
- + break;
- + case PIPE_INTERRUPT:
- + bustime = usb_check_bandwidth(urb->dev, urb);
- + if (bustime < 0)
- + ret = bustime;
- + else if (!(ret = sl811_submit_interrupt(urb)))
- + usb_claim_bandwidth(urb->dev, urb, bustime, 0);
- + break;
- + case PIPE_CONTROL:
- + ret = sl811_submit_control(urb);
- + break;
- + case PIPE_BULK:
- + ret = sl811_submit_bulk(urb);
- + break;
- + }
- +
- + if (ret == 0) {
- + ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies;
- + list_add(&urb->urb_list, head);
- + PDEBUG(4, "add to type list");
- + urb->status = -EINPROGRESS;
- + if (++hc->active_urbs == 1)
- + sl811_enable_interrupt(hc);
- + goto out_unlock;
- + } else {
- + PDEBUG(2, "submit urb fail! error = %d", ret);
- + sl811_free_urb_priv(urb);
- + }
- +
- +out_unlock:
- + spin_unlock(&urb->lock);
- +
- + return ret;
- +}
- +
- +/*
- + * Reset the urb
- + */
- +static void sl811_reset_urb(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- +
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_ISOCHRONOUS:
- + sl811_reset_isochronous(urb);
- + break;
- + case PIPE_INTERRUPT:
- + sl811_reset_interrupt(urb);
- + break;
- + case PIPE_CONTROL:
- + return;
- + case PIPE_BULK:
- + sl811_reset_bulk(urb);
- + break;
- + }
- + urbp->inserttime = jiffies;
- +}
- +
- +/*
- + * Return the result of a transfer
- + */
- +static void sl811_result_urb(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_hc *hc = urb->dev->bus->hcpriv;
- + struct list_head *head = NULL;
- + struct urb *u = NULL;
- + int reset = 0;
- + int ring = 0;
- +
- + if (urb->status != -EINPROGRESS) {
- + PDEBUG(1, "urb status is not EINPROGRESS!");
- + return ;
- + }
- +
- + spin_lock(&urb->lock);
- +
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_ISOCHRONOUS:
- + head = &hc->iso_list;
- + sl811_result_isochronous(urb);
- +
- + // if the urb is not unlink and is in a urb "ring", we reset it
- + if (!urbp->unlink && urb->next)
- + ring = 1;
- + break;
- + case PIPE_INTERRUPT:
- + head = &hc->intr_list;
- + sl811_result_interrupt(urb);
- +
- + // if the urb is not unlink and not "once" query, we reset.
- + if (!urbp->unlink && urb->interval)
- + reset = 1;
- + break;
- + case PIPE_CONTROL:
- + head = &hc->ctrl_list;
- + sl811_result_control(urb);
- + break;
- + case PIPE_BULK:
- + head = &hc->bulk_list;
- + sl811_result_bulk(urb);
- +
- + // if the urb is not unlink and is in a urb "ring", we reset it
- + if (!urbp->unlink && urb->next)
- + ring = 1;
- + break;
- + }
- +
- + PDEBUG(4, "result urb status = %d", urb->status);
- +
- + if (ring && urb->next == urb)
- + reset = 1;
- +
- + if (!reset) {
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_ISOCHRONOUS:
- + usb_release_bandwidth(urb->dev, urb, 1);
- + break;
- + case PIPE_INTERRUPT:
- + usb_release_bandwidth(urb->dev, urb, 0);
- + break;
- + }
- + sl811_free_urb_priv(urb);
- + }
- +
- + spin_unlock(&urb->lock);
- +
- + if (urb->complete)
- + urb->complete(urb);
- +
- + if (reset) {
- + spin_lock(&urb->lock);
- + sl811_reset_urb(urb);
- + if (usb_pipeint(urb->pipe))
- + list_add(&urb->urb_list, &hc->idle_intr_list);
- + else
- + list_add(&urb->urb_list, head);
- + spin_unlock(&urb->lock);
- + } else {
- + if (--hc->active_urbs <= 0) {
- + hc->active_urbs = 0;
- + sl811_disable_interrupt(hc);
- + }
- +
- + if (ring)
- + u = urb->next;
- + else
- + u = sl811_find_same_devep(&hc->wait_list, urb);
- +
- + if (u) {
- + if (!list_empty(&u->urb_list))
- + list_del(&u->urb_list);
- + if (sl811_submit_urb_with_lock(u))
- + list_add(&u->urb_list, &hc->wait_list);
- + }
- + }
- +}
- +
- +
- +#ifdef SL811_TIMEOUT
- +
- +/*
- + * Unlink the urb from the urb list
- + */
- +static int sl811_unlink_urb(struct urb *urb)
- +{
- + unsigned long flags;
- + struct sl811_hc *hc;
- + struct sl811_urb_priv *urbp;
- + int call = 0;
- + int schedule = 0;
- + int count = 0;
- +
- + if (!urb) {
- + PDEBUG(1, "urb is null");
- + return -EINVAL;
- + }
- +
- + if (!urb->dev || !urb->dev->bus) {
- + PDEBUG(1, "dev or bus is null");
- + return -ENODEV;
- + }
- +
- + hc = urb->dev->bus->hcpriv;
- + urbp = urb->hcpriv;
- +
- + /* a request to the virtual root hub */
- + if (usb_pipedevice(urb->pipe) == hc->rh.devnum)
- + return sl811_rh_unlink_urb(urb);
- +
- + spin_lock_irqsave(&hc->hc_lock, flags);
- + spin_lock(&urb->lock);
- +
- + // in wait list
- + if (sl811_find_same_urb(&hc->wait_list, urb)) {
- + PDEBUG(4, "unlink urb in wait list");
- + list_del_init(&urb->urb_list);
- + urb->status = -ENOENT;
- + call = 1;
- + goto out;
- + }
- +
- + // in intr idle list.
- + if (sl811_find_same_urb(&hc->idle_intr_list, urb)) {
- + PDEBUG(4, "unlink urb in idle intr list");
- + list_del_init(&urb->urb_list);
- + urb->status = -ENOENT;
- + sl811_free_urb_priv(urb);
- + usb_release_bandwidth(urb->dev, urb, 0);
- + if (--hc->active_urbs <= 0) {
- + hc->active_urbs = 0;
- + sl811_disable_interrupt(hc);
- + }
- + call = 1;
- + goto out;
- + }
- +
- + if (urb->status == -EINPROGRESS) {
- + PDEBUG(3, "urb is still in progress");
- + urbp->unlink = 1;
- +
- +re_unlink:
- + // Is it in progress?
- + urbp = urb->hcpriv;
- + if (urbp && hc->cur_td == urbp->cur_td) {
- + ++count;
- + if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
- + PDEBUG(3, "unlink: cur td is still in progress! count = %d", count);
- +re_schedule:
- + schedule = 1;
- + spin_unlock(&urb->lock);
- + spin_unlock_irqrestore(&hc->hc_lock, flags);
- + schedule_timeout(HZ/50);
- + spin_lock_irqsave(&hc->hc_lock, flags);
- + spin_lock(&urb->lock);
- + } else {
- + PDEBUG(3, "unlink: lost of interrupt? do parse! count = %d", count);
- + spin_unlock(&urb->lock);
- + sl811_transfer_done(hc, 0);
- + spin_lock(&urb->lock);
- + }
- + goto re_unlink;
- + }
- +
- + if (list_empty(&urb->urb_list)) {
- + PDEBUG(3, "unlink: list empty!");
- + goto out;
- + }
- +
- + if (urb->transfer_flags & USB_TIMEOUT_KILLED) {
- + PDEBUG(3, "unlink: time out killed");
- + // it is timeout killed by us
- + goto result;
- + } else if (urb->transfer_flags & USB_ASYNC_UNLINK) {
- + // we do nothing, just let it be processing later
- + PDEBUG(3, "unlink async, do nothing");
- + goto out;
- + } else {
- + // synchron without callback
- + PDEBUG(3, "unlink synchron, we wait the urb complete or timeout");
- + if (schedule == 0) {
- + PDEBUG(3, "goto re_schedule");
- + goto re_schedule;
- + } else {
- + PDEBUG(3, "already scheduled");
- + goto result;
- + }
- + }
- + } else if (!list_empty(&urb->urb_list)) {
- + PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status);
- + //list_del_init(&urb->urb_list);
- + //call = 1;
- + }
- +
- +out:
- + spin_unlock(&urb->lock);
- + spin_unlock_irqrestore(&hc->hc_lock, flags);
- +
- + if (call && urb->complete)
- + urb->complete(urb);
- +
- + return 0;
- +
- +result:
- + spin_unlock(&urb->lock);
- +
- + list_del_init(&urb->urb_list);
- + sl811_result_urb(urb);
- +
- + spin_unlock_irqrestore(&hc->hc_lock, flags);
- +
- + return 0;
- +}
- +
- +#else
- +
- +/*
- + * Unlink the urb from the urb list
- + */
- +static int sl811_unlink_urb(struct urb *urb)
- +{
- + unsigned long flags;
- + struct sl811_hc *hc;
- + struct sl811_urb_priv *urbp;
- + int call = 0;
- +
- + if (!urb) {
- + PDEBUG(1, "urb is null");
- + return -EINVAL;
- + }
- +
- + if (!urb->dev || !urb->dev->bus) {
- + PDEBUG(1, "dev or bus is null");
- + return -ENODEV;
- + }
- +
- + hc = urb->dev->bus->hcpriv;
- + urbp = urb->hcpriv;
- +
- + /* a request to the virtual root hub */
- + if (usb_pipedevice(urb->pipe) == hc->rh.devnum)
- + return sl811_rh_unlink_urb(urb);
- +
- + spin_lock_irqsave(&hc->hc_lock, flags);
- + spin_lock(&urb->lock);
- +
- + // in wait list
- + if (sl811_find_same_urb(&hc->wait_list, urb)) {
- + PDEBUG(2, "unlink urb in wait list");
- + list_del_init(&urb->urb_list);
- + urb->status = -ENOENT;
- + call = 1;
- + goto out;
- + }
- +
- + if (urb->status == -EINPROGRESS) {
- + PDEBUG(2, "urb is still in progress");
- + urbp->unlink = 1;
- +
- + // Is it in progress?
- + urbp = urb->hcpriv;
- + if (urbp && hc->cur_td == urbp->cur_td) {
- + // simple, let it out
- + PDEBUG(2, "unlink: cur td is still in progress!");
- + hc->cur_td = NULL;
- + }
- +
- + goto result;
- + } else if (!list_empty(&urb->urb_list)) {
- + PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status);
- + list_del_init(&urb->urb_list);
- + if (urbp)
- + goto result;
- + else
- + call = 1;
- + }
- +
- +out:
- + spin_unlock(&urb->lock);
- + spin_unlock_irqrestore(&hc->hc_lock, flags);
- +
- + if (call && urb->complete)
- + urb->complete(urb);
- +
- + return 0;
- +
- +result:
- + spin_unlock(&urb->lock);
- +
- + list_del_init(&urb->urb_list);
- + sl811_result_urb(urb);
- +
- + spin_unlock_irqrestore(&hc->hc_lock, flags);
- +
- + return 0;
- +}
- +
- +#endif
- +
- +static int sl811_get_current_frame_number(struct usb_device *usb_dev)
- +{
- + return ((struct sl811_hc *)(usb_dev->bus->hcpriv))->frame_number;
- +}
- +
- +static struct usb_operations sl811_device_operations =
- +{
- + sl811_alloc_dev_priv,
- + sl811_free_dev_priv,
- + sl811_get_current_frame_number,
- + sl811_submit_urb,
- + sl811_unlink_urb
- +};
- +
- +/*
- + * This functions transmit a td.
- + */
- +static inline void sl811_trans_cur_td(struct sl811_hc *hc, struct sl811_td *td)
- +{
- + sl811_print_td(4, td);
- + sl811_write_buf(hc, SL811_ADDR_A, &td->addr, 4);
- + if (td->len && (td->ctrl & SL811_USB_CTRL_DIR_OUT))
- + sl811_write_buf(hc, td->addr, td->buf, td->len);
- +
- + sl811_write(hc, SL811_CTRL_A, td->ctrl);
- +}
- +
- +
- +/*
- + * This function checks the status of the transmitted or received packet
- + * and copy the data from the SL811HS register into a buffer.
- + */
- +static void sl811_parse_cur_td(struct sl811_hc *hc, struct sl811_td *td)
- +{
- + struct urb *urb = td->urb;
- +#ifdef SL811_DEBUG
- + int dev = usb_pipedevice(td->urb->pipe);
- + int ep = usb_pipeendpoint(td->urb->pipe);
- +#endif
- +
- + sl811_read_buf(hc, SL811_STS_A, &td->status, 2);
- +
- + if (td->status & SL811_USB_STS_ACK) {
- + td->done = 1;
- +
- +/* if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) {
- + PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep);
- + td->td_status = -EILSEQ;
- + }
- +*/
- + if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0)
- + sl811_read_buf(hc, td->addr, td->buf, td->len - td->left);
- +
- + if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) {
- + PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td);
- + td->td_status = -EREMOTEIO;
- + } else
- + td->td_status = 0;
- + } else if (td->status & SL811_USB_STS_STALL) {
- + PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td);
- + td->td_status = -EPIPE;
- + if (urb->dev)
- + usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe));
- + td->done = 1;
- + } else if (td->status & SL811_USB_STS_OVERFLOW) {
- + PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT);
- + td->td_status = -EOVERFLOW;
- + td->done = 1;
- + } else if (td->status & SL811_USB_STS_TIMEOUT ) {
- + PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td);
- + td->td_status = -ETIMEDOUT;
- + if (--td->errcnt == 0)
- + td->done = 1;
- + } else if (td->status & SL811_USB_STS_ERROR) {
- + PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td);
- + td->td_status = -EILSEQ;
- + if (--td->errcnt == 0)
- + td->done = 1;
- + } else if (td->status & SL811_USB_STS_NAK) {
- + ++td->nakcnt;
- + PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt);
- + td->td_status = -EINPROGRESS;
- + if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) {
- + PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt);
- + td->td_status = -ETIMEDOUT;
- + td->done = 1;
- + }
- + }
- +
- + sl811_print_td(4, td);
- +}
- +
- +/*
- + * This function checks the status of current urb.
- + */
- +static int sl811_parse_cur_urb(struct urb *urb)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- + struct sl811_td *td = urbp->cur_td;
- + struct list_head *tmp;
- +
- + sl811_print_td(5, td);
- +
- + // this td not done yet.
- + if (!td->done)
- + return 0;
- +
- + // the last ld, so the urb is done.
- + if (td == urbp->last_td) {
- + PDEBUG(4, "urb = %p is done success", td->urb);
- + if (usb_pipeisoc(td->urb->pipe))
- + PDEBUG(4, "ISO URB DONE, td = %p", td);
- + return 1;
- + }
- +
- + // iso transfer, we always advance to next td
- + if (usb_pipeisoc(td->urb->pipe)) {
- + tmp = &td->td_list;
- + tmp = tmp->next;
- + urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
- + PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td);
- + return 0;
- + }
- +
- + // some error occur, so the urb is done.
- + if (td->td_status) {
- + PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status);
- + return 1;
- + }
- +
- + // short packet.
- + if (td->left) {
- + if (usb_pipecontrol(td->urb->pipe)) {
- + // control packet, we advance to the last td
- + PDEBUG(3, "ctrl short packet, advance to last td");
- + urbp->cur_td = urbp->last_td;
- + return 0;
- + } else {
- + // interrut and bulk packet, urb is over.
- + PDEBUG(3, "bulk or intr short packet, urb is over");
- + return 1;
- + }
- + }
- +
- + // we advance to next td.
- + tmp = &td->td_list;
- + tmp = tmp->next;
- + urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
- +#ifdef SL811_DEBUG
- + PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td);
- + sl811_print_td(5, urbp->cur_td);
- + if (td == urbp->cur_td)
- + PDEBUG(1, "bug!!!");
- +#endif
- + return 0;
- +}
- +
- +/*
- + * Find the next td to transfer.
- + */
- +static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct sl811_td *cur_td)
- +{
- + struct sl811_urb_priv *urbp = urb->hcpriv;
- +
- + PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td);
- +
- + // iso don't schedule the td in the same frame.
- + if (usb_pipeisoc(cur_td->urb->pipe))
- + return NULL;
- +
- + // cur td is not complete
- + if (!cur_td->done)
- + return NULL;
- +
- + // here, urbp->cur_td is already the next td;
- + return urbp->cur_td;
- +}
- +
- +/*
- + * Scan the list to find a active urb
- + */
- +static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct list_head *next)
- +{
- + struct urb *urb;
- + int i;
- +
- + if (list_empty(next))
- + return NULL;
- +
- + if (next == hc->cur_list)
- + return NULL;
- +
- + for (i = 0; i < 4; ++i)
- + if (next == &hc->urb_list[i])
- + return NULL;
- +
- + urb = list_entry(next, struct urb, urb_list);
- + PDEBUG(4, "next urb in list is at %p", urb);
- +
- + return urb;
- +}
- +
- +/*
- + * Find the next td to transfer.
- + */
- +static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head *next)
- +{
- + struct urb *urb = NULL;
- + int back_loop = 1;
- + struct list_head *old_list = hc->cur_list;
- +
- + // try to get next urb in the same list.
- + if (next) {
- + urb = sl811_get_list_next_urb(hc, next);
- + if (!urb)
- + ++hc->cur_list;
- + }
- +
- + // try other list.
- + if (!urb) {
- +re_loop:
- + // try all the list.
- + while (hc->cur_list < &hc->urb_list[4]) {
- + if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next)))
- + return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td;
- + ++hc->cur_list;
- + }
- + // the last list is try
- + if (back_loop && (old_list >= &hc->ctrl_list)) {
- + hc->cur_list = &hc->ctrl_list;
- + back_loop = 0;
- + goto re_loop;
- + }
- + }
- +
- + if (hc->cur_list > &hc->urb_list[3])
- + hc->cur_list = &hc->ctrl_list;
- +
- + return NULL;
- +}
- +
- +/*
- + * This function process the transfer rusult.
- + */
- +static void sl811_transfer_done(struct sl811_hc *hc, int sof)
- +{
- + struct sl811_td *cur_td = hc->cur_td, *next_td = NULL;
- + struct urb *cur_urb = NULL;
- + struct list_head *next = NULL;
- + int done;
- +
- + PDEBUG(5, "enter");
- +
- + if (cur_td == NULL) {
- + PDEBUG(1, "in done interrupt, but td is null, be already parsed?");
- + return ;
- + }
- +
- + cur_urb = cur_td->urb;
- + hc->cur_td = NULL;
- + next = &cur_urb->urb_list;
- + next = next->next;
- +
- + spin_lock(&cur_urb->lock);
- + sl811_parse_cur_td(hc, cur_td);
- + done = sl811_parse_cur_urb(cur_urb);
- + spin_unlock(&cur_urb->lock);
- +
- + if (done) {
- + list_del_init(&cur_urb->urb_list);
- + cur_td = NULL;
- + sl811_result_urb(cur_urb);
- + }
- +
- + if (sof)
- + return ;
- +
- + if (!done) {
- + next_td = sl811_schedule_next_td(cur_urb, cur_td);
- + if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > next_td->bustime)) {
- + hc->cur_td = next_td;
- + PDEBUG(5, "ADD TD");
- + sl811_trans_cur_td(hc, next_td);
- + return ;
- + }
- + }
- +
- + while (1) {
- + next_td = sl811_schedule_next_urb(hc, next);
- + if (!next_td)
- + return;
- + if (next_td == cur_td)
- + return;
- + next = &next_td->urb->urb_list;
- + next = next->next;
- + if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
- + hc->cur_td = next_td;
- + PDEBUG(5, "ADD TD");
- + sl811_trans_cur_td(hc, next_td);
- + return ;
- + }
- + }
- +}
- +
- +/*
- + *
- + */
- +static void inline sl811_dec_intr_interval(struct sl811_hc *hc)
- +{
- + struct list_head *head, *tmp;
- + struct urb *urb;
- + struct sl811_urb_priv *urbp;
- +
- + if (list_empty(&hc->idle_intr_list))
- + return ;
- +
- + head = &hc->idle_intr_list;
- + tmp = head->next;
- +
- + while (tmp != head) {
- + urb = list_entry(tmp, struct urb, urb_list);
- + tmp = tmp->next;
- + spin_lock(&urb->lock);
- + urbp = urb->hcpriv;
- + if (--urbp->interval == 0) {
- + list_del(&urb->urb_list);
- + list_add(&urb->urb_list, &hc->intr_list);
- + PDEBUG(4, "intr urb active");
- + }
- + spin_unlock(&urb->lock);
- + }
- +}
- +
- +/*
- + * The sof interrupt is happen.
- + */
- +static void sl811_start_sof(struct sl811_hc *hc)
- +{
- + struct sl811_td *next_td;
- +#ifdef SL811_DEBUG
- + static struct sl811_td *repeat_td = NULL;
- + static repeat_cnt = 1;
- +#endif
- + if (++hc->frame_number > 1024)
- + hc->frame_number = 0;
- +
- + if (hc->active_urbs == 0)
- + return ;
- +
- + sl811_dec_intr_interval(hc);
- +
- + if (hc->cur_td) {
- + if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
- +#ifdef SL811_DEBUG
- + if (repeat_td == hc->cur_td)
- + ++repeat_cnt;
- + else {
- + if (repeat_cnt >= 2)
- + PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, repeat_cnt);
- + repeat_cnt = 1;
- + repeat_td = hc->cur_td;
- + }
- +#endif
- + return ;
- + } else {
- + PDEBUG(2, "lost of interrupt in sof? do parse!");
- + sl811_transfer_done(hc, 1);
- +
- + // let this frame idle
- + return;
- + }
- + }
- +
- + hc->cur_list = &hc->iso_list;
- +
- + if (hc->active_urbs == 0)
- + return ;
- +
- + next_td = sl811_schedule_next_urb(hc, NULL);
- + if (!next_td) {
- +#ifdef SL811_DEBUG
- + if (list_empty(&hc->idle_intr_list))
- + PDEBUG(2, "not schedule a td, why? urbs = %d", hc->active_urbs);
- +#endif
- + return;
- + }
- + if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
- + hc->cur_td = next_td;
- + sl811_trans_cur_td(hc, next_td);
- + } else
- + PDEBUG(2, "bus time if not enough, why?");
- +}
- +
- +/*
- + * This function resets SL811HS controller and detects the speed of
- + * the connecting device
- + *
- + * Return: 0 = no device attached; 1 = USB device attached
- + */
- +static int sl811_hc_reset(struct sl811_hc *hc)
- +{
- + int status ;
- +
- + sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
- + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
- +
- + mdelay(20);
- +
- + // Disable hardware SOF generation.
- + sl811_write(hc, SL811_CTRL1, 0);
- + mdelay(2);
- + sl811_write(hc, SL811_INTRSTS, 0xff);
- + status = sl811_read(hc, SL811_INTRSTS);
- +
- + if (status & SL811_INTR_NOTPRESENT) {
- + // Device is not present
- + PDEBUG(0, "Device not present");
- + hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
- + hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
- + sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV);
- + return 0;
- + }
- +
- + // Send SOF to address 0, endpoint 0.
- + sl811_write(hc, SL811_LEN_B, 0);
- + sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
- + sl811_write(hc, SL811_DEV_B, 0x00);
- + sl811_write (hc, SL811_SOFLOW, SL811_12M_HI);
- +
- + if (status & SL811_INTR_SPEED_FULL) {
- + /* full speed device connect directly to root hub */
- + PDEBUG (0, "Full speed Device attached");
- +
- + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
- + mdelay(20);
- + sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
- + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF);
- +
- + /* start the SOF or EOP */
- + sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
- + hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
- + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
- + mdelay(2);
- + sl811_write (hc, SL811_INTRSTS, 0xff);
- + } else {
- + /* slow speed device connect directly to root-hub */
- + PDEBUG(0, "Low speed Device attached");
- +
- + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
- + mdelay(20);
- + sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
- + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
- +
- + /* start the SOF or EOP */
- + sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
- + hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
- + mdelay(2);
- + sl811_write(hc, SL811_INTRSTS, 0xff);
- + }
- +
- + hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
- + sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV);
- +
- + return 1;
- +}
- +
- +/*
- + * Interrupt service routine.
- + */
- +static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r)
- +{
- + __u8 status;
- + struct sl811_hc *hc = __hc;
- +
- + status = sl811_read(hc, SL811_INTRSTS);
- + if (status == 0)
- + return ;
- +
- + sl811_write(hc, SL811_INTRSTS, 0xff);
- +
- + if (status & SL811_INTR_INSRMV) {
- + sl811_write(hc, SL811_INTR, 0);
- + sl811_write(hc, SL811_CTRL1, 0);
- + // wait for device stable
- + mdelay(100);
- + sl811_hc_reset(hc);
- + return ;
- + }
- +
- + spin_lock(&hc->hc_lock);
- +
- + if (status & SL811_INTR_DONE_A) {
- + if (status & SL811_INTR_SOF) {
- + sl811_transfer_done(hc, 1);
- + PDEBUG(4, "sof in done!");
- + sl811_start_sof(hc);
- + } else
- + sl811_transfer_done(hc, 0);
- + } else if (status & SL811_INTR_SOF)
- + sl811_start_sof(hc);
- +
- + spin_unlock(&hc->hc_lock);
- +
- + return ;
- +}
- +
- +/*
- + * This function allocates all data structure and store in the
- + * private data structure.
- + *
- + * Return value : data structure for the host controller
- + */
- +static struct sl811_hc* __devinit sl811_alloc_hc(void)
- +{
- + struct sl811_hc *hc;
- + struct usb_bus *bus;
- + int i;
- +
- + PDEBUG(5, "enter");
- +
- + hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL);
- + if (!hc)
- + return NULL;
- +
- + memset(hc, 0, sizeof(struct sl811_hc));
- +
- + hc->rh_status.wPortStatus = USB_PORT_STAT_POWER;
- + hc->rh_status.wPortChange = 0;
- +
- + hc->active_urbs = 0;
- + INIT_LIST_HEAD(&hc->hc_hcd_list);
- + list_add(&hc->hc_hcd_list, &sl811_hcd_list);
- +
- + init_waitqueue_head(&hc->waitq);
- +
- + for (i = 0; i < 6; ++i)
- + INIT_LIST_HEAD(&hc->urb_list[i]);
- +
- + hc->cur_list = &hc->iso_list;
- +
- + bus = usb_alloc_bus(&sl811_device_operations);
- + if (!bus) {
- + kfree (hc);
- + return NULL;
- + }
- +
- + hc->bus = bus;
- + bus->hcpriv = hc;
- +
- + return hc;
- +}
- +
- +/*
- + * This function De-allocate all resources
- + */
- +static void sl811_release_hc(struct sl811_hc *hc)
- +{
- + PDEBUG(5, "enter");
- +
- + /* disconnect all devices */
- + if (hc->bus->root_hub)
- + usb_disconnect(&hc->bus->root_hub);
- +
- + if (hc->addr_io)
- + release_region(hc->addr_io, 1);
- +
- + if (hc->data_io)
- + release_region(hc->data_io, 1);
- +
- + if (hc->irq)
- + free_irq(hc->irq, hc);
- +
- + usb_deregister_bus(hc->bus);
- + usb_free_bus(hc->bus);
- +
- + list_del(&hc->hc_hcd_list);
- + INIT_LIST_HEAD(&hc->hc_hcd_list);
- +
- + kfree (hc);
- +}
- +
- +/*
- + * This function is board specific. It sets up the interrupt to
- + * be an edge trigger and trigger on the rising edge
- + */
- +static void sl811_init_irq(void)
- +{
- + GPDR &= ~(1<<23);
- + set_GPIO_IRQ_edge(1<<23, GPIO_RISING_EDGE);
- +}
- +
- +/*
- + * This function request IO memory regions, request IRQ, and
- + * allocate all other resources.
- + *
- + * Input: addr_io = first IO address
- + * data_io = second IO address
- + * irq = interrupt number
- + *
- + * Return: 0 = success or error condition
- + */
- +static int __devinit sl811_found_hc(int addr_io, int data_io, int irq)
- +{
- + struct sl811_hc *hc;
- +
- + PDEBUG(5, "enter");
- +
- + hc = sl811_alloc_hc();
- + if (!hc)
- + return -ENOMEM;
- +
- + sl811_init_irq();
- +
- + if (!request_region(addr_io, 1, "SL811 USB HOST")) {
- + PDEBUG(1, "request address %d failed", addr_io);
- + sl811_release_hc(hc);
- + return -EBUSY;
- + }
- + hc->addr_io = addr_io;
- +
- + if (!request_region(data_io, 1, "SL811 USB HOST")) {
- + PDEBUG(1, "request address %d failed", data_io);
- + sl811_release_hc (hc);
- + return -EBUSY;
- + }
- + hc->data_io = data_io;
- +
- + usb_register_bus(hc->bus);
- +
- + if (request_irq(irq, sl811_interrupt, 0, "SL811", hc)) {
- + PDEBUG(1, "request interrupt %d failed", irq);
- + sl811_release_hc(hc);
- + return -EBUSY;
- + }
- + hc->irq = irq;
- +
- + printk(KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n",
- + addr_io, data_io, irq);
- +
- + if (sl811_reg_test(hc)) {
- + PDEBUG(1, "SL811 register test failed!");
- + sl811_release_hc(hc);
- + return -ENODEV;
- + }
- +
- + sl811_hc_reset(hc);
- + sl811_connect_rh(hc);
- +
- + return 0;
- +}
- +
- +/*
- + * This is an init function, and it is the first function being called
- + *
- + * Return: 0 = success or error condition
- + */
- +static int __init sl811_hcd_init(void)
- +{
- + int ret;
- +
- + PDEBUG(5, "etner");
- +
- + info(DRIVER_VERSION " : " DRIVER_DESC);
- +
- + ret = sl811_found_hc(sl811_addr_io, sl811_data_io, sl811_irq);
- +
- + return ret;
- +}
- +
- +/*
- + * This is a cleanup function, and it is called when module is unloaded.
- + */
- +static void __exit sl811_hcd_cleanup(void)
- +{
- + struct list_head *list = sl811_hcd_list.next;
- + struct sl811_hc *hc;
- +
- + PDEBUG(5, "enter");
- +
- + for (; list != &sl811_hcd_list; ) {
- + hc = list_entry(list, struct sl811_hc, hc_hcd_list);
- + list = list->next;
- + sl811_release_hc(hc);
- + }
- +}
- +
- +module_init(sl811_hcd_init);
- +module_exit(sl811_hcd_cleanup);
- +
- +MODULE_AUTHOR(DRIVER_AUTHOR);
- +MODULE_DESCRIPTION(DRIVER_DESC);
- diff -Nru a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h
- --- /dev/null Wed Dec 31 16:00:00 1969
- +++ b/drivers/usb/host/sl811.h Mon Jul 14 10:04:12 2003
- @@ -0,0 +1,177 @@
- +#ifndef __LINUX_SL811_H
- +#define __LINUX_SL811_H
- +
- +//#define SL811_DEBUG
- +
- +#ifdef SL811_DEBUG
- + #define PDEBUG(level, fmt, args...) \
- + if (debug >= (level)) info("[%s:%d] " fmt, \
- + __PRETTY_FUNCTION__, __LINE__ , ## args)
- +#else
- + #define PDEBUG(level, fmt, args...) do {} while(0)
- +#endif
- +
- +//#define SL811_TIMEOUT
- +
- +/* Sl811 host control register */
- +#define SL811_CTRL_A 0x00
- +#define SL811_ADDR_A 0x01
- +#define SL811_LEN_A 0x02
- +#define SL811_STS_A 0x03 /* read */
- +#define SL811_PIDEP_A 0x03 /* write */
- +#define SL811_CNT_A 0x04 /* read */
- +#define SL811_DEV_A 0x04 /* write */
- +#define SL811_CTRL1 0x05
- +#define SL811_INTR 0x06
- +#define SL811_CTRL_B 0x08
- +#define SL811_ADDR_B 0x09
- +#define SL811_LEN_B 0x0A
- +#define SL811_STS_B 0x0B /* read */
- +#define SL811_PIDEP_B 0x0B /* write */
- +#define SL811_CNT_B 0x0C /* read */
- +#define SL811_DEV_B 0x0C /* write */
- +#define SL811_INTRSTS 0x0D /* write clears bitwise */
- +#define SL811_HWREV 0x0E /* read */
- +#define SL811_SOFLOW 0x0E /* write */
- +#define SL811_SOFCNTDIV 0x0F /* read */
- +#define SL811_CTRL2 0x0F /* write */
- +
- +/* USB control register bits (addr 0x00 and addr 0x08) */
- +#define SL811_USB_CTRL_ARM 0x01
- +#define SL811_USB_CTRL_ENABLE 0x02
- +#define SL811_USB_CTRL_DIR_OUT 0x04
- +#define SL811_USB_CTRL_ISO 0x10
- +#define SL811_USB_CTRL_SOF 0x20
- +#define SL811_USB_CTRL_TOGGLE_1 0x40
- +#define SL811_USB_CTRL_PREAMBLE 0x80
- +
- +/* USB status register bits (addr 0x03 and addr 0x0B) */
- +#define SL811_USB_STS_ACK 0x01
- +#define SL811_USB_STS_ERROR 0x02
- +#define SL811_USB_STS_TIMEOUT 0x04
- +#define SL811_USB_STS_TOGGLE_1 0x08
- +#define SL811_USB_STS_SETUP 0x10
- +#define SL811_USB_STS_OVERFLOW 0x20
- +#define SL811_USB_STS_NAK 0x40
- +#define SL811_USB_STS_STALL 0x80
- +
- +/* Control register 1 bits (addr 0x05) */
- +#define SL811_CTRL1_SOF 0x01
- +#define SL811_CTRL1_RESET 0x08
- +#define SL811_CTRL1_JKSTATE 0x10
- +#define SL811_CTRL1_SPEED_LOW 0x20
- +#define SL811_CTRL1_SUSPEND 0x40
- +
- +/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */
- +#define SL811_INTR_DONE_A 0x01
- +#define SL811_INTR_DONE_B 0x02
- +#define SL811_INTR_SOF 0x10
- +#define SL811_INTR_INSRMV 0x20
- +#define SL811_INTR_DETECT 0x40
- +#define SL811_INTR_NOTPRESENT 0x40
- +#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */
- +
- +/* HW rev and SOF lo register bits (addr 0x0E) */
- +#define SL811_HWR_HWREV 0xF0
- +
- +/* SOF counter and control reg 2 (addr 0x0F) */
- +#define SL811_CTL2_SOFHI 0x3F
- +#define SL811_CTL2_DSWAP 0x40
- +#define SL811_CTL2_HOST 0x80
- +
- +/* Set up for 1-ms SOF time. */
- +#define SL811_12M_LOW 0xE0
- +#define SL811_12M_HI 0x2E
- +
- +#define SL811_DATA_START 0x10
- +#define SL811_DATA_LIMIT 240
- +
- +
- +/* 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
- +
- +
- +#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep))
- +
- +/* Virtual Root HUB */
- +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 */
- +};
- +
- +struct sl811_td {
- + /* hardware */
- + __u8 ctrl; /* control register */
- +
- + /* write */
- + __u8 addr; /* base adrress register */
- + __u8 len; /* base length register */
- + __u8 pidep; /* PId and endpoint register */
- + __u8 dev; /* device address register */
- +
- + /* read */
- + __u8 status; /* status register */
- + __u8 left; /* transfer count register */
- +
- + /* software */
- + __u8 errcnt; /* error count, begin with 3 */
- + __u8 done; /* is this td tranfer done */
- + __u8 *buf; /* point to data buffer for tranfer */
- + int bustime; /* the bus time need by this td */
- + int td_status; /* the status of this td */
- + int nakcnt; /* number of naks */
- + struct urb *urb; /* the urb this td belongs to */
- + struct list_head td_list; /* link to a list of the urb */
- +};
- +
- +struct sl811_urb_priv {
- + struct urb *urb; /* the urb this priv beloings to */
- + struct list_head td_list; /* list of all the td of this urb */
- + struct sl811_td *cur_td; /* current td is in processing or it will be */
- + struct sl811_td *first_td; /* the first td of this urb */
- + struct sl811_td *last_td; /* the last td of this urb */
- + int interval; /* the query time value for intr urb */
- + int unlink; /* is the this urb unlinked */
- + unsigned long inserttime; /* the time when insert to list */
- +};
- +
- +struct sl811_hc {
- + spinlock_t hc_lock; /* Lock for this structure */
- +
- + int irq; /* IRQ number this hc use */
- + int addr_io; /* I/O address line address */
- + int data_io; /* I/O data line address */
- + struct virt_root_hub rh; /* root hub */
- + struct usb_port_status rh_status;/* root hub port status */
- + struct list_head urb_list[6]; /* set of urbs, the order is iso,intr,ctrl,bulk,inactive intr, wait */
- + struct list_head *cur_list; /* the current list is in process */
- + wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */
- + struct sl811_td *cur_td; /* point to the td is in process */
- + struct list_head hc_hcd_list; /* list of all hci_hcd */
- + struct usb_bus *bus; /* our bus */
- + int active_urbs; /* total number of active usbs */
- + int frame_number; /* the current frame number, we do't use it, any one need it? */
- +};
- +
- +#define iso_list urb_list[0] /* set of isoc urbs */
- +#define intr_list urb_list[1] /* ordered (tree) set of int urbs */
- +#define ctrl_list urb_list[2] /* set of ctrl urbs */
- +#define bulk_list urb_list[3] /* set of bulk urbs */
- +#define idle_intr_list urb_list[4] /* set of intr urbs in its idle time*/
- +#define wait_list urb_list[5] /* set of wait urbs */
- +
- +#endif
复制代码 |
|