注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

LINUX驱动学习——USB驱动(八)之USB键盘(1)  

2011-01-22 15:27:45|  分类: USB系列 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

接着鼠标系列,我们开始键盘,说一句没用的话,自己做鼠标其实是没有什么现实意义的,因为市场上有N多的现成的鼠标的硬件芯片,而且特别的便宜,你写这个怎么也比不过硬件直接实现吧,但是,我们学习这个目的不是为了做鼠标,而是为了学习USB的协议和实现USB的入门,OK,唐僧咧。说今天的正事。

USB键盘的驱动和上文的鼠标没有什么实质的区别,最重要的还是得好好看协议,这几天我看了N次了,有浅浅的定位看,有认真的从头翻,没办法,头大也得看,不然真不明白那些字符啥意思。大老外真是有病,为什么不学习中文捏,太没素质咧。

USB的设备描述符没有什么变化,只是为了区别鼠标,要把产品ID改一下,这个没办法,前面不是讲了,你不改,那么还是加载的鼠标的驱动。什么,没记住,那你回头看前面的鼠标系列吧,不带这样的。今天我可是加班啊,不带这么欺侮人滴啊。

区别是在配置描述符上:二者的长度不同,因为键盘多一个输出端点的长度,前方讲过,端点描述符在标准请求里,在全速和低速的情况下不允许单独返回,只能跟在配置描述符后返回,要记清楚,没办法,这个东西就是这么规定,你问为什么,我也没办法回答。

所以配置描述符的长度就增加了一个7字节的长度,不过因为是sizeof实现的长度计算,所以这里不用改也不会出错。

接口描述符里,由于多了一个输出端点并且是键盘所以要改两个地方,一个是bInterfaceProtocol和 bNumEndpoints,前者为1后者为2;这都可以到HID1.1协议中这个都很清楚,对着一个个找就可以了,这里就不再细说。

前面说了,这里增加了一个输出端点描述符,不过记得输出端点的D7应该为0,为什么,看前面的bmRequestType介绍去,说多少遍了,要多看协议。OK?其它的就简单了,直接把输入的拷过来,改下就可以了。

下面还是说报告描述符,因为键盘支持的杂儿太多,所以估计也就这里键盘和鼠标的描述符有一些区别,真正的区别。

code uint8 ReportDescriptor[]=

{

 //每行开始的第一字节为该条目的前缀,前缀的格式为:

 //D7~D4:bTag。D3~D2:bType;D1~D0:bSize。以下分别对每个条目注释。

 

 //这是一个全局(bType为1)条目,将用途页选择为普通桌面Generic Desktop Page(0x01)

 //后面跟一字节数据(bSize为1),后面的字节数就不注释了,

 //自己根据bSize来判断。

 0x05, 0x01, // USAGE_PAGE (Generic Desktop)

 

 //这是一个局部(bType为2)条目,说明接下来的集合用途用于键盘

 0x09, 0x06, // USAGE (Keyboard)

 

 //这是一个主条目(bType为0)条目,开集合,后面跟的数据0x01表示

 //该集合是一个应用集合。它的性质在前面由用途页和用途定义为

 //普通桌面用的键盘。

 0xa1, 0x01, // COLLECTION (Application)

 

 //这是一个全局条目,选择用途页为键盘(Keyboard/Keypad(0x07))

 0x05, 0x07, //     USAGE_PAGE (Keyboard/Keypad)

 

 //这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左Ctrl键。

 //具体的用途值可在HID用途表中查看。

 0x19, 0xe0, //     USAGE_MINIMUM (Keyboard LeftControl)

 

 //这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右GUI键。

 0x29, 0xe7, //     USAGE_MAXIMUM (Keyboard Right GUI)

 

 //这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)

 //最小为0。因为我们这里用Bit来表示一个数据域,因此最小为0,最大为1。

 0x15, 0x00, //     LOGICAL_MINIMUM (0)

 

 //这是一个全局条目,说明逻辑值最大为1。

 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

 

 //这是一个全局条目,说明数据域的数量为八个。

 0x95, 0x08, //     REPORT_COUNT (8)

 

 //这是一个全局条目,说明每个数据域的长度为1个bit。

 0x75, 0x01, //     REPORT_SIZE (1)

 

 //这是一个主条目,说明有8个长度为1bit的数据域(数量和长度

 //由前面的两个全局条目所定义)用来做为输入,

 //属性为:Data,Var,Abs。Data表示这些数据可以变动,Var表示

 //这些数据域是独立的,每个域表示一个意思。Abs表示绝对值。

 //这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。

 //bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。

 0x81, 0x02, //     INPUT (Data,Var,Abs)

 

 //这是一个全局条目,说明数据域数量为1个

 0x95, 0x01, //     REPORT_COUNT (1)

 

 //这是一个全局条目,说明每个数据域的长度为8bit。

 0x75, 0x08, //     REPORT_SIZE (8)

 

 //这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,

 //数量为1个。它的属性为常量(即返回的数据一直是0)。

 //该字节是保留字节(保留给OEM使用)。

 0x81, 0x03, //     INPUT (Cnst,Var,Abs)

 

 //这是一个全局条目。定义位域数量为6个。

 0x95, 0x06, //   REPORT_COUNT (6)

 

 //这是一个全局条目。定义每个位域长度为8bit。

 //其实这里这个条目不要也是可以的,因为在前面已经有一个定义

 //长度为8bit的全局条目了。

 0x75, 0x08, //   REPORT_SIZE (8)

 

 //这是一个全局条目,定义逻辑最小值为0。

 //同上,这里这个全局条目也是可以不要的,因为前面已经有一个

 //定义逻辑最小值为0的全局条目了。

 0x15, 0x00, //   LOGICAL_MINIMUM (0)

 

 //这是一个全局条目,定义逻辑最大值为255。

 0x25, 0xFF, //   LOGICAL_MAXIMUM (255)

 

 //这是一个全局条目,选择用途页为键盘。

 //前面已经选择过用途页为键盘了,所以该条目不要也可以。

 0x05, 0x07, //   USAGE_PAGE (Keyboard/Keypad)

 

 //这是一个局部条目,定义用途最小值为0(0表示没有键按下)

 0x19, 0x00, //   USAGE_MINIMUM (Reserved (no event indicated))

 

 //这是一个局部条目,定义用途最大值为0x65

 0x29, 0x65, //   USAGE_MAXIMUM (Keyboard Application)

 

 //这是一个主条目。它说明这六个8bit的数据域是输入用的,

 //属性为:Data,Ary,Abs。Data说明数据是可以变的,Ary说明

 //这些数据域是一个数组,即每个8bit都可以表示某个键值,

 //如果按下的键太多(例如超过这里定义的长度或者键盘本身无法

 //扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。

 //Abs表示这些值是绝对值。

 0x81, 0x00, //     INPUT (Data,Ary,Abs)

 

 //以下为输出报告的描述

 //逻辑最小值前面已经有定义为0了,这里可以省略。

 //这是一个全局条目,说明逻辑值最大为1。

 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

 

 //这是一个全局条目,说明数据域数量为5个。

 0x95, 0x05, //   REPORT_COUNT (5)

 

 //这是一个全局条目,说明数据域的长度为1bit。

 0x75, 0x01, //   REPORT_SIZE (1)

 

 //这是一个全局条目,说明使用的用途页为指示灯(LED)

 0x05, 0x08, //   USAGE_PAGE (LEDs)

 

 //这是一个局部条目,说明用途最小值为数字键盘灯。

 0x19, 0x01, //   USAGE_MINIMUM (Num Lock)

 

 //这是一个局部条目,说明用途最大值为Kana灯。

 0x29, 0x05, //   USAGE_MAXIMUM (Kana)

 

 //这是一个主条目。定义输出数据,即前面定义的5个LED。

 0x91, 0x02, //   OUTPUT (Data,Var,Abs)

 

 //这是一个全局条目。定义位域数量为1个。

 0x95, 0x01, //   REPORT_COUNT (1)

 

 //这是一个全局条目。定义位域长度为3bit。

 0x75, 0x03, //   REPORT_SIZE (3)

 

 //这是一个主条目,定义输出常量,前面用了5bit,所以这里需要

 //3个bit来凑成一字节。

 0x91, 0x03, //   OUTPUT (Cnst,Var,Abs)

 

 //下面这个主条目用来关闭前面的集合。bSize为0,所以后面没数据。

 0xc0        // END_COLLECTION

};

俗话说得好,有图有真相,这上面给发一大片,相当滴给力。大家可以自己温习一下返回数据长度的计算还是不要忘记我上文说的,不要把输出的也计算出来。然后再对应一下HID的协议,看看是不是正确,举个小例子,第二项为0x09, 0x06,换算成二进制为 :

(0x09)0000 1001  ;  D0D1为1表示后面跟一个字节长度数据,D2D3为bType,为2说明是一个局部项目,D4~D7为bTag,这里表示Usage,即用途,那么后面跟的这个字节0x06,我们查HID中的Generic Desktop Page,06恰好是keyboard,OK,联想上文中的键盘是0x02,再对应,果然亦如此。明白不,如果不信,我们看我们的Generic Desktop Page,为什么我们用它,我们发现我们的第一个行描述符用的是0x05,0x01, 我就不转换二进制了,自己算算,00也正好代表着全局项目里的Usage,而0x01,却恰好是Generic Desktop Page,明白咧吧。

然后类似于鼠标的输入输出报告的实现,这里就不讲了,这个不是重点儿了,好,今天就到这里。明天接着。

时间紧,任务重啊。

  评论这张
 
阅读(1310)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017