博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
20.Linux-USB鼠标驱动
阅读量:6243 次
发布时间:2019-06-22

本文共 7068 字,大约阅读时间需要 23 分钟。

在上一章分析完后, 接下来开始写一个USB驱动:

本节目的: 将USB鼠标的左键当作L按键,将USB鼠标的右键当作S按键,中键当作回车按键


 

参考/drivers/hid/usbhid/usbmouse.c(内核自带的USB鼠标驱动)

1.本节需要用到的宏如下:

struct usb_device_id usbmouse_id_table []=USB_INTERFACE_INFO(cl,sc,pr);

USB_INTERFACE_INFO()设置usb_driver驱动的id_table成员

cl:接口类,我们USB鼠标为HID类,所以填入0X03,也就是USB_INTERFACE_CLASS_HID

sc:接口子类为启动设备,填入USB_INTERFACE_SUBCLASS_BOOT

pr:接口协议为鼠标协议,填入USB_INTERFACE_PROTOCOL_MOUSE 

 

        

struct usb_device *dev=interface_to_usbdev(intf);

 通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用

 

 

pipe=usb_rcvintpipe(dev,endpoint);

创建一个接收(rcv)中断(int)类型的端点管道(pipe),用来端点和数据缓冲区之间的连接,鼠标为接收中断型

dev: usb_device设备结构体

endpoint:为端点描述符的成员endpoint->bEndpointAddress   //端点地址

  • 对于控制类型的端点管道使用: usb_sndctrlpipe()/usb_rcvctrlpipe()
  • 对于实时类型的端点管道使用: usb_sndisocpipe()/usb_sndisocpipe()
  • 对于批量类型的端点管道使用: usb_sndbulkpipe()/usb_rcvbulkpipe()

 

2.本节需要用到的函数如下:

usb_deregister(struct usb_driver *driver);

注册一个usb_driver驱动,然后内核会通过usb_driver的成员.id_table函数匹配一次USB设备,匹配成功就会调用usb_driver的成员.probe函数

 

 

usb_deregister(struct usb_driver *driver);

注销一个usb_driver驱动,在出口函数中写

 

 

*usb_buffer_alloc(struct usb_device *dev,size_t size,gfp_t mem_flags,dma_addr_t *dma);

分配一个usb缓冲区,该缓存区的物理地址会与虚拟地址的数据一致,分配成功返回一个char型缓冲区虚拟地址

*dev: usb_device设备结构体

size:分配的缓冲区大小,这里填端点描述符的成员endpoint->wMaxPacketSize          //端点最大包长

mem_flags:分配内存的参数,这里填GFP_ATOMIC,表示从不睡眠

dma:分配成功则会返回一个DMA缓冲区物理地址

void usb_buffer_free(struct usb_device *dev,size_t size,void *addr,dma_addr_t dma);

注销分配的usb缓冲区,在usb_driver的disconnect成员函数中使用

addr:要注销的缓冲区虚拟地址

dma: 要注销的DMA缓冲区虚拟地址

 

 

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);

分配一个urb数据结构体, 分配成功返回一个urb结构体

urb全称为usb request block,USB传输数据时,就是打包成urb结构体来传输

iso_packets:表示iso类型的包个数,这里我们不是iso类型包,直接填0

mem_flags:分配内存的参数,这里填入GFP_KERNEL,正常分配

其中urb结构体如下所示:

struct urb{ ... ... struct usb_device *dev;             //指向usb设备 struct usb_host_endpoint *ep;    //指向端点的数据结构  unsigned int pipe;                  //指向端点管道(pipe), 本节的pipe通过usb_rcvintpipe()宏获取 int status;                                 //状态,当status==0,表示数据被成功地收到/发送  unsigned int transfer_flags;     //传输状态 ... .../*以下两个缓冲区通过usb_buffer_alloc ()函数获取 *///urb结构体默认的transfer_flags是URB_NO_SETUP_DMA_MAP ,也就是说没有提供DMA的缓冲区//就会使用transfer_buffer虚拟地址缓冲区来当缓冲区//当支持DMA缓冲区时,就需要手动设置transfer_flags =URB_NO_TRANSFER_DMA_MAP,并手动设置transfer_dma等于获取到的DMA物理地址 void *transfer_buffer;                //虚拟缓冲区 dma_addr_t transfer_dma;          //DMA物理缓冲区 ... ...};

 

 

void usb_free_urb(struct urb *urb);

释放申请的urb,在usb_driver的disconnect成员函数中使用

 

 

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);

初始化中断型端点的urb数据结构体

针对批量型端点的urb使用usb_fill_bulk_urb()

针对控制型端点的urb使用usb_fill_control_urb()
针对等时型端点的urb  需要手动初始化。

urb:指向要初始化的urb

dev:指向要传输的usb设备

pipe:要传输的端点管道, 本节的pipe通过usb_rcvintpipe()宏获取

transfer_buffer:指向要传输数据的虚拟地址缓冲区

buffer_length:数据大小, 这里填端点描述符的成员endpoint->wMaxPacketS //端点最大包长

complete_fn:数据传输完成后产生的中断函数

context:会放在urb->context结构成员中,用来给中断函数用,本节不需要,填NULL即可

interval:间隔时间,表示间隔多少时间读一次数据,填入endpoint-> bInterval即可

 

 

int usb_submit_urb(struct urb *urb,gfp_t mem_flags);

提交urb到内核,初始化urb和中断函数退出时,都要重新提交一次,告诉内核初始化内存缓存等

 

 

void usb_kill_urb(struct urb *urb);

杀掉urb,在usb_driver的disconnect成员函数中使用

 

3.步骤如下:

首先先定义全局变量:usb_driver结构体,input_dev指针结构体 ,虚拟地址缓存区,DMA地址缓存区

3.1在入口函数中

1)通过usb_register()函数注册usb_driver结构体

3.2在usb_driver的probe函数中

1)分配一个input_dev结构体

2)设置input_dev支持L、S、回车、3个按键事件

3)注册input_dev结构体

4)设置USB数据传输:

 ->4.1)通过usb_rcvintpipe()创建一个接收中断类型的端点管道,用来端点和数据缓冲区之间的连接

 ->4.2)通过usb_buffer_alloc()申请USB缓冲区

 ->4.3)申请并初始化urb结构体,urb:用来传输数据

 ->4.4) 因为我们2440支持DMA,所以要告诉urb结构体,使用DMA缓冲区地址

 ->4.5)使用usb_submit_urb()提交urb

3.3在鼠标中断函数中

1)判断缓存区数据是否改变,若改变则上传鼠标事件

2)使用usb_submit_urb()提交urb

3.4.在usb_driver的disconnect函数中

1)通过usb_kill_urb()杀掉提交到内核中的urb

2)释放urb

3)释放USB缓存区

4)注销input_device,释放input_device

3.5在出口函数中

1)通过usb_deregister ()函数注销usb_driver结构体

 

4.代码如下:

#include 
#include
#include
#include
#include
#include
static struct input_dev *myusb_mouse_dev; //input_dev static char *myusb_mouse_buf; //虚拟地址缓存区static dma_addr_t myusb_mouse_phyc; //DMA缓存区;static __le16 myusb_mouse_size; //数据包长度static struct urb *myusb_mouse_urb; //urbstatic void myusb_mouse_irq(struct urb *urb) //鼠标中断函数{ static char buf1=0; //for(i=0;i
cur_altsetting; endpoint = &interface->endpoint[0].desc; //当前接口下的端点描述符 printk("VID=%x,PID=%x\n",dev->descriptor.idVendor,dev->descriptor.idProduct); //打印VID,PID /* 1)分配一个input_dev结构体 */ myusb_mouse_dev=input_allocate_device(); /* 2)设置input_dev支持L、S,回车、3个按键事件*/ set_bit(EV_KEY, myusb_mouse_dev->evbit); set_bit(EV_REP, myusb_mouse_dev->evbit); //支持重复按功能 set_bit(KEY_L, myusb_mouse_dev->keybit); set_bit(KEY_S, myusb_mouse_dev->keybit); set_bit(KEY_ENTER, myusb_mouse_dev->keybit); /* 3)注册input_dev结构体*/ input_register_device(myusb_mouse_dev); /* 4)设置USB数据传输 */ /*->4.1)通过usb_rcvintpipe()创建一个端点管道*/ pipe=usb_rcvintpipe(dev,endpoint->bEndpointAddress); /*->4.2)通过usb_buffer_alloc()申请USB缓冲区*/ myusb_mouse_size=endpoint->wMaxPacketSize; myusb_mouse_buf=usb_buffer_alloc(dev,myusb_mouse_size,GFP_ATOMIC,&myusb_mouse_phyc); /*->4.3)通过usb_alloc_urb()和usb_fill_int_urb()申请并初始化urb结构体 */ myusb_mouse_urb=usb_alloc_urb(0,GFP_KERNEL); usb_fill_int_urb (myusb_mouse_urb, //urb结构体 dev, //usb设备 pipe, //端点管道 myusb_mouse_buf, //缓存区地址 myusb_mouse_size, //数据长度 myusb_mouse_irq, //中断函数 0, endpoint->bInterval); //中断间隔时间 /*->4.4) 因为我们2440支持DMA,所以要告诉urb结构体,使用DMA缓冲区地址*/ myusb_mouse_urb->transfer_dma =myusb_mouse_phyc; //设置DMA地址 myusb_mouse_urb->transfer_flags =URB_NO_TRANSFER_DMA_MAP; //设置使用DMA地址 /*->4.5)使用usb_submit_urb()提交urb*/ usb_submit_urb(myusb_mouse_urb, GFP_KERNEL); return 0;}static void myusb_mouse_disconnect(struct usb_interface *intf){ struct usb_device *dev = interface_to_usbdev(intf); //设备 usb_kill_urb(myusb_mouse_urb); usb_free_urb(myusb_mouse_urb); usb_buffer_free(dev, myusb_mouse_size, myusb_mouse_buf,myusb_mouse_phyc); input_unregister_device(myusb_mouse_dev); //注销内核中的input_dev input_free_device(myusb_mouse_dev); //释放input_dev} static struct usb_device_id myusb_mouse_id_table [] = { { USB_INTERFACE_INFO( USB_INTERFACE_CLASS_HID, //接口类:hid类 USB_INTERFACE_SUBCLASS_BOOT, //子类:启动设备类 USB_INTERFACE_PROTOCOL_MOUSE) }, //USB协议:鼠标协议};static struct usb_driver myusb_mouse_drv = { .name = "myusb_mouse", .probe = myusb_mouse_probe, .disconnect = myusb_mouse_disconnect, .id_table = myusb_mouse_id_table,}; /*入口函数*/static int myusb_mouse_init(void){ usb_register(&myusb_mouse_drv); return 0;}/*出口函数*/static void myusb_mouse_exit(void){ usb_deregister(&myusb_mouse_drv);} module_init(myusb_mouse_init);module_exit(myusb_mouse_exit);MODULE_LICENSE("GPL");

5.测试运行

5.1 重新设置编译内核(去掉默认的hid_USB驱动)

make menuconfig ,进入menu菜单重新设置内核参数:

进入-> Device Drivers -> HID Devices 

<> USB Human Interface Device (full HID) support //hid:人机交互的USB驱动,比如鼠标,键盘等

然后make uImage 编译内核

将新的触摸屏驱动模块放入nfs文件系统目录中

5.2然后烧写内核,装载触摸屏驱动模块

如下图,当我们插上USB鼠标时,可以看到该VID和PID,和电脑上的鼠标的参数一样

 

5.3使用hexdump命令来调试

(hexdump命令调试代码详解地址:)

 

5.4 使用tty1进程测试

 

 

未完待续~~~~~~~~~~  下节 依葫芦画瓢 来写出 

 


 

转载地址:http://cesia.baihongyu.com/

你可能感兴趣的文章
pyrhon脚本小练习(9*9乘法表)
查看>>
Python按行读取文件
查看>>
Linux Shell从一个文件去掉包含在另一个文件的内容
查看>>
Linux CentOS6.5下编译安装MySQL 5.6.16
查看>>
CentOS 7 上systemctl 的用法
查看>>
极验验证的滑动验证码破解
查看>>
【深入Linux块设备驱动层次之一】整体层次
查看>>
Linux登录安全及用户操作审计 ,linux下清理日志脚本
查看>>
一个JS文件中引入另一个JS文件
查看>>
Laravel中pluck的使用——返回指定的字段值信息列表
查看>>
TCP Segment Offload(TSO)的实现原理浅析
查看>>
Android官方开发文档Training系列课程中文版:多样屏幕之支持不同的屏幕尺寸
查看>>
Redmine部署
查看>>
五种开源协议比较:BSD,Apache,GPL,LGPL,MIT
查看>>
Linux上编译hadoop-2.7.1的libhdfs.so和libhdfs.a
查看>>
mysql中函数greatest 与MAX区别
查看>>
ORA-01843: 无效的月份
查看>>
Centos网络管理(三)-网络配置相关
查看>>
php-fpm的pool、php-fpm慢执行日志、open_basedir、php-fpm进程管理
查看>>
编写iptables模块实现不连续IP地址的DNAT-POOL
查看>>