今天是3月31号,这个月的主要收获是编写了一个热敏打印机驱动和4020的usb device的驱动,热敏打印机驱动倒没什么难度就是一个简单的字符型设备,三下五去二就把它给干掉了,但usb gadget的驱动本来以为也不会太难,但最后老是出现问题一大堆,大概从3月8号开始弄一直到3月27号才弄的差不多,花了三周时间,学到的东西也不少从c语言本身到usb的协议到scsi硬盘协议这些都一路趟过来,下面稍微介绍了这整个开发过程和调试经历。
I、 第一周
这一周大概是把usb协议和asixos的代码都通读了一遍,
(1) Usb协议看似庞大但实际上对我们有用也就是那么一些,主要是要弄清楚几个概念,这几个概念弄清楚了,其他的通信协议就不难理解了,下面先说下几个重要的概念:
USB设备的描述符:
usb协议规定了,每个usb设备都得有些基本的元素,称为描述符,有五类描述符是任何一种usb设备都得有的.他们是,device descriptor,configuration descriptor,interface descriptor,endpoint
descriptor.string descriptor。描述符里的冬冬是一个设备出厂的时候就被厂家给固化在设备里了.这种东西不管怎样也改变不了,比如我有个Intel 的U 盘,那里边的固有的信息肯定是在Intel 出厂的时候就被烙在里边了,厂家早已把它的一切,烙上Intel印.所以当我插入U盘,用cat /proc/scsi/scsi命令看一下的话,"Vendor"那一项显示的肯定是Intel. 关于这几种描述符,usb core在总线扫描那会就会去读取,会去获得里边的信息,其中,设备描述符描述的是整个设备,注意了,这个device和咱们一直讲的device和driver那里的device是不一样的.因为一个usb device实际上指的是一种宏观上的概念,它可以是一种多功能的设备,改革开放之后,多功能的东西越来越多了,比如外企常见的多功能一体机,就是集打印机,复印机,扫描仪,传真机于一体的设备, 当然,这不属于usb设备,但是usb设备当然也有这种情况,比如电台DJ可能会用到的,一个键盘,上边带一个扬声器,它们用两个usb接口接到usb hub上去,而device描述符描述的就是这整个设备的特点. 设备描述表给出了 USB 设备的一般信息。这包括对设备及所有设备配置起全程作用的信息。一个 USB 设备只能有一个设备描述表。 所有的 USB 设备都有缺省控制通道。缺省控制通道的最大包长在设备描述表中得到了说明。一个配置的端节点与接口定义在配置描述表中,一个配置和它的接口不包括节点描述表。
那么配置描述符呢,老实说,对我们了解U盘驱动真是没有什么意义,但是作为一个
有责任心的我来说,我得多说几句.一个设备可以有一种或者几种配置,这能理解吧?没见过具体的usb设备?那么好,手机见过吧,每个手机都会有多种配置,或者说"设定",比如,我
的这款,Nokia6300,手机语言,可以设定为English,繁体中文,简体中文,一旦选择了其中一种,那么手机里边所显示的所有的信息都是该种语言/字体.还有最简单的例子,操作模式也有好几种,标准,无声,会议,etc.基本上如果我设为"会议",那么就是只振动不发声,要是设为无声,那么就啥动静也不会有,只能凭感觉了,以前去公司面试的话通常就是设为无声,因为觉得振动也不好,让人家面试官听到了还是不合适.那么usb设备的配置也是如此,不同的usb 设备当然有不同的配置了, 一个 USB 设备有一个或多个配置。每个配置只有一个或多个接口。而每个接口又有 0 个或多个端节点。在一个配置下,一个端节不会在接口之间共享,除非端节点被同一个接口的不同设置使用。在不同配置端节点,可无此限制。.好了,关于配置,就说这么多,更多的我们暂时也不需要了解了.。
接口描述符是在一个配置内给出一个接口的信息。如果一个配置支持不止一个接口,端节点的描述表会跟在接口描述表后被返回,接口描述表总是作为配置描述表的一部分被返回。 接口描述不可直接用Set Description ( )和Get Descriptor ( )存取。
一个接口可能包含备选设置,以使得端节点或他们的特性在设备配置好以后能改变。一个接口的缺省设置总是可选设置。SetInterface ( )与 GetInterface ( )用来选择与返回选择了的接口设置。
端口描述符,每个接口使用的结点都有自己的描述表,此描述表被主机用来决定每个节点的带宽需求。每个结点的描述表总是作为配置描述的一部分返回的,结点 0 无描述符。
字串描述表是可有可无的。如前所述,如果一个设备无字串描述表,所有其它描述表中有关字串描述表的索引都必须为 0。
USB DEVICE的属性就是通过这些东西把他们固化起来了,当你将一个u盘插到电脑上了,这些信息在枚举的时候就会由下位机传到上位机了,电脑就是通过这些信息来区分各类usb设备的。
下面这个图演示了这些东西之间的关系,将用于通信流流动的通道称为管道(Pipe),它的两端就是上下位机的输入和输出端点。
其他的协议部分在这就不多说了,否则这就成了一篇论文了,下面在需要的时候我再说些协议的部分。
(1) 这周看的第二部分就是ASIX OS的usb驱动部分,由于4020上没有ADS的usb驱动代码,只有曾经师兄师姐们在ASIXOS上实现过usb部分,这让我相信sep 4020的usb还是能work的,而这些前辈们都已毕业离去了,只好我们自己肯代码了,ASIX上的代码中usb部分的思路还是比较清晰,设备初始化,上电枚举,中断处理,这三部分就基本上构成了整个usb的传输过程了:
初始化过程:
/*设置sep4020 maxpk最大包长*/
usb_write_reg((U32)USBD_EP0OUTMAXPKTSIZE_V, 0x8); // 控制传输的最大包长为8字节
/*bulk传输的最大包长64字节*/
usb_write_reg((U32)USBD_EP1OUTMAXPKTSIZE_V,0x40); usb_write_reg((U32)USBD_EP1INMAXPKTSIZE_V, 0x40);
usb_write_reg((U32)USBD_EP2OUTMAXPKTSIZE_V, 0x40);
usb_write_reg((U32)USBD_EP2INMAXPKTSIZE_V, 0x40);
/*设置ep1为bulk传输的输入端点,ep2为bulk传输的输出端点*/
usb_write_reg((U32)USBD_EP2OUTBMATTR_V, 0x02); //将端点2设置为bulk端点
usb_write_reg((U32)USBD_EP1INBMATTR_V, 0x02); //将端点1设置为bulk端点
/*设置4020usb工作模式*/
usb_write_reg((U32)USBD_BMATTRIBUTES_V, 0xe0);//设备属性设置为远程唤醒,自供电
/*使能各个工作端点*/
usb_RMW_reg((U32)USBD_EP0OUTSTAT_V, 0x02);
usb_RMW_reg((U32)USBD_EP1INSTAT_V, 0x02);
usb_RMW_reg((U32)USBD_EP2OUTSTAT_V, 0x02);
//full speed设为全速设备//
usb_RMW_reg((U32)USBD_DEVSPEED_V, 0x02); //full speed
//传输方式设为DMA传输//
usb_write_reg((U32)USBD_RECEIVETYPE_V, 0x01);
///clear int reg中断寄存器清零//
usb_write_reg((U32)USBD_PROTOCOLINTR_V,0);
usb_write_reg((U32)USBD_INTRMASK_V, 0x170); // 中断屏蔽寄存器设为,SETUP/OUT/IN/PING/RESET/CTRL_ERR 这些中断未屏蔽
//ENABLE USB IRQ使能USB中断//
INT_ENABLE(INTSRC_USB);
上面这段代码就是配置4020中的usb部分,使它能够正常work起来的代码,其中还少了PUM中配置usb时钟部分,这个是在系统时钟配置的地方进行设置的,也能放在这里进行,其实就两句话:
*(volatile unsigned int *)PMU_PUCR=0xc;
*(volatile unsigned int *)PMU_PUCR=0x800c;
这不结束并不代表usb就正常工作了,实际上对usb device的配置是在将4020通过usb电缆链接到电脑上的时候进行了,这个过程称为枚举的过程:
设备列举(控制传输):从主机的角度来看是这样进行的:
1) 使用预设地址0取的设备描述符:主机向设备发送要求64字节长度的设备描述符,但是设备只是向主机发送8个字节的设备描述符;
2) 设置设备新地址;
3) 使用新地址取得设备描述符,设备发送主机要求长度的描述符;
4) 取得配置描述符―主机要求9个字节,设备响应要求长度的配置描述符;
5) 取得配置描述符―主机要求0xFF字节长度描述符;设备依次发送配置描述符和所有的接口描述符(共32个字节),在向主机发送空包以通知主机数据传输完毕;
6) 设置配置描述符。
在进行完主机对设备的列举过程后即可进行数据传输。
通过这个过程,电脑基本上就能认出你到底是人还是妖了,这个过程可能会出错但电脑会将usb总线重启,然后又配置一次直到把你配置好为止,这步你熬过了来了,也就成功了一半了,下面就只有正常的数据传输了:
对于u盘这类bulk-only设备传输过程是相对比较简单的:
根据协议,每次数据传输过程由三部分组成:CBW,DATA-IN or DATA-OUT,CSW,
其中DATA-IN or DATA-OUT可能因为只是传输命令而没有。