APP下载

Linux下的PCIE同步时钟卡的设备驱动程序开发

2017-02-27杨兵见陈永志

计算机测量与控制 2017年1期
关键词:驱动程序内核字符

杨兵见,魏 丰,陈永志

(华中科技大学 自动化学院,武汉 430074)

Linux下的PCIE同步时钟卡的设备驱动程序开发

杨兵见,魏 丰,陈永志

(华中科技大学 自动化学院,武汉 430074)

为了给系统提供准确的同步时间,向各种自动化装置(如故障录波和数据采集等设备)提供高精度同步绝对时标,能够统一系统中各部分的时间基准,可以在系统发生故障后,为分析故障的情况及开关动作的先后次序提供有力的依据;因此设计了GPS+北斗双系统接收机制作而成的同步时钟源,可接收美国GPS(全球定位系统)卫星和我国北斗卫星发出的定位和时间信息;采用了当今计算机最新PCIE总线用来将计算机与时钟卡进行通讯,获取时间信息;介绍了Linux下PCIE同步时钟卡的驱动程序设计,简要分析了同步时钟卡的系统框架和工作原理,详细阐述了Linux下字符设备驱动开发流程,最后完成了Linux下PCIE设备驱动程序的开发和实现;通过实验验证了驱动程序的完备性和正确性,能够准确读取GPS或者是北斗系统接收机的时间信息。

PCIE总线;设备驱动;Linux;同步时钟;内核编程

0 引言

时间同步在航空航天、电力系统、空间技术,民用等领域是一项非常重要的指标,通过时间服务器提供标准可靠的时钟信号。授时系统就是通过我国的原子钟和协调世界时间得到精确的信号,服务器得到时钟信号之后将其发送给它的客户机,使客户机的时钟信号保持一致的精确性和可靠性。

时间这个因素在各种自动化装置(如电力系统故障检测,数据采集设备等)起着重要的作用,它能给系统提供统一的时间基准。当系统发生故障时,能够明确的分析故障所在原因以及系统中开关动作的先后次序,对维护设备和改善设备性能提供了依据。

PCIE总线作为当今计算机主流总线,其驱动程序的开发也备受关注,不管是在Windows还是在Linux下的驱动开发,都得到了广泛的应用。驱动程序作为软件与硬件之间的桥梁,独立于具体的平台,使其移植更加方便。而且Linux作为开源的操作系统,对于设备驱动的开发人员来说更具有普遍性。

1 同步时钟卡整体框架与工作原理

图1所示为系统框图,此设备是自主研发的基于PCIE总线的同步时钟卡,单片机作为系统的控制单元,接收时钟输入和控制双口RAM数据的读写。可编程逻辑CPLD用于产生高精度的同步绝对时标,将时间信息存储于双口RAM中。PC机通过PCIE总线访问双口RAM中的数据,同时设定单片机和PC机访问双口RAM的通讯协议,这样有效防止双方读写双口RAM数据混乱的情况。CH367芯片作为PCIE总线的接口芯片,EEPROM存储CH367芯片所需要的配置信息。PC机获取双口RAM数据有两种方式:一种是主动读取双口RAM时间数据,另一种是通过外部事件触发中断再读取时间数据。

图1 系统框图

2 Linux下的字符设备驱动程序开发

2.1 Linux设备驱动概述、分类

Linux将所有的外部设备都看成是一类特殊的文件,我们称之为“设备文件”[7]。Linux系统调用是应用程序与Linux内核之间的接口,而设备驱动是Linux内核与外部设备之间的接口。驱动程序向应用程序屏蔽了具体的平台系统,应用程序操作设备文件的读写和控制,也就是在相应的操作外部设备的读写和控制。

一般而言Linux设备可以分为3类:字符设备、块设备、网络设备[1]。

字符设备是以字节为单位逐个进行I/O操作的设备。块设备利用系统一块内存作为缓冲区,对设备进行读写操作。网络设备是通过以数据包的形式在网络媒介上进行数据的发送和接收[1]。

PCIE设备驱动属于字符设备中的一种。

每一个字符设备或块设备都在/dev目录下对应一个设备文件(或称设备节点)。应用程序通过设备文件来控制驱动程序对外部设备进行相应的操作。

2.2 字符设备驱动

2.2.1 字符设备驱动结构

Linux下的大部分设备都是属于字符设备,如图2所示为字符设备、字符设备驱动和用户空间应用程序的结构图[1]。

图2 字符设备驱动的结构

图2中所示,一个字符设备对应一个实例化的cdev结构体变量名,通过模块加载函数初始化cdev结构体中的成员并分配设备号,建立cdev结构体与虚拟文件操作结构体file_operations之间的联系。file_operations定义了字符设备驱动提供给虚拟文件系统的接口函数,通过这些接口,上层的应用程序可以像操作文件那样操作外部设备。当用户空间的应用程序进行Linux系统调用时,就会相应调用驱动程序对应的函数。例如应用程序调用open(),write()等函数时,Linux内核就会相应调用xxx_open(),xxx_write()等函数,这样便完成了对设备的读写和控制等操作。当删除设备时,驱动就会调用模块卸载函数释放设备所占用的相应的资源。

2.2.2 字符设备结构体cdev

在Linux内核中,使用cdev结构体描述一个字符设备,cdev结构体的定义在头文件中。

struct cdev

{

struct kobject kobj; //内嵌的kobject对象

struct module *owner; //所属模块

struct file_operations *ops; //文件操作

struct list_head list;

dev_t dev; // 设备号

unsigned int count;

}

首先模块加载函数初始化cdev结构体,并调用cdev_init(struct cdev *, struct file_operations)函数分配设备号,dev是32位的设备号,其中高12位为主设备号,低20位为次设备号。主设备号代表一个类型的设备驱动,次设备号代表区分该设备中的不同个体。这样也建立了cdev和file_operations之间的连接。设备号申请之后,应该调用 cdev_add(struct cdev *, dev_t, unsigned int)函数添加字符设备到系统中去。这样以便于可以使用此设备驱动对其操作。在不使用设备时,调用cdev_del(struct cdev *)从系统中删除字符设备。

2.2.3 分配和释放设备号

在调用cdev_add()函数向系统注册字符设备之前,应该先调用register_chrdev_region(dev_t from,unsigned count,const char *name)或者alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigend count, const char *name)函数向系统申请设备号。前者用于已知起始设备号的情况;后者用于设备号未知,向系统动态申请未被占用的设备号,如果函数调用成功,会把得到的设备号放入到参数dev中,alloc_chrdev_region()相比于register_chrdev_region()的优点在于它会自动避开设备号重复的冲突[1]。

相应的,在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region(

dev_t from,unsigned count)函数用来释放原先申请的设备号。

2.2.4 file_operations结构体

file_operations是字符设备驱动与内核虚拟文件系统的接口,应用程序通过linux系统调用,就会调用相应驱动程序的函数。这里给出file_operations结构体的主要成员函数。

struct file_operations

{

struct module *owner;

loff_t(*xxx_llseek)(struct file *, loff_t, int); ssize_t(*xxx_read)(struct file *, char __user, size_t, loff_t);

ssize_t(*xxx_write)(struct file *, const char __user, size_t, loff_t);

int(*xxx_open)(struct inode *, struct file *);

int(*xxx_release)(struct inode *, struct

file *);

......

}

xxx_llseek()函数用来修改一个文件的当前读写位置,并将新位置返回。xxx_read()函数用来从设备中读取数据。它与用户空间应用程序的read()函数和fread()函数在进行系统调用时对应。xxx_write()函数向设备发送数据,它与用户空间应用程序的write()函数和fwrite()函数在进行系统调用时对应[6].

在Linux中,由于用户空间和内核空间不能相互直接访问数据,因此必须借助内核函数copy_from_user(void *to, const __user *from, unsigned long count)完成从用户态的数据到内核态的复制,以及copy_to_user(void __user *to

,const void *from,unsigned long count)完成内核态数据到用户态数据的复制。这样内核态与用户态就可以完成数据的读写了。

3 PCIE总线与设备驱动

3.1 PCIE总线概述

PCIE总线是由Intel提出的最新的总线和接口标准,属于高速点对点串行传输,所连的设备独享通道带宽。每个设备在传输数据的时候会各自建立相互独立,互不干扰的的传输通道,数据传输的效率大大提高[5]。

3.2 PCIE设备驱动

3.2.1 PCIE设备驱动概述

PCIE设备驱动由两部分组成:Linux内核的PCIE设备驱动和总线上所载设备本身的驱动。Linux内核的PCIE设备驱动由内核实现,它在安装内核时就已经实现好了。因此本次实验编写的驱动就是总线上所载设备本身的驱动,也就是CH367总线的接口芯片的驱动程序。

PCIE设备有3种地址空间:配置空间、I/O空间、存储器空间。配置空间是由Linux内核完成初始化代码进行相应的操作,I/O空间和存储器空间是由设备驱动程序访问,CPU可以访问所有的PCIE空间。PCIE至少包含256字节配置空间,前面64字节是标准化的必备的配置空间[4],后面的配置空间与具体的接口芯片本身相关。

3.2.2 PCIE设备驱动的注册和注销

PCIE设备驱动的注册和注销通过对pci_driver这个结构体实现的[3],注册PCIE驱动使用函数pci_register_driver(struct pci_driver *driver),注销设备使用pci_unregister_driver(struct pci_driver *driver);

PCIE设备驱动的注册和注销与字符设备驱动的注册和注销并无差异,知识函数调用不同而已,对于PCIE设备的初始化,我们使用pci_driver中的probe()函数,它负责探测硬件并保存配置信息。

3.2.3 PCIE设备驱动的数据结构

针对PCIE驱动,计算机主要完成对所有挂接在总线设备上的轮询以及将设备添加到系统中,这样便可以当做字符设备那样操作PCIE设备,当驱动程序加载之后,硬件相应遍历总线上的设备,如果识别到有相应设备挂接在总线上,那么接着就调用probe()函数进行地址映射、I/O或内存分配和映射,中断号申请,并保存配置信息。其中pci_dev和pci_driver 这两个结构体是对于完成PCIE驱动必不可的数据类型。

struct pci_dev

{

struct list_head global_list;

struct list_head bus_list;

struct pci_bus *bus; // PCIE总线的结构体

unsigned int devfn; // PCIE设备功能号

unsigned short vendor; // 设备产商ID

unsigned short device; // 设备ID

struct pci_driver *driver;

unsigned int irq; // 中断号

......

}

pci_dev结构体详细地描述了一个PCIE设备的硬件信息,包括挂接在总线上的信息和接口芯片本身的信息,如设备厂商ID、设备ID、中断号等等。

struct pci_driver

{

struct list_head node;

char *name;

const struct pci_device_id *id_table;

int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);

void (*remove)(struct pci_dev *dev);

......

}

pci_driver结构体成员id_table包含驱动程序支持哪些设备的属性列表。探测设备的函数probe()用于使能PCIE设备、I/O资源的申请、中断申请和注册字符设备[3]等相关操作以及卸载设备的函数remove()来注销设备,释放资源。

3.2.4 PCIE设备驱动的中断

Linux内核的引导程序已经为每个PCIE设备分配了一个唯一的中断号,它保存在配置空间的Interrupt_Line寄存器中,我们只需要使用读取配置空间的内核函数就可以获取中断号[2]

pci_read_config_byte(dev, Interrupt_Line, &IRQ);

获取中断号后,然后再进行相应的中断程序的处理。

4 PCIE驱动安装与调试

4.1 开发环境搭建

操作系统为Ubuntu14.04,使用自带的vim编辑器编写驱动程序,Ubuntu14.04自带了GCC编译器,所以额外工作不需要做的很多,最后编写Makefile文件。

将驱动程序和Makefile文件放在一个目录中,然后在终端中打开文件所在目录。编译驱动程序使用命令:make。得到以.ko文件为后缀的驱动安装文件。

4.2 驱动程序的安装、卸载与调试

得到可执行文件后。使用下列命令安装驱动、卸载与调试:

(1) insmod xxx.ko 加载驱动模块

(2) cat /pro/devices 查看已经安装设备驱动节点的设备号

(3) ls /dev/驱动名 查看驱动设备文件名

(4) rmmod xxx 卸载驱动模块

驱动安装完成之后,检查有了设备号和设备节点名,如果安装提示权限不够,请在命令前加上sudo取得root权限,然后编写应用程序进行相关调试,编写好应用程序之后使用GCC编译器编译源代码得到可执行的应用程序。使用下列命令命令:

(5) gcc xxx.c -o xxx 编译应用程序

(6) . /xxx 运行应用程序

最后得到调试结果,实现了项目需求,实验结果如图3所示:

4.3 实验数据分析

根据图3所示,应用程序每次间隔一秒读取时钟卡上面的

图3 实验结果

时间信息,时间信息是GPS或者是北斗卫星通过串口给下位机,下位机解析时间信息之后传送到双口RAM,上位机机通过PCIE总线读取双口RAM时间信息,双方设定好通讯协议。同时上位机还可以通过控制命令发送给下位机进行相应操作。数据第一列表示数据项的个数;第二列表示日期;第三列表示时间,后面的四位数代表200us时间刻度;第四列表示锁定的卫星个数,且不为零则表示时间和日期信息均来至于GPS或者北斗时间信息,如果为零则表示时间和日期信息均来至于时钟卡本身的实时时钟;第五列表示B码质量字节;第六列表示板卡为主卡或者从卡的工作方式:slave 或者master;最后一列就是对前面时间信息的显示是来至于GPS或者北斗卫星的信息还是来至于实时时钟的信息:RTC或者GPS。

5 总结

本文以自主研发的PCIE同步时钟卡为实际应用背景,阐明了Linux下字符设备驱动的开发以及相关的文件系统与数据结构,并结合实际项目做了PCIE驱动程序的设计。PCIE总线作为一种通用的总线接口标准,在目前的计算机系统中得到了广泛的应用,不论是在Windows还是Linux操作系统中,其驱动程序的开发也备受关注,Linux作为开源的操作系统,对设备驱动支持具有更大的发展潜力,是今后的一个技术研究方向。

[1] 宋宝华. Linux设备驱动开发详解[M]. 北京:机械工业出版社, 2016.

[2] 王学东,崔琪楣. Linux下的PCIE设备驱动设计与实现[EB/OL]. 北京:中国科技论文在线,2013.

[3] 王 军,韩 力,杜博军,等. 基于PCI-E总线的北斗导航授时卡Linux驱动设计[J].计算机测量与控制, 2016,24(4):115-117.

[4] 董春桥,李 凯. Linux系统PCI设备驱动程序开发[J]. 计算机测量与控制,2005,13(11):1289-1291.

[5] 周立国,梁淮宁,谢冬冬,等. 基于PCI Express总线的数据传输卡的设计与实现[J].电子测量技术,2007,30(11):28-31.

[6] 赵 明. 嵌入式PCI-E设备驱动程序的开发与应用[D]. 西安:西安电子科技大学,2014.

[7] 李 桦,高 飞,孙 磊. 嵌入式Linux设备驱动程序研究[J].微计算机信息,2010,26(5-2):68-70.

Device Driver Development of PCIE Synchronous Clock Card Based on Linux

Yang Bingjian,Wei Feng,Chen Yongzhi

(College of Automation,Huazhong University of Science and Technology, Wuhan 430074, China)

In order to provide the system accurate synchronization time and offer the high precision synchronous absolute time scale to most automation devices(such as fault wave record and data acquisition device),it can unite time benchmark for the parts in the system and also provide powerful basis with failure analysis and switch action sequence after system failure.Therefore, synchronous clock source is made based on the GPS and China's Beidou receiver double system which can receive the GPS(global positioning system) satellites and China's Beidou satellites positioning and timing information. The latest PCIE bus is designed to communicate and obtain time information with PC and synchronous clock source.This paper introduced driver programming development for the PCIE synchronous clock card based on Linux.It briefly analyses the synchronous clock card system framework and its working principle and reveals the Linux character device driver development process. Finally it achieves PCIE device driver development and implementation based on Linux operating system. The driver’s completeness and correctness are verified by experiment and can accurately read GPS receiver or Beidou system time information.

PCIE bus; device dirver; Linux; synchromous clock; kernel programming

2016-08-01;

2016-09-06。

杨兵见(1990-),男,湖北省荆州市人,硕士研究生,主要从事同步时钟源、检测技术、嵌入式软件方向的研究。

1671-4598(2017)01-0098-03DOI:10.16526/j.cnki.11-4762/tp

TP

A

猜你喜欢

驱动程序内核字符
万物皆可IP的时代,我们当夯实的IP内核是什么?
寻找更强的字符映射管理器
强化『高新』内核 打造农业『硅谷』
字符代表几
一种USB接口字符液晶控制器设计
基于嵌入式Linux内核的自恢复设计
Linux内核mmap保护机制研究
消失的殖民村庄和神秘字符
驱动程序更新与推荐
驱动程序更新与推荐