linux 下usb 鼠标的驱动基本上属于USB 协议中HID 设备的中断通信的应用。代码vendor\mstar\kernel\linaro\drivers\hid\usbhid\usbmouse.c,下面一起学习usb 鼠标驱动,usb 键盘vendor\mstar\kernel\linaro\drivers\hid\usbhid\usbkbd.c 后续在学习。

USB mouse 设备结构

struct usb_mouse {char name[128];   //  名称,一般存储制造商名称char phys[64];struct usb_device *usbdev;   //  usb 设备模型struct input_dev *dev;   //  输入设备struct urb *irq;   //   用于usb 设备通信的urb 模块signed char *data;   //  USB  鼠标事件的buffer,存储鼠标的左键,右键,滑轮,坐标事件dma_addr_t data_dma;
};

USB mouse 初始化

static struct usb_driver usb_mouse_driver = {.name      = "usbmouse",.probe      = usb_mouse_probe,.disconnect  = usb_mouse_disconnect,.id_table   = usb_mouse_id_table,
};module_usb_driver(usb_mouse_driver);

通过宏module_usb_driver将usb 鼠标设备usb_mouse_driver注册下去

#define module_usb_driver(__usb_driver) \module_driver(__usb_driver, usb_register, \usb_deregister)
打开宏module_usb_driver可以看到module_init和module_exit 分别安装,卸载usb_mouse_driver 驱动
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

就这样,usb mouse 驱动就被注册到usb 总线上去了。

USB 设备的匹配

static struct usb_device_id usb_mouse_id_table [] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },{ }  /* Terminating entry */
};MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);

上一步完成了驱动的配置,传入了id_table,此处也即usb_mouse_id_table,里面记载了支持的设备列表,USB_INTERFACE_INFO 来定义一类USB鼠标设备。通过这个信息就可以完成驱动和设备的匹配,成功之后就会调用usb_mouse_driver 里面的probe。

USB mouse 设备探测

驱动和设备匹配成功之后就会调用driver 的探测函数,主要任务有:

  1. 获取接口; // 判断端点是否为中断模式
  2. 申请一个input 设备并填充;
  3. 创建管道,设置大小,再申请缓存区;
  4. 申请一个urb 用于与usb 设备通信;
  5. 注册input 设备;
  6. 设置接口数据;
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{//  USB 接口描述符被当成参数传入(USB 的一个接口表示一个功能)// 获取USB 设备描述struct usb_device *dev = interface_to_usbdev(intf);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;// 判断接口是否合法,根据HID规范,鼠标只有一个端点(且不包含端点0)if (interface->desc.bNumEndpoints != 1)return -ENODEV;//  获取端点0的描述符endpoint = &interface->endpoint[0].desc;//  判断端点类型是否合法,HID 规范,鼠标唯一的端点为中断端点,因为鼠标是中断控制if (!usb_endpoint_is_int_in(endpoint))return -ENODEV;//  创建中断管道(in),鼠标属于中断控制pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);/* 返回该端点能够传输的最大的包长度,鼠标的返回的最大数据包为4个字节。*/  //初始化URB的时候会用到这个长度,缓冲区的长度要依照maxp来决定,最大不能超过8maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));// 为mouse 申请内存//mouse结构的主要作用是赋值给usb_interface中的一个属性//以便于触发其它函数的时候通过usb_interface中的这个属性就可以知道相关信息//usb_interface中的这个属性是专门为了储存用户需要的数据的mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);// 创建input设备input_dev = input_allocate_device();if (!mouse || !input_dev)goto fail1;//  为urb 传输申请内存,data 指向该地址空间,初始化urb 缓存区,第四个参数为dma相关mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);if (!mouse->data)goto fail1;//  申请urb mouse->irq = usb_alloc_urb(0, GFP_KERNEL);if (!mouse->irq)goto fail2;// 为mouse 的usbdev,dev 赋值mouse->usbdev = dev;mouse->dev = input_dev;//  为mouse 那么赋值制造商名称if (dev->manufacturer)strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));if (dev->product) {if (dev->manufacturer)strlcat(mouse->name, " ", sizeof(mouse->name));strlcat(mouse->name, dev->product, sizeof(mouse->name));}if (!strlen(mouse->name))snprintf(mouse->name, sizeof(mouse->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));input_dev->name = mouse->name;input_dev->phys = mouse->phys;// 从dev 设备中获取总线类型,设备id,厂商id,版本号,设置父设备usb_to_input_id(dev, &input_dev->id);input_dev->dev.parent = &intf->dev;// 设置输入设备所支持的事件信息// 支持相对坐标和事件input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);//  记录支持的按键值input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);// 支持的相对坐标为鼠标移动坐标和滑轮坐标input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |BIT_MASK(BTN_EXTRA);input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);// 将mouse 传入input_dev,方便通过input_dev 获取全部的mouse 信息input_set_drvdata(input_dev, mouse);// 设置输入设备的open,close函数input_dev->open = usb_mouse_open;input_dev->close = usb_mouse_close;//  填充urb 模块,mouse 作为上下文被设置下去,另外usb_mouse_irq函数为回调// 当usb mouse 有事件产生时,回调被调用usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);//  mouse->irq 就是urb,如下设置DMA传输相关,当flag为URB_NO_TRANSFER_DMA_MAP时// 表示优先使用transfer_dma,而不是transfer buffermouse->irq->transfer_dma = mouse->data_dma;mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//  注册input 设备error = input_register_device(mouse->dev);if (error)goto fail3;// 设置mouse 到 usb interface 中usb_set_intfdata(intf, mouse);return 0;fail3:  usb_free_urb(mouse->irq);
fail2:  usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
fail1:  input_free_device(input_dev);kfree(mouse);return error;
}

usb mouse 设备断开

static void usb_mouse_disconnect(struct usb_interface *intf)
{struct usb_mouse *mouse = usb_get_intfdata (intf);//  断开mouse 和usb interfaceusb_set_intfdata(intf, NULL);if (mouse) {usb_kill_urb(mouse->irq);  // kill urb 模块input_unregister_device(mouse->dev);  //  卸载input设备usb_free_urb(mouse->irq);  //  释放urb 模块// 释放申请的bufferusb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);//  释放mousekfree(mouse);}
}

usb mouse 设备打开

static int usb_mouse_open(struct input_dev *dev)
{struct usb_mouse *mouse = input_get_drvdata(dev);// 设置urb->dev ,urb submit 时需要使用该usb 设备mouse->irq->dev = mouse->usbdev;// urb 提交,之后urb 的通信通道完成启动if (usb_submit_urb(mouse->irq, GFP_KERNEL))return -EIO;return 0;
}

usb mouse 设备关闭

static void usb_mouse_close(struct input_dev *dev)
{struct usb_mouse *mouse = input_get_drvdata(dev);//  断开urb 通信usb_kill_urb(mouse->irq);
}

usb mouse 回调事件处理

在usb mouse 产生事件时,usb_mouse_irq回调会被触发

static void usb_mouse_irq(struct urb *urb)
{struct usb_mouse *mouse = urb->context;  //  mouse 为上下文signed char *data = mouse->data;  // buffer 中存储的事件信息struct input_dev *dev = mouse->dev;  //  int status;//  判断urb 通信是否成功switch (urb->status) {case 0:            /* success */break;case -ECONNRESET:    /* unlink */case -ENOENT:case -ESHUTDOWN:return;/* -EPIPE:  should clear the halt */default:        /* error */goto resubmit;   // 产生错误,重新提交urb}// data 第一个字节代表左右按键,中间按键,都会触发input_report_key(dev, BTN_LEFT,   data[0] & 0x01);input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);input_report_key(dev, BTN_SIDE,   data[0] & 0x08);input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);// 第二个字节代表X的坐标input_report_rel(dev, REL_X,     data[1]);//  第三个字节代码Y的坐标input_report_rel(dev, REL_Y,     data[2]);//  第四个字节代表滑轮的当前值input_report_rel(dev, REL_WHEEL, data[3]);input_sync(dev);
resubmit:status = usb_submit_urb (urb, GFP_ATOMIC);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);
}

usb 键盘/鼠标协议说明
https://blog.csdn.net/peakguy/article/details/49476099
https://www.cnblogs.com/vonly/p/7403823.html
https://blog.csdn.net/jiujiujiuqiuqiuqiu/article/details/47277685

鼠标的协议

鼠标发送给PC的数据每次4个字节
BYTE1 BYTE2 BYTE3 BYTE4
定义分别是:
BYTE1 –
|–bit7: 1 表示 Y 坐标的变化量超出-256 ~ 255的范围,0表示没有溢出
|–bit6: 1 表示 X 坐标的变化量超出-256 ~ 255的范围,0表示没有溢出
|–bit5: Y 坐标变化的符号位,1表示负数,即鼠标向下移动
|–bit4: X 坐标变化的符号位,1表示负数,即鼠标向左移动
|–bit3: 恒为1
|–bit2: 1表示中键按下
|–bit1: 1表示右键按下
|–bit0: 1表示左键按下
BYTE2 – X坐标变化量,与byte的bit4组成9位符号数,负数表示向左移,正数表右移。用补码表示变化量
BYTE3 – Y坐标变化量,与byte的bit5组成9位符号数,负数表示向下移,正数表上移。用补码表示变化量
BYTE4 – 滚轮变化。
由于手上没有USB鼠标,对BYTE1的4-7位没有测试,对于BYTE2 BYTE3做个测试,BYTE1的4-7全为0的时候,BYTE2 BYTE3的正负表示鼠标移动方向

键盘的协议

键盘发送给PC的数据每次8个字节
BYTE1 BYTE2 BYTE3 BYTE4 BYTE5 BYTE6 BYTE7 BYTE8
定义分别是:
BYTE1 –
|–bit0: Left Control是否按下,按下为1
|–bit1: Left Shift 是否按下,按下为1
|–bit2: Left Alt 是否按下,按下为1
|–bit3: Left GUI 是否按下,按下为1
|–bit4: Right Control是否按下,按下为1
|–bit5: Right Shift 是否按下,按下为1
|–bit6: Right Alt 是否按下,按下为1
|–bit7: Right GUI 是否按下,按下为1
BYTE2 – 暂不清楚,有的地方说是保留位
BYTE3–BYTE8 – 这六个为普通按键
键盘经过测试。
例如:键盘发送一帧数据 02 00 0x04 0x05 00 00 00 00
表示同时按下了Left Shift + ‘a’+‘b’三个键

Event 事件上抛

回调里面解析完 usb mouse 事件之后,通过input_report_key丢给linux 的input 系统来处理。至此usb mouse 完成了事件的获取没解析和转发。另外input子系统还有以下input event 转发接口,在其它输入设备中可以用到,例如usb keyboard。

void input_report_key(struct input_dev *dev, unsigned int code, int value)
void input_report_rel(struct input_dev *dev, unsigned int code, int value)
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
void input_report_switch(struct input_dev *dev, unsigned int code, int value)
void input_sync(struct input_dev *dev)
void input_mt_sync(struct input_dev *dev)

深入分析linux input 子系统,
https://blog.csdn.net/yueqian_scut/article/details/48792939

LInux usb mouse(鼠标)驱动分析相关推荐

  1. Linux USB 3.0驱动分析—UAC驱动分析

    转自 https://www.cnblogs.com/wen123456/p/14281917.html 因为项目里面有USB音频外设,所以需要分析一下UAC驱动. USB Audio Class,U ...

  2. linux pl320 mbox控制器驱动分析-(3) pl320驱动代码分析

    linux pl320 mbox控制器驱动分析-(3)pl320驱动代码分析 1 pl320 mbox控制器宏定义 2 初始化接口 3 ipc_handler mbox中断处理函数 4 数据的收发 4 ...

  3. linux pl320 mbox控制器驱动分析 - (1) pl320手册分析

    linux pl320 mbox控制器驱动分析 1 pl320简介 1.1 pl320用途 1.2 pl320 IPCM 由以下部分组成: 1.3 pl320 IPCM可配置的参数 1.4 功能操作 ...

  4. Linux 4.1最新内核usb与hid驱动分析记录

    Linux hid驱动分析记录 1.hid_add_device实现,在hid/hid-core.c中,匹配特殊驱动相关部分分析 该函数是在传输驱动probe中调用,例如usbhid中,也就是说usb ...

  5. linux内核SPI总线驱动分析(一)

    下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) SPI总线驱动分析   1 SPI概述     ...

  6. linux 串口驱动 atmel_set_mctrl何时调用,linux uart serial使用驱动分析

    uart tty serial 驱动分析 内核版本3.14.23 以atmel为例: 起点: static int __init atmel_serial_init(void) { int ret; ...

  7. linux USB大容量设备驱动入门之读取U盘容量

    主要参考了以下资料: usbmassbulk_10.pdf , usbmass-ufi10.pdf, SCSI Commands Reference Manual,spc-3.pdf 以下驱动模块通过 ...

  8. USB WiFi网卡驱动分析--经典

    初始化: 在Linux下,驱动大多都是module,可以被自由的装载卸载.Rt73 usb wireless驱动也不例外,是一个可独立编译的module.Module一般都是由module_init入 ...

  9. cmd52命令发送 mmc_乾坤合一~Linux SD/MMC/SDIO驱动分析(上)

    一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆 ...

  10. linux查询引脚功能复用,linux pinmux 引脚多路复用驱动分析与使用

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/code_style/article/d ...

最新文章

  1. Apache+PHP+MySQL+phpMyAdmin+WordPress搭建
  2. entity.Database.SqlQuery() 和entity.Database.SqlCommand()
  3. TiDB 在金融关键业务场景的实践
  4. 详细设计说明书示例_专利说明书常用句型汇总
  5. [进阶] --- Python3 异步编程详解(史上最全篇)
  6. 基于深度学习的目标检测技术演进:从目标检测到人脸检测
  7. js符号转码_JS 字符串编码函数(解决URL特殊字符传递问题):escape()、encodeURI()、encodeURIComponent()区别详解...
  8. 2020年中职学计算机有前途吗,2020年南昌中专计算机专业都学什么
  9. 看不到日志_迷之 crontab 异常:不运行、不报错、无日志?
  10. 异步流程控制 java_Javascript异步流程控制之串行执行详解
  11. 如何识别媒体偏见_面部识别技术存在偏见:为什么我们不应该盲目相信新技术
  12. 堆排序算法——C/C++
  13. 教你如何布置家庭影院
  14. 文件共享服务器热备,两台云服务器如何实现双机热备
  15. 删除Linux虚拟机中的/dev/sdb磁盘步骤
  16. php如何截取出视频中的指定帧作为图片
  17. 坦克大战游戏Java网络版设计
  18. 数据结构(12)----图(遍历、最小生成树、easyX可视化)
  19. Altium脚本开发
  20. 大地坐标和高斯平面坐标转换

热门文章

  1. Ogre 正常初始化(0xc0150002)失败 解决办法
  2. python爬取微博用户信息_Python爬取新浪微博用户信息及内容
  3. 在线抽签html,抽签网页板代码
  4. VHDL——分频器设计
  5. SAP中与物料BOM有关的表关联
  6. matlab负反馈传函,已知负反馈系统开环传函求阶跃传函
  7. mysql替换占位符_【占位符替换】替换String中的占位符标志位{placeholder}
  8. 真机试用深度linux,推荐使用
  9. 【概率论】边缘分布和联合分布
  10. 超声前置放大器原理是什么意思,前置放大器和功放区别