open回调函数的主要代码如下:
……
usb_fill_bulk_urb(dev->rx_urb, //构造读请求的URB
dev->udev,
usb_rcvbulkpipe(dev->udev, 6), //指定读端点
dev->rx_skb->data,
512,
read_bulk_callback, //使用read_bulk_callback做为URB的
dev //回调函数。
);
if((result = usb_submit_urb(dev->rx_urb, GFP_KERNEL))){ //将URB发送给
…… //USB Core
}
netif_start_queue(netdev); //使能网络传输队列
……
当读请求URB完成时,意味着主机收到了一个数据包或该URB超时,此时read_bulk_callback将会被内核调用。无论是哪种情况,为了将来可能到来的数据包能够及时得被主机读取,驱动程序都应该再发送一个读请求URB给USB Core。而在主机收到数据包的情况下,read_bulk_callback函数构造一个skb_buff数据结构来描述数据包,并调用 netif_rx函数,把该数据包交给上层协议,从而完成一次接受过程。
与接受过程相比,发送数据包的过程简单了很多。当网络子系统准备发送一个数据包时,上层协议将会构造一个skb_buff数据结构来描述数据包,并且调用网络驱动程序注册的hard_start_xmit回调函数来发送该数据包。由于该函数被调用时内核持有xmit_lock自旋锁,因而驱动程序可以不必考虑对设备写操作的同步问题。hard_start_xmit函数根据数据包的长度将其拆分为USB设备可以传输的长度,然后构造相应的写请求 URB,发送至USB Core即可。
hard_start_xmit回调函数的主要代码如下:
……
usb_fill_bulk_urb(dev->tx_urb, //构造写请求的URB
dev->udev,
usb_sndbulkpipe(dev->udev, 2), //指定写端点
skb->data,
512,//count,
write_bulk_callback, //使用write_bulk_callback做为URB的回调函数。
dev
);
if((result = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))){ //将URB发送给
…… //USB Core
}
写请求URB完成时,write_bulk_callback回调函数被内核调用。该函数判断写请求URB是否成功完成。根据URB的完成情况,驱动程序需要更新网络接口的相应统计数据,例如成功/失败发送包的数目等。
5. 小结
本文从工程应用出发,介绍了Linux的体系结构及其网络子系统,并结合USB设备在Linux下的访问机制,研究了USB驱动程序实现异步通知的方法,并给出了USB网络驱动程序的设计框架和实例。在实际测试中,本文分析的驱动程序运行稳定,并且达到了预期的网络传输速度。
参考文献:
[1] J. Corbet, A. Rubini, and G. Kroah-Hartman. Linux Device Drivers, Third Edition. 2005, O'Reilly Media, Inc.
[2]毛德操 胡希明. Linux内核源代码情景分析. 2001. 浙江大学出版社.
[3]Daniel P. Bovet, M. Cesati. Understanding the Linux Kernel, Second Edition. 2002, O'Reilly Media, Inc.
[4]李少甫 何小庆 江文瑞.The Development of Embedded Wireless LAN Application System Based on MontaVista Linux.微计算机信息. 2002年11期49-51