📄 usb-host.c
字号:
/* * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD) * * Copyright (c) 2002, 2003 Axis Communications AB. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/unistd.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/version.h>#include <linux/list.h>#include <linux/spinlock.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/dma.h>#include <asm/system.h>#include <asm/svinto.h>#include <linux/usb.h>/* Ugly include because we don't live with the other host drivers. */#include <../drivers/usb/hcd.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)typedef struct urb urb_t, *purb_t;typedef struct iso_packet_descriptor iso_packet_descriptor_t;typedef struct usb_ctrlrequest devrequest;#endif#include "usb-host.h"#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBRstatic const char *usb_hcd_version = "$Revision: 1.19 $";#undef KERN_DEBUG#define KERN_DEBUG ""#undef USB_DEBUG_RH#undef USB_DEBUG_EPID#undef USB_DEBUG_SB#undef USB_DEBUG_DESC#undef USB_DEBUG_URB#undef USB_DEBUG_TRACE#undef USB_DEBUG_BULK#undef USB_DEBUG_CTRL#undef USB_DEBUG_INTR#undef USB_DEBUG_ISOC#ifdef USB_DEBUG_RH#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)#else#define dbg_rh(format, arg...) do {} while (0)#endif#ifdef USB_DEBUG_EPID#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)#else#define dbg_epid(format, arg...) do {} while (0)#endif#ifdef USB_DEBUG_SB#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)#else#define dbg_sb(format, arg...) do {} while (0)#endif#ifdef USB_DEBUG_CTRL#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)#else#define dbg_ctrl(format, arg...) do {} while (0)#endif#ifdef USB_DEBUG_BULK#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)#else#define dbg_bulk(format, arg...) do {} while (0)#endif#ifdef USB_DEBUG_INTR#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)#else#define dbg_intr(format, arg...) do {} while (0)#endif#ifdef USB_DEBUG_ISOC#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)#else#define dbg_isoc(format, arg...) do {} while (0)#endif#ifdef USB_DEBUG_TRACE#define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering: " __FUNCTION__ "\n"))#define DBFEXIT (printk(KERN_DEBUG __FILE__ ": Exiting: " __FUNCTION__ "\n"))#else#define DBFENTER do {} while (0)#define DBFEXIT do {} while (0)#endif/*------------------------------------------------------------------- Virtual Root Hub -------------------------------------------------------------------*/static __u8 root_hub_dev_des[] ={ 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ 0x00, /* __u16 bcdUSB; v1.0 */ 0x01, 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x00, /* __u8 bDeviceProtocol; */ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ 0x00, /* __u16 idVendor; */ 0x00, 0x00, /* __u16 idProduct; */ 0x00, 0x00, /* __u16 bcdDevice; */ 0x00, 0x00, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ 0x01, /* __u8 iSerialNumber; */ 0x01 /* __u8 bNumConfigurations; */};/* Configuration descriptor */static __u8 root_hub_config_des[] ={ 0x09, /* __u8 bLength; */ 0x02, /* __u8 bDescriptorType; Configuration */ 0x19, /* __u16 wTotalLength; */ 0x00, 0x01, /* __u8 bNumInterfaces; */ 0x01, /* __u8 bConfigurationValue; */ 0x00, /* __u8 iConfiguration; */ 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */ 0x00, /* __u8 MaxPower; */ /* interface */ 0x09, /* __u8 if_bLength; */ 0x04, /* __u8 if_bDescriptorType; Interface */ 0x00, /* __u8 if_bInterfaceNumber; */ 0x00, /* __u8 if_bAlternateSetting; */ 0x01, /* __u8 if_bNumEndpoints; */ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ 0x00, /* __u8 if_bInterfaceSubClass; */ 0x00, /* __u8 if_bInterfaceProtocol; */ 0x00, /* __u8 if_iInterface; */ /* endpoint */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ 0x00, 0xff /* __u8 ep_bInterval; 255 ms */};static __u8 root_hub_hub_des[] ={ 0x09, /* __u8 bLength; */ 0x29, /* __u8 bDescriptorType; Hub-descriptor */ 0x02, /* __u8 bNbrPorts; */ 0x00, /* __u16 wHubCharacteristics; */ 0x00, 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ 0x00, /* __u8 bHubContrCurrent; 0 mA */ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */};static struct timer_list bulk_start_timer;static struct timer_list bulk_eot_timer;/* We want the start timer to expire before the eot timer, because the former might start traffic, thus making it unnecessary for the latter to time out. */#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}#define SLAB_FLAG (in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL)#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)/* Most helpful debugging aid */#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))/* Alternative assert define which stops after a failed assert. *//*#define assert(expr) \{ \ if (!(expr)) { \ err("assert failed at line %d",__LINE__); \ while (1); \ } \}*//* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically? To adjust it dynamically we would have to get an interrupt when we reach the end of the rx descriptor list, or when we get close to the end, and then allocate more descriptors. */#define NBR_OF_RX_DESC 512#define RX_DESC_BUF_SIZE 1024#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)/* The number of epids is, among other things, used for pre-allocating ctrl, bulk and isoc EP descriptors (one for each epid). Assumed to be > 1 when initiating the DMA lists. */#define NBR_OF_EPIDS 32/* Support interrupt traffic intervals up to 128 ms. */#define MAX_INTR_INTERVAL 128/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table must be "invalid". By this we mean that we shouldn't care about epid attentions for this epid, or at least handle them differently from epid attentions for "valid" epids. This define determines which one to use (don't change it). */ #define INVALID_EPID 31/* A special epid for the bulk dummys. */#define DUMMY_EPID 30/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */static __u32 epid_usage_bitmask;/* A bitfield to keep information on in/out traffic is needed to uniquely identify an endpoint on a device, since the most significant bit which indicates traffic direction is lacking in the ep_id field (ETRAX epids can handle both in and out traffic on endpoints that are otherwise identical). The USB framework, however, relies on them to be handled separately. For example, bulk IN and OUT urbs cannot be queued in the same list, since they would block each other. */static __u32 epid_out_traffic;/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line. Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));/* Pointers into RxDescList. */static volatile USB_IN_Desc_t *myNextRxDesc;static volatile USB_IN_Desc_t *myLastRxDesc;static volatile USB_IN_Desc_t *myPrevRxDesc;/* EP descriptors must be 32-bit aligned. */static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set, causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors in each frame. */static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0 results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point it to this buffer. */static int zout_buffer[4] __attribute__ ((aligned (4)));/* Cache for allocating new EP and SB descriptors. */static kmem_cache_t *usb_desc_cache;/* Cache for the registers allocated in the top half. */static kmem_cache_t *top_half_reg_cache;static struct usb_bus *etrax_usb_bus;/* This is a circular (double-linked) list of the active urbs for each epid. The head is never removed, and new urbs are linked onto the list as urb_entry_t elements. Don't reference urb_list directly; use the wrapper functions instead. Note that working with these lists might require spinlock protection. */static struct list_head urb_list[NBR_OF_EPIDS];/* Read about the need and usage of this lock in submit_ctrl_urb. */static spinlock_t urb_list_lock;/* Used when unlinking asynchronously. */static struct list_head urb_unlink_list;/* Wrappers around the list functions (include/linux/list.h). */static inline int urb_list_empty(int epid){ return list_empty(&urb_list[epid]);}/* Returns first urb for this epid, or NULL if list is empty. */static inline urb_t *urb_list_first(int epid){ urb_t *first_urb = 0; if (!urb_list_empty(epid)) { /* Get the first urb (i.e. head->next). */ urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list); first_urb = urb_entry->urb; } return first_urb;}/* Adds an urb_entry last in the list for this epid. */static inline void urb_list_add(urb_t *urb, int epid){ urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG); assert(urb_entry); urb_entry->urb = urb; list_add_tail(&urb_entry->list, &urb_list[epid]);}/* Search through the list for an element that contains this urb. (The list is expected to be short and the one we are about to delete will often be the first in the list.) */static inline urb_entry_t *__urb_list_entry(urb_t *urb, int epid){ struct list_head *entry; struct list_head *tmp; urb_entry_t *urb_entry; list_for_each_safe(entry, tmp, &urb_list[epid]) { urb_entry = list_entry(entry, urb_entry_t, list); assert(urb_entry); assert(urb_entry->urb); if (urb_entry->urb == urb) { return urb_entry; } } return 0;}/* Delete an urb from the list. */static inline void urb_list_del(urb_t *urb, int epid){ urb_entry_t *urb_entry = __urb_list_entry(urb, epid); assert(urb_entry); /* Delete entry and free. */ list_del(&urb_entry->list); kfree(urb_entry);}/* Move an urb to the end of the list. */static inline void urb_list_move_last(urb_t *urb, int epid){ urb_entry_t *urb_entry = __urb_list_entry(urb, epid); assert(urb_entry); list_del(&urb_entry->list); list_add_tail(&urb_entry->list, &urb_list[epid]);}/* For debug purposes only. */static inline void urb_list_dump(int epid){ struct list_head *entry; struct list_head *tmp; urb_entry_t *urb_entry; int i = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -