学习目标:编写USB鼠标驱动程序,并测试(将USB鼠标的左键当作L按键,将USB鼠标的右键当作S按键,中键当作回车按键).


一、怎么写USB设备驱动程序?步骤如下:

1. 首先先定义全局变量usb_driver结构体,并在入口函数中通过usb_register()函数进行注册

2. 分别写usb_driver结构体的成员函数:myusb_mouseprobe、myusb_mousedisconnect、myusb_mouseid_table

--> 2.1 usb_driver的probe函数

  1) 分配一个input_dev结构体

  2) 设置input_dev结构体,使它支持L、S、回车3个按键事件;

  3) 注册input_dev结构体

  4) 硬件相关的操作,即设置USB数据传输3要素和urb结构体:

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

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

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

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

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

--> 2.2 编写probe函数调用的鼠标中断函数

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

  2)使用usb_submit_urb()提交urb

--> 2.3 usb_driver的disconnect函数中

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

  2) 释放urb结构体

  3 )释放USB缓存区

  4)注销并释放input_device结构体

3. 出口函数通过usb_deregister ()函数注销usb_driver结构体

二、程序源码

 #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; //input_dev
static char *usb_buf; //虚拟地址缓存区
static dma_addr_t usb_buf_phys; //DMA缓存区
static int len; //数据包长度
static struct urb *uk_urb; //urb数据传输所用结构体 static struct usb_device_id myusb_mouseid_table [] = {
{ USB_INTERFACE_INFO(
USB_INTERFACE_CLASS_HID, //接口类:hid类
USB_INTERFACE_SUBCLASS_BOOT, //子类:启动设备类
USB_INTERFACE_PROTOCOL_MOUSE) }, // USB协议:鼠标协议
}; static void myusb_mouseirq(struct urb *urb)
{
static unsigned char pre_val;
/* USB鼠标数据含义* data[0]: bit0-左键, 1-按下, 0-松开
* bit1-右键, 1-按下, 0-松开
* bit2-中键, 1-按下, 0-松开 */
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);
}
static int myusb_mouseprobe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); // 设备,通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用
struct usb_host_interface *interface; // 当前接口
struct usb_endpoint_descriptor *endpoint;
int pipe; // 端点管道 interface = intf->cur_altsetting;
endpoint = &interface->endpoint[].desc; // 当前接口下的端点描述符 /* a. 分配一个input_dev */
uk_dev = input_allocate_device();
/* b. 设置 */
/* b.1 能产生哪类事件 */ //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)
set_bit(EV_KEY, uk_dev->evbit);
     set_bit(EV_REP, uk_dev->evbit);

     /* b.2 能产生哪些事件 */
set_bit(KEY_L, uk_dev->keybit);
set_bit(KEY_S, uk_dev->keybit);
set_bit(KEY_ENTER, uk_dev->keybit); /* c. 注册 */
input_register_device(uk_dev); /* d. 硬件相关操作 */
/* 数据传输3要素: 源,目的,长度 */
/* 源: USB设备的某个端点 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* 长度: */
len = endpoint->wMaxPacketSize; /* 目的: */
usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys); /* 使用"3要素" */
/* 分配usb request block */
uk_urb = usb_alloc_urb(, GFP_KERNEL);//usb传输素具的urb结构体
/* 使用"3要素设置urb" */
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, myusb_mouseirq, NULL, endpoint->bInterval);
uk_urb->transfer_dma = usb_buf_phys; //设置DMA地址
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //设置使用DMA地址 /* 使用URB */
usb_submit_urb(uk_urb, GFP_KERNEL); return ;
}
static void myusb_mousedisconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);
//printk("disconnect usbmouse!\n");
usb_kill_urb(uk_urb);
usb_free_urb(uk_urb); usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}
/* 1. 分配/设置usb_driver */
static struct usb_driver myusb_mousedriver = {
.name = "myusb_mouse",
.probe = myusb_mouseprobe,
.disconnect = myusb_mousedisconnect,
.id_table = myusb_mouseid_table,
};
static int myusb_mouseinit(void)
{
/* 2. 注册 */
usb_register(&myusb_mousedriver);
return ;
}
static void myusb_mouseexit(void)
{
usb_deregister(&myusb_mousedriver);
}
module_init(myusb_mouseinit);
module_exit(myusb_mouseexit);
MODULE_LICENSE("GPL");

三、源码分析

3.1 id_table

 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

3.2 usb_rcvintpipe()函数

1 pipe=usb_rcvintpipe(dev,endpoint->bEndpointAddress);

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

dev: usb_device设备结构体

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

1)对于控制类型的端点管道使用: usb_sndctrlpipe()/usb_rcvctrlpipe()

2)对于实时类型的端点管道使用: usb_sndisocpipe()/usb_sndisocpipe()

3)对于批量类型的端点管道使用: usb_sndbulkpipe()/usb_rcvbulkpipe()

3.3 usb_driver注册函数

 usb_deregister(struct usb_driver *driver);

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

3.4 usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);

 char *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   len  //端点最大包长

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缓冲区虚拟地址

3.5 uk_urb = usb_alloc_urb(0, GFP_KERNEL);//usb传输数据的urb结构体

 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,正常分配

3.6 usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, myusb_mouseirq, NULL, endpoint->bInterval);

 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数据结构体

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

2)针对控制型端点的urb使用usb_fill_control_urb()

3)针对等时型端点的urb  需要手动初始化。

urb:指向要初始化的urb

dev:指向要传输的usb设备

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

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

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

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

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

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

3.7 usb_submit_urb(uk_urb, GFP_KERNEL);

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

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

三、测试

1. 在源码下去除原先的usb鼠标驱动,执行 make menuconfig

  --> Device Drivers
      --> HID Devices
        <> USB Human Interface Device (full HID) support

make uImage编译内核,并烧写,启动。

2. ubuntu环境下编译生成驱动模块:myusbmouse.ko

3. 开发板挂载ubuntu的/work/nfs_root/first_fs目录: # mount -t nfs -o nolock,vers=2 10.70.12.103:/work/nfs_root/ /mnt

4. 加载驱动:# insmod myusbmouse.ko

5. 插入USB鼠标,查看设备节点: ls /dev/event*

6.  cat /dev/tty1    点击鼠标按键

(注意:出现乱码,需要关掉QT,#vi /etc/init.d/rcs   屏蔽一行 #/bin/qpe.sh &)

执行命令 # exec 0</dev/tty0 可以当做键盘使用ls命令,(这里exec 0</dev/tty1 表示将/dev/tty1的输入作为标准输,0代表的是STDIN,标准输入)

退出时输入:# exec 0 即可。

7.  hexdump /dev/event1


参考:https://www.cnblogs.com/lifexy/p/7641602.html

8.1 编写USB鼠标驱动程序,并测试的更多相关文章

  1. Linux USB 鼠标驱动程序详解(转)

    Linux USB 鼠标驱动程序详解 USB 总线引出两个重要的链表!一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以 ...

  2. Linux驱动之USB鼠标驱动编写

    本篇博客分以下几部分讲解 1.介绍USB四大描述 2.介绍USB鼠标驱动程序功能及框架 3.介绍程序用到的结构体 4.介绍程序用到的函数 5.编写程序 6.测试程序 1.介绍USB四大描述符 USB设 ...

  3. Linux驱动之USB总线驱动程序框架简析

    通用串行总线(USB)是主机和外围设备之间的一种连接.USB总线规范有1.1版和2.0版,当然现在已经有了3.0版本.USB1.1支持两种传输速度:低速为1.5Mbps,高速为12Mbps.USB2. ...

  4. USB驱动程序之USB设备驱动程序1简单编写

    1.驱动编写分析 (1)usb总线驱动程序在我们接入USB设备的时候会帮我们构造一个新的usb_device.注册到总线里面来.左边这一块已经帮我们做好了,我们要做的是右边这一块.我们要构造一个usb ...

  5. USB驱动程序之USB设备驱动程序2鼠标用作键盘学习笔记

    1.usbmouse.c (1)probe函数 在这个probe函数后判断是不是一个鼠标,先得到usb_host_interface结构体,除了端点0外,端点个数如果不是1,返回错误,表示不是自己能支 ...

  6. 学习Linux下s3c2440的USB鼠标驱动笔记

    1.ARM-Linux下USB驱动程序开发1.1.1.linux下USB配置:*********(MassStorage:存储设备)********************************** ...

  7. USB设备驱动程序(一)

    USB驱动编程原理: 当我们把USB设备插入USB口时会提示需要安装相对应的驱动,如USB鼠标.USB键盘等,这些电脑自己自身已经自带有相对于的驱动程序, 当电脑检查到该USB设备类型相同就去帮你安装 ...

  8. USB设备驱动程序学习笔记(一)

    现象:把USB设备接到PC1. 右下角弹出"发现android phone"2. 跳出一个对话框,提示你安装驱动程序 问1. 既然还没有"驱动程序",为何能知道 ...

  9. USB设备驱动程序(二)

    首先我们来看USB设备描述符的结构: 在USB总线识别设备阶段就将USB描述符发送给了USB总线驱动程序,设备的数据传输对象是端点,端点0是特殊端点,在USB总线驱动程序识别阶段, 会分配一个地址给U ...

随机推荐

  1. select * from pet where species regexp '^c';

    select * from pet where species regexp '^c';

  2. js:JavaScript中的ActiveXObject对象

    JavaScript中的ActiveXObject对象作用: https://blog.csdn.net/pl1612127/article/details/77862174

  3. django模板templates详解(二)

    1 总体结构 ​ Django是MTV结构,即:Model, Template, View Model:定义数据的存储格式,并且提供了数据库访问的API. View:定义那些数据被显示,是业务逻辑处理 ...

  4. June 01st 2017 Week 22nd Thursday

    Do what you say, say what you do. 做你说过的,说你能做的. Do what I have said, live up to my promise, answer th ...

  5. February 25 2017 Week 8 Saturday

    Energy and persistence can conquer all things. 能量和毅力可以征服一切. I have the persistence, but it seems I a ...

  6. NO.002-2018.02.07《越人歌》先秦:佚名

    参考之后略有修改,疑问点“不訾诟耻”释义 越人歌原文.翻译及赏析_古诗文网 蒙羞被好兮不訾诟耻_释义_吴江诗词网   越人歌 先秦:佚名 今夕何夕兮,搴舟中流.今晚是怎样的晚上啊河中漫游.搴(qiān ...

  7. HDU 1083 Courses 【二分图完备匹配】

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1083 Courses Time Limit: 20000/10000 MS (Java/Others)  ...

  8. js正则判断日期

    //****************************************************************************// Function ID : Commo ...

  9. android jni 之C语言基础

    *含义 1.乘法 3*5 2.定义指针变量 int * p://定义了一个名字叫p的变量,能够存放int数据类型的地址 3.指针运算符, //如果p是一个已经定义好的指针变量则*p表示以p的内容为地址 ...

  10. Android学习笔记_61_手机安全卫士知识点归纳(1)状态/形状图形 GPS 设备管理器DeviceAdminReceiver ImageView属性

    1.在做程序自动安装更新的时候 ,必须保证程序的签名和包名是相同.  C:\Documents and Settings\zehua\.android  \ debug.keystore  debug ...