?? ftdi-elan.c
字號:
/** USB FTDI client driver for Elan Digital Systems's Uxxx adapters** 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 client 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/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/ioctl.h>#include <linux/pci_ids.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/kref.h>#include <linux/mutex.h>#include <asm/uaccess.h>#include <linux/usb.h>#include <linux/workqueue.h>#include <linux/platform_device.h>MODULE_AUTHOR("Tony Olech");MODULE_DESCRIPTION("FTDI ELAN driver");MODULE_LICENSE("GPL");#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)static int distrust_firmware = 1;module_param(distrust_firmware, bool, 0);MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren" "t setup");extern struct platform_driver u132_platform_driver;static struct workqueue_struct *status_queue;static struct workqueue_struct *command_queue;static struct workqueue_struct *respond_queue;/** ftdi_module_lock exists to protect access to global variables**/static struct mutex ftdi_module_lock;static int ftdi_instances = 0;static struct list_head ftdi_static_list;/** end of the global variables protected by ftdi_module_lock*/#include "usb_u132.h"#include <asm/io.h>#include "../core/hcd.h" /* FIXME ohci.h is ONLY for internal use by the OHCI driver. * If you're going to try stuff like this, you need to split * out shareable stuff (register declarations?) into its own * file, maybe name <linux/usb/ohci.h> */#include "../host/ohci.h"/* Define these values to match your devices*/#define USB_FTDI_ELAN_VENDOR_ID 0x0403#define USB_FTDI_ELAN_PRODUCT_ID 0xd6ea/* table of devices that work with this driver*/static struct usb_device_id ftdi_elan_table[] = { {USB_DEVICE(USB_FTDI_ELAN_VENDOR_ID, USB_FTDI_ELAN_PRODUCT_ID)}, { /* Terminating entry */ }};MODULE_DEVICE_TABLE(usb, ftdi_elan_table);/* only the jtag(firmware upgrade device) interface requires* a device file and corresponding minor number, but the* interface is created unconditionally - I suppose it could* be configured or not according to a module parameter.* But since we(now) require one interface per device,* and since it unlikely that a normal installation would* require more than a couple of elan-ftdi devices, 8 seems* like a reasonable limit to have here, and if someone* really requires more than 8 devices, then they can frig the* code and recompile*/#define USB_FTDI_ELAN_MINOR_BASE 192#define COMMAND_BITS 5#define COMMAND_SIZE (1<<COMMAND_BITS)#define COMMAND_MASK (COMMAND_SIZE-1)struct u132_command { u8 header; u16 length; u8 address; u8 width; u32 value; int follows; void *buffer;};#define RESPOND_BITS 5#define RESPOND_SIZE (1<<RESPOND_BITS)#define RESPOND_MASK (RESPOND_SIZE-1)struct u132_respond { u8 header; u8 address; u32 *value; int *result; struct completion wait_completion;};struct u132_target { void *endp; struct urb *urb; int toggle_bits; int error_count; int condition_code; int repeat_number; int halted; int skipped; int actual; int non_null; int active; int abandoning; void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, int toggle_bits, int error_count, int condition_code, int repeat_number, int halted, int skipped, int actual, int non_null);};/* Structure to hold all of our device specific stuff*/struct usb_ftdi { struct list_head ftdi_list; struct mutex u132_lock; int command_next; int command_head; struct u132_command command[COMMAND_SIZE]; int respond_next; int respond_head; struct u132_respond respond[RESPOND_SIZE]; struct u132_target target[4]; char device_name[16]; unsigned synchronized:1; unsigned enumerated:1; unsigned registered:1; unsigned initialized:1; unsigned card_ejected:1; int function; int sequence_num; int disconnected; int gone_away; int stuck_status; int status_queue_delay; struct semaphore sw_lock; struct usb_device *udev; struct usb_interface *interface; struct usb_class_driver *class; struct delayed_work status_work; struct delayed_work command_work; struct delayed_work respond_work; struct u132_platform_data platform_data; struct resource resources[0]; struct platform_device platform_dev; unsigned char *bulk_in_buffer; size_t bulk_in_size; size_t bulk_in_last; size_t bulk_in_left; __u8 bulk_in_endpointAddr; __u8 bulk_out_endpointAddr; struct kref kref; u32 controlreg; u8 response[4 + 1024]; int expected; int recieved; int ed_found;};#define kref_to_usb_ftdi(d) container_of(d, struct usb_ftdi, kref)#define platform_device_to_usb_ftdi(d) container_of(d, struct usb_ftdi, \ platform_dev)static struct usb_driver ftdi_elan_driver;static void ftdi_elan_delete(struct kref *kref){ struct usb_ftdi *ftdi = kref_to_usb_ftdi(kref); dev_warn(&ftdi->udev->dev, "FREEING ftdi=%p\n", ftdi); usb_put_dev(ftdi->udev); ftdi->disconnected += 1; mutex_lock(&ftdi_module_lock); list_del_init(&ftdi->ftdi_list); ftdi_instances -= 1; mutex_unlock(&ftdi_module_lock); kfree(ftdi->bulk_in_buffer); ftdi->bulk_in_buffer = NULL;}static void ftdi_elan_put_kref(struct usb_ftdi *ftdi){ kref_put(&ftdi->kref, ftdi_elan_delete);}static void ftdi_elan_get_kref(struct usb_ftdi *ftdi){ kref_get(&ftdi->kref);}static void ftdi_elan_init_kref(struct usb_ftdi *ftdi){ kref_init(&ftdi->kref);}static void ftdi_status_requeue_work(struct usb_ftdi *ftdi, unsigned int delta){ if (!queue_delayed_work(status_queue, &ftdi->status_work, delta)) kref_put(&ftdi->kref, ftdi_elan_delete);}static void ftdi_status_queue_work(struct usb_ftdi *ftdi, unsigned int delta){ if (queue_delayed_work(status_queue, &ftdi->status_work, delta)) kref_get(&ftdi->kref);}static void ftdi_status_cancel_work(struct usb_ftdi *ftdi){ if (cancel_delayed_work(&ftdi->status_work)) kref_put(&ftdi->kref, ftdi_elan_delete);}static void ftdi_command_requeue_work(struct usb_ftdi *ftdi, unsigned int delta){ if (!queue_delayed_work(command_queue, &ftdi->command_work, delta)) kref_put(&ftdi->kref, ftdi_elan_delete);}static void ftdi_command_queue_work(struct usb_ftdi *ftdi, unsigned int delta){ if (queue_delayed_work(command_queue, &ftdi->command_work, delta)) kref_get(&ftdi->kref);}static void ftdi_command_cancel_work(struct usb_ftdi *ftdi){ if (cancel_delayed_work(&ftdi->command_work)) kref_put(&ftdi->kref, ftdi_elan_delete);}static void ftdi_response_requeue_work(struct usb_ftdi *ftdi, unsigned int delta){ if (!queue_delayed_work(respond_queue, &ftdi->respond_work, delta)) kref_put(&ftdi->kref, ftdi_elan_delete);}static void ftdi_respond_queue_work(struct usb_ftdi *ftdi, unsigned int delta){ if (queue_delayed_work(respond_queue, &ftdi->respond_work, delta)) kref_get(&ftdi->kref);}static void ftdi_response_cancel_work(struct usb_ftdi *ftdi){ if (cancel_delayed_work(&ftdi->respond_work)) kref_put(&ftdi->kref, ftdi_elan_delete);}void ftdi_elan_gone_away(struct platform_device *pdev){ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); ftdi->gone_away += 1; ftdi_elan_put_kref(ftdi);}EXPORT_SYMBOL_GPL(ftdi_elan_gone_away);static void ftdi_release_platform_dev(struct device *dev){ dev->parent = NULL;}static void ftdi_elan_do_callback(struct usb_ftdi *ftdi, struct u132_target *target, u8 *buffer, int length);static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi);static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi);static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi);static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi);static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi);static int ftdi_elan_synchronize(struct usb_ftdi *ftdi);static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi);static int ftdi_elan_command_engine(struct usb_ftdi *ftdi);static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi);static int ftdi_elan_hcd_init(struct usb_ftdi *ftdi){ int result; if (ftdi->platform_dev.dev.parent) return -EBUSY; ftdi_elan_get_kref(ftdi); ftdi->platform_data.potpg = 100; ftdi->platform_data.reset = NULL; ftdi->platform_dev.id = ftdi->sequence_num; ftdi->platform_dev.resource = ftdi->resources; ftdi->platform_dev.num_resources = ARRAY_SIZE(ftdi->resources); ftdi->platform_dev.dev.platform_data = &ftdi->platform_data; ftdi->platform_dev.dev.parent = NULL; ftdi->platform_dev.dev.release = ftdi_release_platform_dev; ftdi->platform_dev.dev.dma_mask = NULL; snprintf(ftdi->device_name, sizeof(ftdi->device_name), "u132_hcd"); ftdi->platform_dev.name = ftdi->device_name; dev_info(&ftdi->udev->dev, "requesting module '%s'\n", "u132_hcd"); request_module("u132_hcd"); dev_info(&ftdi->udev->dev, "registering '%s'\n", ftdi->platform_dev.name); result = platform_device_register(&ftdi->platform_dev);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -