当前位置: 首页 > news >正文

USB设备驱动代码分析(鼠标)

定义鼠标ID表

static struct usb_device_id usbmouse_as_key_id_table [] = {{USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE)}, {}
};

这段代码是用于定义一个 USB 鼠标设备的 ID 表。它使用了 Linux 内核中的 usb_device_id 结构,其中包含了 USB 设备的类、子类和协议信息。

这个ID表定义了USB接口,其类别为HID(Human Interface Device),子类别为BOOT(表示鼠标设备),协议为MOUSE(表示鼠标协议)。这个表示在Linux内核的USB驱动中使用,可以匹配对应类型的USB设备,并进行相应的操作。在最后添加一个空结构表示结束符。

中断处理函数

static void usbmouse_as_key_irq(struct urb* urb) {static unsigned char pre_val;if ((pre_val & (1 << 0)) != (usb_buf[0] & (1 << 0))) {input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1 << 0)) ? 1 : 0);input_sync(uk_dev);}if ((pre_val & (1 << 1)) != (usb_buf[0] & (1 << 1))) {input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1 << 1)) ? 1 : 0);input_sync(uk_dev);}if ((pre_val & (1 << 2)) != (usb_buf[0] & (1 << 2))) {input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1 << 2)) ? 1 : 0);input_sync(uk_dev);}pre_val = usb_buf[0];usb_submit_urb(uk_urb, GFP_KERNEL);
}
  1. pre_val是一个静态变量,用于保存上一次读取到的鼠标事件。

  2. 函数首先通过比较pre_valusb_buf[0]的位状态,检测左键、右键和中建是否发生了变化。

  3. 如果左键发生了变化,则使用input_event()函数向输入设备发送相应的事件(EV_KEY)和按键(KEY_L),如果该位为1,则表示按下;如果该位为0,则表示松开。然后通过input_sync()同步输入事件。

  4. 同样的,如果右键或中键发生了变化,也会发送相应的事件给输入设备。

  5. 最后,将当前的usb_buf[0]赋值给pre_val,以便在下一次中断时比较。

  6. 在函数最后,重新提交urb(USB Request Block)

检测设备

static int usbmouse_as_key_probe(struct usb_interface* intf, const struct usb_device_id* id) {struct usb_device* dev = interface_to_usbdev(intf);struct usb_host_interface* interface;struct usb_endpoint_descriptor* endpoint;int pipe;interface = intf->cur_altsetting;endpoint = &interface->endpoint[0].desc;uk_dev = input_allocate_device();set_bit(EV_KEY, uk_dev->evbit);set_bit(EV_REP, uk_dev->evbit);set_bit(KEY_L, uk_dev->keybit);set_bit(KEY_S, uk_dev->keybit);set_bit(KEY_ENTER, uk_dev->keybit);input_register_device(uk_dev);pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);len = endpoint->wMaxPacketSize;usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);uk_urb = usb_alloc_urb(0, GFP_KERNEL);usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);uk_urb->transfer_dma = usb_buf_phys;uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;usb_submit_urb(uk_urb, GFP_KERNEL);return 0;
}
  1. 通过传入的usb_interfaceusb_device_id参数获取USB设备对象dev

  2. 获取当前配置的接信息interface,并获得第一个端点的描述符endpoint

  3. 创建一个用于输入设备的input_dev结构体对象,并分配内存空间,保存在uk_dev变量中。

  4. 设置该input_dev对象支持产生按键时间(EV_KEY)和重复时间(EV_REP)。

  5. 设置该input_dev对象支持产生左键(KEY_L)、右键(KEY_S)和回车键(KEY_ENTER)事件。

  6. 注册该input_dev对象,使其可以被系统识别和使用。

  7. 进行硬件相关操作前,需要确定数据传输的三个要素:源、目的和长度。其中,源是USB设备的某个端点,通过函数usb_rcvintpipt()获取相应管道号存储在pipe变量中;长度通过端点描述符中的最大包长度字段wMaxPacketSize获取存储在变量len中。

  8. 使用函数usb_alloc_coherent()分配一块连续的内存作为目的缓冲区,并将物理地址保存在变量usb_buf_phys中,虚拟地址保存在变量usb_buf中。

  9. 分配一个USB Request Block(URB)作为数据传输的控制块,使用函数usb_alloc_urb()进行分配,并将URB相关信息填充:设备对象、管道号、缓冲区指针、缓冲区长度、中断处理函数usbmouse_as_key_irq等。

  10. 设备URB的传输物理地址和标志位,确保不进行DMA映射。

  11. 使用函数usb_submit_urb()提交URB,驱动数据传输过程。

断开释放

static void usbmouse_as_key_disconnect(struct usb_interface *intf) {struct usb_device *dev = interface_to_usbdev(intf);usb_kill_urb(uk_urb);usb_free_urb(uk_urb);usb_free_coherent(dev, len, usb_buf, usb_buf_phys);input_unregister_device(uk_dev);input_free_device(uk_dev);
}
  1. usb_kill_urb(uk_urb): 停止和取消正在进行的URB(USB请求块)。

  2. usb_free_urb(uk_urb): 释放之前分配的URB资源。

  3. usb_free_coherent(dev, len, usb_buf, usb_buf_phys): 释放通过usb_alloc_coherent()分配的一段连续内存区域。

  4. input_unregister_device(uk_dev): 注销输入设备,将其从系统中移除。

  5. input_free_device(uk_dev): 释放输入设备占用的资源。

插入和退出内核

static int usbmouse_as_key_init(void) {/* 2. 注册 */usb_register(&usbmouse_as_key_driver);return 0;
}static void usbmouse_as_key_exit(void) {usb_deregister(&usbmouse_as_key_driver);	
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 如何设置PowerBI报告展示在屏幕的大小?
  • 【AI赋能游戏】《黑神话:悟空》专属黑悟空无限创意生成器!(整合包分享)
  • 选择排序(直接选择排序和堆排序)
  • Go Convey测试框架入门(go convey gomonkey)
  • 特殊类设计和类型转换
  • 进阶SpringBoot之 SpringSecurity(2)用户认证和授权
  • TIM输出比较之PWM驱动直流电机应用案例
  • UEFI启动流程
  • 《黑神话:悟空》到底是用什么语言开发的
  • 从0到1构建视频汇聚生态:EasyCVR视频汇聚平台流媒体协议支持的前瞻性布局
  • 依靠 VPN 生存——探索 VPN 后利用技术
  • 【Python】列表和元组
  • 两个独立的SpringBoot项目如何互相引用?
  • 20240824给飞凌OK3588-C的核心板刷Ubuntu22.04后适配SONY索尼的HDMI OUT的机芯8530
  • 【通俗易懂】限流、降级、熔断有什么区别?
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • angular2开源库收集
  • js中forEach回调同异步问题
  • PHP CLI应用的调试原理
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • Python 反序列化安全问题(二)
  • Redux系列x:源码分析
  • SpiderData 2019年2月13日 DApp数据排行榜
  • Swoft 源码剖析 - 代码自动更新机制
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 工作手记之html2canvas使用概述
  • 人脸识别最新开发经验demo
  • 探索 JS 中的模块化
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • scrapy中间件源码分析及常用中间件大全
  • 国内开源镜像站点
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • #define,static,const,三种常量的区别
  • #pragma once与条件编译
  • #stm32整理(一)flash读写
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • #知识分享#笔记#学习方法
  • (145)光线追踪距离场柔和阴影
  • (day18) leetcode 204.计数质数
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (接口封装)
  • (未解决)macOS matplotlib 中文是方框
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)【Hibernate总结系列】使用举例
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • (转)为C# Windows服务添加安装程序
  • .NET Core 项目指定SDK版本
  • .Net Memory Profiler的使用举例
  • .NET/C# 的字符串暂存池