基于USB设备的Linux网络驱动程序开发
图3
图4
这些资源中,端点对于USB设备有着最重要的意义,实际的数据传输就是通过对端点的读写来实现的。驱动程序通过描述符来获取这些资源。在初始化时,USB驱动程序从设备端点0读取描述符,经过解析后保存这些资源的属性,为传输数据做准备。
由于采用了抽象的硬件资源,Linux下的USB设备管理也采用了与网络子系统类似的栈结构,如图4所示。
USB Core对USB驱动程序屏蔽了不同USB主机控制器之间的差异,使它们对于USB驱动程序来说具有统一的接口。USB驱动程序通过发送URB(USB Request Block)与USB Core交换数据,USB Core解释URB,并将URB中包含的数据请求通过USB主机控制器发送给对应的USB设备。另一方面,USB Core负责检测USB设备的插入和拔出等事件,当这些事件发生时,USB Core通知内核,使内核能够调用驱动程序的相应回调函数来通知驱动程序对这些事件做出响应。
4.2 USB网络设备驱动程序设计
除了必要的回调函数以外,Linux下的每一种驱动程序都必须有初始化函数和卸载函数。初始化函数需要根据相应的硬件设备,向内核注册不同的数据结构,来声明自己对该设备的支持。对于USB设备来说,初始化函数中需要注册struct usb_driver,该数据结构中的关键域分别为:owner,用于内核维护模块使用计数;name,驱动程序名称;probe,设备初始化函数指针;disconnect,设备删除函数指针;id_table,驱动程序支持设备列表。设备列表指明该驱动程序所支持的设备标识,对于USB设备来说,一般是Vendor ID和Product ID。每当一个USB设备插入系统,内核将查找现有的所有USB设备列表,判断应该调用哪个驱动程序所注册的probe函数来完成设备初始化。当USB设备拔出时,相应的disconnect函数也会被调用,来处理驱动程序的卸载。因此,USB网络驱动程序应在probe函数中初始化设备和注册网络接口。在disconnect函数中注销网络接口。
probe函数的主要代码如下:
ether_setup(netdev); //使用内核通用的以太网回调函数设定hard_header等函数
SET_MODULE_OWNER(netdev); //设定模块拥有者,用于维护使用计数
netdev->open = thu_plc_open; //设定open函数
netdev->stop = thu_plc_close; //设定stop函数
netdev->tx_timeout = thu_plc_tx_timeout; //设定超时函数
netdev->hard_start_xmit = thu_plc_start_xmit; //设定发送函数
netdev->get_stats = thu_plc_netdev_stats; //设定状态统计函数
netdev->watchdog_timeo = THU_PLC_TX_TIMEOUT; //设定超时值
netdev->mtu = THU_PLC_MTU; //配置网络接口的MTU
……
if(!thu_plc_config_dev(dev, intf, id)) { //配置USB网络设备
printk("couldn't configure the devicen");
break;
}
……
if(register_netdev(netdev) != 0) { //注册ethernet接口
printk("couldn't register the devicen");
break;
}
……
其中thu_plc_config_dev函数用来检测和配置USB设备。当probe函数成功返回时,驱动程序已经完成了USB设备的检测和网络接口的注册。而网络接口的正式启用还需要用户或应用程序使能该接口。例如用户可以使用ifconfig命令来启用网络接口。当接口被正式启用时,驱动程序的open回调函数被调用,由于USB设备没有类似于硬件中断的异步通知方式,需要主机主动查询是否有数据需要读取,而网络设备则需要有能力来异步通知操作系统数据包的到达,因此,在open函数中需要向USB Core发送一个读请求的URB,使得当USB设备需要将数据包输入主机时,Linux能够及时响应。
linux操作系统文章专题:linux操作系统详解(linux不再难懂)
评论