本文共 33063 字,大约阅读时间需要 110 分钟。
To understand all the Linux-USB framework, you'll use these resources:
* This source code. "make pdfdocs", "usb.pdf" for host side "gadget.pdf" for peripheral side * The USB 2.0 specification (from ). * Chip specifications for USB controllers. host controllers (on PCs, servers, and more); peripheral controllers (in devices with Linux firmware, like printers or cell phones); hard-wired peripherals like Ethernet adapters. * Specifications for other protocols implemented by USB peripheral functions. Some are vendor-specific; others are vendor-neutral but just standardized outside of the team.Here is a list of what each subdirectory here is, and what is contained in them.
core/ - This is for the core USB host code, including the usbfs files and the hub class driver ("khubd").
host/ - This is for USB host controller drivers. This includes UHCI, OHCI, EHCI, and others that might
be used with more specialized "embedded" systems.gadget/ - This is for USB peripheral controller drivers and the various gadget drivers which talk to them.
Individual USB driver directories. A new driver should be added to the first subdirectory in the list below that it fits into.image/ - This is for still image drivers, like scanners or digital cameras.
input/ - This is for any driver that uses the input subsystem,like keyboard, mice, touchscreens, tablets, etc.media/ - This is for multimedia drivers, like video cameras,radios, and any other drivers that talk to the v4l subsystem.net/ - This is for network drivers.serial/ - This is for USB to serial drivers.storage/ - This is for USB mass-storage drivers.atm/ - Automaticmon/ - Monitorclass/ - This is for all USB device drivers that do not fit into any of the above categories, and work for a range of USB Class specified devices. misc/ - This is for all USB device drivers that do not fit into any of the above categories.驱动结构:
USB 设备驱动 : USB设备和主机通讯 Gadget 驱动 :具体USB设备功能的实现,使设备表现出"网络连接" "打印机""USB Mass Storage"的特性USB Core : 定义一些数据结构,宏和功能函数.提供上下层的API Gadget API : Callback API 的简单包装
通过全局变量维护系统的USB信息,HotPlug控制,总线数据传输USB 主机驱动 : 控制插入的USB设备 UDC 驱动 :控制USB设备和HOST的底层通讯,向上提供Callback API
USB控制器硬件OHCI/EHCI/UHCI <<-->> USB Bus <<-->> USB控制器硬件OHCI/EHCI/UHCI
数据结构的定义: in /kernel/include/linux/usb_ch9.h
/* * This file holds USB constants and structures that are needed for USB * device APIs. These are used by the USB device model, which is defined * in chapter 9 of the USB 2.0 specification. Linux has several APIs in C * that need these: * * - the master/host side Linux-USB kernel driver API; * - the "usbfs" user space API; and * - the Linux "gadget" slave/device/peripheral side driver API. * * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems * act either as a USB master/host or as a USB slave/device. That means * the master and slave side APIs benefit from working well together. * * There's also "Wireless USB", using low power short range radios for * peripheral interconnection but otherwise building on the USB framework. *//* USB_DT_DEVICE: Device descriptor */ struct usb_device_descriptor { 设备描述符 如USB扬声器 __u8 bLength; 描述符的长度 __u8 bDescriptorType; 描述符类型的编号__le16 bcdUSB; USB版本号
__u8 bDeviceClass; USB的设备类code __u8 bDeviceSubClass; USB的子类code __u8 bDeviceProtocol; USB的协议code __u8 bMaxPacketSize0; 默认端口端口0的最大包大小 __le16 idVendor; 厂商编号 __le16 idProduct; 产品编号 __le16 bcdDevice; 设备出厂的编号 __u8 iManufacturer; 描述厂商的字符串的索引 __u8 iProduct; 描述产品的字符串的索引 __u8 iSerialNumber; 描述设备序列号的字符串的索引 __u8 bNumConfigurations; 可能的配置数量 } __attribute__ ((packed)); struct usb_config_descriptor { 多个配置描述符 __u8 bLength; 描述符的长度 __u8 bDescriptorType; 描述符类型的编号__le16 wTotalLength; 配置 所返回的所有数据的大小
__u8 bNumInterfaces; 配置 所支持的interface数 __u8 bConfigurationValue; Set_Configuration命令需要的参数值 __u8 iConfiguration; 描述该配置的字符串的索引值 __u8 bmAttributes; 供电模式的选择 __u8 bMaxPower; 设备从总线提取的最大电流 } __attribute__ ((packed)); /* USB_DT_INTERFACE: Interface descriptor */ struct usb_interface_descriptor { 多个接口描述符 如音频接口,旋钮和按钮接口 __u8 bLength; 描述符的长度 __u8 bDescriptorType; 描述符类型的编号__u8 bInterfaceNumber; 接口的编号
__u8 bAlternateSetting; 备用的接口描述符编号,提供不同质量的服务参数. __u8 bNumEndpoints; 该接口所使用的端口数,不包括端口0 __u8 bInterfaceClass; 接口类型 __u8 bInterfaceSubClass; 接口子类型 __u8 bInterfaceProtocol; 接口所遵循的协议 __u8 iInterface; 描述该接口的字符串索引值 } __attribute__ ((packed) /* USB_DT_ENDPOINT: Endpoint descriptor */ struct usb_endpoint_descriptor { 多个端口描述符 __u8 bLength; 描述符长度 __u8 bDescriptorType; 描述符的类型__u8 bEndpointAddress; 端点号:0-3是端口点,第7位是方向(0-OUT,1-IN)
__u8 bmAttributes; 端口属性:bit[0:1] 00:控制端点 01:同步传输端点 02:批量传输端点 03:中断端点 __le16 wMaxPacketSize; 数据包的最大容量 __u8 bInterval; 轮询数据传送端点的时间间隔,即中断传送时轮询间隔,此域为1-255 批量和控制传送时,此域忽略 同步传送时,为1 /* NOTE: these two are _only_ in audio endpoints. */ /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ __u8 bRefresh; __u8 bSynchAddress; } __attribute__ ((packed)); /* USB_DT_STRING: String descriptor */ struct usb_string_descriptor { 字符串描述符 __u8 bLength; 描述符长度 __u8 bDescriptorType; 描述符类型__le16 wData[1]; /* UTF-16LE encoded */
} __attribute__ ((packed));1,USB主机驱动: OHCI: Open Host Controller Interface,None PC or SiS/ALi chip PC USB
UHCI: Universal,Used by PC USB EHCI: Enhanced usb2.0 一,数据结构: in /kernel/drivers/usb/core/hcd.h /*-------------------------------------------------------------------------*/ /* * USB Host Controller Driver (usb_hcd) framework * * Since "struct usb_bus" is so thin, you can't share much code in it. * This framework is a layer over that, and should be more sharable. */ /*-------------------------------------------------------------------------*/1,struct usb_hcd { /* usb_bus.hcpriv points to this */ 描述USB的Host Controller Driver
/* * housekeeping 家务管理 */ struct usb_bus self; /* hcd is-a bus */ 所代表的USB Busconst char *product_desc; /* product/vendor string */ 产品字符串
char irq_descr[24]; /* driver + bus # */ 驱动+总线struct timer_list rh_timer; /* drives root-hub polling */ 根hub轮询
struct urb *status_urb; /* the current status urb */ 目前的状态urb /* * hardware info/state 硬件资源/状态 */ const struct hc_driver *driver; /* hw-specific hooks */ 硬件特定的钩子函数,操作HC硬件 unsigned saw_irq : 1; unsigned can_wakeup:1; /* hw supports wakeup? */ unsigned remote_wakeup:1; /* sw should use wakeup? */ unsigned rh_registered:1; /* is root hub registered? *//* The next flag is a stopgap"权益之计", to be removed when all the HCDs
* support the new root-hub polling mechanism. */ unsigned uses_new_polling:1; 支持新的根hub轮询? unsigned poll_rh:1; /* poll for rh status? */ 轮询根hub状态? unsigned poll_pending:1; /* status has changed? */ 状态已改变?int irq; /* irq allocated */ 被分配的irq
void __iomem *regs; /* device memory/io */ 设备的IO内存 u64 rsrc_start; /* memory/io resource start */ IO内存起始地址 u64 rsrc_len; /* memory/io resource length */ IO内存长度 unsigned power_budget; /* in mA, 0 = no limit */ 最大电流#define HCD_BUFFER_POOLS 4
struct dma_pool *pool [HCD_BUFFER_POOLS];int state; 状态
#define __ACTIVE 0x01 #define __SUSPEND 0x04 #define __TRANSIENT 0x80#define HC_STATE_HALT 0
#define HC_STATE_RUNNING (__ACTIVE) #define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE) #define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT) #define HC_STATE_SUSPENDED (__SUSPEND)#define HC_IS_RUNNING(state) ((state) & __ACTIVE) 宏
#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)/* more shared queuing code would be good; it should support
* smarter scheduling, handle transaction translators, etc; * input size of periodic table to an interrupt scheduler. * (ohci 32, uhci 1024, ehci 256/512/1024). */ /* The HC driver's private data is stored at the end of * this structure. 私有数据 */ unsigned long hcd_priv[0] __attribute__ ((aligned (sizeof(unsigned long)))); };2,struct hc_driver {
const char *description; /* "ehci-hcd" etc */ ehci-hcd等 const char *product_desc; /* product/vendor string */ 产品字符串 size_t hcd_priv_size; /* size of private data */ 私有数据的size/* irq handler */
irqreturn_t (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);中断处理函数int flags;
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */ HC的寄存器使用的内存或I/O #define HCD_USB11 0x0010 /* USB 1.1 */ #define HCD_USB2 0x0020 /* USB 2.0 *//* called to init HCD and root hub */
int (*reset) (struct usb_hcd *hcd); 初始化HCD int (*start) (struct usb_hcd *hcd); 初始化根Hub/* NOTE: these suspend/resume calls relate to the HC as
* a whole, not just the root hub; they're for bus glue. */ /* called after all devices were suspended */ int (*suspend) (struct usb_hcd *hcd, pm_message_t message);挂起Hub,进入D3(etc)前调用/* called before any devices get resumed */
int (*resume) (struct usb_hcd *hcd); 进入D0(etc)后,恢复Hub/* cleanly make HCD stop writing memory and doing I/O */
void (*stop) (struct usb_hcd *hcd); HCD停止MM和IO/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd); 返回目前帧数/* manage i/o requests, device state */
int (*urb_enqueue) (struct usb_hcd *hcd, 插入和退出urb队列 struct usb_host_endpoint *ep, struct urb *urb, unsigned mem_flags); int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);/* hw synch, freeing endpoint resources that urb_dequeue can't */
void (*endpoint_disable)(struct usb_hcd *hcd, 释放endpoint资源 struct usb_host_endpoint *ep);/* root hub support */ 根Hub操作
int (*hub_status_data) (struct usb_hcd *hcd, char *buf); int (*hub_control) (struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); int (*hub_suspend)(struct usb_hcd *); int (*hub_resume)(struct usb_hcd *); int (*start_port_reset)(struct usb_hcd *, unsigned port_num); void (*hub_irq_enable)(struct usb_hcd *); /* Needed only if port-change IRQs are level-triggered */ };/**
* usb_create_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd * @dev: device for this HC, stored in hcd->self.controller * @bus_name: value to store in hcd->self.bus_name * Context: !in_interrupt() * * Allocate a struct usb_hcd, with extra space at the end for the * HC driver's private data. Initialize the generic members of the * hcd structure. * * If memory is unavailable, returns NULL. */ 3,struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, char *bus_name) /** * usb_add_hcd - finish generic HCD structure initialization and register * @hcd: the usb_hcd structure to initialize * @irqnum: Interrupt line to allocate * @irqflags: Interrupt type flags * * Finish the remaining parts of generic HCD initialization: allocate the * buffers of consistent memory, register the bus, request the IRQ line, * and call the driver's reset() and start() routines. */ 4,int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)/**
* usb_remove_hcd - shutdown processing for generic HCDs * @hcd: the usb_hcd structure to remove * Context: !in_interrupt() * * Disconnects the root hub, then reverses the effects of usb_add_hcd(), * invoking the HCD's stop() method. */ 5,void usb_remove_hcd(struct usb_hcd *hcd);/*
* This is the full ohci controller description * * Note how the "proper" USB information is just * a subset of what the full implementation needs. (Linus) */6,struct ohci_hcd { 作为usb_hcd结构体的hcd_priv
spinlock_t lock; /* * I/O memory used to communicate with the HC (dma-consistent) */ struct ohci_regs __iomem *regs; 与主机控制器通信的IO内存(DMA 一致)/*
* main memory used to communicate with the HC (dma-consistent). * hcd adds to schedule for a live hc any time, but removals finish * only at the start of the next frame. */ struct ohci_hcca *hcca; 与主机控制器通信的主存(DMA 一致) dma_addr_t hcca_dma;struct ed *ed_rm_list; /* to be removed */ 将移除的OHCI Endpoint Descriptor (ED)
struct ed *ed_bulktail; /* last in bulk list */ 批量队列尾
struct ed *ed_controltail; /* last in ctrl list */ 控制队列尾 struct ed *periodic [NUM_INTS];/* shadow int_table */ ??/*
* OTG controllers and transceivers need software interaction; * other external transceivers should be software-transparent */ struct otg_transceiver *transceiver; OTG控制器和收发器需要软件交互,其他的外部收发器应该是软件透明的./*
* memory management for queue data structures 队列数据的内存管理 */ struct dma_pool *td_cache; struct dma_pool *ed_cache; struct td *td_hash [TD_HASH_SIZE]; struct list_head pending;/*
* driver state 状态 */ int load [NUM_INTS]; u32 hc_control; /* copy of hc control reg */ 主机控制器控制寄存器的复制 unsigned long next_statechange; /* suspend/resume */ u32 fminterval; /* saved register */ 被保存的寄存器struct work_struct rh_resume;
struct notifier_block reboot_notifier;unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ #define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */ #define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */ #define OHCI_BIG_ENDIAN 0x08 /* big endian HC */ #define OHCI_QUIRK_ZFMICRO 0x10 /* Compaq ZFMicro chipset*/ // there are also chip quirks/bugs in init logic };7,struct ohci_hcd *hcd_to_ohci(struct usb_hcd *hcd); 内联函数usb_hcd与ohci_hcd的转换,得到私有数据
struct usb_hcd *ohci_to_hcd(const struct ohci_hcd *ohci); 通过conainer_of()从结构体成员获得所在结构体指针8,static int ohci_init (struct ohci_hcd *ohci); 初始化
static int ohci_run (struct ohci_hcd *ohci) 开启 static void ohci_stop (struct usb_hcd *hcd); 停止 static void ohci_usb_reset (struct ohci_hcd *ohci) 复位二,S3c2410主机驱动实例:
S3c2410的集成的USB主机控制器兼容并实现OCHI1.0,USB1.1协议层. 0x4900 0000 OHCI HcRevision HcControl HcCommonStatus HcInterruptStatus HcInterruptDisable HcHCCA HcPeriodCuttentED HcControlHeadED HcControlCurrentED HcBulkHeadED HcBulkCurrentED HcDoneHead HcRmInterval HcFmRemaining HcFmNumber HcPeriodicStart HcLSThreshold HcRhDescriptorA HcRhDescriptorB HcRhStatus HcRhportStatus1 HcRhPortStatus2定义:static const struct hc_driver ohci_s3c2410_hc_driver = {
.description = hcd_name, .product_desc = "S3C24XX OHCI", .hcd_priv_size = sizeof(struct ohci_hcd), /* * generic hardware linkage 通用硬件连接 */ .irq = ohci_irq, .flags = HCD_USB11 | HCD_MEMORY, /* * basic lifecycle operations 基本生命周期操作 */ .start = ohci_s3c2410_start, .stop = ohci_stop, /* * managing i/o requests and associated device resources I/O操作 */ .urb_enqueue = ohci_urb_enqueue, .urb_dequeue = ohci_urb_dequeue, .endpoint_disable = ohci_endpoint_disable, /* * scheduling support 调度 */ .get_frame_number = ohci_get_frame, /* * root hub support 根Hub操作 */ .hub_status_data = ohci_s3c2410_hub_status_data, .hub_control = ohci_s3c2410_hub_control,#if defined(CONFIG_USB_SUSPEND) && 0 挂起和恢复
.hub_suspend = ohci_hub_suspend, .hub_resume = ohci_hub_resume, #endif }; 实现函数: static int ohci_s3c2410_start (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret;if ((ret = ohci_init(ohci)) < 0)
return ret;if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name); ohci_stop (hcd); return ret; } return 0; }2,Linux的USB设备驱动
USB设备文件 usbdevfs,usbfs 是动态产生的 /dev/ttyUSBn (串口Major188) 挂载: 在/etc/fstab 加入 none /proc/bus/usb usbfs defaults 或输入: mount -t usbfs none /proc/bus/usb 显示: cat /proc/bus/usb/devices 拓扑,带宽,产品ID,Device Des,Config Des,Interface Des,Endpoint Des,Char Des. sysfs:/sys/bus/usb ->devices ->drivers -> hub -> usb -> usbfs /sys/devices /sys/drivers usb_driver:USB设备驱动描述 /** * struct usb_driver - identifies USB driver to usbcore * @owner: Pointer to the module owner of this driver; initialize * it using THIS_MODULE. * @name: The driver name should be unique among USB drivers, * and should normally be the same as the module name. * @probe: Called to see if the driver is willing to manage a particular * interface on a device. If it is, probe returns zero and uses * dev_set_drvdata() to associate driver-specific data with the * interface. It may also use usb_set_interface() to specify the * appropriate altsetting. If unwilling to manage the interface, * return a negative errno value. * @disconnect: Called when the interface is no longer accessible, usually * because its device has been (or is being) disconnected or the * driver module is being unloaded. * @ioctl: Used for drivers that want to talk to userspace through * the "usbfs" filesystem. This lets devices provide ways to * expose information to user space regardless of where they * do (or don't) show up otherwise in the filesystem. * @suspend: Called when the device is going to be suspended by the system. * @resume: Called when the device is being resumed by the system. * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set * or your driver's probe function will never get called. * @driver: the driver model core driver structure. * * USB drivers must provide a name, probe() and disconnect() methods, * and an id_table. Other driver fields are optional. * * The id_table is used in hotplugging. It holds a set of descriptors, * and specialized data may be associated with each entry. That table * is used by both user and kernel mode hotplugging support. * * The probe() and disconnect() methods are called in a context where * they can sleep, but they should avoid abusing the privilege. Most * work to connect to a device should be done when the device is opened, * and undone at the last close. The disconnect code needs to address * concurrency issues with respect to open() and close() methods, as * well as forcing all pending I/O requests to complete (by unlinking * them as necessary, and blocking until the unlinks complete). */ struct usb_driver { USB Core 总线部分 struct module *owner; 内核模块 const char *name; 唯一的USB设备名int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); 探测函数
void (*disconnect) (struct usb_interface *intf); 断开函数 int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf); IO控制函数 int (*suspend) (struct usb_interface *intf, pm_message_t message); 挂起 int (*resume) (struct usb_interface *intf); 恢复 const struct usb_device_id *id_table; hotplugging探测设备表struct device_driver driver; 驱动模块core
}; usb_device usb_interface usb_host_interface usb_host_endpointlinux下usb驱动编写(内核2.4)——2.6与此接口有区别
我们知道了在Linux下如何去使用一些最常见的USB设备。但对于做系统设计的程序员来说,我们还需要具有驱动程序的阅读、修改和开发能力。USB骨架程序(usb-skeleton),是USB驱动架构。/driver/usb/usb-skeleton.c
那些linux下不支持的USB设备几乎都是生产厂商特定的产品。他们需要特定的驱动程序。有些生产厂商根本不公开他们的USB协议。因为每一个不同的协议都会产生一个新的驱动程序,所以就有了这个通用的USB驱动骨架程序,它是以pci 骨架为模板的。
如果你准备写一个linux驱动程序,首先要熟悉USB协议规范。还要有USB urbs的概念。
Linux USB 骨架驱动程序:
1,Init,并在USB Core子系统里注册,将这个驱动程序支持那种设备,当被支持的设备从系统插入或拔出时,会有哪些动作等信息都传送到USB Core子系统中. module_init (usb_skel_init); 初始化包括usb_register usb_register(&skel_driver); static struct usb_driver skel_driver = { name: "skeleton", 对驱动程序进行描述 probe: skel_probe, 函数指针,当设备与在id_table 中变量信息匹配时,此函数被调用。 disconnect: skel_disconnect, 函数指针 fops: &skel_fops, minor: USB_SKEL_MINOR_BASE, id_table: skel_table, }; fops和minor变量是可选的。 大多usb驱动程序钩住另外一个驱动系统,例如SCSI,网络或者tty子系统。这些驱动程序在其他驱动系统中注册,同时任何用户空间的交互操作通 过那些接口提供,比如我们把SCSI设备驱动作为我们USB驱动所钩住的另外一个驱动系统,那么我们此USB设备的read、write等操作,就相应按SCSI设备的 read、write函数进行访问。 但是对于扫描仪等驱动程序来说,并没有一个匹配的驱动系统可以使用,那我们就要自己处理与用户空间的read、write等交互函数。 Usb子系统提供一种方法去注册一个次设备号和file_operations函数指针,这样就可以与用户空间实现方便地交互。2,probe当usb设备插入时,为了使linux-hotplug(PCI、USB等设备热插拔支持)系统自动装载驱动程序,你需要创建一个MODULE_DEVICE_TABLE。代码如下(这个模块仅支持某一特定设备):
/* table of devices that work with this driver */ static struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_DEVICE宏利用厂商ID和产品ID提供了一个设备的唯一标识。 USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, skel_table);当插入一个ID匹配的USB设备到USB总线时,驱动会在USB core中注册。驱动程序中probe 函数也就会被调用。
static int skel_probe( struct usb_interface *interface, 接口指针 const struct usb_device_id *id) 接口ID { struct usb_skel *dev = NULL; 设备资源 struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; size_t buffer_size; int i; int retval = -ENOMEM;/* allocate memory for our device state and initialize it */
dev = kmalloc(sizeof(*dev), GFP_KERNEL); 申请资源 if (dev == NULL) { err("Out of memory"); goto error; } memset(dev, 0x00, sizeof(*dev)); 初始化资源 kref_init(&dev->kref); 引用计数初始化 dev->udev = usb_get_dev(interface_to_usbdev(interface));usb_device dev->interface = interface; usb_interface/* set up the endpoint information */ 设置端点信息
/* use only the first bulk-in and bulk-out endpoints */只使用第一个批量IN和批量OUT端点 iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if ( !dev->bulk_in_endpointAddr && ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) ) { /* we found a bulk in endpoint */ 找到一个批量IN端点 buffer_size = le16_to_cpu( endpoint->wMaxPacketSize ); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if ( !dev->bulk_in_buffer ) { err("Could not allocate bulk_in_buffer"); goto error; } }if (!dev->bulk_out_endpointAddr &&
((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk out endpoint */ 找到一个批量OUT端点 dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; } } if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { err("Could not find both bulk-in and bulk-out endpoints"); goto error; }/* save our data pointer in this interface device */ 把usb_skel保存到interface
usb_set_intfdata(interface, dev);/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class); 注册设备到USB Core, 1,ask for a minor number. 2,creates the devfs file for the usb device,devfs_mk_cdev(). class_device_create() 3,creates a usb class device in the sysfs tree. if (retval) { /* something prevented us from registering this driver */ err("Not able to get a minor for this device."); usb_set_intfdata(interface, NULL); goto error; }/* let the user know what node this device is now attached to */
info("USB Skeleton device now attached to USBSkel-%d", interface->minor); return 0;error:
if (dev) kref_put(&dev->kref, skel_delete); return retval; } /* Structure to hold all of our device specific stuff */ struct usb_skel { 自定义的数据结构usb_skel struct usb_device *udev; /* the usb device for this device */ struct usb_interface *interface; /* the interface for this device */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ struct kref kref; }; /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with devfs and the driver core */ static struct usb_class_driver skel_class = { .name = "usb/skel%d", .fops = &skel_fops, .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, .minor_base = USB_SKEL_MINOR_BASE, }; static struct file_operations skel_fops = { .owner = THIS_MODULE, .read = skel_read, .write = skel_write, .open = skel_open, .release = skel_release, }; 驱动程序需要确认插入的设备是否可以被接受,如果不接受,或者在初始化的过程中发生任何错误,probe函数返回一个NULL值。否则返回一个含有设备驱动程序状 态的指针。通过这个指针,就可以访问所有结构中的回调函数。3, 如果设备从usb总线拔掉,设备指针会调用disconnect函数。驱动程序就需要清除那些被分配了的所有私有数据、关闭urbs,并且释放设备文件devfs句柄。
usb_set_intfdata(interface, NULL); usb_deregister_dev(interface, &skel_class); kref_put(&dev->kref, skel_delete);4,注销,当要从系统卸载驱动程序时,需要注销usb子系统。即需要usb_unregister函数处理:
module_exit(usb_skel_exit); static void __exit usb_skel_exit(void) { /* deregister this driver with the USB subsystem */ usb_deregister(&skel_driver); } 5,skeleton驱动就已经和设备绑定上了,任何用户态程序要操作此设备都可以通过file_operations结构所定义的函数进行了。 static struct file_operations skel_fops = { .owner = THIS_MODULE, .read = skel_read, .write = skel_write, .open = skel_open, .release = skel_release, }; 1,static int skel_open(struct inode *inode, struct file *file) struct usb_skel *dev; dev = usb_get_intfdata(interface); /* increment our usage count for the device */ kref_get(&dev->kref); /* save our object in the file's private structure */ file->private_data = dev;2,static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
dev = (struct usb_skel *)file->private_data; /* do a blocking bulk read to get data from the device */ retval = usb_bulk_msg(dev->udev, 对usb设备进行一次读或写,这个函数能够不需要创建urbs和操作urb函数 usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), &bytes_read, 10000); 3,static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) 利用urbs批量写 /* 建立一个urb及其buffer,复制user_buffer到urb */ urb = usb_alloc_urb(0, GFP_KERNEL); buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; } /* 设定urb */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, count, skel_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); exit: return count;4,static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
skel_write_bulk的回调函数5,static void skel_delete(struct kref *kref)
6,static int skel_release(struct inode *inode, struct file *file)7,USB请求块 urbs
数据结构分析:
/usb/core config.c void usb_release_interface_cache(struct kref *ref) int usb_get_configuration(struct usb_device *dev) void usb_destroy_configuration(struct usb_device *dev) buffer.c int hcd_buffer_create ( struct usb_hcd *hcd ) void *hcd_buffer_alloc ( struct usb_bus *bus, size_t size, unsigned mem_flags, dma_addr_t *dma ) void hcd_buffer_free ( struct usb_bus *bus,size_t size, void *addr,dma_addr_t dma ) void hcd_buffer_destroy ( struct usb_hcd *hcd ) devices.c void usbfs_conn_disc_event(void) struct file_operations usbfs_devices_fops = { .llseek = usb_device_lseek, .read = usb_device_read, .poll = usb_device_poll, .open = usb_device_open, .release = usb_device_release, }; devio.c struct file_operations usbfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, .ioctl = usbdev_ioctl, .open = usbdev_open, .release = usbdev_release, }; file.c int usb_major_init(void) void usb_major_cleanup(void) int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) inode.c void usbfs_update_special (void) void usbfs_add_bus(struct usb_bus *bus) void usbfs_remove_bus(struct usb_bus *bus) void usbfs_add_device(struct usb_device *dev) void usbfs_remove_device(struct usb_device *dev) int __init usbfs_init(void) void usbfs_cleanup(void) sysfs.c void usb_create_sysfs_dev_files (struct usb_device *udev) void usb_remove_sysfs_dev_files (struct usb_device *udev) void usb_create_sysfs_intf_files (struct usb_interface *intf) void usb_remove_sysfs_intf_files (struct usb_interface *intf) message.c int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout) int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) int usb_sg_init ( struct usb_sg_request *io, struct usb_device *dev, unsigned pipe, unsigned period, struct scatterlist *sg, int nents, size_t length, unsigned mem_flags ) void usb_sg_wait (struct usb_sg_request *io) void usb_sg_cancel (struct usb_sg_request *io) int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size) int usb_string(struct usb_device *dev, int index, char *buf, size_t size) int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) int usb_get_status(struct usb_device *dev, int type, int target, void *data) int usb_clear_halt(struct usb_device *dev, int pipe) void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) void usb_disable_device(struct usb_device *dev, int skip_ep0) int usb_set_interface(struct usb_device *dev, int interface, int alternate) int usb_reset_configuration(struct usb_device *dev) int usb_set_configuration(struct usb_device *dev, int configuration) urb.c void usb_init_urb(struct urb *urb) void usb_free_urb(struct urb *urb) int usb_submit_urb(struct urb *urb, unsigned mem_flags) int usb_unlink_urb(struct urb *urb) void usb_kill_urb(struct urb *urb) usb.c int usb_register(struct usb_driver *new_driver) void usb_deregister(struct usb_driver *driver) struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,unsigned int altnum) int usb_driver_claim_interface(struct usb_driver *driver,struct usb_interface *iface, void* priv) void usb_driver_release_interface(struct usb_driver *driver,struct usb_interface *iface) const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) struct usb_device *usb_get_dev(struct usb_device *dev) void usb_put_dev(struct usb_device *dev) void usb_put_intf(struct usb_interface *intf) void usb_lock_device(struct usb_device *udev) int usb_trylock_device(struct usb_device *udev) int usb_lock_device_for_reset(struct usb_device *udev, void usb_unlock_device(struct usb_device *udev) void usb_lock_all_devices(void) void usb_unlock_all_devices(void) struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) int usb_get_current_frame_number(struct usb_device *dev) int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr) void *usb_buffer_alloc (struct usb_device *dev, size_t size,unsigned mem_flags,dma_addr_t *dma ) void usb_buffer_free ( struct usb_device *dev, size_t size, void *addr, dma_addr_t dma ) int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,struct scatterlist *sg, int nents) void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,struct scatterlist *sg, int n_hw_ents) struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .hotplug = usb_hotplug, .suspend = usb_generic_suspend, .resume = usb_generic_resume, }; int usb_disabled(void) hub.c void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) void usb_set_device_state(struct usb_device *udev,enum usb_device_state new_state) void usb_disconnect(struct usb_device **pdev) int usb_new_device(struct usb_device *udev) int usb_suspend_device(struct usb_device *udev, pm_message_t state) int usb_resume_device(struct usb_device *udev) void usb_resume_root_hub(struct usb_device *hdev) int usb_suspend_device(struct usb_device *udev, pm_message_t state) int usb_hub_init(void) void usb_hub_cleanup(void) int usb_reset_device(struct usb_device *udev) hcd.c struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,struct device *dev, char *bus_name) 创建一个usb_hcd结构体并初始化. usb_hcd,usb_bus,hc_driver,timer_list,device usb_devmap,usb_operations,usb_device,dentry,class_device,mon_bus int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags) 复位 retval = hcd->driver->reset(hcd) 定位一段usb DMA, retval = hcd_buffer_create(hcd) register the bus, usb_register_bus(&hcd->self) request the IRQ line, request_irq(irqnum, &usb_hcd_irq, irqflags,hcd->irq_descr, hcd)) start(). rhdev = usb_alloc_dev(NULL, &hcd->self, 0) hcd->driver->start(hcd) usb_device void usb_remove_hcd(struct usb_hcd *hcd) usb_disconnect(&hcd->self.root_hub); free_irq(hcd->irq, hcd); usb_deregister_bus(&hcd->self); hcd_buffer_destroy(hcd); void usb_hcd_poll_rh_status(struct usb_hcd *hcd) Root hub中断后的定时传输函数 static void rh_timer_func (unsigned long _hcd) { usb_hcd_poll_rh_status((struct usb_hcd *) _hcd); }struct usb_bus *usb_bus_get(struct usb_bus *bus)
void usb_bus_put(struct usb_bus *bus) int usb_host_init(void) void usb_enable_root_hub_irq (struct usb_bus *bus) long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) int usb_check_bandwidth (struct usb_device *dev, struct urb *urb) void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc) void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc) void usb_hcd_resume_root_hub (struct usb_hcd *hcd) int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) void usb_hc_died (struct usb_hcd *hcd) void usb_put_hcd (struct usb_hcd *hcd)int usb_mon_register (struct usb_mon_operations *ops)
void usb_mon_deregister (void) hcd-api.c int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) void usb_hcd_pci_remove (struct pci_dev *dev) int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) int usb_hcd_pci_resume (struct pci_dev *dev) /usb/host /usb/gadget
3,USB固件驱动
UDC Gadger API Gadget
转载地址:http://oucqi.baihongyu.com/