?? u132-hcd.c
字號:
/** Host Controller Driver for the Elan Digital Systems U132 adapter** Copyright(C) 2006 Elan Digital Systems Limited* http://www.elandigitalsystems.com** Author and Maintainer - Tony Olech - Elan Digital Systems* tony.olech@elandigitalsystems.com** 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, version 2.*** This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)* based on various USB host drivers in the 2.6.15 linux kernel* with constant reference to the 3rd Edition of Linux Device Drivers* published by O'Reilly** The U132 adapter is a USB to CardBus adapter specifically designed* for PC cards that contain an OHCI host controller. Typical PC cards* are the Orange Mobile 3G Option GlobeTrotter Fusion card.** The U132 adapter will *NOT *work with PC cards that do not contain* an OHCI controller. A simple way to test whether a PC card has an* OHCI controller as an interface is to insert the PC card directly* into a laptop(or desktop) with a CardBus slot and if "lspci" shows* a new USB controller and "lsusb -v" shows a new OHCI Host Controller* then there is a good chance that the U132 adapter will support the* PC card.(you also need the specific client driver for the PC card)** Please inform the Author and Maintainer about any PC cards that* contain OHCI Host Controller and work when directly connected to* an embedded CardBus slot but do not work when they are connected* via an ELAN U132 adapter.**/#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/pci_ids.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/usb.h>#include <linux/workqueue.h>#include <linux/platform_device.h>#include <linux/pci_ids.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/byteorder.h>#include "../core/hcd.h"#include "ohci.h"#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \ OHCI_INTR_WDH)MODULE_AUTHOR("Tony Olech - Elan Digital Systems Limited");MODULE_DESCRIPTION("U132 USB Host Controller Driver");MODULE_LICENSE("GPL");#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)INT_MODULE_PARM(testing, 0);/* Some boards misreport power switching/overcurrent*/static int distrust_firmware = 1;module_param(distrust_firmware, bool, 0);MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren" "t setup");static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);/** u132_module_lock exists to protect access to global variables**/static struct semaphore u132_module_lock;static int u132_exiting = 0;static int u132_instances = 0;static struct list_head u132_static_list;/** end of the global variables protected by u132_module_lock*/static struct workqueue_struct *workqueue;#define MAX_U132_PORTS 7#define MAX_U132_ADDRS 128#define MAX_U132_UDEVS 4#define MAX_U132_ENDPS 100#define MAX_U132_RINGS 4static const char *cc_to_text[16] = { "No Error ", "CRC Error ", "Bit Stuff ", "Data Togg ", "Stall ", "DevNotResp ", "PIDCheck ", "UnExpPID ", "DataOver ", "DataUnder ", "(for hw) ", "(for hw) ", "BufferOver ", "BuffUnder ", "(for HCD) ", "(for HCD) "};struct u132_port { struct u132 *u132; int reset; int enable; int power; int Status;};struct u132_addr { u8 address;};struct u132_udev { struct kref kref; struct usb_device *usb_device; u8 enumeration; u8 udev_number; u8 usb_addr; u8 portnumber; u8 endp_number_in[16]; u8 endp_number_out[16];};#define ENDP_QUEUE_SHIFT 3#define ENDP_QUEUE_SIZE (1<<ENDP_QUEUE_SHIFT)#define ENDP_QUEUE_MASK (ENDP_QUEUE_SIZE-1)struct u132_urbq { struct list_head urb_more; struct urb *urb;};struct u132_spin { spinlock_t slock;};struct u132_endp { struct kref kref; u8 udev_number; u8 endp_number; u8 usb_addr; u8 usb_endp; struct u132 *u132; struct list_head endp_ring; struct u132_ring *ring; unsigned toggle_bits:2; unsigned active:1; unsigned delayed:1; unsigned input:1; unsigned output:1; unsigned pipetype:2; unsigned dequeueing:1; unsigned edset_flush:1; unsigned spare_bits:14; unsigned long jiffies; struct usb_host_endpoint *hep; struct u132_spin queue_lock; u16 queue_size; u16 queue_last; u16 queue_next; struct urb *urb_list[ENDP_QUEUE_SIZE]; struct list_head urb_more; struct delayed_work scheduler;};struct u132_ring { unsigned in_use:1; unsigned length:7; u8 number; struct u132 *u132; struct u132_endp *curr_endp; struct delayed_work scheduler;};#define OHCI_QUIRK_AMD756 0x01#define OHCI_QUIRK_SUPERIO 0x02#define OHCI_QUIRK_INITRESET 0x04#define OHCI_BIG_ENDIAN 0x08#define OHCI_QUIRK_ZFMICRO 0x10struct u132 { struct kref kref; struct list_head u132_list; struct semaphore sw_lock; struct semaphore scheduler_lock; struct u132_platform_data *board; struct platform_device *platform_dev; struct u132_ring ring[MAX_U132_RINGS]; int sequence_num; int going; int power; int reset; int num_ports; u32 hc_control; u32 hc_fminterval; u32 hc_roothub_status; u32 hc_roothub_a; u32 hc_roothub_portstatus[MAX_ROOT_PORTS]; int flags; unsigned long next_statechange; struct delayed_work monitor; int num_endpoints; struct u132_addr addr[MAX_U132_ADDRS]; struct u132_udev udev[MAX_U132_UDEVS]; struct u132_port port[MAX_U132_PORTS]; struct u132_endp *endp[MAX_U132_ENDPS];};/** these cannot be inlines because we need the structure offset!!* Does anyone have a better way?????*/#define ftdi_read_pcimem(pdev, member, data) usb_ftdi_elan_read_pcimem(pdev, \ offsetof(struct ohci_regs, member), 0, data);#define ftdi_write_pcimem(pdev, member, data) usb_ftdi_elan_write_pcimem(pdev, \ offsetof(struct ohci_regs, member), 0, data);#define u132_read_pcimem(u132, member, data) \ usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \ ohci_regs, member), 0, data);#define u132_write_pcimem(u132, member, data) \ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \ ohci_regs, member), 0, data);static inline struct u132 *udev_to_u132(struct u132_udev *udev){ u8 udev_number = udev->udev_number; return container_of(udev, struct u132, udev[udev_number]);}static inline struct u132 *hcd_to_u132(struct usb_hcd *hcd){ return (struct u132 *)(hcd->hcd_priv);}static inline struct usb_hcd *u132_to_hcd(struct u132 *u132){ return container_of((void *)u132, struct usb_hcd, hcd_priv);}static inline void u132_disable(struct u132 *u132){ u132_to_hcd(u132)->state = HC_STATE_HALT;}#define kref_to_u132(d) container_of(d, struct u132, kref)#define kref_to_u132_endp(d) container_of(d, struct u132_endp, kref)#define kref_to_u132_udev(d) container_of(d, struct u132_udev, kref)#include "../misc/usb_u132.h"static const char hcd_name[] = "u132_hcd";#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | \ USB_PORT_STAT_C_SUSPEND | USB_PORT_STAT_C_OVERCURRENT | \ USB_PORT_STAT_C_RESET) << 16)static void u132_hcd_delete(struct kref *kref){ struct u132 *u132 = kref_to_u132(kref); struct platform_device *pdev = u132->platform_dev; struct usb_hcd *hcd = u132_to_hcd(u132); u132->going += 1; down(&u132_module_lock); list_del_init(&u132->u132_list); u132_instances -= 1; up(&u132_module_lock); dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13" "2=%p going=%d pdev=%p\n", hcd, u132, u132->going, pdev); usb_put_hcd(hcd);}static inline void u132_u132_put_kref(struct u132 *u132){ kref_put(&u132->kref, u132_hcd_delete);}static inline void u132_u132_init_kref(struct u132 *u132){ kref_init(&u132->kref);}static void u132_udev_delete(struct kref *kref){ struct u132_udev *udev = kref_to_u132_udev(kref); udev->udev_number = 0; udev->usb_device = NULL; udev->usb_addr = 0; udev->enumeration = 0;}static inline void u132_udev_put_kref(struct u132 *u132, struct u132_udev *udev){ kref_put(&udev->kref, u132_udev_delete);}static inline void u132_udev_get_kref(struct u132 *u132, struct u132_udev *udev){ kref_get(&udev->kref);}static inline void u132_udev_init_kref(struct u132 *u132, struct u132_udev *udev){ kref_init(&udev->kref);}static inline void u132_ring_put_kref(struct u132 *u132, struct u132_ring *ring){ kref_put(&u132->kref, u132_hcd_delete);}static void u132_ring_requeue_work(struct u132 *u132, struct u132_ring *ring, unsigned int delta){ if (delta > 0) { if (queue_delayed_work(workqueue, &ring->scheduler, delta)) return; } else if (queue_delayed_work(workqueue, &ring->scheduler, 0)) return; kref_put(&u132->kref, u132_hcd_delete); return;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -