邹龙 王德志 刘忠诚 周治坤
摘要:文章分析了Linux系统的设备驱动原理,USB接口设备的驱动程序编写与内核编译原理,结合实例完成了4G模块的驱动程序与内核编译,并对编译后的Linux系统进行了验证,验证了系统内核能够正确识别4G模块并分配内存,成功实现了Linux系统的4G模块驱动。
关键词:Linux;设备驱动;4G;USB
中图分类号:TP391 文献标识码:A 文章编号:1009-3044(2015)27-0206-04
Abstract: The device driver of Linux system is analyzed, and the USB interface device driver is compiled with the kernel principle. The 4G module is compiled with an example. The Linux system is verified by the 4G system. The system kernel can correctly identify the 4G module and allocate memory..
Key words: Linux; device driver; 4G; USB
Linux系统以其良好的可剪裁性、强稳定性以及易操作等特点,已在物联网,程序控制,电子消费,智能家居等领域得到广泛的使用。4G网络的推广和应用也在各领域展开。因此,将Linux设备与4G网络有机地结合起来,为新一代物联网构造一个更加高速,更加安全,更加稳定的网络通信环境,将会成为一个应用热点。
本文介绍了一种Linux系统驱动4G模块的方法,Linux系统通过USB接口驱动4G终端模块,实现4G网络的接入。首先,文章介绍了整体的软硬件应用环境,然后分析了Linux系统下的设备驱动以及USB接口设备驱动的编写原理,完成了4G终端模块在Linux系统中的驱动程序编写和内核编译,并且最后对驱动的内核烧入进行了验证性测试。
1 Linux系统设备驱动原理
当一个新的硬件设备接入Linux系统时[1],我们需要加载与其对应的驱动程序,之后驱动程序会根据自己的类型向Linux系统注册,注册成功后系统会为驱动程序配置与其类型相应的软件接口以及反饋一个主设备号给驱动程序,然后驱动程序会根据这个主设备号在/dev目录下创建一个设备文件,这样,我们就可以通过这个设备文件来对接入的硬件设备进行控制了。
1.1 Linux系统设备驱动类型
Linux采用的是整体式的内核结构[3],这种结构的内核一般不能动态的增加新的功能。为此,Linux提供了一种叫可安装“模块”的机制,这种机制可以根据需要,在不必对内核重新编译连接的条件下,将可安装模块从运行的内核中动态的插入或移除。使得内核的内存映象保持最小,又同时具有很大的灵活性和可扩充性。绝大多数的设备驱动程序便是以内核模块的形式存在于系统当中的。
驱动程序的类型主要有三种:字符设备、块设备或网络设备。驱动示原理结构如图1所示。
字符设备是指在I/O传输中使用字符为单位进行传输的设备,可以在/dev目录下通过对应的设备文件去访问。由字符设备驱动程序来实现,字符设备驱动程序通常需要至少实现open、close、read和write系统调用。
块设备是为大量数据的传输和慢速设备为设计的,能够容纳文件系统,也可以通过/dev目录下的文件系统节点来访问。和字符设备驱动程序相比,块设备驱动程序与内核之间的软件接口完全不同。
网络设备是指一个经过形成网络接口,能够和其他主机交换数据的设备。与字符设备或块设备不同的是网络设备不能通过对应的设备文件去访问。
在Linux系统中,终端属于字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。
1.2 USB驱动终端模块原理
Linux内核支持两种方式的USB驱动程序[4][5]:作为主机的驱动程序和作为设备的驱动程序。从主机观点来看,主机的驱动程序控制插入其中的设备,而设备上的驱动程序控制该设备如何与主机通信。设备上的驱动程序都是由产商在生产时写入设备的,我们需要编写的就是主机系统上的驱动程序。
在Linux系统中[5],USB驱动程序存在于不同的内核子系统和USB硬件控制器之间,如图2所示,USB核心为USB驱动程序提供了一个用于访问和控制USB硬件的接口,而不必考虑系统当前存在的各种不同类型的USB硬件控制器。可以大大减少驱动开发的工作量。
在Linux USB子系统中,USB主机控制驱动直接和硬件进行交互,是USB协议栈的最底层部分,是USB主机控制器硬件和数据传输的一种抽象。USB主机控制驱动向上仅对USB核心服务。HCD向下则直接管理和检测主控制器硬件的各种行为。
USB核心(USBD)是对整个USB子系统的抽象,在整个子系统中起着承上启下的作用,USBD通过定义一组宏、数据结构和函数来抽象出所有硬件或是设备具有依赖关系的部分。USBD中主要有四个数据结构,如图3所示。
其中,usb_device保存USB设备的信息,包括设备地址,设备描述符,配置描述符等。usb_bus保存一个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使用情况等。usb_driver保存客户驱动信息,包括驱动名称,以及驱动提供给USB内核使用的函数指针等。URB(Universal Request Block)是进行USB通信的数据结构,USBD通过URB在USB设备类驱动和USBD、USBD和HCD间进行数据传输。
1.3驱动程序的编译
Linux驱动程序有2种编译方式,一种是直接与内核一同编译,与内核同时启动,这样在硬件设备连接上主机后,主机能够自动识别,并通过驱动设备来使用硬件资源;另一种是生成模块,当应用程序需要使用硬件资源的时候调用ko文件加载驱动模块,加载后便可对硬件设备进行控制与操作了,但每次模块卸载或主机重启后,需对驱动模块重新加载才能使用硬件设备。这种情况能保证内核镜像的最小,但是每次使用之前需要编译,使用起来较为繁琐。
在这里我们以第一种方式,即与内核一同编译作为实例。
2 4G模块驱动的实现
2.1 驱动程序的编写
在Linux kernel源码目录中的driver/usb/usb-skeleton.c文件为我们提供了一个最基础的USB驱动程序。我们称为USB骨架。为我们提供了良好的驱动编写框架,根据以联芯4G终端模块LC1761为例[2],完成驱动程序的主要内容如下。
1)通过创建结构体struct usb_driver,来注册(init)或注销(exit)USB驱动程序。
static struct usb_driver usb2com_driver = {
.name = "lc_ltetty",
.probe = usb2com_probe,
.disconnect = usb2com_disconnect,
.id_table = usb2com_table,
.suspend = usb2com_suspend,
.resume = usb2com_resume,
};
static int __init usb2com_init(void)
{...}
static void __exit usb2com_exit(void)
{...}
usb_driver結构体向USB子系统提供各种相关信息,当中的每个成员都需要单独实现
static int usb2com_probe(struct usb_interface *interface, const struct usb_device_id *id)
{...}
static void usb2com_disconnect(struct usb_interface *interface)
{...}
static struct usb_device_id usb2com_table[]=
{...}
static int usb2com_suspend(struct usb_interface *intf, pm_message_t message)
{...}
static int usb2com_resume(struct usb_interface *intf)
{...}
其中,探测(probe)函数通过检测每个端点的的信号结构类型,用于判断本驱动是否适合这个设备
static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd)
{...}
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{...}
static inline int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
{...}
static inline int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
{...}
static inline int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd)
{...}
static inline int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd)
{...}
断开(disconnect)函数在设备被移除或不再控制设备时做适当清理。
设备列表(id_table)函数将列举驱动的设备支持列表,包括驱动对象的供应商号和设备号
static struct usb_device_id usb2com_table[] = {
{ USB_DEVICE(0x1ab7, 0x5160), },
{ USB_DEVICE(0x1ab7, 0x6160), },
{ USB_DEVICE(0x1ab7, 0x1761), },
{ USB_DEVICE(0x1ab7, 0x1766), },
{ USB_DEVICE(0x1ab7, 0x1768), },
{}
};
休眠(suspend)和唤醒(resume)函数用于支持usb的休眠和唤醒功能。
2)编写file_operations结构体来控制usb接口。
static struct file_operations usb2com_fops = {
.owner = THIS_MODULE,
.ioctl = usb2com_ioctl,
.read = usb2com_read,
.write = usb2com_write,
.open = usb2com_open,
.release = usb2com_release,
};
file_operations结构体用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址。
完成驱动主程序后再完成驱动模块的Kconfig文件和Makefile文件。
Makefile文件如下:
2.2驱动程序的内核编译
Linux内核中增加程序驱动需要完成以下步骤:
1)将编写好的文件放入文件夹fold,然后将文件夹添加到linux内核文件包的drivers/usb/serial目录下。
2)修改drivers/usb/serial目录下的Kconfig文件。向里面添加代码:
source “drivers/usb/serial/fold/Kconfig”
3)回到内核的代码的根目录使用make menuconfig命令,将新加入的驱动勾选上,如图4所示:
按y键表示驱动将包含于内核之中一同编译。
4)修改drivers/usb/serial目录下的Makefile文件,添加如下代码。
Obj-$(CONFIG_LC_LTETTY) += fold/
5)回到内核包的根目录,执行make命令
至此包含了4G终端模块驱动的Linux内核镜像文件就编译好了,然后通过交叉编译将Linux内核烧入系统中。
2.3 驱动程序的验证
将烧好内核版本的Linux开发板与4G模块连接,并通过交叉串口线连接到PC。在PC侧打开超级终端,设置比特位115200,数据位8,奇偶校验无,停止位1,数据流控制无,与开发板通信。将开发板加电,通过超级终端观察开发板各模块加载情况。
如图5所示,Linux系统的USB端口已经成功识别了联芯的LC1761模块,并且分配了内存。此时的系统已经能够成功驱动4G模块,并通过模块接入4G网络了。
3 结语
当系统能够识别和调用4G模块时,程序员就能够根据需求开发各种基于4G模块的应用程序,将4G网络与物联网相互融合起来了。本文结合目前已经广泛应用的Linux系统,对于4G终端模块在Linux下的驱动做了详细的分析,并以LC5761为例完成了Linux系统上的驱动,并且验证了驱动的内核编译。文中展示了Linux系统下LC1761模块USB驱动开发的详细流程,对不同类型的4G终端模块在Linux系统上的驱动开发具有示范作用,同时也为Linux系统下开发基于4G网络的应用打下了良好的基础。
参考文献:
[1] 刘江曼,何东之.嵌入式linux的I2C驱动开发[J],电子测试,2015(1).
[2] LC1761硬件接口手册[EB/OL].2013-12-2.http://www.leadcoretech.com.
[3] 冯光磊,郭忠文等.基于ARM和Linux的USB OHCI驱动的设计与实验[J].计算机应用,2009,29(6):53-56.
[4] Universal Serial Bus Specification Reversion3.0 [S].2009-04.
[5] 高珂.嵌入式USB设备驱动的研究与设计开发[D].北方工業大学,2010.
[6] 黎春波,陈维峰,赖雪锦.嵌入式Linux系统下的USB驱动开发[J].中国集成电路,2013,22(11):45-48.
[7] 徐海林.Linux下USB设备驱动程序设计[J].电子技术与软件工程,2014,15(3):267.
[8] 姚震,闫波.ARM-Linux下USB主机驱动程序研究与实现[J].实验科学与技术,2012,10(2):75-77.
[9] 翟哲.基于Linux的USB移动设备的控制与研究[J].电脑知识与技术,2015,21(1)168-171.