20.Linux-USB鼠标驱动
在上一章分析完USB总线驱动程序后, 接下来开始写一个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 <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 *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; //urb static void myusb_mouse_irq(struct urb *urb) //鼠标中断函数
{
static char buf1=;
//for(i=0;i<myusb_mouse_size;i++)
// printk("%02x ",myusb_mouse_buf[i]);
// printk("\n"); /*bit 1-左右中键 0X01:左键 0X02:右键 0x04:中键 */
if((buf1&(0X01)) != (myusb_mouse_buf[]&(0X01)))
{
input_report_key(myusb_mouse_dev, KEY_L, buf1&(0X01)? :);
input_sync(myusb_mouse_dev);
}
if((buf1&(0X02)) != (myusb_mouse_buf[]&(0X02)))
{
input_report_key(myusb_mouse_dev, KEY_S, buf1&(0X02)? :);
input_sync(myusb_mouse_dev);
}
if((buf1&(0X04)) != (myusb_mouse_buf[]&(0X04)) )
{
input_report_key(myusb_mouse_dev, KEY_ENTER, buf1&(0X04)? :);
input_sync(myusb_mouse_dev);
} buf1=myusb_mouse_buf[]; //更新数据 usb_submit_urb(myusb_mouse_urb, GFP_KERNEL);
} static int myusb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); //设备
struct usb_endpoint_descriptor *endpoint;
struct usb_host_interface *interface; //当前接口
int pipe; //端点管道 interface=intf->cur_altsetting;
endpoint = &interface->endpoint[].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(,GFP_KERNEL);
usb_fill_int_urb (myusb_mouse_urb, //urb结构体
dev, //usb设备
pipe, //端点管道
myusb_mouse_buf, //缓存区地址
myusb_mouse_size, //数据长度
myusb_mouse_irq, //中断函数
,
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 ;
} 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 ;
} /*出口函数*/
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命令调试代码详解地址:http://www.cnblogs.com/lifexy/p/7553550.html)
5.4 使用tty1进程测试
未完待续~~~~~~~~~~ 下节 依葫芦画瓢 来写出 USB键盘驱动
20.Linux-USB鼠标驱动的更多相关文章
- Linux USB摄像头驱动【转】
本文转载自:http://www.itdadao.com/articles/c15a509940p0.html 在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 ...
- linux usb总线驱动(一)
目录 linux usb总线驱动框架 USB 介绍 传输类型 控制器接口 2440接口 基本流程 alloc_dev choose_address hub_port_init usb_get_devi ...
- Linux USB 摄像头驱动
在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 USB 接口的摄像头.这一章主要是介绍 USB 摄像头的设备驱动程序.在我们印象中,驱动程序都是一个萝卜一个坑, ...
- Linux USB 鼠标驱动程序详解(转)
Linux USB 鼠标驱动程序详解 USB 总线引出两个重要的链表!一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以 ...
- Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析
下面将给出Hi3559AV100外接UVC/MJPEG相机实时采图设计的整体流程,主要实现是通过V4L2接口将UVC/MJPEG相机采集的数据送入至MPP平台,经过VDEC.VPSS.VO最后通过HD ...
- 学习Linux下s3c2440的USB鼠标驱动笔记
1.ARM-Linux下USB驱动程序开发1.1.1.linux下USB配置:*********(MassStorage:存储设备)********************************** ...
- Linux usb子系统(一) _写一个usb鼠标驱动
USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...
- Linux驱动之USB鼠标驱动编写
本篇博客分以下几部分讲解 1.介绍USB四大描述 2.介绍USB鼠标驱动程序功能及框架 3.介绍程序用到的结构体 4.介绍程序用到的函数 5.编写程序 6.测试程序 1.介绍USB四大描述符 USB设 ...
- Linux USB 鼠标输入驱动具体解释
平台:mini2440 内核:linux 2.6.32.2 USB设备插入时.内核会读取设备信息,接着就把id_table里的信息与读取到的信息做比較.看是否匹配,假设匹配.就调用probe函数. U ...
- 嵌入式Linux USB WIFI驱动的移植
硬件平台:飞思卡尔MX258开发板 操作系统:Linux2.6.31 WIFI: RT2860 USB WIFI模组 交叉编译环境:gcc version 4.1.2 调试步骤: 第一步:测试U ...
随机推荐
- pip源相关问题
指定源地址安装: pip install -i http://pypi.douban.com/simple/ packagename pip install -i http://pypi.tuna.t ...
- spring mvc:事务引起的try/catch失效
在测试一个接口时,发现一个奇怪的现象:该接口使用@ResponseBody注解返回json格式数据,并且使用try/catch包括全部逻辑代码,debug后发现返回数据没有任何错误,只包含一段因产生异 ...
- 【前端基础】动态脚本与JSONP
博主入职两个月了,越来越感受到打好基础对于前端工程师的重要性,在向着狂拽酷炫的框架&构建工具高速狂奔之前,必须有一个坚实的基础打底,才不至于轻易翻车.所以博主最近一直在恶补<JS高级程序 ...
- 持续交付Jenkins使用
简介 Jenkins是一个独立的开源自动化服务器,可用于自动化各种任务,如构建,测试和部署软件.Jenkins可以通过本机系统包Docker安装,甚至可以通过安装Java Runtime Enviro ...
- 短视频 SDK 功能点技术实现方式详解
第三方短视频解决方案作为快速切入短视频行业的首选方式,选择一款功能齐全.性能优异的短视频解决方案十分重要. 今天我们来谈谈短视频 SDK 6大重要功能点及其技术实现方式. 短视频拍摄 断点续拍 指在拍 ...
- C# 模拟网站登陆并截图
1.在窗体上加一个按钮,为按钮添加点击事件 private void button1_Click(object sender, EventArgs e) { Bitmap m_Bitmap = Web ...
- ★10 个实用技巧,让Finder带你飞~
10 个实用技巧,让 Finder 带你飞 Finder 是 Mac 电脑的系统程序,有的功能类似 Windows 的资源管理器.它是我们打开 Mac 首先见到的「笑脸」,有了它,我们可以组织和使用 ...
- 201521044091 《Java程序设计》第5周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. ●接口可以定义行为,但却不能定义操作: ●使用abstract方法修饰可能变化的 ...
- Java实现Windows平台下Ping的最佳方法
先上结论:通过调用系统自带的Ping命令来实现,使用exitValue()值来判断Ping的结果.按照惯例,0表示ok,1表示不通. private static void pingTest1() t ...
- 201521123072《java程序设计》第十四周学习总结
201521123072<java程序设计>第十四周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库 ...