Linux驱动之USB鼠标驱动编写
本篇博客分以下几部分讲解
1、介绍USB四大描述符
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));
2、介绍USB鼠标驱动程序功能及框架
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、硬件相关的设置 */
3、介绍程序用到的结构体
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 + ];//绝对坐标的最大值
int absmin[ABS_MAX + ];//绝对坐标的最小值
int absfuzz[ABS_MAX + ];//绝对坐标的干扰值,默认为0,
int absflat[ABS_MAX + ];//绝对坐标的平焊位置,默认为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:; /* the interface is not suspended */
unsigned needs_remote_wakeup:; /* 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[]; //
/* (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:;
unsigned int supports_autosuspend:;
};
4、介绍程序用到的函数
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请求块的指针
5、编写程序
直接放出程序源码,可以看到这个程序的结构和输入子系统的结构差不多。
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 & (<<)) != (usb_buf[] & (<<)))//左键发生变化
{
input_event(uk_dev,EV_KEY, KEY_L, (usb_buf[]?:));
input_sync(uk_dev); //上传同步事件
} if((pre_val & (<<)) != (usb_buf[] & (<<)))//右键发生变化
{
input_event(uk_dev,EV_KEY, KEY_S, (usb_buf[]?:));
input_sync(uk_dev); //上传同步事件
} if((pre_val & (<<)) != (usb_buf[] & (<<)))//中键发生变化
{
input_event(uk_dev,EV_KEY, KEY_ENTER, (usb_buf[]?:));
input_sync(uk_dev); //上传同步事件
}
pre_val = usb_buf[]; /* 重新提交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[].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(, 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 -;
return ;
} 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");
6、测试程序
测试流程如下:
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、测试成功
Linux驱动之USB鼠标驱动编写的更多相关文章
- Linux usb子系统(一) _写一个usb鼠标驱动
USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...
- Linux下的USB总线驱动(一)
版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.USB理论 1. USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...
- synaptics驱动,插入USB鼠标禁用触控板注册表
Title:synaptics驱动,插入USB鼠标禁用触控板注册表 --2010-11-01 22:21 记得以前用过一个synaptics的驱动,自带有插入USB鼠标禁用触控板的选项的,有些没有自带 ...
- Linux 串口、usb转串口驱动分析(2-2) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...
- Linux 串口、usb转串口驱动分析(2-1) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...
- linux驱动基础系列--Linux 串口、usb转串口驱动分析
前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...
- 学习Linux下s3c2440的USB鼠标驱动笔记
1.ARM-Linux下USB驱动程序开发1.1.1.linux下USB配置:*********(MassStorage:存储设备)********************************** ...
- linux设备驱动之USB主机控制器驱动分析 【转】
转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html ---------------------------------------- ...
- Kali Linux安装TL-WN821N USB无线网卡驱动(make失败)
主要有下面几个步骤: 1.刚插上网卡,network-manager识别出来了网卡,也能搜索到WiFi,但就是连接不上.查看/var/log/syslog日志或者使用nmcli m查看网络信息,显示认 ...
随机推荐
- Linux中docker的使用(2)
容器下安装jdk和tomcat:通过挂载文件的方法,把宿主机上的文件挂载到容器中,然后解压到opt目录下:tar -zxvf 包名 -C /opt//opt目录下drwxr-xr-x 8 10 143 ...
- ReentrantLock原理
ReentrantLock主要利用CAS+CLH队列来实现.它支持公平锁和非公平锁,两者的实现类似. CAS:Compare and Swap,比较并交换.CAS有3个操作数:内存值V.预期值A.要修 ...
- Shiro的认证和权限控制
权限控制的方式 从类别上分,有两大类: - 认证:你是谁?–识别用户身份. - 授权:你能做什么?–限制用户使用的功能. 权限的控制级别 从控制级别(模型)上分: - URL级别-粗粒度 - 方法级别 ...
- thinkphp3.2.3集成腾讯云短信文档流程
昨天晚上折腾了一个小时没解决 今天折腾了20分钟就搞定了 看了thinkphp3.2.3的命名空间解释方法 把文件放到这里 /ThinkPHP/Library/Org/ 把新建了一个Sms文件夹 把 ...
- Ajax学习笔记——基本原理
Ajax(Asynchronous JavaScript + XML)不是语音,不是框架,也不能算是一种技术,而是一种模式.通过这种模式实现不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内 ...
- 重写 final关键字 多态调用子类特有的属性及行为(向上向下转型)
1.override 重写:在继承中,子类与父类方法名相同,参数列表相同,的方法叫重写,与返回值有关; 主要应用于系统升级. 2.final 关键字: 可修饰:1.类-->被修饰后该类不能被继 ...
- nginx内容
nginx工作在7层:web server(静态内容 static contents)web reverse proxy(反向代理http,https,mail),cache(带缓存功能) proxy ...
- 2018-2019-2 网络对抗技术 20165304 Exp4 恶意代码分析
2018-2019-2 网络对抗技术 20165304 Exp4 恶意代码分析 原理与实践说明 1.实践目标 监控你自己系统的运行状态,看有没有可疑的程序在运行. 分析一个恶意软件,就分析Exp2或E ...
- Docker技术应用场景(转载)
场景一:节省项目环境部署时间 1.单项目打包 每次部署项目到测试.生产等环境,都要部署一大堆依赖的软件.工具,而且部署期间出现问题几率很大,不经意就花费了很长时间. Docker主要理念就是环境打包部 ...
- sqlserver一些对象的创建
1.db_link 一 如何创建Dblink1)SQLServer 到 SQLServerExec sp_droplinkedsrvlogin PDALink,Null --删除映射(录与链接服务器上 ...