linux驱动程序的编写
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
linux驱动程序的编写
一、实验目的
1.掌握linux驱动程序的编写方法
2.掌握驱动程序动态模块的调试方法
3.掌握驱动程序填加到内核的方法
二、实验内容
1. 学习linux驱动程序的编写流程
2. 学习驱动程序动态模块的调试方法
3. 学习驱动程序填加到内核的流程
三、实验设备
PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱
四、linux的驱动程序的编写
嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。
嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。
系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。
字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。
1 字符设备驱动结构
Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。
1)cdev结构体
在linux2.6内核中,使用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结构体的dev_t成员定义了设备号,为32位,其中12位主设备号,20位次设备号。使用下列宏可以从dev_t获得主设备号和次设备号。*dev_t这个不是structure,是简单变量,只用于保存一组major number和minor number.Linux提供一组mactor对其进行读写:MAJOR(dev_t dev);//读取设备的major number
MINOR(dev_t dev);//读取设备的minor number
而使用下列宏则可以通过主设备号和次设备号生成dev_t;
MKDEV(int major,int minor);//从一组指定的major number和minor number创建一个dev_t
cdev结构体的另一个重要成员file_operations定义了字符设备提供给虚拟文件系统的接口函数。
Linux 2.6内核提供了一组函数用于操作cdev结构体:
void cdev_init(struct cdev *,struct file_operations *);
struct cdev *cdev_alloc(void);/*动态申请一个cdev空间内存*/
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *,dev_t,unsigned);/*向系统添加、注册一个cdev*/
void cdev_del(struct cdev *);/*向系统注销一个cdev*/
cdev_init()函数用于初始化cdev的成员,并建立cdev和file_operations之间的连接,其源代码如下所示:
void cdev_init(struct cdev *cdev,struct file_operations *fops)
{
memset(cdev,0,sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj,&ktype_cdev_default);
cdev->ops=fops;/*将传入的文件操作结构体指针赋值给cdev的ops*/
}
cdev_alloc()函数用于动态申请一个cdev内存,其源代码清单如下:
struct cdev *cdev_alloc(void)
{
struct cdev *p=kzalloc(sizeof(struct cdev),GFP_KERNEL);
if(p){
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj,&ktype_cdev_dynamic);
}
return p;
}
cdev_add()函数和cdev_del()函数分别向系统添加和删除一个cdev,完成字符设备的注册和注销。对cdev_add()的调用通常发生在字符设备驱动模块加载函数中,而对cdev_del()函数的调用通常发生在字符设备驱动模块卸载函数中。
2)分配和释放设备号
在调用cdev_add()函数向系统注册字符设备之前,应首先调用
register_chrdev_region()或alloc_chrdev_region()函数向系统申请设备号,这两个函数的原型为:
int register_chrdev_region(dev_t from,unsigned count,const char *name);
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);
register_chrdev_region()函数用于已知起始设备的设备号的情况,而
alloc_chrdev_regione用于设备号未知,向系统动态申请未被占用的设备号的情况,函数调用成功之后,会把得到的设备号放入第一个参数dev中。后者的优点在于它会自动避开设备号重复的冲突。
相反地,在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()应该被调用以释放原先申请的设备号,这个函数的原型为:
void unregister_chrdev_region(dev_t from,unsigned count);
from:要分配设备编号范围的起始值,经常设置为0.
count:所请求的连续设备编号的个数。
Name:是和该设备范围关联的设备名称,它将出现在/proc/devices和sysfs中。
3)file_operations结构体