linux usb_gadget:设备控制器驱动测试(包含更新主机内核的方法)

时间:2022-05-23 05:48:10

一、简介

        目前正在做的项目的那部分是将SOC作为一个USB从设备,插到电脑上能被识别为U盘。当在SOC上安装好设备控制器的驱动之后,你需要检测你做的驱动是否符合规范。

        测试的原理是这样的,在主机端有usbtest内核模块,他是一个专用于测试的usb主机上的设备驱动,该模块通过ioctl接口提供了各种测试类型,比如Simple non-queued bulk I/O tests,Queued bulk I/O tests等等,在主机端的应用层有一个程序及脚本(testusb.c and test.sh),用于触发各种测试。对应的在设备端,有各种gadget驱动配合测试,比如g_zero便是其中之一。这一套流程可以用来测试主机端及设备端的控制器驱动。http://www.linux-usb.org/usbtest/index.html(这里提供测试方法的介绍)。

        这里我结合在全志A20板子上所作的测试来作一个介绍。


二、测试流程

        最好找一台专用于测试的电脑,因为后面还要更新操作系统的内核。

        1、在linux3.3及linux3.3之前的kernel版本里,编译内核的时候以模块的方式选中"USB testing driver"便会去编译/usb/src/linux-source-3.xx.0/drivers/usb/misc/目录下的Usbtest.c文件,在/lib/modules/3.xx.0.24-generic/kernel/drivers/usb/misc/下生成usbtest.ko文件,

        在主机端,安装内核模块:modprobe usbtest

        在主机端,安装usbfs:mount -t usbfs none /proc/bus/usb,这样在/proc/bus/usb/下就会列出usb的设备信息(此时主机端驱动程序通过usbfs与用户空间的应用程序交互),应用程序testusb通过寻找usbfs挂载点来找到对应的usb device,即在/proc/bus/usb/目录下寻找usb设备信息。

        2、在linux3.3之后的kernel版本中,已经没有了usbfs,但是设备节点长在/dev/bus/usb目录,应用程序通过寻找usb设备子目录来找到对应的usb设备,调试的信息debugfs在/sys/kernel/debug/usb/devices中,此时,

        在主机端只要安装内核模块即可:modprobe usbtest

        3、在主机端编译出应用程序:进入/usr/src/tools/usb/目录,执行make生成testusb应用程序。

        4、在设备端,将zero gadget编译进内核

        发现虽然将zero gadget编译进内核了,但是A20板子插到电脑上却没有任何反应,原来需要在composite_bind函数的最后添加usb_gadget_connect(cdev->gadget),哈哈,在主机上lsusb发现"Bus 002 Device 016: ID 0525:a4a0 Netchip Technology, Inc.Linux-USB "Gadget Zero""。

      

         5、在主机端开始测试,sudo ./testusb -a,发现在测试testnum 10(queued control message)的case 8(clear endpoint halt)的时候设备停在那边不动了,原因是设备端在进入usb中断的时候中断处理函数已经获取sw_udc的lock,在sw_udc_set_halt里面又去获取sw_udc的lock,发生死锁,但是sw_udc_set_halt()这个函数还要被非中断处理函数调用,里面当然要加锁,所以当在中断处理函数里面要去调用sw_udc_set_halt的时候必须先spin_unlock_irqrestore(&ep->dev->lock, flags);,执行完sw_udc_set_halt()函数后在spin_lock_irqsave(&ep->dev->lock, flags);

        6、也可以不使用缺省值测试,关于testusb的参数说明如下。

linux usb_gadget:设备控制器驱动测试(包含更新主机内核的方法)

#以下可以正常运行(当然,可能多跑几次,就失败)

./testusb -a -c10 -t1 -s4096 -g32 -v32

./testusb -a -c10 -t2 -s4096 -g32 -v32

./testusb -a -c10 -t3 -s4096 -g32 -v32

./testusb -a -c10 -t4 -s4096 -g32 -v512

./testusb -a -c10 -t5 -s512 -g32 -v512

./testusb -a -c10 -t6 -s512 -g32 -v512

./testusb -a -c10 -t7 -s512 -g32 -v512

./testusb -a -c10 -t8 -s512 -g32 -v512

./testusb -a -c10 -t9 -s512 -g32 -v256

./testusb -a -c10 -t10 -s512 -g16 -v32

./testusb -a -c10 -t11 -s512 -g32 -v256

./testusb -a -c10 -t12 -s512 -g32 -v256

./testusb -a -c10 -t13 -s512 -g32 -v256

./testusb -a -c10 -t14 -s256 -g32 -v1

#以下一般会出错

#./testusb -a -c10 -t4 -s4096 -g32 -v32 #error

#./testusb -a -c10 -t4 -s4096 -g32 -v256 #error

#./testusb -a -c10 -t7 -s512 -g32 -v32#error

#./testusb -a -c10 -t7 -s512 -g32 -v256#error

#./testusb -a -c10 -t8 -s512 -g32 -v32#error

#./testusb -a -c10 -t8 -s512 -g32 -v256#error

#./testusb -a -c10 -t10 -s512 -g32 -v256#error

#./testusb -a -c10 -t14 -s512 -g32 -v256#error


        7、目前还存在以下几个测试的项目有问题,等待继续解决,由于需要主机测试驱动打印更多的信息,需要更新主机的内核。

        linux usb_gadget:设备控制器驱动测试(包含更新主机内核的方法)

     8、test9 --> 33(Numerical argument out of domain),但是这个错误在第一次./testusb -a -c1这样不会出现,第二次再执行命令的时候就提示这个错误了。

     在主机端echo 8 > /proc/sys/kernel/printk,使能打印出usbtest driver的消息。

     发现在usbtest driver的ch9_postconfig中打印了get config --> 1 0 (1 3),code是这样的:dev_err(&iface->dev, "get config --> %d %d (1 %d)\n", retval, dev->buf[0], expected);,也就是说dev->buf[0]中保存的是0,而期待的值是3,所以错误。

     这个错误可能有两个原因造成:1、发送端,但是我查找了get configuration之后的usb_ep_queue是正常把数据值为3发送出去了,除非用CATC抓取数据看看是否在通道上确实是值3;2、接受端,usb_control_message()是否存在没有接收到数据的风险。

     采取的办法暂时是将ch9_postconfig中get_configuration注销。


     9、test13 --> 110(Connection time out),通过主机端内核打印消息"ep 81 couldn't  get no-halt status,-110",找到在usbtest driver中的verify_not_halted()的第一句话usb_get_status(),这里没有等到device传来的status,于是fail了,但是我查看device的sw_udc_get_status()函数发现device确实把status发送了出来,为啥这里主机又没有接收到呢?会不会和上面get_configuration一个道理,usb_control_msg的消息当超时了不管是否收到数据都返回的原因呢?

     在sw_udc_get_status中因为要读取端点(可能是端点0、1、2等)的状态/控制寄存器,所以必须先写usb寄存器INDEX来选取目标端点,USBC_SelectActiveEp(g_sw_udc_io.usb_bsp_hdle, ep_num);与USBC_SelectActiveEp(g_sw_udc_io.usb_bsp_hdle, old_ep_index);同时要有保证处理完对应的端点之后在再回到原来的端点操作。

     ok了,但是在verify_halted()函数又出错了,原因是之前设置端点halt之后再读取这个端点设备应该回的status是0x0001(表示halted)才对,但是设备回了0,在设备端查看STALL的寄存器,发现在sw_udc_set_halt()函数中确实将USBC_BP_TXCSG_D_SEND_STALL置位,之后主机收到STALL信号应该给设备发送STALL的握手信号,从而设备的USBC_BP_TXCSG_D_SENT_STALL寄存器为1,但是我在sw_udc_get_status()函数中USBC_Dev_IsEpStall()读到USBC_BP_TXCSG_D_SENT_STALL为0,可能主机没有进行STALL的握手信号。


     10、test14、test21 --> 22(Invalid argument),解决办法是执行测试的时候设置好对应的参数:./testusb -a -c10 -t14 -v256,经过查看usbtest测试驱动的代码发现,param->vary的值必须小于param->length。



三、ubuntu更新内核的方法

        这个要盗用老大在cnblogs写的博客“用make-kpkg简化Ubuntu系统的内核编译过程”。

        本文介绍的make-kpkg可以用于所有Debian系的发行版如Debian,Ubuntu等,传统方式我们编译内核大概要经历以下几个步骤:1、配置内核,make menuconfig;2、编译内核和模块,make,make modules,make modules install,make install;3、生成initramfs并配置grub,经过第二个步骤的make install ,kbuild系统会把生成的内核镜像拷贝到INSTALL_PATH路径下(默认是/boot),但是这时不能用,我们必须配置手动grub才可以。另外很多版本会使用initramfs来作引导之用(还有部分发行版采用initrd),我们还需要为新内核手动生成initramfs镜像。

        但是如果你是Ubuntu/Debian用户,可以使用make-kpkg简化这个过程,还能带来其他好处

        1、sudo apt-get install kernel-package

        2、配置内核,可以在发行版默认的config基础上在进行配置,这样配置出的内核和发行版本身才有好的兼容性。因此在执行make menuconfig之前执行命令"cp /boot/config-2.6.35-24-generic .config",或者在menuconfig里面将/boot/config-2.6.35-24-generic load进来

    linux usb_gadget:设备控制器驱动测试(包含更新主机内核的方法)

        3、编译内核

        make-kpkg --initrd --revision wwang.001 --append-to-version -20110107 kernel_image

        或者sudo make-kpkg --initrd kernel_image kernel_headers

        --initrd选项会让make-kpkg自动帮我们生成initramfs

       --revision会给生成的deb文件加上一个版本信息,这个参数是会影响到文件名,如果不指定默认会是"10.00.Custom"

       --append-to-version也是一种版本信息,他不仅出现在deb安装包的文件名里,也会影响到kernel的名称,比如在本例中,内核更新完成,用uname -r察看发现"2.6.36-20110107"

        kernel_image表示生成内核和默认模块的安装包

        kernel_headers,这样make-kpkg会在生成一个内核头文件的安装包

        4、编译完之后,会在上层目录里生成一个deb安装包,本例中生成的安装包的文件名是"linux-image-2.6.36-20110107_wwang.001_i386.deb",之后我们就可以用dpkg命令或者在文件浏览器中双击安装了,安装完毕之后重启就可以选择进入新的内核。使用make-kpkg命令来编译内核还有其他好处,因为我们通过包管理器来安装新的内核,当不再需要这个内核时,就可以简单的通过dpkg命令、新立得软件包管理器或者ubuntu软件中心来完全卸载了,而不需要一个个手动删除修改。

        5、单独编译某个模块,比如编译usbtest.c驱动模块

cd /usr/src/linux-3.xx.0/drivers/usb/misc

make -C /usr/src/linux-3.xx.0/ M=$PWD modules

编译完了之后安装这个模块make -C /usr/src/linux-3.xx.0/ M=$PWD modules_install

这样只编译你需要的模块,避免编译整个内核模块,节省了不少时间。


文章转自:Linux-USB Gadget:Part 5:测试PXA UDC驱动,http://blog.csdn.net/zjujoe/article/details/2705321

还有一篇关于zero gadget的文章可以参考一下:http://blog.csdn.net/zjujoe/article/details/2675095