学习目标:编写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. Java IntelliJ IDEA 不能显示项目里的文件结构的解决方案

    按下列步骤操作:1. 关闭IDEA2.然后删除项目文件夹下的.idea文件夹3.重新用IDEA工具打开项目

  2. Android应用开发基础之七:广播与服务(一)

    广播 广播的概念 现实:电台通过发送广播发布消息,买个收音机,就能收听 Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件. Android系统 ...

  3. WAKE-WIN10-SOFT-python配置

    1,anaconda下载,安装 1,1 官网:https://www.continuum.io/downloads 下载:https://repo.continuum.io/archive/ win1 ...

  4. Jmeter入门11 使用Simple Controller组织接口测试用例

    接口测试实践中,可以使用Simple Controller来组织测试用例. 官网上说该控制器的主要用途用来组织采样器和其他的逻辑控制器等. 以下为一个接口测试项目结构示例: 1 测试计划 >添加 ...

  5. BestCoder Round #81 (div.2)

    HDU:5670~5764 A题: 是一个3进制计数: #include <bits/stdc++.h> using namespace std; ]; int calc(long lon ...

  6. Uva 11419 我是SAM

    题目链接:https://vjudge.net/problem/UVA-11419 题意:一个网格里面有一些目标,可以从某一行,某一列发射一发子弹,可以打穿: 求最少的子弹,和在哪里打? 分析: 听说 ...

  7. POJ 2365【YY水题】

    题目链接:POJ 2365 Rope Rope Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7488   Accepted ...

  8. Mysql跨数据库(在同一IP地址中)复制表

    数据库表间数据复制分类 在利用数据库开发时,常常会将一些表之间的数据互相导入.当然可以编写程序实现,但是,程序常常需要开发环境,不方便.最方便是利用sql语言直接导入.既方便而修改也简单.以下就是导入 ...

  9. 【题解】洛谷P2822 [NOIP2016TG ]组合数问题 (二维前缀和+组合数)

    洛谷P2822:https://www.luogu.org/problemnew/show/P2822 思路 由于n和m都多达2000 所以暴力肯定是会WA的 因为整个组合数是不会变的 所以我们想到存 ...

  10. 总结的MR中连接操作

    1 reduce side join在map端加上标记, 在reduce容器保存,然后作笛卡尔积缺点: 有可能oom 2 map side join  2.1 利用内存和分布式缓存,也有oom风险 2 ...