基于USB设备的Linux网络驱动程序开发

发布者:BlissfulCharm最新更新时间:2012-05-21 来源: 微计算机信息 关键字:Linux  USB  网络驱动程序 手机看文章 扫描二维码
随时随地手机看文章
1 引言

做为开放源代码 (Open Source) 运动重要组成部分,Linux操作系统吸引了数以万计的程序员共同开发。由于Linux比较完整的继承了各种UNIX版本的稳定和高效,并且克服和改进了传统UNIX中的很多缺点,更因为其开放的开发模式,Linux成为一个具有强大网络服务功能的操作系统。它支持主流的TCP/IP以及IPX/SPX、 NETBEUI等众多网络协议,无论在嵌入式系统,服务器还是桌面操作系统领域,Linux都取得了广泛的应用。网络驱动程序和网络硬件设备实现网络协议栈中的数据链路层和物理层,对上层协议提供支持,是网络协议栈的重要组成部分,对Linux的网络性能起着决定作用。本文主要讨论基于USB总线的 Linux网络驱动程序的设计和实现方法。

2.   Linux体系结构

出于稳定性和安全性的考虑,现代处理器往往具有至少两个运行级别。权限较低的级别无法访问所有的寄存器,不能对硬件直接操作。而权限较高的级别能够进行所有的硬件操作和访问任何系统资源。Linux设计充分利用了现代处理器的上述特性,其内核部分运行于高权限级别,应用程序运行于低权限级别。作为宏内核结构的操作系统,Linux将进程管理、内存管理、网络协议栈、设备驱动和文件系统等服务都集成在内核中,而应用程序则通过系统调用与内核通讯,内核结构如图1所示。

    

图1 

 图2

Linux网络子系统基本可以分为系统调用接口、BSD套接字、INET套接字、TCP/IP以及数据链路层。其中,BSD套接字由INET套接字层提供支持,而INET套接字管理着基于IP的TCP或UDP协议端,实现IP分组排序以及控制网络子系统效率等功能。各种网络驱动程序位于IP层之下,它们具有访问硬件设备的能力,实现数据链路层的功能。网络子系统的结构如图2所示。

3.   Linux网络驱动程序结构

一个完整的驱动程序是一组回调(Callback)函数的集合。内核根据用户或自身的需要来调用驱动程序提供的函数指针,将控制或数据请求交给相应的驱动程序。驱动程序负责了解相应硬件设备的访问和控制方式,将内核的请求翻译成设备可以理解的操作。这样的层次结构使得内核不必了解硬件设备的访问机制和细节,驱动程序也无需明白内核的控制策略,大大提高了驱动程序的兼容性,同时也方便了程序的调试。根据驱动程序类型的不同,内核要求驱动程序提供的回调函数也不同。

Linux下的Ethernet驱动程序需要注册的回调函数分为“必要”和“可选”两类。“必要”的回调函数是指一个Ethernet驱动程序正常工作所需要的回调函数最小集合,而“可选”的回调函数则是在“必要”的基础上提供更丰富的特性和功能。“必要”的回调函数如表1所示。

函数名称

功能

open

打开网络接口;将硬件设备配置为正常工作状态。

stop

关闭网络接口;停止硬件设备工作,释放打开该接口时分配的系统资源。

tx_timeout

当上层协议认为某个数据包发送超时的时候调用;该函数应该解决数据包发送超时问题,并保证函数返回以后,数据包能够正常发送。

hard_start_xmit

由上层协议在希望发送数据包时调用;将来自上层协议的数据包转换为硬件设备能够处理的格式,发送至硬件设备。

get_stats

当上层希望了解驱动程序控制的网络设备的统计信息的时候被调用。

hard_header

根据网络子系统先前地址解析的结果,构造数据包的硬件包头。

rebuild_header

在ARP解析完成之后,由上层协议在发送任何新数据包之间调用,重新构造硬件包头。

set_config

改变网络接口的配置,例如中断号和I/O地址等。

                                                 表1

为了方便Ethernet驱动程序的设计,Linux内核为hard_header、rebuild_header和set_config提供了通用的回调函数。如果对硬件包头或设备配置没有特殊的要求,通用的回调函数就能够满足网络子系统的要求。

4.   基于USB总线的Linux网络驱动程序设计

4.1 USB设备的访问和控制

与PCI、ISA等设备不同,USB、1394等新一代总线没有IO/MEM映射、中断和DMA硬件资源。取而代之的,是抽象出来的硬件资源概念。对于USB设备来说,资源主要包括配置(configuration)、接口(interface)和端点(endpoint)。三者之间的关系如图3所示。[page]

 

图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 device\n");

       break;

}

……

if(register_netdev(netdev) != 0) {                //注册ethernet接口

       printk("couldn't register the device\n");

       break;

}

……

其中thu_plc_config_dev函数用来检测和配置USB设备。当probe函数成功返回时,驱动程序已经完成了USB设备的检测和网络接口的注册。而网络接口的正式启用还需要用户或应用程序使能该接口。例如用户可以使用ifconfig命令来启用网络接口。当接口被正式启用时,驱动程序的open回调函数被调用,由于USB设备没有类似于硬件中断的异步通知方式,需要主机主动查询是否有数据需要读取,而网络设备则需要有能力来异步通知操作系统数据包的到达,因此,在open函数中需要向USB Core发送一个读请求的URB,使得当USB设备需要将数据包输入主机时,Linux能够及时响应。[page]

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

关键字:Linux  USB  网络驱动程序 引用地址:基于USB设备的Linux网络驱动程序开发

上一篇:嵌入式WEB服务器中TCP/IP协议栈的设计与实现
下一篇:基于嵌入式系统的彩色液晶显示驱动控制

推荐阅读最新更新时间:2024-03-16 12:59

OK6410A 开发板 (八) 35 linux-5.11 OK6410A 内存管理第三阶段
C setup_arch- paging_init- bootmem_init- memblock_allow_resize返回 - mm_init- mem_init返回 ----此时memblock消亡,buddy初始化完成,开启了基于虚拟内时代的 buddy内存管理器时代 流程 setup_arch(&command_line);- paging_init bootmem_init find_limits(&min_low_pfn, &max_low_pfn, &max_pfn); sparse_init zone_sizes_init(min_low_pfn, max_low_pfn, max_p
[单片机]
基于ARM处理器S3C2440和Linux系统的I2C触摸屏
0 引言 随着计算机相关技术的发展,ARM嵌入式系统受到越来越广泛的应用,与人们生活的结合也越来越紧密。触摸屏设备因其友善的人机交互性,操作方便灵活,输入速度快,被广泛的应用于这种嵌入式领域中。嵌入式Linux系统具有开发源代码、内核稳定、可裁减性等特点,吸引着众多商业公司和自由软件开发者的目光,成为嵌入式系统领域不可或缺的操作系统之一。触摸屏是一种输入设备,操作简单易学,可靠性高,不占额外的空间,是最常用的便携式系统的输入设备。特别是电阻式触摸屏,它结构简单,成本低,透光效果好,工作环境和外界完全隔离,不怕灰尘和水气,同时具有高解析度、高速传输反应、一次校正、稳定性高、不漂移等特点,因而被广泛用于工业控制领域。 1 电阻式触摸屏
[单片机]
基于ARM处理器S3C2440和<font color='red'>Linux</font>系统的I2C触摸屏
Linux基金会宣布欧洲LinuxCon + CloudOpen + Embedded Linux大会会议日程
欧洲最重要的开源大会和同场举办的活动表明Linux和开源理念在各技术领域发展。 Marketwired 2014年8月15日美国加利福尼亚州旧金山消息――致力于促进Linux和协同发展的非营利性组织Linux基金会(The Linux Foundation)今天宣布了欧洲LinuxCon + CloudOpen + Embedded Linux大会的日程和会议安排,本次大会将于2014年10月13日至15日在德国杜塞尔多夫举行。 今年大会安排的演讲嘉宾和分会议将展现不同行业的领导者利用开源和协同的力量进行技术创新和发展。Linux和其他开源项目支持的开源理念业已发展到被应用于医疗、制造和数据科学等许多其他领域。
[其他]
Linux-2.6.32.2内核在mini2440上的移植(四)---根文件系统制作(2)
移植环境 1,主机环境:VMare下CentOS 5.5 ,1G内存。 2,集成开发环境:Elipse IDE 3,编译编译环境:arm-linux-gcc v4.4.3,arm-none-linux-gnueabi-gcc v4.5.1。 4,开发板:mini2440,2M nor flash,128M nand flash。 5,u-boot版本:u-boot-2009.08 6,linux 版本:linux-2.6.32.2 7,参考文章: 嵌入式linux应用开发完全手册,韦东山,编著。 Mini2440 之Linux 移植开发实战指南 http://linux.chinaunix.net/techdoc/system
[单片机]
玩转USB HID系列:Linux下使用C语言和libusb开发USB HID
实验环境 ubuntu 16.04 LTS 64-bit gcc,vim STM32做下位机 开发步骤 安装libusb sudo apt-get install libusb-1.0-0 注意:我在网上找到了多个安装教程写的安装命令和我的不一样: sudo apt-get install libusb-dev sudo apt-get install libusb-1.0-0-dev 这些命令安装的libusb我没有探索过,但有一点我知道,也是特容易踩坑的地方: 不同的libusb引用的头文件和函数还有编译时加的库名有细微的区别!!坑死我了 而好多教程都是上来就列代码,这就导致我们在看文章的时候觉得挺好,但是在自己机器上
[单片机]
玩转<font color='red'>USB</font> HID系列:<font color='red'>Linux</font>下使用C语言和libusb开发<font color='red'>USB</font> HID
研华科技发布AIM-Linux社区并邀请用户加入
2022年6月,研华科技(Advantech)宣布启动一个技术支持论坛,即 AIM-Linux社区 。这是一个在线社区,旨在为研华科技和用户提供一个交流平台,在上面基于Arm和Linux的开发人员可与研华科技及其合作伙伴的工程师保持联系。 研华科技一直以来由研发工程师向用户提供丰富且直接的技术支持,然而我们也意识到,很多用户往往具有类似的疑问。因此,研华科技希望提供一个在线论坛——AIM-Linux社区,用户可以在这里发布问题、公开列出工程师反馈和技术解决方案。这样做的目的是通过共享集体知识、技术和活动造福社区。 研华科技副总裁Aaron Su表示,“研华科技投入了大量时间来了解我们的客户希望访问哪些信息,以及如何以
[工业控制]
研华科技发布AIM-<font color='red'>Linux</font>社区并邀请用户加入
Mini2440烧写Linux系统
按照国嵌课程在Linux系统下使用dnw软件烧写系统失败,开机白屏。 烧写步骤: 1.保持S2开关拨至nor flash开关处,连接串口线打开串口工具以便观察运行情况,打开电源开关。 2.使用jlink,配置好工程文件,并将supervivi-128M.bin烧写进nor flash中,记得Target- Program前先Target- Erase。 常见错误:开发板上电后串口无输出,检查少写完系统安装辅助软件后,有没有将JTAG线拔下。 3.连接USB下载线到PC机,在虚拟机中Linux系统下安装USB驱动(“insmod”命令),成功安装后即可点亮Linux状态栏USB图标,在SecureCRT窗口下输入
[单片机]
Mini2440烧写<font color='red'>Linux</font>系统
XL2403芯片概述、应用领域及特性
XL2403 内置了2.4GHz 数据收发芯片和带USB 驱动高性能的微处理器,采用TSSOP16 薄体封装,适用于 PC 外设和其他带操作系统的控制平台的USB Dongle。全兼容低速USB1.1规格。精简单指令8051内核,有着2个16位定时器,2个UART串口。还内置了MCURC振荡器。该芯片在各种USB Dongle上都有着较好的应用。 一、典型应用领域: 1、PC 设备USB Dongle; 2、带操作系统的网络播放器USB Dongle; 3、带操作系统的机顶盒USB Dongle; 4、无线游戏手柄USB Dongle; 5、其他的带操作系统的USB 应用。 二、XL2403芯片特性 1、工作电压范围:4.
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved