1、usbmouse的定义:usb鼠标既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性

struct usb_mouse {

char name[];///USB鼠标设备名称

char phys[];///路径

struct usb_device *usbdev;///USB设备

struct input_dev *dev;///Input 设备

struct urb *irq; ///urb结构体

signed char *data;///数据传输缓冲区指针

dma_addr_t data_dma;///

};

2、usbmouse的加载:

module_usb_driver(usb_mouse_driver);///系统启动时注册usb_mouse_driver

3、usbmouse的初始化:

 static int usb_mouse_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;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM; interface = intf->cur_altsetting;///获取 usb_host_interface if (interface->desc.bNumEndpoints != )/// usb鼠标端点有且只有一个控制端点,否则返回 ENODEV
return -ENODEV; endpoint = &interface->endpoint[].desc; ///usb鼠标只有一个端点,获取端点描述符
if (!usb_endpoint_is_int_in(endpoint))///检查端点是不是中断类型的输入端点
return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);////获取 pipe(),根据端点地址bEndpointAddress,中断方式,IN端点就可以得到一个pipe,然后主机就知道跟谁去通信,该如何通信
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));///获取主机和设备一次通讯的最大字节数 mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);///分配一个usb_mouse
input_dev = input_allocate_device();///初始化 input 设备
if (!mouse || !input_dev)
goto fail1; mouse->data = usb_alloc_coherent(dev, , GFP_ATOMIC, &mouse->data_dma);///为usbmouse的data 分配8个字节的空间
if (!mouse->data)
goto fail1; mouse->irq = usb_alloc_urb(, GFP_KERNEL);///申请分配 urb ,赋值给 usb_mouse 的 urb
if (!mouse->irq)
goto fail2; mouse->usbdev = dev; //设置usb鼠标设备的usb设备对象
mouse->dev = input_dev;//设备usb鼠标设备的input设备对象 if (dev->manufacturer)///枚举时候有获取到有效的厂商名
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));///复制厂商名到 name if (dev->product) { //枚举时候有获取到有效的产品名
if (dev->manufacturer) //如果也有厂商名
strlcat(mouse->name, " ", sizeof(mouse->name)); //则用空格将厂商名和产品名隔开
strlcat(mouse->name, dev->product, sizeof(mouse->name));//追加产品名到name
} if (!strlen(mouse->name)) //如果厂商和产品名都没有
snprintf(mouse->name, sizeof(mouse->name), //则直接根据厂商id和产品id给name赋值
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct)); usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //设置设备路径名
strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //追加/input0 input_dev->name = mouse->name; //输入设备的名字设置成usb鼠标的名字
input_dev->phys = mouse->phys; //输入设备的路径设置成usb鼠标的路径
usb_to_input_id(dev, &input_dev->id); //设置输入设备的bustype,vendor,product,version
input_dev->dev.parent = &intf->dev; //usb接口设备为输入设备的父设备 ////evbit 关于设备支持事件类型的 bitmap
input_dev->evbit[] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); ///BIT_MASK 找到参数值所在的 bit位,输入事件按键类型 + 相对位移
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | ///鼠标支持左键、右键、中键三个按键
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->relbit[] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); ///REL_X REL_Y 表示鼠标的位置信息 x \ Y
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | ///在已有按键的基础上加上一个边键和一个而外的键
BIT_MASK(BTN_EXTRA);
input_dev->relbit[] |= BIT_MASK(REL_WHEEL);///给相对事件加上滚轮的事件 input_set_drvdata(input_dev, mouse);///usb鼠标驱动文件作为输入设备的设备文件的驱动数据 " input_dev -> dev->driver_data = mouse " input_dev->open = usb_mouse_open; //设置输入事件的打开方法
input_dev->close = usb_mouse_close; //设置输入事件的关闭方法 usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, ///初始化 urb (中断传输方式),并指定 urb 的回调函数是 usb_mouse_irq
(maxp > ? : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);//// usb_mouse_irq --回调函数,上下文信息 -- mouse
mouse->irq->transfer_dma = mouse->data_dma;//dma数据缓冲区指向usb鼠标设备的data_dma成员
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;///没有 DMA 映射 error = input_register_device(mouse->dev);///注册设备驱动 mouse->dev
if (error)
goto fail3; usb_set_intfdata(intf, mouse);///usb 鼠标驱动文件作为 usb 接口设备的设备文件的驱动数据 ;intf->dev->driver_data = mouse ;
return ; fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, , mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}

经过probe过程,注册了输入设备则会在/dev/input/目录下会产生对应的鼠标设备节点,应用程序可以打开该节点来控制usb鼠标设备

关键函数调用顺序:

.open   =   evdev_open,///上层应用程序通过系统调用open 打开设备

static int evdev_open(struct inode *inode, struct file *file);

evdev_open_device(evdev);

input_open_device(&evdev->handle);

dev->open(dev);////---->调用usbmouse_open()

input_dev->open = usb_mouse_open; //设置输入事件的打开方法

usb_submit_urb(mouse->irq, GFP_KERNEL)

usb_mouse_irq(struct urb *urb)

input_report_key\input_report_rel\input_sync ///提交鼠标数据给input 子系统

usb_submit_urb (urb, GFP_ATOMIC);///usb设备提交urb,主机再次轮询usb设备

static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); //通过输入设备获取usb鼠标设备 mouse->irq->dev = mouse->usbdev; //设置urb设备对应的usb设备
if (usb_submit_urb(mouse->irq, GFP_KERNEL))///提交 urb ,只有打开设备的时候,才会把 urb 发送出去
return -EIO; return ;
}
 static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context; ///获取 usb 鼠标设备
signed char *data = mouse->data; ///数据传输缓冲区指针
struct input_dev *dev = mouse->dev;//输入设备
int status; switch (urb->status) {///判断 urb 传输的状态
case : /* success */ ///传输成功跳出 switch
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
} input_report_key(dev, BTN_LEFT, data[] & 0x01);////提交按键信息,data[0] 的第 0 位为 1,表示左键按下
input_report_key(dev, BTN_RIGHT, data[] & 0x02);////提交按键信息,data[0] 的第 1 位为 1,表示右键按下
input_report_key(dev, BTN_MIDDLE, data[] & 0x04);////提交按键信息,data[0] 的第 2 位为 1,表示中键按下
input_report_key(dev, BTN_SIDE, data[] & 0x08);////提交按键信息,data[0] 的第 3 位为 1,表示边键按下
input_report_key(dev, BTN_EXTRA, data[] & 0x10);////提交按键信息,data[0] 的第 4 位为 1,表示而外键按下 input_report_rel(dev, REL_X, data[]);///提交鼠标相对坐标值,data[1] 为 X 坐标
input_report_rel(dev, REL_Y, data[]);///提交鼠标相对坐标值,data[2] 为 Y 坐标
input_report_rel(dev, REL_WHEEL, data[]);///提交鼠标滚轮相对值,data[3] 为 滚轮相对值 input_sync(dev);///同步信息,表示上面的信息作为完整一帧传递给上层系统
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC);///usb设备提交urb,主机再次轮询usb设备
if (status)
dev_err(&mouse->usbdev->dev,
"can't resubmit intr, %s-%s/input0, status %d\n",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}

(注:以上图片来自麦子学院 金鑫老师的课程,在此对其辛勤付出和无私分享表示真挚的感谢!)

Linux USB驱动学习总结(三)---- USB鼠标的加载、初始化和通信过程的更多相关文章

  1. Linux内核驱动学习(三)字符型设备驱动之初体验

    Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...

  2. Linux USB驱动学习总结(一)---- USB基本概念及驱动架构

    USB,Universal Serial Bus(通用串行总线),是一个外部总线标准,用于规范电脑与外部设备的连接和通讯.是应用在PC领域的接口技术.USB接口支持设备的即插即用和热插拔功能.USB是 ...

  3. Linux内核驱动学习(八)GPIO驱动模拟输出PWM

    文章目录 前言 原理图 IO模拟输出PWM 设备树 驱动端 调试信息 实验结果 附录 前言 上一篇的学习中介绍了如何在用户空间直接操作GPIO,并写了一个脚本可以产生PWM.本篇的学习会将写一个驱动操 ...

  4. Linux内核驱动学习(二)添加自定义菜单到内核源码menuconfig

    文章目录 目标 drivers/Kconfig demo下的Kconfig 和 Makefile Kconfig Makefile demo_gpio.c 目标 Kernel:Linux 4.4 我编 ...

  5. JDBC 学习笔记(四)—— JDBC 加载数据库驱动,获取数据库连接

    1. 加载数据库驱动 通常来说,JDBC 使用 Class 类的 forName() 静态方法来加载驱动,需要输入数据库驱动代表的字符串. 例如: 加载 MySQL 驱动: Class.forName ...

  6. Yaf零基础学习总结5-Yaf类的自动加载

    Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的 ...

  7. Android三种基本的加载网络图片方式(转)

    Android三种基本的加载网络图片方式,包括普通加载网络方式.用ImageLoader加载图片.用Volley加载图片. 1. [代码]普通加载网络方式 ? 1 2 3 4 5 6 7 8 9 10 ...

  8. NeHe OpenGL教程 第三十一课:加载模型

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  9. 6_1 持久化模型与再次加载_探讨(1)_三种持久化模型加载方式以及import_meta_graph方式加载持久化模型会存在的变量管理命名混淆的问题

    笔者提交到gitHub上的问题描述地址是:https://github.com/tensorflow/tensorflow/issues/20140 三种持久化模型加载方式的一个小结论 加载持久化模型 ...

  10. Spring5.0源码学习系列之浅谈懒加载机制原理

    前言介绍 附录:Spring源码学习专栏 在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文挑一个比较重要的知识点Bean的懒加载进行学习 1.什么是懒加载? 懒加载(Lazy-ini ...

随机推荐

  1. ansible操作(一)

    ansible晋级操作之ad-hoc命令 所谓的ad-hoc命令! 如果我们敲入一些命令去比较快的完成一些事情,而不需要将这些执行的命令特别保存下来, 这样的命令就叫做 ad-hoc 命令.Ansib ...

  2. Petr and Permutations CodeForces - 987E(逆序对)

    题意: 给出一个长度为n的序列,求出是谁操作的(原序列为从小到大的序列),Peter的操作次数为3n,Alex的操作次数为7n+1 解析: 我们来看这个序列中的逆序对,逆序对的个数为偶数则操作次数为偶 ...

  3. 【agc023E】Inversions(线段树,动态规划)

    [agc023E]Inversions(线段树,动态规划) 题面 AT 给定\(a_i\),求所有满足\(p_i\le a_i\)的排列\(p\)的逆序对数之和. 题解 首先如何计算排列\(p\)的个 ...

  4. 【UOJ#80】二分图最大权匹配(KM)

    题面 UOJ 题解 模板qaq #include<iostream> #include<cstdio> #include<cstdlib> #include< ...

  5. BZOJ 3143 游走 | 数学期望 高斯消元

    啊 我永远喜欢期望题 BZOJ 3143 游走 题意 有一个n个点m条边的无向联通图,每条边按1~m编号,从1号点出发,每次随机选择与当前点相连的一条边,走到这条边的另一个端点,一旦走到n号节点就停下 ...

  6. BZOJ 2738 子矩阵第k大 | 二维树状数组 整体二分 分治

    BZOJ 2738 "矩阵乘法"(子矩阵第k大) 题意 给出一个矩阵,多次询问子矩阵中第k大的数是多少. 题解 我做这道题之前先照着这道题出了一道题,是这道题的一维版本,在这里:h ...

  7. 洛谷 P2805 [NOI2009]植物大战僵尸 解题报告

    P2805 [NOI2009] 植物大战僵尸 题目描述 Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏.Plants(植物)和Zombies(僵尸)是游戏的主角,其中Plan ...

  8. rovio视觉里程计的笔记

    rovio是一个紧耦合,基于图像块的滤波实现的VIO. 他的优点是:计算量小(EKF,稀疏的图像块),但是对应不同的设备需要调参数,参数对精度很重要.没有闭环,没有mapping thread.经常存 ...

  9. 四、java面向对象编程_2

    目录 六.对象的创建和使用 七.this关键字 八.static关键字 九.package和import语句 十.类的继承 十一.访问控制 十二.方法的重写 十三.super关键字 十四.继承中的构造 ...

  10. docker:轻量级图形页面管理之Portainer

    docker:轻量级图形页面管理之Portainer 原创甘兵2018-03-05 14:26:56评论(8)2586人阅读   1.介绍 docker 图形化管理提供了很多工具,有Portainer ...