本篇博客分以下几部分讲解
USB设备驱动程序里定义了许多与驱动程序密切相关的描述符。这里介绍一下四种比较关键的描述符:设备描述符、配置描述符、接口描述符、端点描述符。这几个描述符都位于include\linux\usb\ch9.h中,先看一下每个描述直接的关系,从图中可以看出每一个查到USB主机上的USB设备都有一个设备描述符,设备描述符下面可以接多个配置描述符,配置描述符下面又可以接多个
当USB设备接到USB控制器上后,USB控制器第一次读取到的数据包,总共8字节
/*当USB设备接到USB控制器上后,USB控制器第一次读取到的数据包,总共8字节*/ struct usb_ctrlrequest { __u8 bRequestType; __u8 bRequest; __le16 wValue; __le16 wIndex; __le16 wLength; } __attribute__ ((packed));
设备描述符是在设备连接时,主机第一个读取的描述符,包含了主机需要从设备读取的基本信息。设备描述符有14个字段,如下所示。依照功能来分,设备描述符的字段包含了描述符本身、设备、配置以及类别4大类。
/* USB_DT_DEVICE: Device descriptor */ struct usb_device_descriptor { __u8 bLength; //描述符长度 __u8 bDescriptorType; //描述符类型 __le16 bcdUSB; //USB规范版本号码,BCD码表示 __u8 bDeviceClass; //USB设备类别 __u8 bDeviceSubClass; //USB设备子类别 __u8 bDeviceProtocol; //USB设备协议码 __u8 bMaxPacketSize0; //端点0的最大信息包大小(端点0用于控制传输,既能输出也能输入) __le16 idVendor; //厂商ID __le16 idProduct; //产品ID __le16 bcdDevice; //设备版本编号,BCD码表示 __u8 iManufacturer; //制造者的字符串描述符的索引值 __u8 iProduct; //产品的字符串描述符的索引值 __u8 iSerialNumber; //序号的字符串描述符的索引值 __u8 bNumConfigurations;//可能配置的数目 } __attribute__ ((packed));
在读取设备描述符后,主机可以读取该设备的配置、接口以及端点描述符。每一个设备都至少有一个配置描述符,用来描述该设备的特性与能力。通常一个设置配置就已经足够,不过多用途或模式的设备可以支持多个设置配置,在同一时间只能有一个作用。 每一个设置配置都需要一个描述符,此描述符包含设备中的电源使用以及支持的接口数目。每一个配置描述符都有附属的描述符,包含一个或多个接口描述符,以及选择性的端点描述符。
struct usb_config_descriptor { __u8 bLength; //描述符长度 __u8 bDescriptorType; //描述符类型02 __le16 wTotalLength; //此配置传回的所有数据大小(字节) __u8 bNumInterfaces; //此配置支持的接口数目 __u8 bConfigurationValue; //Set_configuration与get_configuration要求的标识符 __u8 iConfiguration; //此配置的字符串描述符的索引值 __u8 bmAttributes; //自身电源/总线电源以及远程唤醒设置 __u8 bMaxPower; //需要总线电源,标识法为(最大mA/2) } __attribute__ ((packed));
接口表示被设备的特性或功能所使用的端点、配置的接口描述符,包含该接口所支持的端点信息。每一个设置配置必须支持一个接口,对许多设备来说,一个接口就已经足够,不过一个设置配置,可以同时又多个作用中的接口。每一个接口有它自己的接口描述符,此接口所支持的所有端点又各有一个附属描述符。如果一个设备拥有同时多个作用中接口的设置配置,它就是一个复合设备,主机会为每一个接口,加载一个驱动程序。
/* USB_DT_INTERFACE: Interface descriptor */ struct usb_interface_descriptor { __u8 bLength; //描述符长度 __u8 bDescriptorType; //描述符类型04 __u8 bInterfaceNumber; //识别此接口的数量 __u8 bAlternateSetting; //用来选择一个替代设置的数值 __u8 bNumEndpoints; //除了端点0外,支持的端点数量 __u8 bInterfaceClass; //接口类别码 __u8 bInterfaceSubClass;//接口子类别码 __u8 bInterfaceProtocol;//接口协议码 __u8 iInterface; //此接口的字符串描述符的索引值 } __attribute__ ((packed));
每一个指定在接口描述符内的端点,都有一个端点描述符。端点0没有端点描述符,因为每一个端点都必须支持断点0。设备描述符包含最大信息包大小的信息,而端点描述符则是定义端点的其他信息。
/* USB_DT_ENDPOINT: Endpoint descriptor */ struct usb_endpoint_descriptor { __u8 bLength; //描述符长度 __u8 bDescriptorType;//描述符类型05 __u8 bEndpointAddress;//端点数目与方向 __u8 bmAttributes; //支持的传输类型 __le16 wMaxPacketSize; //支持的最大信息包大小 __u8 bInterval; //最大延迟/轮询时距/NAK速率 /* NOTE: these two are _only_ in audio endpoints. */ /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ __u8 bRefresh; __u8 bSynchAddress; } __attribute__ ((packed));
USB鼠标一共有三个按键:左键、右键、中键。在这里把这三个分别作为l、s、enter键。这就是这个USB设备驱动程序的功能。
要实现这个功能,还是需要用到输入子系统的框架,与触摸屏驱动一样,再回顾一下输入系统的框架
输入子系统按框架可以分为设备驱动层、事件层、以及核心层。
整个调用过程如下:
app_read->evdev_read->kbtab_irq->input_report_key->input_event->evdev_event->evdev_read
应用层 事件层 设备层 核心层 核心层 事件层 事件层
如果要自己添加一个输入子系统的设备,只需要添加设备层的文件即可。
1、在里面添加设备层input_dev结构并初始化
2、编写中断处理程序
在USB驱动程序中,中断处理程序就不是真正的CPU中断的处理程序了。而是USB总线驱动程序接收完成一包数据后的回调函数。
编写程序步骤:其中硬件相关的设置就是设置USB驱动设备传输的数据来源、数据存放地址、数据长度、怎么样处理数据
/* a、分配一个 input_dev结构体*/ /* b、设置 */ /* b.1 能产生哪类事件 */ /* b.2 能产生哪些事件 */ /* c、注册 */ /* d、硬件相关的设置 */
1、struct input_dev结构体
struct input_dev { void *private; const char *name;//设备名字 const char *phys;//文件路径,比如 input/buttons const char *uniq; struct input_id id; unsigned long evbit[NBITS(EV_MAX)];//表示支持哪类事件,常用于以下几种事件(可以多选) //EV_SYN 同步事件,当使用input_event()函数后,就要使用这个上报个同步事件 //EV_KEY 键盘事件 //EV_REL (relative)相对坐标事件,比如鼠标 //EV_ABS (absolute)绝对坐标事件,比如摇杆、触摸屏感应 unsigned long keybit[NBITS(KEY_MAX)];//存放支持的键盘按键值 //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)、BTN_TOUCH(触摸屏的按键) unsigned long relbit[NBITS(REL_MAX)];//存放支持的相对坐标值 unsigned long absbit[NBITS(ABS_MAX)];//存放支持的绝对坐标值 unsigned long mscbit[NBITS(MSC_MAX)]; unsigned long ledbit[NBITS(LED_MAX)]; unsigned long sndbit[NBITS(SND_MAX)]; unsigned long ffbit[NBITS(FF_MAX)]; unsigned long swbit[NBITS(SW_MAX)]; ... ... int absmax[ABS_MAX + 1];//绝对坐标的最大值 int absmin[ABS_MAX + 1];//绝对坐标的最小值 int absfuzz[ABS_MAX + 1];//绝对坐标的干扰值,默认为0, int absflat[ABS_MAX + 1];//绝对坐标的平焊位置,默认为0 ... ... };
2、struct usb_device结构体,描述整个USB设备的结构体,一般不会用到里面的变量,这里不做详细注释。
3、struct usb_interface结构体,这个结构体是由USB核心传递给USB驱动程序的,用它来描述USB接口。USB驱动程序负责后续的控制。
struct usb_interface { /* array of alternate settings for this interface, * stored in no particular order */ struct usb_host_interface *altsetting;//一个接口结构体数组,包含了所有可能用于该接口的可选设置 struct usb_host_interface *cur_altsetting; /* the currently active alternate setting *///表示该接口的当前活动设置 unsigned num_altsetting; /* number of alternate settings *///可选设置数量 int minor; /* minor number this interface is bound to */ //USB核心分配的次设备号 enum usb_interface_condition condition; /* state of binding */ unsigned is_active:1; /* the interface is not suspended */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ struct device dev; /* interface specific device info */ struct device *usb_dev; /* pointer to the usb class's device, if any */ int pm_usage_cnt; /* usage counter for autosuspend */ };
4、struct usb_host_interface结构体,主要用来描述USB接口
struct usb_host_interface { struct usb_interface_descriptor desc;//接口描述符 /* array of desc.bNumEndpoint endpoints associated with this * interface setting. these will be in no particular order. */ struct usb_host_endpoint *endpoint;//端点描述符指针 char *string; /* iInterface string, if present *///接口描述符的名字字符指针 unsigned char *extra; /* Extra descriptors *///额外的描述字符指针 int extralen;//额外描述大小 };
5、usb_host_endpoint ,主要用来描述USB端点
struct usb_host_endpoint { struct usb_endpoint_descriptor desc;//端点描述符 struct list_head urb_list;//端点描述符列表指针,可以根据这个结构体找到所有的处于同一指针链表的usb_host_endpoint结构 void *hcpriv; struct ep_device *ep_dev; /* For sysfs info */ unsigned char *extra; /* Extra descriptors */ int extralen; };
6、struct usb_endpoint_descriptor结构体,端点描述符
/* USB_DT_ENDPOINT: Endpoint descriptor */ struct usb_endpoint_descriptor { __u8 bLength; //描述符长度 __u8 bDescriptorType;//描述符类型05 __u8 bEndpointAddress;//端点数目与方向 __u8 bmAttributes; //支持的传输类型 __le16 wMaxPacketSize; //支持的最大信息包大小 __u8 bInterval; //最大延迟/轮询时距/NAK速率 /* NOTE: these two are _only_ in audio endpoints. */ /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ __u8 bRefresh; __u8 bSynchAddress; } __attribute__ ((packed));
7、struct urb结构体,是一个USB请求块,作用是和所有的USB设备通讯。
struct urb { /* private: usb core and host controller only fields in the urb */ struct kref kref; /* reference count of the URB */ spinlock_t lock; /* lock for the URB */ void *hcpriv; /* private data for host controller */ atomic_t use_count; /* concurrent submissions counter */ u8 reject; /* submissions will fail */ /* 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 usb_device *dev; /* (in) pointer to associated device *///urb所发送的目标usb_devices指针 unsigned int pipe; /* (in) pipe information */ //端点信息,可以设置为控制、批量、中断、等时等端点输入或输出 int status; /* (return) non-ISO status */ //当urb结束后或者正在被USB核心处理时,该变量被设置为当前的状态 unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ //USB数据传输标志,可以通过这个标志判断数据传输情况 void *transfer_buffer; /* (in) associated data buffer */ //以DMA方式传输数据到USB设备的缓存区指针,必须用kmalloc来创建 dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer *///以DMA方式传输数据到USB设备的地址 int transfer_buffer_length; /* (in) data buffer length */ //以DMA方式传输数据到USB设备的缓冲区的长度 int actual_length; /* (return) actual transfer length */ //当urb结束后,实际接收到的或者发送的数据长度 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) */ //urb被轮询的时间间隔,仅对中断或者等时urb有效 int error_count; /* (return) number of ISO errors */ //用于等时urb结束后,报告的类型错误的数量 void *context; /* (in) context for completion */ //指向一个可用被USB驱动程序设置的数据块 usb_complete_t complete; /* (in) completion routine *///指向一个结束处理例程的指针,当urb被完全传输或者错误时调用这个函数 struct usb_iso_packet_descriptor iso_frame_desc[0]; // /* (in) ISO ONLY */ };
8、struct usb_device_id结构体,提供不同类型的该驱动程序支持的USB设备,USB核心使用该列表来判断对于一个设备改使用哪一个驱动程序
struct usb_device_id { /* which fields to match against? */ __u16 match_flags;//在设备插上后需要匹配下面的哪几个参数来匹配驱动 /* Used for product specific matches; range is inclusive */ __u16 idVendor;//USB制造商ID __u16 idProduct;//USB产品ID __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;//接口协议 /* not matched against */ kernel_ulong_t driver_info;// };
9、struct usb_driver结构体,USB驱动程序必须创建的主要结构体,它向USB核心代码描述USB驱动程序。
struct usb_driver { const char *name;//执行驱动程序名字的指针 int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);//指向USB驱动程序中的探测函数的指针 void (*disconnect) (struct usb_interface *intf);//指向USB驱动程序中的断开函数的指针 int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);//执行USB驱动程序中的ioctl指针 int (*suspend) (struct usb_interface *intf, pm_message_t message);//指向USB驱动程序中的挂起函数指针 int (*resume) (struct usb_interface *intf);//指向USB驱动程序中的恢复函数指针 void (*pre_reset) (struct usb_interface *intf);// void (*post_reset) (struct usb_interface *intf); const struct usb_device_id *id_table;//指向struct usb_device_id表的指针 struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; };
1、输入子系统相关的函数
struct input_dev *input_allocate_device(void);//分配一个struct input_dev结构体,返回的是struct input_dev * inline void set_bit(int nr, volatile unsigned long *addr);//这是一个内联函数,在调用的时候展开,功能为设置*addr的nr位为1 int input_register_device(struct input_dev *dev);//注册输入子系统设备驱动,输入参数为struct input_dev * void input_unregister_device(struct input_dev *dev);//反注册输入子系统的设备驱动,输入参数为struct input_dev * void input_free_device(s3c_ts_input);//释放分配的input_dev结构,,输入参数为struct input_dev * static inline void input_sync(struct input_dev *dev);//上传同步事件,表示这次事件数据已经传送完成了 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);//上传输入事件 //struct input_dev *dev表示哪个输入子系统设备的事件上传 //unsigned int type,表示上传的事件类型 //unsigned int code,表示事件类型中的哪类事件 //value表示事件的值
2、USB核心相关的函数
static inline int usb_register(struct usb_driver *driver);//注册一个usb驱动结构体driver到USB核心 void usb_deregister(struct usb_driver *driver);//从USB核心释放一个usb驱动结构体driver usb_rcvintpipe(dev,endpoint);//这是一个宏。设置usb设备dev的端点endpoint为中断IN端点 void *usb_buffer_alloc(struct usb_device *dev,size_t size,gfp_t mem_flags,dma_addr_t *dma);//分配一个地址作为USB接收到的数据,返回分配地址的首地址 //struct usb_device *dev是USB设备结构体 //size_t size分配的内存的大小 //gfp_t mem_flags是分配的标志 //dma_addr_t *dma是分配完成后返回的物理地址 struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);//分配一个urb结构体 //int iso_packets是等时数据包 //gfp_t mem_flags内存分配标志 static inline void usb_fill_int_urb (struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context, int interval);//初始化即将被发送到USB设备的中断端点的urb //struct urb *urb是指向需要初始化的urb的指针 //struct usb_device *dev是该urb所发送的目标USB设备 //unsigned int pipe是该urb所发送的目标USB设备的特点端点。该值由usb_sndintpipe或usb_rcvintpipe函数创建 //void *transfer_buffer用于保存外发数据或接收数据的缓冲区的指针 //int buffer_length是transfer_buffer指针所指向缓存区的大小 //usb_complete_t complete_fn指向当该urb结束之后调用的结束处理例程的指针 //void *context指向一个小数据块,该块被添加到urb结构体中以便进行结束处理例程后面的查找 //int interval该urb应该被调度的间隔 int usb_submit_urb(struct urb *urb, gfp_t mem_flags);//提交urb //struct urb *urb表示需要提交的urb控制块 //gfp_t mem_flags内存分配标志 void usb_buffer_free(struct usb_device *dev,size_t size,void *addr,dma_addr_t dma);//释放分配的usb缓存数据 //struct usb_device *dev表示目标USB设备 //size_t size释放的缓冲区的大小 //void *addr指向释放的缓冲区的指针 //dma_addr_t dma表示释放的缓冲区的物理地址 int usb_unlink_urb(struct urb *urb);//释放urb请求块 //struct urb *urb表示指向释放的urb请求块的指针
直接放出程序源码,可以看到这个程序的结构和输入子系统的结构差不多。
1、首先加载这个模块后,会调用usb_mouse_as_key_init函数,然后usb_mouse_as_key_driver 结构体被注册到USB核心
2、当插上USB鼠标设备后,如果此设备和此驱动的接口类、接口子类、接口协议相同(位于usb_mouse_as_key_id_table ),那么usb_mouse_as_key_probe函数被调用
3、usb_mouse_as_key_probe函数是核心函数,在里面做许多事情,具体看代码
/* a、分配一个 input_dev结构体*/ /* b、设置 */ /* b.1 能产生哪类事件 */ /* b.2 能产生哪些事件 */ /* c、注册 */ /* d、硬件相关的设置 */
4、当按下USB按键后在第三步中设置的usb_mouse_as_key_irq函数被调用
5、数据包会在usb_mouse_as_key_irq回调函数被处理。至于数据的含义需要自己根据USB设备来判断然后定义,我用的鼠标按键的值位于usb_buf[1]中,所以可以根据这个值判断是哪一个按键被按下或松开,然后上传事件到输入子系统。
详细的解释可以参考如下代码:
#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> static struct input_dev *uk_dev; //定义一个输入子系统设备结构体 static dma_addr_t usb_buf_phys; //物理地址 static char *usb_buf; //从USB主控制器接收到的数据存放的导致 static int len; //从USB主控制器接收到的数据的长度 static struct urb *uk_urb; //定义一个USB请求块 /* 一包数据接收完成后处理函数*/ static void usb_mouse_as_key_irq(struct urb *urb) { // int i; // static int cnt = 0; // printk("data cnt %d: ", ++cnt); // for (i = 0; i < len; i++) // { // printk("%02x ", usb_buf[i]); // } // printk("\n"); /* * USB鼠标数据含义 * data[0]: bit0-左键, 1-按下, 0-松开 * bit1-右键, 1-按下, 0-松开 * bit2-中键, 1-按下, 0-松开 */ static unsigned char pre_val;//前一个按键的按键值,每当按键值变化才上传 if((pre_val & (1<<0)) != (usb_buf[1] & (1<<0)))//左键发生变化 { input_event(uk_dev,EV_KEY, KEY_L, (usb_buf[1]?1:0)); input_sync(uk_dev); //上传同步事件 } if((pre_val & (1<<1)) != (usb_buf[1] & (1<<1)))//右键发生变化 { input_event(uk_dev,EV_KEY, KEY_S, (usb_buf[1]?1:0)); input_sync(uk_dev); //上传同步事件 } if((pre_val & (1<<2)) != (usb_buf[1] & (1<<2)))//中键发生变化 { input_event(uk_dev,EV_KEY, KEY_ENTER, (usb_buf[1]?1:0)); input_sync(uk_dev); //上传同步事件 } pre_val = usb_buf[1]; /* 重新提交urb */ usb_submit_urb(uk_urb, GFP_KERNEL);//提交URB,将URB的控制还给USB核心处理程序 } static int usb_mouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf);//根据usb接口,取得usb设备 struct usb_host_interface *interface; //定义一个USB主机控制器接口描述符 struct usb_endpoint_descriptor *endpoint; //定义一个端点描述符 int pipe; interface = intf->cur_altsetting; //获得usb控制器的接口描述符 endpoint = &interface->endpoint[0].desc;//取得usb 控制器的第一个端点描述符 printk("found usbmouse!\n"); printk("bcdUSB = %x\n",dev->descriptor.bcdUSB); //从USB设备描述符中获取USB版本 printk("vidUSB = %x\n",dev->descriptor.idVendor); //从USB设备描述符中获取厂商ID printk("pidUSB = %x\n",dev->descriptor.idProduct);//从USB设备描述符中获取产品ID printk("bdcUSB = %x\n",intf->cur_altsetting->desc.bInterfaceClass);//从USB设备获取设备类 printk("bdsUSB = %x\n",intf->cur_altsetting->desc.bInterfaceSubClass);//从USB设备获取设备从类 printk("bdpUSB = %x\n",intf->cur_altsetting->desc.bInterfaceProtocol);//从USB设备获取设备协议 /* a、分配一个 input_dev结构体*/ uk_dev = input_allocate_device();//分配一个input_dev结构体 /* b、设置 */ /* b.1 能产生哪类事件 */ set_bit(EV_KEY, uk_dev->evbit);//产生按键事件 set_bit(EV_REP, uk_dev->evbit);//产生重复事件 /* b.2 能产生哪些事件 */ set_bit(KEY_L, uk_dev->keybit);//产生按键事件的L事件 set_bit(KEY_S, uk_dev->keybit);//产生按键事件的S事件 set_bit(KEY_ENTER, uk_dev->keybit);//产生按键事件的ENTER时间 /* c、注册 */ input_register_device(uk_dev);//注册一个输入设备 /* d、硬件相关的设置 */ /* 数据传输三要素: 源、目的、长度*/ /* 源:USB设备某个端点 */ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//设置端点为中断IN端点 /* 长度 */ len = endpoint->wMaxPacketSize;//长度为最大包长度 /* 目的 */ usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);//分配一个地址作为USB接收到的数据 /* 使用三要素*/ uk_urb= usb_alloc_urb(0, GFP_KERNEL); //分配一个USB请求块 /* 使用三要素,设置urb */ usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len,usb_mouse_as_key_irq, NULL, endpoint->bInterval);//初始化即将被发送到USB设备的中断端点的URB uk_urb->transfer_dma = usb_buf_phys; //usb控制器完成数据接收后将数据存放的物理地址 uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //当URB包含一个即将传输的DMA缓冲区时应该设置URB_NO_TRANSFER_DMA_MAP /* 使用URB */ ret = usb_submit_urb(uk_urb, GFP_KERNEL);//提交urb if(ret) return -1; return 0; } static void usb_mouse_as_key_disconnect(struct usb_interface *intf) { struct usb_device *dev = interface_to_usbdev(intf); input_free_device(uk_dev);//释放一个input_dev结构体 input_unregister_device(uk_dev);//反注册一个输入设备 usb_buffer_free(dev, len, usb_buf, usb_buf_phys);//释放分配的usb缓存数据 usb_unlink_urb(uk_urb);//不使用urb控制块 printk("disconnetc usbmouse\n"); } static struct usb_device_id usb_mouse_as_key_id_table [] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, { } /* Terminating entry *///终止入口项*/ }; static struct usb_driver usb_mouse_as_key_driver = { .name = "usbmouse_askey", .probe = usb_mouse_as_key_probe, .disconnect = usb_mouse_as_key_disconnect, .id_table = usb_mouse_as_key_id_table, }; static int __init usb_mouse_as_key_init(void) { int retval = usb_register(&usb_mouse_as_key_driver);//注册一个usb驱动 return retval; } static void __exit usb_mouse_as_key_exit(void) { usb_deregister(&usb_mouse_as_key_driver); } module_init(usb_mouse_as_key_init); module_exit(usb_mouse_as_key_exit); MODULE_LICENSE("GPL");
测试流程如下:
1、insmod 11th_usbmouse_as_key_drv.ko
2、ls /dev/event*
3、接上USB鼠标
4、ls /dev/event*后可以看到新增了一个event1
5、cat dev/tty1 然后按鼠标按键,左键、右键、中键分别为l、s、enter
6、测试成功
来源:https://www.cnblogs.com/andyfly/p/9593199.html