usb杂谈

当我们通过usb协议大体了解了usb描述符和标准请求后,我们就有了解usb枚举过程的基础,在看完usb枚举过程之后,我们要明白一个问题,就是主机是怎么认识usb设备的

   首先,主机是usb通信的发起者,usb设备只能被动接受命令。

   下面的枚举过程通过bushound可以探测到,对于bushound的使用,一搜一片。

   usb控制器与集线器都在机箱里,集线器检测到了新设备信号,它先要和usb控制器商量一下,下面是商量的内容:

   1.主机的集线器检测新设备

   电脑主机的主板上都会有几个集线器,每个集线器都会有多个usb口,usb口就是我们肉眼看到的机箱外部的usb插口。主机集线器轮巡每个端口的信号电压,当有新设备接入usb口时就会察觉。原因在于集线器端口的两根信号线的每一根都有一个15KΩ的下拉电阻,每一个设备在D+线上都有一个1.5KΩ的上拉电阻。当总线将主机和设备接通后,设备的上拉电阻使信号线上的电位升高,此时主集线器就会检测到这个信号。

   2.报告集线器事件

   当检测到接入信号后,集线器用中断来报告给主机usb控制器(简称主机),控制器知道后,给集线器发送一个Get_Status请求,集线器告诉主机新设备是什么时候连接到主机上的。

   3.重新设置新设备

   当主机确定此时有新设备后,主机给集线器发送一个Set_Feature请求,集线器使得usb数据线处于reset状态至少10ms

   4.重发Get_Status请求

   主机重发Get_Status请求以检查设备是否真的处于重启态。当集线器释放重启态后,设备进入默认态,此时可以通过端点0进行控制传输了,使用默认地址0x0与主机通信。

   5.集线器通过D+/D-信号线在空闲时电压的高低来断定usb此时是全速还是低速。

   好了,控制器与集线器商量了一会,心里有底了,现在可以询问设备了

1.       主机向端点0发送获取设备描述符请求,在有限时间内等待usb设备回答,不管usb设备此时返回多少字节,主机只读前8个字节,如果等待几ms设备没有反应,会持续三次

     2.  主机发送Set_Address请求给设备,同时为设备分配一个新地址。设备读到这个请

求后,返回给主机确认信息,同时保存新地址,下面开始用新地址通信。

  3.  主机重发获取设备描述符请求给设备,这次会读取全部的usb设备描述符,了解

设备的VIDPID等信息。一次读不全,会重复发送该请求。

4.   主机发送获取字符串描述符,获得厂商,产口描述,型号等信息。

     5.   到这儿主机右下角就会弹出发现新硬件,接着弹出厂商,产品描述,型号等

     6.   主机根据设备描述符和设备配置信息在自己的驱动库中查找是否有合适的驱动,win98以上的windows系统(不包括98)支持HID设备,打印机,扫描仪等,如果搜不到,会弹出对话框,提示安装设备驱动。

     7.   驱动加载以后,主机发送设置配置请求为设备选一个合适的配置。如果配置成功,

usb进入配置状态,设备可以和主机上应用软件通信。

 

不做实际的固件开发,上面的过程有些地方很难理解,读完之后只需有一个大体印象,

usb枚举失败之后,通过bushound我们就知道到在哪一个阶段出了问题。

 

 

 

 

 

我们在做应用层开发时,是通过接口类GUID来获取设备的句柄的,而不是安装类GUID

XP系统

分别看看它们在注册表中的位置,以免混淆。

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Class,在这个目录下就可以找到安装类GUID子键(可以通过DS生成的inf文件,找到这个GUID),单击这个子键,可以看到inf文件中设置的类名,图标等。展开这个子键,在0000子键下,可以看到在DS向导中我们自己设置的子键,单击这个子键,可以从右边看到我们在DS中为这个子键加的注册表项(当然在inf文件中也可以找到,因为它是记录在inf文件中的。)

0000子键下还有很多表项,,其中DriverDesc就是设备在设备管理器中显示的名称,InfPath是安装后备份在Windows/inf目录(属性为隐藏)下的inf文件,通过这个表项,我们可以找到这个设备在windows下的inf文件.MatchingDeviceId就是要匹配的硬件IDVIDPID等)。

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/DeviceClasses子键下就可以找到接口类GUID,可以通过DS生成的intrface.h找到这个GUID子键,展开这个子键,然后展开#号子键,找到SymbolicLink表项,它的值就是在打开驱动时用的路径。

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Enum/USB/子键下显示了除ROOT_HUB(根集线器)外,也列出了所有曾经安装过和现在已安装驱动程序的设备的VID/PID。这里的VID/PID即决定了哪些驱动程序将会被加载。(如果你在设备管理器中卸载了设备的驱动程序,但是在这个位置还会保留它的信息,等你下次再装这个设备的驱动的时候,因为设备的VID/PID还是一样的,所以有的os会根据这个键下的VID/PID加载windows下的驱动,有的os会列出这个VID/PID曾经加载过的驱动的inf,当我们修改了设备的描述符时,如修改了端点的大小等,这时上位机驱动肯定修改了,不一样了,如果还是加载旧驱动,那usb就会出问题,所以我们在删除设备驱动时要一并把这儿的信息也删除,最好把windows/inf下的驱动inf也删除,这样我们在加载驱动时可以手动加载我们新写好的驱动了。当然我们修改设备的VID/PID也行,但显然在做产品时前者是可取的。)。

usb第一次被枚举成功时,会在历右下角弹出发现新硬件对话框,那是因为os虽然认识了这个设备,也知道了它有哪些功能,但它不知道要用这个设备的这些功能做什么,所以它要求我们安装这个设备的驱动,来告诉它用这个设备能做哪些事儿。安装好设备的驱动后,就会把设备的相关信息存入上面写的三个注册表路径当中。最重要的还是第三个路径,因为当设备再一次被插入的时候,os会找注册表的这个位置,看到有VID/PID与这个设备一样,就知道这个设备已经被自己认识了,所以就不会在右下角弹出发现新硬件的对话框。

每当用户重新插入设备或重新启动windows时,就会再一次执行设备枚举(这点非常重要)。

如果要删除某个设备,只是在设备管理器下执行删除是不够的,还要在注册表中将设备的项目删除掉,才能达到完全删除的目的。最后三个目录下的有关设备的信息全删掉。

对于ROOT_HUB的信息不能删,我们找不到它的加载驱动重新安装。

 

如果你对usb描述符和usb标准请求有一个大体认识,并仔细了解了你所用芯片对usb中断处理方式,那下面的内容或许对你会一些帮助。

usb描述符中要说两点,

.配置描述符,接口描述符,端点描述符三者是一起返回上位机的,接口描述符与端点描述符不能单独被返回。

    .usb是小端模式,注意wLength,wIndex,wValue这些双字节位域的赋值问题

         

      usb固件以usb中断,usb请求和usb描述符为基本框架进行编程,usb中断怎么处理各芯片会有差别,但大体都是产生usb中断后,固件进一步判断某一寄存器各个位来断定当前是usb何种中断,假设当前产生的是setup中断(端点0),那接下来就进行setup中断处理,在setup中断处理中我们就要通过标准请求来处理各种描述符。

      void USB0_ISR(void) interrupt 8(这里的interrupt 8C8051F340对中断的处理方式)       

      {

         读相关寄存器来判断具体是usb何种中断

         {

            if 恢复中断        

            {

               调用恢复中断处理函数

            }

            if 复位中断

            {

               复位中断处理

            }

            if setup中断

            {                               

              Handle_Setup(); // setup中断处理

            }

            挂起中断,端点中断(包括除端点0外不同端点的输入/输出中断,视具体要求)

      }

      //setup中断处理

      void Handle_Setup(void)

      {

            1.buffer中读取上位机发来的setup

            2.读取setup包各域进行分支处理(虽然过长的switch

      case不是好的编程思想,但是当用单片机做开发时考虑到单片机有限的存储空间,还是选switch case最合适)

               switch(Setup.bRequest)    //对标准请求中bRequest域进行判断处理   

               {                           

                  case GET_STATUS:  

                     Get_Status();

                     break;

                  case CLEAR_FEATURE:

                     Clear_Feature();

                     break;

                  case SET_FEATURE:

                     Set_Feature();

                     break;

                  case SET_ADDRESS:

                     Set_Address();       //设置地址

                     break;

                  case GET_DESCRIPTOR:

                     Get_Descriptor();  

      //获取设备描述符,你把准备好的设备描述符写到buffer,等待上位机读buffer

                     break;

                  case GET_CONFIGURATION:

                     Get_Configuration(); //获取配置描述符

                     break;

                  case SET_CONFIGURATION:

                     Set_Configuration(); //设置配置描述符

                     break;

                  case GET_INTERFACE:

                     Get_Interface();

                     break;

                  case SET_INTERFACE:

                     Set_Interface();

                     break;

                  default:

                     Force_Stall();        

                     break;

               }  

      }

              setup其它的域,wLength,wIndex,wValue在各描述符中处理。

              各描述符的处理函数可放一单独文件中统一管理。

              接下来写各除端点0的各端点的输入/输出中断处理函数以及读写buffer函数

你可能感兴趣的:(编程,windows,OS,buffer,interface,Descriptor)