文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>SEP4020的USB DEVICE 在linux下驱动的实现(3)

SEP4020的USB DEVICE 在linux下驱动的实现(3)

时间:2009-07-07  来源:fpseustar

第二周:编写linux中usb gadget驱动

这一周主要是写linux驱动代码,而代码主要是翟婷婷同学写的,我就在旁边随便晃悠下,其实她在上一周就将代码写的差不多了,这周前几天修改完善了下代码,然后就进入了我们期待的编译过程了,我们总是希望linux能把我们自己写的程序一次编译通过,但事实是没有一次linux成全过我,老是报一大堆error,不过这些语法error只要根据编译器arm-linux-gcc的提示很快就将大部分error搞定了,但最后有个error这么该也改不掉,为此困惑了好些天,错误提示如下:

drivers/usb/gadget/sep4020_udc.c: In function `handle_ep0':

drivers/usb/gadget/sep4020_udc.c:1278: error: parse error before numeric constant

drivers/usb/gadget/sep4020_udc.c:1158: warning: unused variable `len'

就是中间的这个error,我苦思了几天无解,主要由于这代码也不是自己写的,对这代码的分布要不怎么了解,没办法只好先看下代码,看了一天的驱动源文件,内容基本上是懂了,但对这个错误还是望洋生叹啊,莫办法,这几天又感冒,根本就没心思看代码了,3月17号中午我睡不着,左看右看这个error:

drivers/usb/gadget/sep4020_udc.c:1278: error: parse error before numeric constant

再分析下源码:

int usb_gadget_register_driver(struct usb_gadget_driver *driver)

{

    struct sep4020_udc *udc = the_controller;

    int             retval;

    int           (*fp)(struct usb_gadget *,const struct usb_ctrlrequest *);

       fp = driver->setup;      //FP

       return 1;

}

setup是driver结构体的一个函数成员,但fp = driver->setup;这句话老编译不过去一直报下列这个错误

 error: parse error before numeric constant

于是我将它改为:

int usb_gadget_register_driver(struct usb_gadget_driver *driver)

{

    struct sep4020_udc *udc = the_controller;

    int             retval;

    int           (*fp)(struct usb_gadget *,const struct usb_ctrlrequest *);

       fp = setup;      //FP

       return 1;

}

这时应该会找不到这个函数,应该报如下错误:

drivers/usb/gadget/sep4020_udc.c:1639: error: `setup' undeclared (first use in this function)

drivers/usb/gadget/sep4020_udc.c:1639: error: (Each undeclared identifier is reported only once

drivers/usb/gadget/sep4020_udc.c:1639: error: for each function it appears in.)

但他却报

drivers/usb/gadget/sep4020_udc.c:1639: warning: assignment makes pointer from integer without a cast

这个warning,说明这个标识符在哪还定义过,于是我在和usb gadge驱动相关的文件中找,最后发现在HA_usbcommon.h 这个头文件中定义了一个这样的宏:

#define   setup        0x01 

#define   in           0x02 

#define   out          0x04

#define   ping         0x08

总算找到原因了,原来是宏这个预编译指令在作怪,哈哈

把这个宏去掉,就一切OK了,看来以后宏定义一定得用大写字母才行了,这就是随意乱写变量名,函数名和宏名的代价了。下午心情大好,看完病回去睡觉了。

第二天,接着编译,其他问题基本都解决了,然后我们就期望这个生成的模块能奇迹般的work起来,但奇迹从来就没发生过,不过还是出现了好些令人欣慰的信息

/ # insmod g_file_storage.ko file=/dev/mtdblock2 stall=0 removable=1

0.02 USB: usb_gadget_register_driver() 'g_file_storage'

0.03 USB: binding gadget driver 'g_file_storage'

0.04 USB: sep4020_set_selfpowered()

g_file_storage gadget: File-backed Storage Gadget, version: 28 November 2005

g_file_storage gadget: Number of LUNs=1

g_file_storage gadget-lun0: ro=0, file: /dev/mtdblock2

0.05 USB: udc_enable called

这些信息足以说明上层已经成功得带动了下层我们的驱动了,接着我们将usb电缆先插到了电脑上但没有任何信息反应出来,电脑上就报一个“发现一个无法识别的设备”,这个其实也是我们预料之中的,不过还是令我们有些懊恼,问题出在哪呢??

后来我们在驱动源代码里面每个函数里都加上printk,这样可能会影响时序,但在驱动刚开始还是可以考虑的,因为这时候还不能肯定硬件有没工作,还没有到考虑时序那一步,所以这个调试方法还是很有效的,

1)  加了调试信息后发现第一次setup传输,主机要求发18个设备描述符时,我们4020却只发了16个字节,最后两个字节没有发,这个貌似是fifo只有满的时候才会硬件发包的问题,于是我们排查write_fifo函数,最后发现是USBD的DMA的burst长度只能是16,其他burst长度都不行,都会出现发一会就不会发包的问题;

2)  在查write_packet函数时我们又发现4020的USBD的包长固定为64个字节,这句话的含义是DMA每次向USBD的fifo里面读写数据的长度必须是64,如果时间的包长没有64时,我们必须对余下的fifo填0或读0,而usb协议中的包长是通过硬件寄存器USBD_TXLENGTH和USBD_RXLENGTH自动完成封包的;

3)  另外我们发现usb协议中控制传输和数据bulk中的数据包握手包ACK,NAK都是sep4020硬件自动完成的,不用我们手动回复,但如果在控制传输中USBD在IN传输中但发的最后一个包是8字节(因为4020的控制包长最大为8字节),必须要发一个空包给电脑,这时候就需要我们对USBD_EP0OUTSTAT_V的第5位赋1来强制硬件发一个空包,完成usb协议的控制传输的要求;

4)  在控制传输的最后是使能一个数据输出端点和一个数据输入端点,这里我是准备将ep1设为输入端点而ep2设为输出端点,而当我按一般配置来写时,它居然将ep1设为输入,同时又将其设为输出,气死人了,于是我们一不做二不休将ep1的输出寄存器全部赋0,主要是将USBD_EP1OUTSTAT_V赋0,再将USBD_EP2INSTAT_V赋0,这样就强制ep1为输入,ep2为输出了。

经过上面的调试bug,总算是让usb的枚举过程完成了,在电脑的右下方总算是看到了一个箭头标志表明是一个mass storage device设备了,总算把这个控制过程顺利完成了,本以为数据传输过程应该容易些,但事情还是不顺利,原来由于是控制传输,所以加printk没多大问题,毕竟速度不是很快,printk不怎么影响时序,但在bulk阶段,时序要求比较严格了,加了printk很可能就会死在printk上,然后一直在串口打印信息就这么让系统挂掉了,所以必须将串口打印信息减小到最小,这时候有发现以下问题:

1)  我们如果打开USBD的IN中断的话,在bulk传输的第一次传输中,当4020接到上位机的CBW(Inquiry)命令后就一直死在IN中断中出不出来了,正file_storage的主线程都down掉了,这个令人很是郁闷,为此想到了屏蔽IN中断,于是就在sep4020_queue里面每次第一次传输IN传输的时候就将IN中断打开,在接到CBW的时候又将IN中断屏蔽掉,这样效率很是不高,但也没有办法,后面在时序优化部分我们就彻底解决了此问题,将IN中断给屏蔽掉了,这个等会再讲;

2)  上面只是让usbd跑了一步,很快usbd又挂掉了,但这次挂在SCSI的0x15命令上面了这条命令是设置usbd的模式,这里电脑向4020连续发了两个OUT包,第一个是CBW的命令,第二个是out数据包,但我发现在read_packet中接收的OUT包长度就只有24,第二个包怎么也接收不到,我将USBD_RXLENGTH_V打印出来看发现第一次在接收CBW的时候这个接收包长度寄存器就是24了,也就是说4020两个out包一起接收了,而且后面那个包把前面那个out包给冲掉了,查了半天我们觉得是OUT中断函数没有处理好,因为我们发现自己的代码居然是在中断函数返回时才将中断给清除掉,而这时很可能我们已经将刚才在一次发生的那个OUT中断也给清除掉了,这个在中断函数没屏蔽中断时是很有可能会发生的,于是我们将中断清除操作放在了进中断函数的时候,但问题并没有就此解决了,还是存在问题,看来是out处理速度不行,没办法自己手动在程序中判断读取,如果在OUT包中发现前四个字节是0x43425355的时候说明这是一个CBW命令于是强制在接收fifo中读取31个字节将CBW强制读取出来,这样这一关又幸运过掉了;

3)  但做到上面两点并没有让我们的usb程序走得更远,而是让我们的usbdevice死的更惨,而现在的问题是在数据传输阶段,不知道会在什么时候它就会挂掉,这更让人摸不着头脑,晕,没办法看来这种传输方式是存在问题的,看来得重新编写数据传输部分了,这次我们是想彻底屏蔽掉IN中断和OUT中断,完全让上层的协议帮助我们全部实现,不让中断打断,我们想让DMA根据最后的传输长度自动传输,中间传输过程就都不用软件参与了,这样肯定能让传输速度快不少,修改数据传输代码后,传输问题基本解决,但在格式化u盘的时候,在最后一步清空FAT的文件分配表的时候出现了问题,由于这次传输的数据量很大,基本是64k的数据,这中间几乎每次都会卡掉,后来我们查原因发现在sep4020_queue函数中的输入部分中USBD_RECEIVETYPE_V的第二位老为0,也就是接收fifo总是空的没有数据,而按理这时候应该fifo肯定存在数据的,最后怀疑是usb_read_reg(USBD_RECEIVETYPE_V)这个读寄存器的方法出问题了,这个寄存器是可以直接读写而不用加任何读写锁机制,也就是直接*(RP)USBD_RECEIVETYPE_V操作就行了,这样基本上虚拟u盘就能工作了,哈哈

但实际测试发现我们4020的虚拟u盘支持FAT16格式的而不支持FAT32格式的

相关阅读 更多 +
排行榜 更多 +
模拟修脚达人

模拟修脚达人

模拟经营 下载
无敌赛车王

无敌赛车王

赛车竞速 下载
多人汽车聚会

多人汽车聚会

赛车竞速 下载