声明:本文章是看完韦东山老师的usb鼠标驱动视频所写的关于usb鼠标的驱动,因此如果有相关内容与其他网友相同,敬请原谅。同时我还是想说本文只是总结自己的学习所得,同时也将自己所学到的知识写下来,所以如果这篇文章对你有帮助,那是我的荣幸。

在介绍驱动程序前我想向大家介绍一下usb_bus_type(usb总线驱动类),内核中有不同的总线类型,不同的总线有不同的匹配方式,如我们前面所学的platform_bus_type是使用名字来匹配的,而这里要讲的usb_bus_type的匹配是通过id_table来匹配的,但是各种总线的匹配流程大致还是一样的。因为要想将设备和驱动通过总线连接起来就不可避免的用到了match函数。就像你要相亲你就要将你的要求都写出来,而女方也要将自己对另一半的要求写出来,然后你们双方都把各自的请求交给婚介所,而婚介所所做的事就是将你的要求与每一个女士的请求进行比较,注意这里就用到了比较(match),当他们发现有一个女士满足你的要求,而你也正好满足这个女士的要求时,他就会对你说“给你匹配到了合适的女士”,而同时他也会对那位女士说“给你匹配到了合适的男士”,然后就安排你们相亲了。而我们的总线——设备——驱动模型就类似这个相亲模型。其中你是设备,那些女士是驱动,而婚介所就是总线了。

有了上面的例子,我们结合这个例子分析一下这个匹配流程:

上面这幅图就将,usb_bus_type的框架大致描绘出来了,下面我们详细的说一下。如上图所示,总线模型的最主要部分就是位于上层的总线,总线中有一个match函数,他会将通过usb_new_device向上注册的usb_interface和通过usb_register向上注册的usb_driver中的id_table一一比较(这个过程就类似于你和那些女士分别向婚介所投递个人信息),当发现设备和驱动匹配时,他就会调用的driver中的probe函数(这就相当于当发现你与其中一位女士匹配时就会通知你们相亲)。很多朋友可能会问“两个人相亲他们匹配的可能是性格,三观,收入等等,而设备和驱动他们匹配的是什么那?”,我们说了,不同的总线类型匹配的标准不一样,但总要有个可以匹配的吧。是的,在usb总线类型中,我们匹配的是id_table,可能很多人会问可以讲的细点吗?就像你说的相亲的时候匹配三观,可三观太大了,可以细分一下吗?,这个是可以的我们打开id_table的代码就会发现有:

<span style="color:#333300">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 */
};</span>
#define USB_INTERFACE_INFO(cl,sc,pr) \.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

上面就是详细的比较类型了,他们有接口类,接口子类,以及接口协议,三部分组成。不过你还可以加附加条件比如:

{USB_DEVICE(设备ID,产品ID)},

这样就可以在原有的基础上缩小范围了。

而具体的代码描述为:

在hub.c的hub_port_connect_change函数中,有udev = usb_alloc_dev(hdev, hdev->bus, port1);而usb_alloc_dev就是分配一个设备结构体,而这个设备中都分配了什么那?我们可以进去看一下,我们可以在usb_alloc_dev函数中看到这样的代码:

dev->dev.bus = &usb_bus_type;

这行代码就定义了usb_bus_type,那么我们进去看看这个总线中又定义了什么:

struct bus_type usb_bus_type = {.name =        "usb",.match =   usb_device_match,     //非常重要的match函数.uevent =  usb_uevent,.suspend =  usb_suspend,.resume =  usb_resume,
};

我们可以从中看到一个match函数,我们在前面说过,虽然不同的总线他们匹配的数据可能不一样,但是他们的大致过程是一样的。下面我们进入usb_device_match函数中:

static int usb_device_match(struct device *dev, struct device_driver *drv)intf = to_usb_interface(dev);usb_drv = to_usb_driver(drv);id = usb_match_id(intf, usb_drv->id_table);

通过上面的代码我们就可以知道他们所匹配的是dev的信息和id_table的 信息。

有了上面的了解,我们现在来写一个简单的usb鼠标驱动,我们的目标是:当按下左键时上报按键L,当按下右键时上报S,中键ENTRR。

从上面的目标可以看出我们需要用到输入子系统来上报按键。从usb_bus_type中我们知道usb的总线和设备部分已经写好,而我们可以做的就是写出驱动程序,下面我们开始写驱动程序。与其他的驱动程序一样,我们还是先搭好这个驱动程序的框架,然后在向其中填入想要做的事情的代码,而usb驱动的框架为:

1.分配/设置usb_driver结构体

2.在入口函数中注册这个结构体,在出口函数中注销这个结构体

而详细的代码为:

static struct usb_driver usb_mouse_drv = {        /* 分配设置usb_driver结构体 */.name      = "usbmouse",.probe      = usb_mouse_probe,.disconnect  = usb_mouse_disconnect,.id_table   = usb_mouse_id_table,
};int usb_drv_init(void)
{usb_register(&usb_mouse_drv);             /* 注册usb_driver结构体 */return 0;
}void usb_drv_exit(void)
{usb_deregister(&usb_mouse_drv);/* 注销usb_driver结构体 */
}

通过上面的代码我们可以看出,usb_driver结构体中有id_table(用于匹配设备),probe函数(当匹配成功时调用),disconnect函数(当匹配的设备离开时调用),注意:这三个是必不可少的。而其他的选项则是可以选择的。在上面我已经说过id_table了。所以这里并不详细说,只是介绍一下他的内容,代码如下:

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 */
};

而其中USB_INTERFACE_INFO是一个宏:

/*** USB_INTERFACE_INFO - macro used to describe a class of usb interfaces * @cl: bInterfaceClass value* @sc: bInterfaceSubClass value* @pr: bInterfaceProtocol value** This macro is used to create a struct usb_device_id that matches a* specific class of interfaces.*/
#define USB_INTERFACE_INFO(cl,sc,pr) \.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

通过介绍上面的注解可以看出,他提供的是 接口类,接口子类,和接口协议。所以你可以提供相应的这三项来进行匹配。同时如果你对厂家ID和设备ID有其他的要求也可以通过id_table中的USB_DEVICE选项设置。

写好id_table 后设备就可以与驱动程序进行匹配了,那么我们接下来就应该写匹配成功后要进入的probe函数了。那么进入probe后我们应该干什么那?我们要想一下自己的目的是通过按下鼠标实现按键功能,既然是按键功能就要用到输入子系统了,那么输入子系统的框架又是什么那?

1.分配 input_dev结构体

2.设置input_dev结构体

3.注册input_dev结构体

4.硬件相关的操作

此处硬件相关的操作与以往不同,以前的按键,触摸屏是通过读寄存器或者ADC值,而现在的硬件相关操作是在usb驱动框架中的操作,所以此处应当由usb总线驱动提供usb读写函数来进行数据传输。

下面是probe函数的代码:

int usb_mouse_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;interface = intf->cur_altsetting;endpoint = &interface->endpoint[0].desc;/*1   分配一个input_dev结构体 */uk_dev = input_allocate_device();/*2   设置 *//*2.1 产生哪类事件 */set_bit(EV_KEY,uk_dev->evbit);    //产生按键类事件set_bit(EV_REP,uk_dev->evbit);    //产生重复类事件/*2.2 产生这类事件中的那个 */set_bit(KEY_L,uk_dev->keybit);   //按键类中的按键Lset_bit(KEY_S,uk_dev->keybit);   //按键类中的按键Sset_bit(KEY_ENTER,uk_dev->keybit); //按键类中的按键ENTER/*3  注册 */input_register_device(uk_dev);/*4 硬件相关设置:通过使用USB设备总线获取读写函数 *//* 数据传输三要素:源,目的,长度 *//* 源:USB设备的某个端点 */pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);/* 长度 */len = endpoint->wMaxPacketSize;/* 目的:  */usb_buf = usb_buffer_alloc(dev,len,GFP_KERNEL,&usb_buf_phys);/* 使用三要素 *//* 分配一个urb(USB request block) */uk_urb = usb_alloc_urb(0,GFP_KERNEL);/* 设置使用urb */usb_fill_int_urb(uk_urb, dev, pipe, usb_buf,len,usb_mouse_irq, NULL, endpoint->bInterval);uk_urb->transfer_dma = usb_buf_phys;uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/* 使用urb */usb_submit_urb(uk_urb,GFP_KERNEL);return 0;
}

上面的程序已经说明了input_dev的框架,只是有些同学可能会问这代码中的第四部分是怎么回事?

这就要介绍另一个非常有用的结构体USB请求块(USB request block,URB),URB是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,与网络设备驱动中的sk_buff结构体类似,是USB主机与设备之间传输数据的封装。

一个urb包含了执行usb传输所需要的所有信息。当要进行数据传输时,需要分配一个urb结构体,对其进行初始化,然后将其提交给usb核心。USB核心对urb进行解析,将控制信息提交给主机控制器,由主机控制器负责数据到设备的传输。这时,驱动程序只需等待,当数据回传到主机控制器后,会转发给USB核心,唤醒等待的驱动程序,由驱动程序完成剩下的工作。

更为具体地说,Linux中的设备驱动程序只要为每一次请求准备一个urb结构体,然后把它填充好,就可以调用函数usb_submit_urb()提交给USB核心。然后USB核心将urb传递给USB主机控制器,最终传递给USB设备。USB设备获得urb结构体后,会解析这个结构体,并以相反的路线将数据返回给Linux内核。

注:上面的描述是从百度百科抄来的,我看他写的比较简单就抄下来分享给大家了。

写完这些我们就应该完成usb_fill_int_urb函数中的usb_mouse_irq函数了,也许有些朋友可能会问这是中断函数吗?我的回答是这是中断函数,只不过这是主机控制器产生的中断而不是从机设备:

像上面这个示意图一样,主机控制器不断的查询usb设备获得数据后将数据放入buffer中,然后主机控制器就会产生一个中断从而使是上面的这个usb_mouse_irq函数被调用。

而在usb_mouse_irq函数中要做的就是将将获得的按键值上报:

static void usb_mouse_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];/* 从新提交urb */usb_submit_urb(uk_urb,GFP_KERNEL);
}

上面就是这个驱动的主要部分了,但是当设备离开时会调用相应的disconnect函数,下面我们来写disconcert函数:

void usb_mouse_disconnect(struct usb_interface *intf)
{struct usb_device *dev = interface_to_usbdev(intf);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);}

完成disconnect函数,这个驱动程序就算完成了。

而这里还有几篇文章是我参考过得:

ARM-Linux开发之USB驱动鼠标控制 :这篇文章也是按韦东山老师的方式所写的驱动 

嵌入式Linux USB驱动开发之教你一步步编写USB驱动程序

嵌入式linux下usb驱动开发方法--看完少走弯路 :这篇文章虽然文字不多,对学习usb驱动的很有帮助

嵌入式Linux驱动学习之路(二十)USB设备驱动:这篇文章是对老师上课内容的总结,并有完整的代码

Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结:这篇文章虽然不是讲的usb鼠标,但就usb驱动的很多的原理做了介绍

嵌入式linux下如何使用usb键盘和鼠标

:这篇文章是一篇很好的就是usb鼠标的文章。

嵌入式Linux —— usb鼠标驱动相关推荐

  1. 嵌入式linux usb wifi驱动移植

    文档名称:嵌入式linux usb wifi驱动移植 版本历史 版本号        时间        内容 v1.0b001        2012-6-18        初始版本,介绍在嵌入式 ...

  2. Linux USB鼠标驱动入门以及处理流程

    1 需要 禁用掉 CONFIG_USB_HID,不然下面的驱动不会被探测. 2 下面的驱动模块代码只打印了鼠标左键是否被按下,左键没有被按下打印0,左键被按下打印1. 3 参考了这里 4 内核版本 4 ...

  3. 嵌入式Linux USB WIFI驱动的移植

    硬件平台:飞思卡尔MX258开发板 操作系统:Linux2.6.31 WIFI:    RT2860 USB WIFI模组 交叉编译环境:gcc version 4.1.2 调试步骤: 第一步:测试U ...

  4. 嵌入式Linux USB WIFI驱动的移植 1

    硬件平台:飞思卡尔MX258开发板 操作系统:Linux2.6.31 WIFI:    RT2860 USB WIFI模组 交叉编译环境:gcc version 4.1.2 调试步骤: 第一步:测试U ...

  5. 嵌入式linux usb wifi移植[s3c6410平台原创]

    文档名称:嵌入式linux usb wifi驱动移植 版本历史 v1.0b001 2012-6-18 linuxusb wifi 嵌入式linux usb wifi移植参考文档 ■ realARM 6 ...

  6. 十五、Linux驱动之USB鼠标驱动

    1. 如何编写USB鼠标驱动 结合十四.Linux驱动之USB驱动分析中的分析,我们开始写一个USB鼠标驱动.      USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动, ...

  7. Linux下的USB总线驱动(03)——USB鼠标驱动 usbmouse.c

    USB鼠标驱动 usbmouse.c 原文链接:http://www.linuxidc.com/Linux/2012-12/76197p7.htm drivers/hid/usbhid/usbmous ...

  8. 嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记

    Linux USB设备驱动 二十.Linux USB设备驱动 20.1 USB简介 20.1.1 USB2.0总线拓扑 20.1.2 USB总线枚举和设备布局 20.1.3 USB数据传输 20.1. ...

  9. Linux usb设备驱动

    原文地址:http://blog.csdn.net/chenjin_zhong/article/details/6329316 1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linu ...

  10. Linux usb设备驱动详解

    1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linux内核几乎支持所有的usb设备,包括键盘,鼠标,打印机,modem,扫描仪.Linux的usb驱动分为主机驱动与gadget驱动 ...

最新文章

  1. 对RTMP视频流进行BitmapData.draw()出错的解决办法
  2. sql中的并、交、差
  3. (优秀文章保存)Quartz优秀文章保存
  4. echarts 饼图移动端_VUE移动端项目中使用Echart
  5. 好险!一入职,就遇到MySQL这么大Bug!差点背锅走人~
  6. Atitit.每周末总结 于每周一计划日程表 流程表 v8 -------------import 上周遗漏日志补充 检查话费 检查流量情况 Crm问候 Crm表total and 问候
  7. codeblocks下载安装教程
  8. librdkafka
  9. IE下载vsix插件踩坑
  10. 基于3DGIS的智慧“云”综合产业园区建设
  11. JZOJ 3337. 【NOI2013模拟】wyl8899的TLE【暴力】
  12. python openpyxl怎么将数组写入excel_Python-使用openpyxl模块写入Excel文件
  13. 面部识别软件揭示家族成员相似性
  14. 数据库系统期末总结(三)(往届试卷2018年12月A卷、B卷、E卷,2019年5月A卷,选择题终篇)
  15. Java实现 蓝桥杯 历届试题 带分数
  16. jy-12-SPRINGMYBATIS02——云笔记02-刘苍松
  17. vulnhub-Brainpan (考点:windows exe和linux elf下的两种缓冲区溢出)
  18. python代码混淆工具,Python版代码混淆工具
  19. 计算器(只能进行加减乘除,其他算法可以自己加)
  20. input实现文字超出省略号功能

热门文章

  1. 互联网产品设计思路参考
  2. 数博会“十佳大数据案例”发布,我们入选了。
  3. 大数据营销在电商领域的应用案例
  4. 【顺序表】SqList *L是什么意思
  5. java web前端邮件,JavaMail:在Web应用上完整接收、解析复杂邮件(转)
  6. 计算机网络上有个红叉没无线,电脑无线网络连接不上显示红叉
  7. eeepc linux 窗口管理器,EeePC安装Windows 7全教程 全机型适用
  8. OceanBase-概述
  9. 安徽自招计算机基础考试试题,点划自招|东南大学、中国药大2017年自主招生考情揭秘,附真题。...
  10. 软件工程导论——课堂学习笔记