霡霂的个人空间 https://blog.eetop.cn/maimu [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

USB 无线网卡在嵌入式硬件平台上的设计与实现

已有 1671 次阅读| 2006-6-22 17:57 |个人分类:备份

天气: 晴朗
心情: 高兴
USB 无线网卡在嵌入式硬件平台上的设计与实现

冯非,卫宏儒
(湖南大学,长沙 410082)

摘要: 本文讨论了在嵌入式硬件平台上实现通用 USB 无线网卡的一般方法。首先介绍了如何对驱动进行交叉编译,然后分析了开发过程中常遇到的一些典型错误,并提出了相应的解决方案。
关键词: 交叉编译;嵌入式平台;分包传输;死锁
中图分类号: TP334.7 文献标识码 : B 文章编号: 1003-0107(2004) 08

1 裁减驱动

    根据 Linux Open Source 的原则,网络上出现了很多 GPL 的自由软件, GPL 即 GNU 通用公共许可证,它力图保证开发者共享和修改自由软件的自由 —— 保证自由软件对所有用户是自由的。因此下面的网卡实现将选择具有 GPL 的自由驱动进行。

1.1 修改 Makefile
    在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入 gcc 命令进行编译的话,则会非常不方便。因此,人们通常利用 make 工具来自动完成编译工作。这些工作包括:如果修改了某几个源文件,则只会重新编译这几个源文件;如果某个头文件被修改了,编译所有包含该头文件的源文件。利用这种自动编译可大大简化开发工作,避免不必要的重新编译。
makefile 文件就是 make 工具用来完成并自动维护编译工作的。 makefile 需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执行文件,并定义了源文件之间的依赖关系。当修改了其中某个源文件时,如果其他源文件依赖于该文件,则也要重新编译所有依赖该文件的源文件。
    默认情况下, GNU make 工具在当前工作目录中按如下顺序搜索 makefile :
    * GNUmakefile
    * makefile
    * Makefile
    在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile ,则可利用类似下面的 make 命令选项指定 makefile 文件: $ make -f Makefile.debug

    1.2 交叉编译
    关于什么是交叉编译,本文前面已经有了介绍。当 Makefile 改写完毕且编译没有出现问题后,下一步工作就是将 USB 驱动用到的相关源程序文件和头文件放到要打到目标板的内核中。
    由于本项目开发几种不同芯片的无线网卡,因此在内核中另外建了一个目录 wireless 。通过下列命令:
    cp xx.c linux-2.4.18/drivers/usb/wireless
    cp xxx.h linux-2.4.18/drivers/usb/wireless
    将 USB 驱动需要的源程序文件和头文件拷贝到内核中,其中 xx 和 xxx 代表不同的文件名称。之后在内核根目录下编译生成     zImage 文件(二进制文件):
    make zImage
    该文件是 elf 格式的。 linux 环境下不是所有的二进制文件都有相同的格式, linux 系统使用二进制文件的处理程序来实现对不同二进制格式文件的分别处理,而 ELF 就是现在主流的 linux 二进制文件格式。尽管如此,它和其他格式一样需要使用二进制处理程序,因此无法在 PC 上执行。使用下列命令
    cp arch/yourchip/boot/zImage /tftp/zImage
    将生成的二进制文件放到 /tftp/ 目录下,该目录是下载核心的目录,而 boot 目录对应为 linux 启动程序的目录, boot 目录的路径根据不同的硬件开发环境,不同的交叉编译环境有所不同。这样,目标板就可以从事先设定好的 ftp 上下载 zImage 文件启动运行了。

1.3 修改菜单配置界面
    将驱动打入内核,就需要在菜单配置界面里安排选项,以便用户日后进行配置。使用下列命令:
    vim linux-2.4.18/drivers/usb/Makefile
    进入菜单配置的 Makefile 文件,在其中加入: subdir-$(CONFIG_ADD_WLAN) += wireless
这样就在内核的 drivers/usb/ 目录下生成了一个 wireless 文件夹,驱动的相关文件就是放在这里。之后进入 wireless 目录下,编辑相应的 Makefile 文件,在其中加入:
    obj-$(CONFIG_USB_WIRELESS_ADAPTER) += xxxx.o xxxxx.o xxx.o ( xxx 代表驱动源程序生成的相应目标文件)
修改完毕,系统就会根据 wireless 目录下的 C 程序文件及头文件生成所需的目标文件,从而为设备提供所需的支持。

2 开发过程中遇到的问题
    开发过程总是充满了未知的错误,尤其是 USB Wireless 开发,它建立在 USB 驱动程序的基础之上,两者的磨合更非一帆风顺。
2.1 分包传输问题
    在实现无线网卡 Prism 驱动的过程中,需要一并实现应用层的配置工具: wlanctl-ng 。交叉编译之后,会出现如下问题:
    hfa384x_docmd: ctlx failure=REQ_FAILED
    hfa384x_drvr_start: cmd_initialize() failed, result=-5
    prism2sta_ifstate: hfa384x_drvr_start() failed,result=-5
    message=lnxreq_ifstate
    ifstate=enable
    resultcode=implementation_failure
    此问题的前提是该平台已经成功实现了 Prism 网卡的驱动。

2.2 分包问题的探索和分析
    经过大约一个月的调查和追踪,发现该问题在程序中的调用顺序如下:
    hfa384x_drvr_start()->hfa384x_cmd_initialize(hw)->hfa384x_docmd()->hfa384x_usbctlx_submit_wait() 。也即设备的 urb 请求发送出去以后,一直处在等待响应的阶段。因此问题深入到 OHCI 驱动层。
    经过深入研究发现,由于该硬件平台没有 bus master ,所以 USB hcd chip 只能访问片内内存(容量很小只有 4M ),所以不能同使用标准 OHCI 那样申请 transbuffer ( urb 结构里面的一个成员,放用来传输的数据) , ed , td (用于发送接收数据的 ohci 定义的结构)等数据结构。它重新定义了自己的内存分配函数。并且采用了一种 divide 机制。这种机制的含义就是把一个比较长的 transbuffer 的 urb 请求,分配成为多个比较短的 urb 请求。但是由于代码对此处理的不是很好,对于 urb 请求的     transbuffer 实际上,很多时候实际传输长度,并不能和请求的长度相等。有时候,甚至没有一个经过分配的长度长。
而该芯片的 OHCI 驱动并没有考虑到这一点。例如,一个请求的长度是 4000 ,驱动把它分为两个 urb 请求发送,每一个请求长度 2000 。但是实际传输量不到 2000 ,第一个正确地返回以后, OHCI 并没有返回设备驱动那里。而是又发送第二个,这样外围设备已经没有需要返回的数据了,而 OHCI 还在那里等待第二次传输,造成了 OHCI 的停止运作。以下是问题的简要图示:



2.3 解决办法
    解决该问题的方法是,在每一个 urb 返回的时候,含有一个数据成员表示实际传输数据量,根据这个数据和 2000 比较,然后判断是否需要第二次传输。
    如果实际传输的数据量 =HCD 申请的一个数据块的大小 , 如 >=2000 ,说明 USB 设备或许还有数据没有传输完毕,此时 OHCI 将等待第二次甚至多次的传输。逻辑关系如下图 2 所示:



2.4 死锁问题的出现
    在开发平台上实现无线 Prism 网卡驱动之后,不仅需要正常完成各项功能,还需进行健壮性的测试,比如即插即用,带电热插拔( Hot Plug )等等,以达到智能网关的要求。
    实际上,在实现无线网卡驱动之前,首先需要将网卡在 PC - Linux 上进行一下性能的评估和使用测试,以发现驱动本身是否有 bug ,并摸索正确的配置方法。驱动基本开发完毕之后,在目标板上进行 Prism 网卡的热插拔测试时,将 USB 网卡带电拔下之后,目标板上有时会出现错误信息提示。
    本机终端上反复打印此调试信息( dmesg 的结果中也会继续增加此调试信息的数目)然后 usb 系统基本崩溃。再次连接 usb 设备,则驱动失败, dmesg 也无任何关于发现新设备的报告。 重启 Linux 操作系统之后,执行修复硬盘命令: fsck –f 。再次重启, Linux 系统才能恢复正常。

2.5 死锁问题的探索和分析
    由于之前在 PC-Linux 上的网卡评估中并没有出现热插拔错误,所以第一个想到的问题所在就是目标板上的底层驱动程序中可能仍然存在 bug 。在调试该 bug 的过程中,还是遵循着一贯的思路,即由上层应用程序逐步探索到底层 USB 驱动,直至最后的 hub 驱动程序。最后在根 hub 和物理 hub 的驱动程序 hub.c 中发现了症结所在: usb_reset_device ()函数(重置 hub )与其他进程间发生了死锁。
    在 Linux 等多用户操作系统中,提供一种信号灯机制来防止两个或多个进程同时访问共享资源。在铁路系统中信号灯可防止两列火车在同一轨道上相撞,在计算机系统中,信号灯能保证访问共享资源不会发生混乱。互斥操作要求任何时刻只能有一个或一类进程访问信号灯 ( 执行一个特定的代码区域 ) 。 linux 下有两种实现数据互斥的基本机制,包括 semaphore (信号量)和 spinlock (自旋锁)。自旋锁的主要功能是支持 smp (对称多处理系统)的互斥。理解自旋锁的最简单方法是把它作为一个变量看待,该变量把一个例程或者标记为“我当前在另一个进程上运行,请稍等一会”,或者标记为“我当前不在运行”。如果 1 号进程首先进入该例程,它就获取该自旋锁。当 2 号进程试图进入同一个例程时,该自旋锁告诉它自己已为 1 号进程所持有,需等到 1 号进程释放自己后才能进入。
    在 hub.c 程序中,出现一种隐性死锁,程序进入互斥区间处理拔掉设备的收尾工作,之后等待另一个进程的通知: wait_ms(10) ,同时会继续保持锁。然而由于进程在互斥区等待,其他进程无法通知该进程运行结果,因此导致了死锁的产生。收尾工作没有顺利完成,导致错误信息: waiting for eth1 to become free 。

2.6 解决办法
    这是一种在等待时占有的锁,是一种隐性的锁,由于并不显而易见,需要仔细分析代码才能看出。但问题的解决相对简单:只需修改 usb_reset_device ()函数,将 wait_ms(10) 函数移出互斥区即可。在互斥区外等待其他进程传递信息,会保证通讯的顺利进行。

3 结语
    本文讨论的是在嵌入式硬件平台上实现通用 USB 无线网卡的一般方法,并就开发过程中遇到的问题做了详细的分析,最后给出了各个问题相应的解决方案。由于本文涉及的 OHCI 驱动是专门开发出来的用于匹配特殊目标板的 USB 驱动,本身就可能有很多不完善的地方,这就决定了在无线网卡的开发过程中势必会有上层驱动与底层 USB 的磨合。本文是一个完整的 USB 无线网卡的实现,对 USB 无线网卡的开发者有一定的参考价值。

参考文献 :
    •  毛德操、胡希明 编著。《 Linux 内核原代码情景分析 (上、下)》。浙江大学出版社, 2001
    •  李善平等,编著。《 Linux 内核 2.4 版原代码分析大全》。北京工业机械出版社, 2002 。
    •  W. Richard Stevens 编著。《 UNIX 环境高级编程》机械工业出版社, 2003 。
    •  周立功 等编著。 《 PDIUSBD12 USB 固件编程与驱动开发》北京航空航天大学出版社, 2003 。
    •  Alessandro Rubini & Jonathan Corbet 著,魏永明等译。《 Linux 设备驱动程序》第二版。中国电力出版社, 2002 。
    •  IrDA 。《 ‘IrCOMM': Serial and Parallel Port Emulation over IR (Wire Replacement) 》 Infrared Data Association , version 1.0 , 1995 。
    •  邹思轶 主编。《嵌入式 Linux 设计与应用》。清华大学出版社, 2002 。


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 3

    粉丝
  • 1

    好友
  • 6

    获赞
  • 45

    评论
  • 3123

    访问数
关闭

站长推荐 上一条 /2 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-16 22:13 , Processed in 0.031638 second(s), 14 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部