厂商自定义USB设备固件程序及特性
通过前面的学习,大家应该对USB固件程序结构有了比较深的认识,现在再来详细说说固件里决定设备识别成厂商自定义USB设备的地方有哪些,或者说厂商自定义USB设备的固件特性有哪些。
之前不止一次说过学习USB固件编程的要点是要学会各种描述符及主机命令以及设备的枚举过程,理解设备枚举过程是主线,在这一过程中需要掌握主机各种命令及USB相关描述符(本站《USB命令请求及描述符详解(速查手册)》和《用USB Monitor监视USB枚举(配置、识别)过程(USB枚举过程分析)》对此进行了总结),一般USB接口芯片生产商都提供有固件范例,但USB设备类型非常多,不可能都有相应的示例,我们设计固件时可以直接在范例的基础上修改,修改的地方也主要是各种描述符以及命令的处理上(有些设备类甚至都不需要修改命令处理代码)。影响设备是否被识别成厂商自定义USB设备固件程序的地方主要在以下几个地方:
1、设备描述符
- bDeviceClass 字段,设备类码,此段为0xFF时,设备的类型由厂商定义
- bDeviceSubClass字段,设备子类掩码,如果bDeviceClass字段的值为0xFF,此字段的值也必须为0xFF
- bDevicePortocol字段,设备协议码,如果此字段的值为0xFF,此设备使用厂商定义的协议
修改好的设备描述符:
- //设备描述符
- code USB_DEVICE_DESCRIPTOR DeviceDescr =
- {
- sizeof(USB_DEVICE_DESCRIPTOR), //设备描述符长度,= 12H
- USB_DEVICE_DESCRIPTOR_TYPE, //设备描述符类型,= 01H
- 0x00,0x01, //协议版本,= 1.10
- 0xFF, //测试设备类型 ,=0xFF为厂商自定义设备
- 0xFF, //设备协议
- EP0_PACKET_SIZE, //端点0最大数据包大小,= 10H
- 0x71,0x04, //PHILIPS公司的设备ID
- 0x00,0x09, //设备制造商定的产品ID
- 0x00,0x01, //设备系列号
- , , , //索引
- //可能的配置数
- };
2、接口描述符
- bInterfaceClass字段,接口所属于类别,为0xFF时代表厂商自定义
- bInterfaceSubClass字段,接口子类码,只说明了当bInterfaceClass为零时此字段也必须为零,这里暂时也设为0xFF
bInterfaceProtocol字段,接口协议,对于厂商自定义设备并没有说明,这时暂时设为0xFF
修改好的接口描述符:
- //接口描述符
- {
- sizeof(USB_INTERFACE_DESCRIPTOR), //接口描述符长度,= 09H
- USB_INTERFACE_DESCRIPTOR_TYPE, //接口描述符类型,= 04H
- , //接口数,只有1个
- , //可选配置,只有1个
- NUM_ENDPOINTS, //除端点0的端点索引数目,= 04H
- 0xFF, //接口别类,0xFF=厂商自定义
- 0xFF, //子类代码
- 0xFF, //协议代码
- //字符串描述符索引
- },
3、对USB主机命令的处理
在《USB命令请求及描述符详解(速查手册)》一文的表1列出了USB主机命令结构,其中bmRequestType字段的第5至第6位(D5~D6)命令种类(标准、类或者厂商自定义),当为厂商自定义命令时,此时bRequest字段的值就是厂商自定义的命令ID,wValue字段可以传送两个字节长度的数据,当然厂商自定义的命令也可能没有带数据,或者带的数据不止两个字节,如果带的数据不止两个字节,那么应该在wLength字段指明所带数据长度,而真正的数据将在紧接着的数据阶段传输。
厂商自定义命令有哪些?那当然是按设备需要具备的功能,由厂商自己来定义了,比如此开源项目,我打算实现的自定义USB设备可以通过主机来控制电路板上的八个LED,还可以读取板子上的按键状态,这两个功能都可以用厂商定义命令来实现。比如可以定义USB命令的bRequest字段为0时代表控制LED状态,为1时读取按键状态。控制LED状态时肯定主机要带数据来表明LED应该转换的状态,我们可以用一个字节的八个位分别对应八个LED的状态,那么所带数据就可以放在wValue字段里。而读取按键状态需要两步,第一步是主机发自定义USB命令,命令ID(即bRequest字段的值)为1,当设备接收到这个命令后,应该立即将当前按键状态通过控制端点发送给主机,按键状态可以用一个字节的低两位表示,主机发送厂商自定义命令是通过Windows API函数DeviceIoControl来完成的,其功能类似于另一个API函数WriteFile,DeviceIoControl发送命令时,如果设备有数据返回,DeviceIoControl函数的第5个参数会带回设备返回的数据,所以在读取按键状态时,调用DeviceIoControl函数后可以查询DeviceIoControl函数里的第5个参数得知按键状态。
下面是需要修改的命令处理相关代码:
1)Chap_9.c文件中的control_handler函数是判断命令别类(即判断是标准、类或者厂商自定义)的,需要增加厂商自定义命令调用接口,可以参考标准请求代码的写法,内容如下:
- /*************************************************************
- ** 函数名称: void control_handler(void)
- ** 功能描述: 主机命令类型服务程序
- **************************************************************/
- void control_handler(void)
- {
- INT8U type, req,wValue;
- type = ControlData.DeviceRequest.bmRequestType & USB_REQUEST_TYPE_MASK;
- //读取请求代码
- req = ControlData.DeviceRequest.bRequest & USB_REQUEST_MASK;
- wValue = ControlData.DeviceRequest.wValue;
- if (type == USB_STANDARD_REQUEST)
- (*StandardDeviceRequest[req])(); //标准请求处理
- else if (type == USB_VENDOR_REQUEST) //厂商请求
- (*VendorDeviceRequest[req])(wValue);
- //else if(type == USB_CLASS_REQUEST)
- // (*ClassDeviceRequest[req])(); //类请求,如大容量类
- else
- stall_ep0(); //无效请求,返回STALL
- }
以上代码中,当判断到USB命令数据包里的bRequest字节为USB_VENDOR_REQUEST(0x02)时调用(*VendorDeviceRequest[req])(wValue) 一句,VendorDeviceRequest是在代码区定义的一个函数指定组数,这种调用方法可以根据数组下标(req)来决定调用哪个函数,VendorDeviceRequest函数指针数组的定义如下(同样是参考标准请求代码的写法):
- //USB厂商请求入口地址指针表
- code void (*VendorDeviceRequest[]) (INT16U wValue) =
- {
- control_led,
- get_key_state
- };
当req的值(即命令ID)为0时调用control_led函数,当req的值为1时调用get_key_state函数,同时把wValue作为参数传递给这两个函数(当然参数对get_key_state函数没用),以下是这两个函数的代码:
- sbit K1 = P3^;
- sbit K2 = P3^;
- /****************************************
- ** 函数名称: void control_led(INT16U wValue)
- ** 功能描述: 控制扩展板EXT-BOARD-A上的LED
- ** 参 数:INT16U wValue->控制值,低字节有效,如wValue为0x0000时LED全灭,wValue为0x00FF时LED全亮
- *****************************************/
- void control_led(INT16U wValue)
- {
- P0 = wValue % ; //返回一个空的数据表示执行完毕
- single_transmit(, ); //返回一个空的数据表示执行完毕
- }
- /****************************************
- ** 函数名称: void get_key_state(INT16U wValue)
- ** 功能描述: 取得扩展板EXT-BOARD-A上按键状态
- ** 参 数:INT16U wValue->无意义
- *****************************************/
- void get_key_state(INT16U wValue)
- {
- INT8U ucKeyState[],i;
- ucKeyState[] = 0x00;
- K1 = ;
- K2 = ;
- ;i<;i++);
- if(~K1) //K1按下
- {
- ucKeyState[] |= 0x01;
- }
- if(~K2) //K2按下
- {
- ucKeyState[] |= 0x02;
- }
- single_transmit(ucKeyState,);
- }
另外还需要在Chap_9.h中增加以下内容,对以上新加函数作申明:
- extern code void (*VendorDeviceRequest[])(INT16U wValue);
- extern void control_led(INT16U wValue);
- extern void get_key_state(INT16U wValue);
至此,厂商自定义USB设备的代码修改工作已经完成,除了以上说的USB设备固件里影响厂商自定义USB设备特性的地方外,如果设备还用到了非控制端点外的其它端点,还应该定义相应端点描述符,以及用这些端对接收到的数据处理代码以及通过这些端点发送数据的代码。
厂商自定义的命令必须通过控制管道传输,自定义USB设备也可以通过非控制管道传输数据,所以我们也可能过非控制管道的数据来控制LED状态和读取按键状态,只不过需要自己来定义数据包里各个字节的含义,这种用法有点像传统串口通信一样需要定义数据帧格式,比如可以把数据包的第一个字节固定为命令字节,后面是数据。
厂商自定义设备必须有相应的主机驱动程序对设备进行支持,所以光完成上面的工作还不行,还得写相应的主机设备驱动程序才行,除了主机驱动程序,也许还需要相应的主机应用程序,下面的文章我将会一步一步介绍怎么来实现USB设备的Windows驱动程序以及Windows应用程序。
特别备注:当设备被指定为一个USB协议定义里的标准设备类别而主机操作系统没有提供相关驱动程序,这时操作系统也会提示安装驱动程序,如果您所写的驱动程序并没有按USB协议定义的标准设备规范来,那么其实这个设备也等同于一个厂商自定义设备,本开源项目的最初版本定义设备类别是一种测试设备类(bDeviceClass = 0xDC),但写的驱动程序程序却并不符合特定设备类的特性,所以也相当于是一种自定义USB设备,以前发布的EASY USB 51 PROGRMAER第一个版本的时候就是按这种方法做的厂商自定义USB设备,最新PLUS版本对这种不严格的方法进行了更正。
![]() |
本节完成后的USB固件源码 |
厂商自定义USB设备固件程序及特性的更多相关文章
- 厂商自定义USB设备类概述
USB协会将常用具有相同/相似功能的设备归为一类,并制定了相关的设备类规范,这样就能保障只要依照同样的规范标准,即使不同的厂商开发的USB设备也可以使用同样的驱动程序,而且操作系统中无须为每种设备提供 ...
- 基于libUSB的USB设备固件更新程序(下载数据)(转)
源:基于libUSB的USB设备固件更新程序(下载数据) 本文紧接上一篇日志:基于libUSB-Win32的USB设备固件更新程序(前言),相关背景以及起因等,此处不再赘述,如感兴趣请移步. libU ...
- C# 上位机的USB设备拔插检测
我们做USB通信时,通信成功后,往往要检测USB设备的拔插状态,这里就USB拔插进行一下说明. 参考:https://www.imooc.com/article/17438 先说明一下,我这里只是用C ...
- USB2.0学习笔记连载(十四):USB驱动安装及固件程序的编写
在之前的博客中已经讲过,驱动程序最核心的两个文件,一个是xxx.sys文件,一个是xxx.inf文件,主机是寻找xxx.inf文件. 在下面的文件中有相关关于USB驱动的说明.对于用户来说,xxx.s ...
- C# 实现自定义的USB设备与上位机进行通信(上位机部分)
因为以前没用过USB,对USB也不了解,于是上网查了很多资料,不过网上的资料都是零零散散,不清不楚的,于是我自己总结了一下,下面几个链接是网上这么多零散资料里,我觉得比较有参考意义的. USB设备连接 ...
- C#:基于WMI查询USB设备信息 及 Android设备厂商VID列表
/* ---------------------------------------------------------- 文件名称:WMIUsbQuery.cs 作者:秦建辉 MSN:splashc ...
- Android应用程序无法读写USB设备的解决方法
假设android系统中的API或者apk无法读写usb设备.可能是没有加入读写usb的权限,须要依照例如以下方法进行设置: 1. 在android.hardware.usb.host.xml文件里加 ...
- usb设备(移动硬盘或U盘),弹出时提示“有进程或程序占用,无法弹出”。解决办法
测试环境:Win7(其他Windows系统环境,也可参考) 总结办法来源,https://bbs.csdn.net/topics/392297251?page=1文章中热心网友的评论指引 1. 控制面 ...
- USB设备的基本概念
在终端用户看来,USB设备为主机提供了多种多样的附加功能,如文件传输,声音播放等,但对USB主机来说,它与所有USB设备的接口都是一致的.一个USB设备由3个功能模块组成:USB总线接口.USB逻辑设 ...
随机推荐
- phalcon
phalcon 代码内在sql 内用Ad去取数据库内的ad表. osx上没问题, centos报找不到表. 应该是centos上mysql区分大小写,可改写mysql的配置.
- 数据的加密传输——单片机上实现TEA加密解密算法
各位大侠在做数据传输时,有没有考虑过把数据加密起来进行传输,若在串口或者无线中把所要传的数据加密起来,岂不是增加了通信的安全性.常用的加密解密算法比如DES.RSA等,受限于单片机的内存和运算速度,实 ...
- QML的渲染方式相较于之前的版本也有了重大的更新(CPU线程负责绘制,GPU线程负责渲染),还有好多经常评论 good
作者:qyvlik链接:http://www.zhihu.com/question/38867614/answer/78583440来源:知乎著作权归作者所有,转载请联系作者获得授权. 做UI啊.如果 ...
- Hibernate 配置详解(8)
hibernate.generate_statistics 这个配置大家应该都很熟悉,用于开启Hibernate统计信息,便于对Hibernate相关性能调试提供数据依据.在开发过程当中,可以把这个选 ...
- 【转】如何查看linux版本 如何查看LINUX是多少位
原文网址:http://sopace.blog.51cto.com/1227753/670526 如何得知自己正在使用的linux是什么版本呢,下面的几种方法将给你带来答案! 1. 查看内核版本命令: ...
- 2014第3周三JS进阶书籍
本来想尝试每天回答或看已解决的3个问题来学习总结今天的知识点,看了下博文里面的问答,在问的和已解决的都提不起兴趣.就看了下知识库里面一些文章,把里面感觉好的段落再摘录一下,为自己再看时备忘. 第一阶段 ...
- UESTC_邱老师降临小行星 2015 UESTC Training for Search Algorithm & String<Problem B>
B - 邱老师降临小行星 Time Limit: 10000/5000MS (Java/Others) Memory Limit: 65536/65535KB (Java/Others) Su ...
- mongoDB windows reinstall add auth
Mongodb默认启动是不带认证,也没有账号,只要能连接上服务就可以对数据库进行各种操作,这样可不行.现在,我们得一步步开启使用用户和认证. 第一步,我们得定位到mongodb的安装目录.我本机的是C ...
- 一笔画问题(floyd+oular+dfs)
一笔画问题 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下 ...
- sqlite数据库读写在linux下的权限问题
近期在学linux,恰巧有个php项目要做.于是配置好环境打算在linux下做. 无奈站点执行后一片空白.经过调试发现是sqlite数据库的问题. 安装sqlite扩展 apt-get install ...