?? hc_crisv10.c
字號:
/* * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD) * * Copyright (c) 2002, 2003 Axis Communications AB. */#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/unistd.h>#include <linux/interrupt.h>#include <linux/init.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/arch/svinto.h>#include <linux/usb.h>/* Ugly include because we don't live with the other host drivers. */#include <../drivers/usb/core/hcd.h>#include <../drivers/usb/core/usb.h>#include "hc_crisv10.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.1.1.1 $";#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(": Entering: %s\n", __FUNCTION__))#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__))#else#define DBFENTER do {} while (0)#define DBFEXIT do {} while (0)#endif#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)/*------------------------------------------------------------------- Virtual Root Hub -------------------------------------------------------------------*/static __u8 root_hub_dev_des[] ={ 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ 0x00, /* __le16 bcdUSB; v1.0 */ 0x01, 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x00, /* __u8 bDeviceProtocol; */ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ 0x00, /* __le16 idVendor; */ 0x00, 0x00, /* __le16 idProduct; */ 0x00, 0x00, /* __le16 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, /* __le16 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, /* __le16 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 DEFINE_TIMER(bulk_start_timer, NULL, 0, 0);static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0);/* 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() ? GFP_ATOMIC : GFP_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 struct kmem_cache *usb_desc_cache;/* Cache for the registers allocated in the top half. */static struct kmem_cache *top_half_reg_cache;/* Cache for the data allocated in the isoc descr top half. */static struct kmem_cache *isoc_compl_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;/* for returning string descriptors in UTF-16LE */static int ascii2utf (char *ascii, __u8 *utf, int utfmax){ int retval; for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) { *utf++ = *ascii++ & 0x7f; *utf++ = 0; } return retval;}static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len){ char buf [30]; // assert (len > (2 * (sizeof (buf) + 1))); // assert (strlen (type) <= 8); // language ids if (id == 0) { *data++ = 4; *data++ = 3; /* 4 bytes data */ *data++ = 0; *data++ = 0; /* some language id */ return 4; // serial number } else if (id == 1) { sprintf (buf, "%x", serial); // product description } else if (id == 2) { sprintf (buf, "USB %s Root Hub", type); // id 3 == vendor description // unsupported IDs --> "stall" } else return 0; data [0] = 2 + ascii2utf (buf, data + 2, len - 2); data [1] = 3; return data [0];}/* 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 struct urb *urb_list_first(int epid){ struct urb *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(struct urb *urb, int epid){ urb_entry_t *urb_entry = 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(struct urb *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(struct urb *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(struct urb *urb, int epid){ urb_entry_t *urb_entry = __urb_list_entry(urb, epid); assert(urb_entry); list_move_tail(&urb_entry->list, &urb_list[epid]);}/* Get the next urb in the list. */static inline struct urb *urb_list_next(struct urb *urb, int epid){ urb_entry_t *urb_entry = __urb_list_entry(urb, epid); assert(urb_entry); if (urb_entry->list.next != &urb_list[epid]) { struct list_head *elem = urb_entry->list.next; urb_entry = list_entry(elem, urb_entry_t, list); return urb_entry->urb; } else { return NULL; }}/* 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 + -