📄 hcd.c
字号:
/* * Copyright (c) 2001-2002 by David Brownell * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/config.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/kmod.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/completion.h>#include <linux/uts.h> /* for UTS_SYSNAME */#ifdef CONFIG_USB_DEBUG #define DEBUG#else #undef DEBUG#endif#include <linux/usb.h>#include "hcd.h"#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>/*-------------------------------------------------------------------------*//* * USB Host Controller Driver framework * * Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing * HCD-specific behaviors/bugs. * * This does error checks, tracks devices and urbs, and delegates to a * "hc_driver" only for code (and data) that really needs to know about * hardware differences. That includes root hub registers, i/o queues, * and so on ... but as little else as possible. * * Shared code includes most of the "root hub" code (these are emulated, * though each HC's hardware works differently) and PCI glue, plus request * tracking overhead. The HCD code should only block on spinlocks or on * hardware handshaking; blocking on software events (such as other kernel * threads releasing resources, or completing actions) is all generic. * * Happens the USB 2.0 spec says this would be invisible inside the "USBD", * and includes mostly a "HCDI" (HCD Interface) along with some APIs used * only by the hub driver ... and that neither should be seen or used by * usb client device drivers. * * Contributors of ideas or unattributed patches include: David Brownell, * Roman Weissgaerber, Rory Bolt, ... * * HISTORY: * 2001-12-12 Initial patch version for Linux 2.5.1 kernel. *//*-------------------------------------------------------------------------*//* host controllers we manage */static LIST_HEAD (hcd_list);/* used when updating list of hcds */static DECLARE_MUTEX (hcd_list_lock);/* used when updating hcd data */static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;static struct usb_operations hcd_operations;/*-------------------------------------------------------------------------*//* * Sharable chunks of root hub code. *//*-------------------------------------------------------------------------*/#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)/* usb 2.0 root hub device descriptor */static const u8 usb2_rh_dev_descriptor [18] = { 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ 0x00, 0x02, /* __u16 bcdUSB; v2.0 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ 0x00, 0x00, /* __u16 idVendor; */ 0x00, 0x00, /* __u16 idProduct; */ KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */ 0x03, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ 0x01, /* __u8 iSerialNumber; */ 0x01 /* __u8 bNumConfigurations; */};/* no usb 2.0 root hub "device qualifier" descriptor: one speed only *//* usb 1.1 root hub device descriptor */static const u8 usb11_rh_dev_descriptor [18] = { 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ 0x10, 0x01, /* __u16 bcdUSB; v1.1 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ 0x00, 0x00, /* __u16 idVendor; */ 0x00, 0x00, /* __u16 idProduct; */ KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */ 0x03, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ 0x01, /* __u8 iSerialNumber; */ 0x01 /* __u8 bNumConfigurations; */};/*-------------------------------------------------------------------------*//* Configuration descriptors for our root hubs */static const u8 fs_rh_config_descriptor [] = { /* one configuration */ 0x09, /* __u8 bLength; */ 0x02, /* __u8 bDescriptorType; Configuration */ 0x19, 0x00, /* __u16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 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; */ /* USB 1.1: * USB 2.0, single TT organization (mandatory): * one interface, protocol 0 * * USB 2.0, multiple TT organization (optional): * two interfaces, protocols 1 (like single TT) * and 2 (multiple TT mode) ... config is * sometimes settable * NOT IMPLEMENTED */ /* one 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; [usb1.1 or single tt] */ 0x00, /* __u8 if_iInterface; */ /* one endpoint (status change endpoint) */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ 0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */};static const u8 hs_rh_config_descriptor [] = { /* one configuration */ 0x09, /* __u8 bLength; */ 0x02, /* __u8 bDescriptorType; Configuration */ 0x19, 0x00, /* __u16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 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; */ /* USB 1.1: * USB 2.0, single TT organization (mandatory): * one interface, protocol 0 * * USB 2.0, multiple TT organization (optional): * two interfaces, protocols 1 (like single TT) * and 2 (multiple TT mode) ... config is * sometimes settable * NOT IMPLEMENTED */ /* one 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; [usb1.1 or single tt] */ 0x00, /* __u8 if_iInterface; */ /* one endpoint (status change endpoint) */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */};/*-------------------------------------------------------------------------*//* * helper routine for returning string descriptors in UTF-16LE * input can actually be ISO-8859-1; ASCII is its 7-bit subset */static int ascii2utf (char *s, u8 *utf, int utfmax){ int retval; for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { *utf++ = *s++; *utf++ = 0; } return retval;}/* * rh_string - provides manufacturer, product and serial strings for root hub * @id: the string ID number (1: serial number, 2: product, 3: vendor) * @pci_desc: PCI device descriptor for the relevant HC * @type: string describing our driver * @data: return packet in UTF-16 LE * @len: length of the return packet * * Produces either a manufacturer, product or serial number string for the * virtual root hub device. */static int rh_string ( int id, struct usb_hcd *hcd, u8 *data, int len) { char buf [100]; // language ids if (id == 0) { *data++ = 4; *data++ = 3; /* 4 bytes string data */ *data++ = 0; *data++ = 0; /* some language id */ return 4; // serial number } else if (id == 1) { strcpy (buf, hcd->bus_name); // product description } else if (id == 2) { strcpy (buf, hcd->product_desc); // id 3 == vendor description } else if (id == 3) { sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE, hcd->description); // unsupported IDs --> "protocol stall" } else return 0; data [0] = 2 + ascii2utf (buf, data + 2, len - 2); data [1] = 3; /* type == string */ return data [0];}/* Root hub control transfers execute synchronously */static int rh_call_control (struct usb_hcd *hcd, struct urb *urb){ devrequest *cmd = (devrequest *) urb->setup_packet; u16 typeReq, wValue, wIndex, wLength; const u8 *bufp = 0; u8 *ubuf = urb->transfer_buffer; int len = 0; typeReq = (cmd->requesttype << 8) | cmd->request; wValue = le16_to_cpu (cmd->value); wIndex = le16_to_cpu (cmd->index); wLength = le16_to_cpu (cmd->length); if (wLength > urb->transfer_buffer_length) goto error; /* set up for success */ urb->status = 0; urb->actual_length = wLength; switch (typeReq) { /* DEVICE REQUESTS */ case DeviceRequest | USB_REQ_GET_STATUS: // DEVICE_REMOTE_WAKEUP ubuf [0] = 1; // selfpowered ubuf [1] = 0; /* FALLTHROUGH */ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: case DeviceOutRequest | USB_REQ_SET_FEATURE: dbg ("no device features yet yet"); break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: ubuf [0] = 1; /* FALLTHROUGH */ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: break; case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (wValue & 0xff00) { case USB_DT_DEVICE << 8: if (hcd->driver->flags & HCD_USB2) bufp = usb2_rh_dev_descriptor; else if (hcd->driver->flags & HCD_USB11) bufp = usb11_rh_dev_descriptor; else goto error; len = 18; break; case USB_DT_CONFIG << 8: if (hcd->driver->flags & HCD_USB2) { bufp = hs_rh_config_descriptor; len = sizeof hs_rh_config_descriptor; } else { bufp = fs_rh_config_descriptor; len = sizeof fs_rh_config_descriptor; } break; case USB_DT_STRING << 8: urb->actual_length = rh_string ( wValue & 0xff, hcd, ubuf, wLength); break; default: goto error; } break; case DeviceRequest | USB_REQ_GET_INTERFACE: ubuf [0] = 0; /* FALLTHROUGH */ case DeviceOutRequest | USB_REQ_SET_INTERFACE: break; case DeviceOutRequest | USB_REQ_SET_ADDRESS: // wValue == urb->dev->devaddr dbg ("%s root hub device address %d", hcd->bus_name, wValue); break; /* INTERFACE REQUESTS (no defined feature/status flags) */ /* ENDPOINT REQUESTS */ case EndpointRequest | USB_REQ_GET_STATUS: // ENDPOINT_HALT flag ubuf [0] = 0; ubuf [1] = 0; /* FALLTHROUGH */ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_SET_FEATURE: dbg ("no endpoint features yet"); break; /* CLASS REQUESTS (and errors) */ default: /* non-generic request */ urb->status = hcd->driver->hub_control (hcd, typeReq, wValue, wIndex, ubuf, wLength); break;error: /* "protocol stall" on error */ urb->status = -EPIPE; dbg ("unsupported hub control message (maxchild %d)", urb->dev->maxchild); } if (urb->status) { urb->actual_length = 0; dbg ("CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d", typeReq, wValue, wIndex, wLength, urb->status); } if (bufp) { if (urb->transfer_buffer_length < len) len = urb->transfer_buffer_length; urb->actual_length = len; // always USB_DIR_IN, toward host memcpy (ubuf, bufp, len); } /* any errors get returned through the urb completion */ usb_hcd_giveback_urb (hcd, urb); return 0;}/*-------------------------------------------------------------------------*//* * Root Hub interrupt transfers are synthesized with a timer. * Completions are called in_interrupt() but not in_irq(). */static void rh_report_status (unsigned long ptr);static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) { int len = 1 + (urb->dev->maxchild / 8); /* rh_timer protected by hcd_data_lock */ if (timer_pending (&hcd->rh_timer) || urb->status != -EINPROGRESS || !HCD_IS_RUNNING (hcd->state) || urb->transfer_buffer_length < len) { dbg ("not queuing status urb, stat %d", urb->status); return -EINVAL; } urb->hcpriv = hcd; /* nonzero to indicate it's queued */ init_timer (&hcd->rh_timer); hcd->rh_timer.function = rh_report_status; hcd->rh_timer.data = (unsigned long) urb; /* USB 2.0 spec says 256msec; this is close enough */ hcd->rh_timer.expires = jiffies + HZ/4; add_timer (&hcd->rh_timer); return 0;}/* timer callback */static void rh_report_status (unsigned long ptr){ struct urb *urb; struct usb_hcd *hcd; int length; unsigned long flags; urb = (struct urb *) ptr; spin_lock_irqsave (&urb->lock, flags); if (!urb->dev) { spin_unlock_irqrestore (&urb->lock, flags); return; } hcd = urb->dev->bus->hcpriv; if (urb->status == -EINPROGRESS) { if (HCD_IS_RUNNING (hcd->state)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -