热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

闲暇就玩USB之:USB鼠标和键盘

其实这个问题很多人都玩过了,而且HIDSpec上有标准例子,但是USB鼠标和键盘的确很有意思,而且俺还尝试了一点和别人不一样的东西,在此以记之。HIDSPEC上讲的键盘和鼠标都是支

其实这个问题很多人都玩过了,而且HID Spec上有标准例子,但是USB鼠标和键盘的确很有意思,而且俺还尝试了一点和别人不一样的东西,在此以记之。

HID SPEC上讲的键盘和鼠标都是支持boot的,就是可以被Bios支持的,比如在开机的时候设置Bios的时候就可以用。因此那个Report Descriptor真的是相当的复杂啊,都63个字节了,就差一个字节就超过俺的EP0的Max Pack Size。其实介绍Report Descriptor的最好网络文章是《USB/HID设备报告描述符详解》,看用词像个台湾同胞写的,可以在下列地址阅读:

http://blog.chinaunix.net/u2/63560/showart.php?id=1900045

其实这个似乎都还是比较复杂,我做了一个不支持boot的键盘的Report Descriptor,只支持一个字节的输入,其实一个字节也是可以输入101个键的,HID Spec里面的Descriptor其实是支持6个键同时输入的,所以用了6个字节。下面俺的简陋型HID Descriptor就是这个样子的:

char HidBoardReportDescriptor[23] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x1E,                    //   USAGE_MINIMUM (Keyboard ! and 1)
    0x29, 0x25,                    //   USAGE_MAXIMUM (Keyboard * and 8)

    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0xff,                    //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION
};

Descriptor中的几个术语大概是这个意思,俺的通俗理解:

Usage Page:相当于用法类别,或者功能类别,用我的地址做比较,相当于“北京市海淀区”的“北京市”

Usage:相当于具体的用法,比如“海淀区”,一个完整的用法需要Usage Page和Usage配合才能完整表达,用地址类比一下。。。好像不能用“北京市海淀区”,海淀区中国只听说过这一个!比如说“石门坎”吧,云贵川有几百个地方叫“石门坎”,因此必须说明“四川省宁南县华弹镇石门坎”才有意义,兄弟,扯得有点远了!

由于Usage Page是全局的,因此只声明一次就行了,除非下面要什么新的Usage Page。而Usage是要一个一个的声明的。但是101个键要写101次太麻烦,因此使用USAGE_MINIMUM 和USAGE_MAXIMUM来定义一个范围,比如我上面的蓝色的两行就把1-8八个键都描述了。

LOGICAL_MINIMUM 和 LOGICAL_MAXIMUM 对应的是输入数据的范围,超出这个范围不予处理。这个Lgical值的0表示没输入,1和上面的USAGE_MINIMUM是对应,也就是输入1对应计算机的1,如果把USAGE_MINUM和USAGE_MAXIM改成如下:

    0x19, 0x04,                    //   USAGE_MINIMUM (Keyboard a and A)
    0x29, 0x0B,                    //   USAGE_MAXIMUM (Keyboard h and H)
那样输入1对应的就是计算机端的'A'了。

即原来的对应是1-->0x1E("1"键或"!"键),修改后为1-->0x04("a" or "A"),关于每个USAGE对应的按键,在HID标准中有描述,这样就把逻辑值和最后的键对应起来了。

REPORT_SIZE:表面的输入的位宽度,俺的是8位, REPORT_COUNT是这样的数有几个,俺的只有一个。后面的INPUT表示有一个输入。

COLLECTION在俺看来就是个大括号。

这样一个键盘的简单Descriptor就OK了。鼠标的就采用Boot就可以了。

HID的Report Descriptor是可以支持多个设备的,比如同时支持一个鼠标和键盘,这时就需要Report ID来参与了,这样在上传数据的时候就需要多一个字节表示Report ID来标识是哪个设备的数据,Report ID位于发送数据的第一个字节。切记,Report ID不能为0,因为系统默认已经用过了。(有人说这种复合设备在Configuration里Subclass不能为Boot,但是好像没关系的)

比如假设鼠标的Report ID是2,则发送的数据应该如下:

0x02,00,05,00, 00

后面的红色部分是原来不采用Report ID时发送的数据。下面是一个采用HID SPEC的键盘和鼠标Descriptor做的支持两个设备的Descriptor。一旦枚举成功,恭喜你,你会在设备管理器看到多出了一个鼠标和一个键盘.

const char HidBoardReportDescriptor[] = {
// Descriptors for Keyboard
  0x05, 0x01,                    /* USAGE_PAGE (Generic Desktop) */
  0x09, 0x06,                    /* USAGE (Keyboard) */
  0xa1, 0x01,                    /* COLLECTION (Application) */
  0x85, 0x03,                    /*   Report ID (3) */
  0x05, 0x07,                    /*   USAGE_PAGE (Keyboard) */
 0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
 0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
 0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
 0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
 0x75, 0x01,                    //   REPORT_SIZE (1)
 0x95, 0x08,                    //   REPORT_COUNT (8)
 0x81, 0x02,                    //   INPUT (Data,Var,Abs)
 0x95, 0x01,                    //   REPORT_COUNT (1)
 0x75, 0x08,                    //   REPORT_SIZE (8)
 0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
 0x95, 0x05,                    //   REPORT_COUNT (5)
 0x75, 0x01,                    //   REPORT_SIZE (1)
 0x05, 0x08,                    //   USAGE_PAGE (LEDs)
 0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
 0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
 0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
 0x95, 0x01,                    //   REPORT_COUNT (1)
 0x75, 0x03,                    //   REPORT_SIZE (3)
 0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
 0x95, 0x06,                    //   REPORT_COUNT (6)
 0x75, 0x08,                    //   REPORT_SIZE (8)
 0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
 0x25, 0xFF,                    //   LOGICAL_MAXIMUM (255)
 0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
 0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
 0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
 0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
 0xc0,                           // END_COLLECTION

// Descriptors for Mouse
  0x05, 0x01,                    /* Usage Page (Generic Desktop)  */
  0x09, 0x02,                    /* Usage (Mouse)                 */
  0xA1, 0x01,                    /* Collection (Application)      */
  0x09, 0x01,                    /*   Usage (Pointer)               */
  0xA1, 0x00,                    /*   Collection (Physical)         */
  0x85, 0x02,                    /*     Report ID (2) */
  0x05, 0x09,                    /*     Usage Page (Buttons)          */
  0x19, 0x01,                    /*     Usage Minimum (01)            */
  0x29, 0x03,                    /*     Usage Maximum (03)            */
  0x15, 0x00,                    /*     Logical Minimum (0)           */
  0x25, 0x01,                    /*     Logical Maximum (1)           */
  0x75, 0x01,                    /*     Report Size (1)               */
  0x95, 0x03,                    /*     Report Count (3)              */
  0x81, 0x02,                    /*     Input (Data, Variable, Absolute)*/
  0x75, 0x05,                    /*     Report Size (5)                 */
  0x95, 0x01,                    /*     Report Count (1)                */
  0x81, 0x01,                    /*     Input (Constant)    ;5 bit padding */
  0x05, 0x01,                    /*     Usage Page (Generic Desktop)       */
  0x09, 0x30,                    /*     Usage (X)                          */
  0x09, 0x31,                    /*     Usage (Y)                       */
  0x09, 0x38,                    /*     Usage (Wheel)                   */
  0x15, 0x81,                    /*     Logical Minimum (-127)          */
  0x25, 0x7F,                    /*     Logical Maximum (127)           */
  0x75, 0x08,                    /*     Report Size (8)                 */
  0x95, 0x03,                    /*     Report Count (3)                */
  0x81, 0x06,                    /*     Input (Data, Variable, Relative)*/
  0xC0,                          /*   End Collection                  */
  0xC0                           /* End Collection                  */

};

 

 


推荐阅读
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本教程涵盖OpenGL基础操作及直线光栅化技术,包括点的绘制、简单图形绘制、直线绘制以及DDA和中点画线算法。通过逐步实践,帮助读者掌握OpenGL的基本使用方法。 ... [详细]
  • 本文详细介绍了 GWT 中 PopupPanel 类的 onKeyDownPreview 方法,提供了多个代码示例及应用场景,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • MQTT技术周报:硬件连接与协议解析
    本周开发笔记重点介绍了在新项目中使用MQTT协议进行硬件连接的技术细节,涵盖其特性、原理及实现步骤。 ... [详细]
  • 本文详细介绍了如何构建一个高效的UI管理系统,集中处理UI页面的打开、关闭、层级管理和页面跳转等问题。通过UIManager统一管理外部切换逻辑,实现功能逻辑分散化和代码复用,支持多人协作开发。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • 本文探讨了 Objective-C 中的一些重要语法特性,包括 goto 语句、块(block)的使用、访问修饰符以及属性管理等。通过实例代码和详细解释,帮助开发者更好地理解和应用这些特性。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 使用Vultr云服务器和Namesilo域名搭建个人网站
    本文详细介绍了如何通过Vultr云服务器和Namesilo域名搭建一个功能齐全的个人网站,包括购买、配置服务器以及绑定域名的具体步骤。文章还提供了详细的命令行操作指南,帮助读者顺利完成建站过程。 ... [详细]
  • 根据最新发布的《互联网人才趋势报告》,尽管大量IT从业者已转向Python开发,但随着人工智能和大数据领域的迅猛发展,仍存在巨大的人才缺口。本文将详细介绍如何使用Python编写一个简单的爬虫程序,并提供完整的代码示例。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
author-avatar
小妞丁林林
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有