USB设备驱动之驱动

版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/83014292
上一篇文章USB设备驱动之设备讲到
USB设备的EEPROM中,固化了设备的一些描述信息和一些程序。当USB设备插入USB插槽时,会引起一个电信号的变化,主机控制器捕获这个电信号,并命令USB核心处理对设备的加载工作。USB核心读取USB设备固件中的设备的信息,分配一个usb_device,并将设备固件中的设备信息填充到这个usb_device结构体中,并将这个usb_device挂载到USB总线中的设备链表中。所以USB设备驱动模型中的设备都是USB核心去完成,并没有device程序

那么下面来看一下driver部分,与其他的总线模型中的driver类似
一、USB驱动结构 usb_driver
在USB设备驱动模型中,USB设备驱动使用usb_driver结构体来表示

struct usb_driver {
	const char *name;

	int (*probe) (struct usb_interface *intf,
		      const struct usb_device_id *id);

	void (*disconnect) (struct usb_interface *intf);

	int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
			void *buf);

	int (*suspend) (struct usb_interface *intf, pm_message_t message);
	int (*resume) (struct usb_interface *intf);
	int (*reset_resume)(struct usb_interface *intf);

	int (*pre_reset)(struct usb_interface *intf);
	int (*post_reset)(struct usb_interface *intf);

	const struct usb_device_id *id_table;

	struct usb_dynids dynids;
	struct usbdrv_wrap drvwrap;
	unsigned int no_dynamic_id:1;
	unsigned int supports_autosuspend:1;
	unsigned int disable_hub_initiated_lpm:1;
	unsigned int soft_unbind:1;
};

注意:USB设备与驱动的匹配不是通过name成员来匹配的,而是通过usb_driver 结构体中的成员id_table来匹配
二、设备与驱动的匹配
我们知道,一个驱动可以支持多个设备,那么怎样知道驱动支持哪些设备呢?通过usb_driver结构中的id_table成员就可以完成这个功能。id_table成员描述了一个USB驱动所支持的所有USB设备列表,它指向一个usb_device_id数组。usb_device_id结构体包含了USB设备的制造商ID、产品ID、产品版本、结构类信息。USB设备的EEPROM中的固件程序中就包含了这些信息。当USB设备中的信息和总线上驱动的id_table信息中的一项相同时,就将USB设备与驱动绑定,由于一个驱动可以适用于多个设备,所以id_table表项中可能有很多项。

struct usb_device_id {
	/* which fields to match against? */
	__u16		match_flags;

	/* Used for product specific matches; range is inclusive */
	__u16		idVendor;
	__u16		idProduct;
	__u16		bcdDevice_lo;
	__u16		bcdDevice_hi;

	/* Used for device class matches */
	__u8		bDeviceClass;
	__u8		bDeviceSubClass;
	__u8		bDeviceProtocol;

	/* Used for interface class matches */
	__u8		bInterfaceClass;
	__u8		bInterfaceSubClass;
	__u8		bInterfaceProtocol;

	/* Used for vendor-specific interface matches */
	__u8		bInterfaceNumber;

	/* not matched against */
	kernel_ulong_t	driver_info
		__attribute__((aligned(sizeof(kernel_ulong_t))));
};

成员match_flags表示设备的固件信息与usb_device_id的哪些字段相匹配
另外,Linux中也提供了初始化usb_device_id 结构的宏。

三、USB驱动的注册函数

/* use a define to avoid include chaining to get THIS_MODULE & friends */
#define usb_register(driver) \
	usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

四、USB数据传输
1.USB请求块(urb)
USB请求块(USB request block, urb)是USB主机控制器和设备通信的主要数据结构,主机与设备之间通过urb进行数据传输。在urb中,包含了执行usb传输所需要的所有信息。当主机控制器需要与设备交互时,只需填充一个urb结构,然后将其提交给USB核心,由USB核心负责对其进行处理。

urb 请求块就像一个装东西的“袋子”,USB 驱动程序把“空袋子”提交给 USB core,然后再交给主控制器,主控制器把数据放入这个“袋子”后再将装满数据的“袋子”通过 USB core 交还给 USB 驱动程序,这样一次数据传输就完成了。

在Linux中,USB请求块由struct urb结构体来描述,该结构体的定义如下:

struct urb {
	/* private: usb core and host controller only fields in the urb */
	struct kref kref;		/* reference count of the URB */
	void *hcpriv;			/* private data for host controller */
	atomic_t use_count;		/* concurrent submissions counter */
	atomic_t reject;		/* submissions will fail */
	int unlinked;			/* unlink error code */

	/* public: documented fields in the urb that can be used by drivers */
	struct list_head urb_list;	/* list head for use by the urb's
					 * current owner */
	struct list_head anchor_list;	/* the URB may be anchored */
	struct usb_anchor *anchor;
	struct usb_device *dev;		/* (in) pointer to associated device */
	struct usb_host_endpoint *ep;	/* (internal) pointer to endpoint */
	unsigned int pipe;		/* (in) pipe information */
	unsigned int stream_id;		/* (in) stream ID */
	int status;			/* (return) non-ISO status */
	unsigned int transfer_flags;	/* (in) URB_SHORT_NOT_OK | ...*/
	void *transfer_buffer;		/* (in) associated data buffer */
	dma_addr_t transfer_dma;	/* (in) dma addr for transfer_buffer */
	struct scatterlist *sg;		/* (in) scatter gather buffer list */
	int num_mapped_sgs;		/* (internal) mapped sg entries */
	int num_sgs;			/* (in) number of entries in the sg list */
	u32 transfer_buffer_length;	/* (in) data buffer length */
	u32 actual_length;		/* (return) actual transfer length */
	unsigned char *setup_packet;	/* (in) setup packet (control only) */
	dma_addr_t setup_dma;		/* (in) dma addr for setup_packet */
	int start_frame;		/* (modify) start frame (ISO) */
	int number_of_packets;		/* (in) number of ISO packets */
	int interval;			/* (modify) transfer interval
					 * (INT/ISO) */
	int error_count;		/* (return) number of ISO errors */
	void *context;			/* (in) context for completion */
	usb_complete_t complete;	/* (in) completion routine */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
					/* (in) ISO ONLY */
};

  1. use_count表示一个计数。在USB通信的整个阶段,当urb提交给USB主机控制器是,其值加1,表示urb未处理,当urb从主机控制器返回USB驱动程序时,其值减1.表示urb已处理
  2. urb_list,每个端点都会有这个urb队列,这个urb_list将该队列的成员一个个的链接起来。主机控制器每收到一个urb,就会将它添加到urb指定的端点的urb队列中。这个链表的头对应端点的struct list_head结构体成员。
  3. dev,这个指针指向urb关联的设备,也就是urb需要发送到的设备,这样才能找到目的设备
  4. pipe,表示urb需要发送到的管道。管道对应设备上的一个端点。根据不同的端点类型,有不同的管道类型。
  5. transfer_buffer,该指针指向一个缓冲区。缓冲区的数据可以从设备发送到主机,或者从主机发送到设备。
  6. transfer_dma,指向以DMA方式传输数据到USB设备的缓冲区
  7. transfer_buffer_length,表示transfer_buffer或者transfer_dma的长度

在这里插入图片描述

正如前面说的,urb 请求块就像一个装东西的“袋子”,USB 驱动程序把“空袋子”提交给 USB core,然后再交给主控制器,主控制器把数据放入这个“袋子”后再将装满数据的“袋子”通过 USB core 交还给 USB 驱动程序,这样一次数据传输就完成了。
那么,USB设备的数据又是如何到主机控制器的呢?

五、USB设备的数据如何到主机控制器

在这里插入图片描述

各位还记得”任何传输都是由host发起的”这句话么~
在usb设备插入pc中到拔出usb设备,都是由host进行询问的
一个usb鼠标的工作流程可以表达如下:
usb鼠标插入pc中:
主机询问设备:给我你的设备信息(控制传输)
主机根据usb鼠标的设备信息进行驱动配置,配置结束后
主机询问设备:给我你的数据信息(中断传输)
一定的时间间隔之后…
主机询问设备:给我你的数据信息(中断传输)
一定的时间间隔之后…
主机询问设备:给我你的数据信息(中断传输)

直到这个设备被拔出,主机不断要求设备提供鼠标的按键和移动数据

六、USB鼠标驱动程序

以下是完全注释后的鼠标驱动程序代码 usbmouse.c


view plaincopy to clipboardprint?
/*  
 * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $  
 *  
 *  Copyright (c) 1999-2001 Vojtech Pavlik 
 *  
 *  USB HIDBP Mouse support  
 */  
  
#include <linux/kernel.h>    
#include <linux/slab.h>    
#include <linux/module.h>    
#include <linux/init.h>    
#include <linux/usb/input.h>    
#include <linux/hid.h>    
  
/*  
 * Version Information  
 */  
#define DRIVER_VERSION "v1.6"    
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"    
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"    
#define DRIVER_LICENSE "GPL"    
  
MODULE_AUTHOR(DRIVER_AUTHOR);   
MODULE_DESCRIPTION(DRIVER_DESC);   
MODULE_LICENSE(DRIVER_LICENSE);   
  
/*  
 * 鼠标结构体,用于描述鼠标设备。  
 */  
struct usb_mouse    
{   
    /* 鼠标设备的名称,包括生产厂商、产品类别、产品等信息 */  
    char name[128];    
    /* 设备节点名称 */  
    char phys[64];     
    /* USB 鼠标是一种 USB 设备,需要内嵌一个 USB 设备结构体来描述其 USB 属性 */  
    struct usb_device *usbdev;  
    /* USB 鼠标同时又是一种输入设备,需要内嵌一个输入设备结构体来描述其输入设备的属性 */  
    struct input_dev *dev;    
    /* URB 请求包结构体,用于传送数据 */  
    struct urb *irq;   
    /* 普通传输用的地址 */  
    signed char *data;  
    /* dma 传输用的地址 */  
    dma_addr_t data_dma;           
};   
  
/*  
 * urb 回调函数,在完成提交 urb 后,urb 回调函数将被调用。  
 * 此函数作为 usb_fill_int_urb 函数的形参,为构建的 urb 制定的回调函数。  
 */  
static void usb_mouse_irq(struct urb *urb)   
{   
    /*  
     * urb 中的 context 指针用于为 USB 驱动程序保存一些数据。比如在这个回调函数的形参没有传递在 probe中为 mouse 结构体分配的那块内存的地址指针,而又需要用到那块内存区域中的数据,context 指针则帮了大忙了! 
     * 在填充 urb 时将 context 指针指向 mouse 结构体数据区,在这又创建一个局部 mouse 指针指向在 probe函数中为 mouse 申请的那块内存,那块内存保存着非常重要数据。 
     * 当 urb 通过 USB core 提交给 hc 之后,如果结果正常,mouse->data 指向的内存区域将保存着鼠标的按键和移动坐标信息,系统则依靠这些信息对鼠标的行为作出反应。  
     * mouse 中内嵌的 dev 指针,指向 input_dev 所属于的内存区域。 
     */  
    struct usb_mouse *mouse = urb->context;   
    signed char *data = mouse->data;   
    struct input_dev *dev = mouse->dev;   
    int status;   
  
    /*  
     * status 值为 0 表示 urb 成功返回,直接跳出循环把鼠标事件报告给输入子系统。  
     * ECONNRESET 出错信息表示 urb 被 usb_unlink_urb 函数给 unlink 了,ENOENT 出错信息表示 urb 被usb_kill_urb 函数给 kill 了。usb_kill_urb 表示彻底结束 urb 的生命周期,而 usb_unlink_urb 则是停止 urb,这个函数不等 urb 完全终止就会返回给回调函数。这在运行中断处理程序时或者等待某自旋锁时非常有用,在这两种情况下是不能睡眠的,而等待一个 urb 完全停止很可能会出现睡眠的情况。 

     * ESHUTDOWN 这种错误表示 USB 主控制器驱动程序发生了严重的错误,或者提交完 urb 的一瞬间设备被拔出。 
     * 遇见除了以上三种错误以外的错误,将申请重传 urb。  
     */  
    switch (urb->status)   
    {   
    case 0:     /* success */  
        break;   
    case -ECONNRESET:   /* unlink */  
    case -ENOENT:   
    case -ESHUTDOWN:   
        return;   
    /* -EPIPE:  should clear the halt */  
    default:        /* error */  
        goto resubmit;   
    }   
  
    /*  
     * 向输入子系统汇报鼠标事件情况,以便作出反应。  
     * data 数组的第0个字节:bit 0、1、2、3、4分别代表左、右、中、SIDE、EXTRA键的按下情况;  
     * data 数组的第1个字节:表示鼠标的水平位移;  
     * data 数组的第2个字节:表示鼠标的垂直位移;  
     * data 数组的第3个字节:REL_WHEEL位移。  
     */  
    input_report_key(dev, BTN_LEFT,   data[0] & 0x01);   
    input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);   
    input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);   
    input_report_key(dev, BTN_SIDE,   data[0] & 0x08);   
    input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);   
    input_report_rel(dev, REL_X,     data[1]);   
    input_report_rel(dev, REL_Y,     data[2]);   
    input_report_rel(dev, REL_WHEEL, data[3]);   
  
    /*  
     * 这里是用于事件同步。上面几行是一次完整的鼠标事件,包括按键信息、绝对坐标信息和滚轮信息,输入子系统正是通过这个同步信号来在多个完整事件报告中区分每一次完整事件报告。示意如下: 
     * 按键信息 坐标位移信息 滚轮信息 EV_SYC | 按键信息 坐标位移信息 滚轮信息 EV_SYC ...  
     */  
    input_sync(dev);   
  
    /*  
     * 系统需要周期性不断地获取鼠标的事件信息,因此在 urb 回调函数的末尾再次提交 urb 请求块,这样又会  
     * 调用新的回调函数,周而复始。  
     * 在回调函数中提交 urb 一定只能是 GFP_ATOMIC 优先级的,因为 urb 回调函数运行于中断上下文中,在提交 urb 过程中可能会需要申请内存、保持信号量,这些操作或许会导致 USB core 睡眠,一切导致睡眠的行为都是不允许的。 
     */  
resubmit:   
    status = usb_submit_urb (urb, GFP_ATOMIC);   
    if (status)   
        err ("can't resubmit intr, %s-%s/input0, status %d",   
                mouse->usbdev->bus->bus_name,   
                mouse->usbdev->devpath, status);   
}   
  
/*  
 * 打开鼠标设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。  
 */  
static int usb_mouse_open(struct input_dev *dev)   
{   
    struct usb_mouse *mouse = dev->private;   
  
    mouse->irq->dev = mouse->usbdev;   
    if (usb_submit_urb(mouse->irq, GFP_KERNEL))   
        return -EIO;   
  
    return 0;   
}   
  
/*  
 * 关闭鼠标设备时,结束 urb 生命周期。  
 */  
static void usb_mouse_close(struct input_dev *dev)   
{   
    struct usb_mouse *mouse = dev->private;   
  
    usb_kill_urb(mouse->irq);   
}   
  
/*  
 * 驱动程序的探测函数  
 */  
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)  
{   
    /*   
     * 接口结构体包含于设备结构体中,interface_to_usbdev 是通过接口结构体获得它的设备结构体。 
     * usb_host_interface 是用于描述接口设置的结构体,内嵌在接口结构体 usb_interface 中。  
     * usb_endpoint_descriptor 是端点描述符结构体,内嵌在端点结构体 usb_host_endpoint 中,而端点结构体内嵌在接口设置结构体中。 
     */  
    struct usb_device *dev = interface_to_usbdev(intf);   
    struct usb_host_interface *interface;   
    struct usb_endpoint_descriptor *endpoint;   
    struct usb_mouse *mouse;   
    struct input_dev *input_dev;   
    int pipe, maxp;   
  
    interface = intf->cur_altsetting;   
  
    /* 鼠标仅有一个 interrupt 类型的 in 端点,不满足此要求的设备均报错 */  
    if (interface->desc.bNumEndpoints != 1)   
        return -ENODEV;   
  
    endpoint = &interface->endpoint[0].desc;   
    if (!usb_endpoint_is_int_in(endpoint))   
        return -ENODEV;   
  
    /*  
     * 返回对应端点能够传输的最大的数据包,鼠标的返回的最大数据包为4个字节,数据包具体内容在 urb  
     * 回调函数中有详细说明。  
     */  
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  


bEndpointAddress这是这个特定端点的 USB 地址. 还包含在这个 8-位 值的是端点的方向. 位掩码 USB_DIR_OUT 和 USB_DIR_IN 可用来和这个成员比对, 来决定给这个端点的数据是到设备还是到主机.

pipe,要访问的端点所对应的管道,使用usb_sndintpipe()usb_rcvintpipe()创建


    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));  
  
    /* 为 mouse 设备结构体分配内存 */  
    mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);   
    /* input_dev */  
    input_dev = input_allocate_device();   
    if (!mouse || !input_dev)   
        goto fail1;   
  
    /*  
     * 申请内存空间用于数据传输,data 为指向该空间的地址,data_dma 则是这块内存空间的 dma 映射,即这块内存空间对应的 dma 地址。在使用 dma 传输的情况下,则使用 data_dma 指向的 dma 区域,否则使用 data 指向的普通内存区域进行传输。 
GFP_ATOMIC 表示不等待,GFP_KERNEL 是普通的优先级,可以睡眠等待,由于鼠标使用中断传输方式,不允许睡眠状态,data 又是周期性获取鼠标事件的存储区,因此使用 GFP_ATOMIC 优先级,如果不能分配到内存则立即返回 0。 
     */  
    mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);   
    if (!mouse->data)   
        goto fail1;   
  
    /*  
     * 为 urb 结构体申请内存空间,第一个参数表示等时传输时需要传送包的数量,其它传输方式则为0。申请的内存将通过下面即将见到的 usb_fill_int_urb 函数进行填充。  
    mouse->irq = usb_alloc_urb(0, GFP_KERNEL);   
    if (!mouse->irq)   
        goto fail2;   
  
    /* 填充 usb 设备结构体和输入设备结构体 */  
    mouse->usbdev = dev;   
    mouse->dev = input_dev;   
  
    /* 获取鼠标设备的名称 */  
    if (dev->manufacturer)   
        strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));   
  
    if (dev->product)    
    {   
        if (dev->manufacturer)   
            strlcat(mouse->name, " ", sizeof(mouse->name));   
        strlcat(mouse->name, dev->product, sizeof(mouse->name));   
    }   
  
    if (!strlen(mouse->name))   
        snprintf(mouse->name, sizeof(mouse->name),   
             "USB HIDBP Mouse %04x:%04x",   
             le16_to_cpu(dev->descriptor.idVendor),   
             le16_to_cpu(dev->descriptor.idProduct));   
  
    /*  
     * 填充鼠标设备结构体中的节点名。usb_make_path 用来获取 USB 设备在 Sysfs 中的路径,格式  
     * 为:usb-usb 总线号-路径名。  
     */  
    usb_make_path(dev, mouse->phys, sizeof(mouse->phys));   
    strlcat(mouse->phys, "/input0", sizeof(mouse->phys));   
  
    /* 将鼠标设备的名称赋给鼠标设备内嵌的输入子系统结构体 */  
    input_dev->name = mouse->name;   
    /* 将鼠标设备的设备节点名赋给鼠标设备内嵌的输入子系统结构体 */  
    input_dev->phys = mouse->phys;   
    /*  
     * input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符中的编号赋给内嵌的输入子系统结构体  
     */  
    usb_to_input_id(dev, &input_dev->id);   
    /* cdev 是设备所属类别(class device) */  
    input_dev->cdev.dev = &intf->dev;   
  
    /* evbit 用来描述事件,EV_KEY 是按键事件,EV_REL 是相对坐标事件 */  
    input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);   
    /* keybit 表示键值,包括左键、右键和中键 */  
    input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);  
    /* relbit 用于表示相对坐标值 */  
    input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);   
    /* 有的鼠标还有其它按键 */  
    input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);   
    /* 中键滚轮的滚动值 */  
    input_dev->relbit[0] |= BIT(REL_WHEEL);   
  
    /* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将鼠标结构体对象赋给它 */ 
    input_dev->private = mouse;   
    /* 填充输入设备打开函数指针 */  
    input_dev->open = usb_mouse_open;   
    /* 填充输入设备关闭函数指针 */  
    input_dev->close = usb_mouse_close;   
  
    /*  
     * 填充构建 urb,将刚才填充好的 mouse 结构体的数据填充进 urb 结构体中,在 open 中递交 urb。  
     * 当 urb 包含一个即将传输的 DMA 缓冲区时应该设置URB_NO_TRANSFER_DMA_MAP。USB核心使用  
     * transfer_dma变量所指向的缓冲区,而不是transfer_buffer变量所指向的。  
     * URB_NO_SETUP_DMA_MAP 用于 Setup 包,URB_NO_TRANSFER_DMA_MAP 用于所有 Data 包。  
     */  
    usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,  
             (maxp > 8 ? 8 : maxp),  
             usb_mouse_irq, mouse, endpoint->bInterval);  
    mouse->irq->transfer_dma = mouse->data_dma;   
    mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;   
  
    /* 向系统注册输入设备 */  
    input_register_device(mouse->dev);   
  
    /*  
     * 一般在 probe 函数中,都需要将设备相关信息保存在一个 usb_interface 结构体中,以便以后通过  
     * usb_get_intfdata 获取使用。这里鼠标设备结构体信息将保存在 intf 接口结构体内嵌的设备结构体中的 driver_data 数据成员中,即 intf->dev->dirver_data = mouse。 
    usb_set_intfdata(intf, mouse);   
    return 0;   
  
fail2:  usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);   
fail1:  input_free_device(input_dev);   
    kfree(mouse);   
    return -ENOMEM;   
}   
  
/*  
 * 鼠标设备拔出时的处理函数  
 */  
static void usb_mouse_disconnect(struct usb_interface *intf)   
{   
    /* 获取鼠标设备结构体 */  
    struct usb_mouse *mouse = usb_get_intfdata (intf);   
  
    /* intf->dev->dirver_data = NULL,将接口结构体中的鼠标设备指针置空。*/  
    usb_set_intfdata(intf, NULL);   
    if (mouse)   
    {   
        /* 结束 urb 生命周期 */  
        usb_kill_urb(mouse->irq);   
        /* 将鼠标设备从输入子系统中注销 */  
        input_unregister_device(mouse->dev);   
        /* 释放 urb 存储空间 */  
        usb_free_urb(mouse->irq);   
        /* 释放存放鼠标事件的 data 存储空间 */  
        usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);  
        /* 释放存放鼠标结构体的存储空间 */  
        kfree(mouse);   
    }   
}   
  
/*  
 * usb_device_id 结构体用于表示该驱动程序所支持的设备,USB_INTERFACE_INFO 可以用来匹配特定类型的接口, 
 * 这个宏的参数意思为 (类别, 子类别, 协议)。  
 * USB_INTERFACE_CLASS_HID 表示是一种 HID (Human Interface Device),即人机交互设备类别; 
 * USB_INTERFACE_SUBCLASS_BOOT 是子类别,表示是一种 boot 阶段使用的 HID; 
 * USB_INTERFACE_PROTOCOL_MOUSE 表示是鼠标设备,遵循鼠标的协议。 
 */  
static struct usb_device_id usb_mouse_id_table [] = {   
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,   
        USB_INTERFACE_PROTOCOL_MOUSE) },   
    { } /* Terminating entry */  
};   
  
/*  
 * 这个宏用来让运行在用户空间的程序知道这个驱动程序能够支持的设备,对于 USB 驱动程序来说,第一个参数必须  
 * 是 usb。  
 */  
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);  
  
/*  
 * 鼠标驱动程序结构体  
 */  
static struct usb_driver usb_mouse_driver = {   
    .name       = "usbmouse",   
    .probe      = usb_mouse_probe,   
    .disconnect = usb_mouse_disconnect,   
    .id_table   = usb_mouse_id_table,   
};   
  
/*  
 * 驱动程序生命周期的开始点,向 USB core 注册这个鼠标驱动程序。  
 */  
static int __init usb_mouse_init(void)   
{   
    int retval = usb_register(&usb_mouse_driver);   
    if (retval == 0)   
        info(DRIVER_VERSION ":" DRIVER_DESC);   
    return retval;   
}   
  
/*  
 * 驱动程序生命周期的结束点,向 USB core 注销这个鼠标驱动程序。  
 */  
static void __exit usb_mouse_exit(void)   
{   
    usb_deregister(&usb_mouse_driver);   
}   
  
module_init(usb_mouse_init);   
module_exit(usb_mouse_exit);
  1. void *usb_buffer_alloc (struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma);
    这个函数是usbcore提供的.从名字上就能知道它是用来申请内存的,内核中给出了一些介绍。
    第一个参数就是struct usb_device结构体的指针,第二个参数申请的buffer的大小,第三个参数,GFP_KERNEL,是一个内存申请的flag,通常内存申请都用这个flag,除非是中断上下文,不能睡眠,那就得用GPF_ATOMIC,这里没那么多要求.而usb_buffer_alloc()的第四个参数涉及到dma传输.该函数不仅进行内存分配,还会进行DMA映射,这里第四个参数将被设置为DMA地址。
    这个地址用于传输DMA缓冲区数据的urb。
    用usb_buffer_alloc申请的内存空间需要用它的搭档usb_buffer_free()来释放.

  2. 对于中断urb,使用usb_fill_int_urb函数来初始化:

static inline void usb_fill_int_urb (struct urb *urb,要初始化的urb指针。
                     struct usb_device *dev,所要访问的设备
                     unsigned int      pipe,要访问的端点所对应的管道,使用usb_sndintpipe()usb_rcvintpipe()创建
                     void              *transfer_buffer,要传输的数据缓冲区
                     int               buffer_length,缓冲区长度
                     usb_complete_t    complete_fn,当完成该urb所请求的操作时,要调用的回调函数
                     void              *context,complet_fn函数所需的上下文,通常取值为dev
                     int               interval)urb被调度的时间间隔

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值