一个简单字符设备驱动实例
【嵌入式LINUX操作系统】字符设备驱动程序编写举例

《嵌入式Linux应用开发菜鸟进阶》第11章对字符设备驱动的模块框架有一个宏观的认识:#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>︙#include<XXX>这里根据实际的驱动需要添加头文件static int mem_major = 251;︙/*这里定义驱动需要的一些静态数据或者指针,当然作为全局变量,一般不要轻易使用这些静态变量,它们很占内存,并且浪费资源*/︙实现file_operation中挂接的函数static const struct file_operations mem_operation={.owner = THIS_MODULE,︙};根据驱动需要实现相应的系统调用函数static int mymem_init(void)1{︙}模块驱动的注册函数static void mymem_exit(void){︙}模块的释放函数MODULE_AUTHOR("Lin Hui");MODULE_LICENSE("GPL");定义模块编写的作者以及遵循的协议module_init(mymem_init);module_exit(mymem_exit);定义模块初始化入口函数以上就是一个驱动基本不变的部分,针对字符变化的部分进行详细的讲解。
首先是字符设备的注册。
字符设备的注册主要分为4 步:设备号、分配设备号、定义并初始化file_operation结构体和字符设备的注册。
其中,设备号与分配设备号在11.1节中已经详述,这里不再重复。
下面介绍字符设备注册的详细步骤。
(1)设备号。
(2)分配设备号。
(3)定义并初始化file_operations结构体。
字符设备驱动开发实验

字符设备驱动实验实验步骤:1、将设备驱动程序使用马克file文件编译生成模块firstdev.ko2、将模块加载到系统中insmod firstdev.ko3、手动创建设备节点mknod /dev/first c 122 04、使用gcc语句编译firsttest.c生成可执行文件5、运行可执行文件firsttest,返回驱动程序中的打印输出语句。
查看设备号:cat /proc/devices卸载驱动:rmmod firstdev删除设备节点:rm /dev/first显示printk语句,(打开一个新的终端)while truedosudo dmesg -csleep 1done源码分析设备驱动程序firstdev.c#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/irq.h>//#include <asm/hardware.h>static int first_dev_open(struct inode *inode, struct file *file){//int i;printk("this is a test!\n");return 0;}static struct file_operations first_dev_fops ={.owner = THIS_MODULE,.open = first_dev_open,};static int __init first_dev_init(void){int ret;ret = register_chrdev(122,"/dev/first",&first_dev_fo ps);printk("Hello Modules\n");if(ret<0){printk("can't register major number\n");return ret;}printk("first_dev initialized\n");return 0;}static void __exit first_dev_exit(void){unregister_chrdev(122,"/dev/first");printk("Bye Modules\n");}module_init(first_dev_init);module_exit(first_dev_exit);makefile分析:ifneq ($(KERNELRELEASE),)obj-m:= firstdev.oelseKDIR :=/lib/modules/3.13.0-32-generic/buildall:make -C $(KDIR) M=$(PWD) modules clean:rm -f *.ko *.o *.mod.o *.mod.c *.symvers endif应用程序firsttest.c#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/ioctl.h>int main(){int fd;fd = open ("/dev/first",0);if (fd<0){printf("can't open /dev/first");return -1;}close(fd);return 0; }。
简单的虚拟字符设备驱动的实现

简单的虚拟字符设备驱动的实现Linux業已成为嵌入式系统的主流,而各种Linux驱动程序的类型中,字符设备无疑是应用最广泛的。
本文实现了一个简单的虚拟字符设备的驱动程序,用以演示Linux字符设备驱动的基本原理。
在嵌入式Linux的教学中具有重要的作用。
标签:Linux 驱动程序字符设备虚拟嵌入式Linux作为一种开放源代码的操作系统,在嵌入式系统领域业已成为主流,而为嵌入式Linux系统开发设备驱动程序,也成为一项重要的工作。
Linux系统中的驱动程序主要分为三种:字符设备驱动程序、块设备驱动程序和网络驱动程序。
其中字符设备是一类只能顺序读写,没有缓存的驱动程序,其实现方法相对简单,而应用则最为广泛。
在嵌入式Linux的教学中,字符设备驱动程序也是一项重要内容。
为了让学生能够理解字符设备驱动程序的原理,需要一个简单的字符设备驱动的例子,用以进行演示。
一、基本原理把设备当作文件处理,是Linux系统的重要思想,即“一切皆文件”。
在用户空间中,应用程序对字符设备的操作跟读写普通文件没有什么区别,也是通过open()、close()、read()、write()等函数实现的。
操作系统将这些用户空间中的函数分别映射到内核空间中由驱动程序提供的对应接口。
因此,内核空间中的驱动程序就需要通过对对应接口函数的实现来实现对用户空间中应用程序的支持。
file_opreations是字符设备驱动中最重要的结构,它包含了字符设备各种可能的接口函数。
通常在嵌入式编程中,我们不需要全部实现,只需要实现我们实际用到的接口就可以了,这样可以有效降低程序的大小。
该结构被定义在头文件“linux/fs.h”中,使用时只需声明该结构的一个变量并进行填充即可。
二、环境准备为了进行嵌入式Linux的开发,必须首先安装Linux系统。
这里采用最常用的Windows系统+VMWare虚拟机的形式,系统版本为RedHat Enterprise Linux 6.4,其自带的内核版本为2.6.32-358.el6.i686。
一个简单字符设备驱动实例

如何编写Linux设备驱动程序Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。
在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。
本文是在编写一块多媒体卡编制的驱动程序后的总结,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。
以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正.一、Linux device driver 的概念系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。
设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。
设备驱动程序是内核的一部分,它完成以下的功能:1)对设备初始化和释放;2)把数据从内核传送到硬件和从硬件读取数据;3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据;4)检测和处理设备出现的错误。
在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备。
字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。
块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.已经提到,用户进程是通过设备文件来与实际的硬件打交道。
课件:实验教案-简单字符设备驱动

四、实验步骤:
➢ 参考《嵌入式系统设计》课程第04讲内容,根据实验 内容要求设计编写驱动程序及应用程序
➢ 编译和加载驱动程序 ➢ 运行测试程序进行驱动程序测试 ➢ 卸载驱动程序
定义一个全局结构指针,初始值为NULL,该数据结构中包 含一个大小为1024的buffer和一个count整形变量
在open中对该全局结构进行NULL判断,为NULL则为其分配 内存,并将buffer初始化为0,将count自加
在release中如果count为0,则释放,否则进行count自减 在read里面对该buffer进行读取 在write里面对该buffer进行赋值(任意赋值即可) 写测试程序进行测试
第一次上机实验
1
一、实验目的
➢ 掌握简单字符设备驱动程序编写方法。 ➢ 编写应用程序对驱动程序进行测试,学习应用程序与、实验要求
➢ 按实验内容编写驱动程序及测试程序 ➢ 编译驱动程序和应用程序 ➢ 在嵌入式设备上加载驱动程序并进行测试
3
三、实验内容:
➢ 写一个简单的字符设备驱动程序,要求:
实验二:字符设备驱动实验

实验二:字符设备驱动实验一、实验目的通过本实验的学习,了解Linux操作系统中的字符设备驱动程序结构,并能编写简单的字符设备的驱动程序以及对所编写的设备驱动程序进行测试,最终了解Linux操作系统如何管理字符设备。
二、准备知识字符设备驱动程序主要包括初始化字符设备、字符设备的I/O调用和中断服务程序。
在字符设备驱动程序的file_operations结构中,需要定义字符设备的基本入口点。
open()函数;release()函数read()函数write()函数ioctl()函数select()函数。
另外,注册字符设备驱动程序的函数为register_chrdev()。
register_chrdev() 原型如下:int register_chrdev(unsigned int major, //主设备号const char *name, //设备名称struct file_operations *ops); //指向设备操作函数指针其中major是设备驱动程序向系统申请的主设备号。
如果major为0,则系统为该驱动程序动态分配一个空闲的主设备号。
name是设备名称,ops是指向设备操作函数的指针。
注销字符设备驱动程序的函数是unregister_chrdev(),原型如下:int unregister_chrdev(unsigned int major,const char *name);字符设备注册后,必须在文件系统中为其创建一个设备文件。
该设备文件可以在/dev目录中创建,每个设备文件代表一个具体的设备。
使用mknod命令来创建设备文件。
创建设备文件时需要使用设备的主设备号和从设备号作为参数。
阅读教材相关章节知识,了解字符设备的驱动程序结构。
三、实验内容根据教材提供的实例。
编写一个简单的字符设备驱动程序。
要求该字符设备包括open()、write()、read()、ioctl()和release()五个基本操作,并编写一个测试程序来测试所编写的字符设备驱动程序。
最简单的字符设备驱动程序

最简单的字符设备驱动程序⾸先,先理清⼀下简单字符设备驱动程序的思路:(1)申请设备号动态申请:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)静态申请:int register_chrdev_region(dev_t from, unsigned count, const char *name)成功返回0,失败返回负数,并置于errno(2)分配cdev ,可以使⽤struct cdev *cdev_alloc(void) ,或者静态定义全局cdev变量(3)初始化cdev若使⽤动态分配,则需要进⾏初始化:void cdev_init(struct cdev *cdev, const structfile_operations *fops) ,mem_cdev.owner = THIS_MODULE;若动态内存定义初始化:struct cdev *mem_cdev = cdev_alloc(); mem_cdev->ops =&fops; mem_cdev->owner = THIS_MODULE(4)添加cdevint cdev_add(struct cdev *p, dev_t dev,unsigned count)若使⽤内存模拟字符设备,则还需申请空间:mem_devp = kmalloc( 2 * sizeof(struct mem_dev), GFP_KERNEL);if(!mem_devp){result = -ENOMEM;goto fail_malloc;}memset(mem_devp, 0, sizeof(struct mem_dev));for(i = 0; i < 2; i++){mem_devp[i].size = MEMDEV_SIZE;mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);memset(mem_devp[i].data, 0, MEMDEV_SIZE);}申请失败情况下,记得注销设备号,使⽤void unregister_chrdev_region(dev_t from, unsigned count)(5)构造file_operations结构体(结构体字段的初始化)static const struct file_operations mem_fops ={.owner = THIS_MODULE,.llseek = mem_llseek,.read = mem_read,.write = mem_write,.open = mem_open,.release = mem_release,};(6)实现file_operations⽀持的函数int mem_open(struct inode *inode, struct file *filp){struct mem_dev *dev;int num = MINOR(inode->i_rdev);if(num >= MEMDEV_NR_DEVS)return -ENODEV;dev = &mem_devp[num];filp->private_data = dev;return 0;}static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct mem_dev *dev = filp->private_data;if(p > MEMDEV_SIZE)return 0;if(count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;if(copy_to_user(buf, (void *)(dev->data + p), count)){ret = -EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO "read %d bytes from %ld", count, p);}return ret;}static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct mem_dev *dev = filp->private_data;if(p > MEMDEV_SIZE)return 0;if(count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;if(copy_from_user(dev->data + p, buf, count)){ret = -EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO "writen %d bytes from %ld", count, p);}return ret;}static loff_t mem_llseek(struct file *filp, loff_t offset, int whence){loff_t newpos;switch(whence){case 0:newpos = offset;break;case 1:newpos = filp->f_pos+offset;break;case 2:newpos = MEMDEV_SIZE - 1 + offset;break;default:return -EINVAL;}if((newpos < 0) || (newpos > MEMDEV_SIZE)) return -EINVAL;filp->f_pos = newpos;return newpos;}int mem_release(struct inode *inode, struct file *filp) {return 0;}测试代码:#include <stdio.h>int main(){FILE *fp = NULL;char Buf[4096];strcpy(Buf, "mem is char dev!");printf("Buf:%s\n",Buf);fp = fopen("/dev/memdev1", "r+");if(fp == NULL){printf("open memdev1 error!\n");}fwrite(Buf, sizeof(Buf), 1, fp);fseek(fp, 0, SEEK_SET);strcpy(Buf,"Buf is NULL!");printf("Buf: %s\n",Buf);fread(Buf, sizeof(Buf), 1, fp);printf("Buf: %s\n",Buf);return 0;}。
字符设备驱动实验报告(3篇)

第1篇一、实验背景与目的随着计算机技术的飞速发展,操作系统对硬件设备的支持越来越丰富。
设备驱动程序作为操作系统与硬件之间的桥梁,扮演着至关重要的角色。
本实验旨在通过学习Linux字符设备驱动的开发,加深对设备驱动程序的理解,提高实践能力。
二、实验环境与工具1. 操作系统:Linux Ubuntu 20.042. 编程语言:C3. 开发工具:gcc、make4. 驱动框架:Linux内核三、实验内容本实验主要完成以下内容:1. 字符设备驱动程序的基本框架2. 字符设备的打开、读取、写入和关闭操作3. 字符设备驱动的注册与注销4. 字符设备驱动的用户空间交互四、实验步骤1. 创建设备文件首先,我们需要在`/dev`目录下创建一个名为`mychar`的字符设备文件。
可以使用以下命令:```bashmknod /dev/mychar c 123 0```其中,`123`是主设备号,`0`是次设备号。
2. 编写字符设备驱动程序创建一个名为`mychar.c`的文件,并编写以下代码:```cinclude <linux/module.h>include <linux/fs.h>include <linux/uaccess.h>static int major = 123; // 设备号static int device_open(struct inode inode, struct file filp);static int device_release(struct inode inode, struct file filp);static ssize_t device_read(struct file filp, char __user buf, size_t count, loff_t pos);static ssize_t device_write(struct file filp, const char __user buf, size_t count, loff_t pos);static struct file_operations fops = {.open = device_open,.release = device_release,.read = device_read,.write = device_write,};static int __init mychar_init(void) {major = register_chrdev(0, "mychar", &fops);if (major < 0) {printk(KERN_ALERT "mychar: can't get major number\n");return major;}printk(KERN_INFO "mychar: registered correctly with major number %d\n", major);return 0;}static void __exit mychar_exit(void) {unregister_chrdev(major, "mychar");printk(KERN_INFO "mychar: Goodbye from the LKM!\n");}static int device_open(struct inode inode, struct file filp) {printk(KERN_INFO "mychar: Device has been opened\n");return 0;}static int device_release(struct inode inode, struct file filp) {printk(KERN_INFO "mychar: Device has been closed\n");return 0;}static ssize_t device_read(struct file filp, char __user buf, size_t count, loff_t pos) {printk(KERN_INFO "mychar: Device has been read\n");return count;}static ssize_t device_write(struct file filp, const char __user buf, size_t count, loff_t pos) {printk(KERN_INFO "mychar: Device has been written\n"); return count;}module_init(mychar_init);module_exit(mychar_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("A simple character device driver");```保存文件,并使用以下命令编译:```bashmake```3. 加载字符设备驱动程序将编译生成的`mychar.ko`文件加载到内核中:```bashinsmod mychar.ko```4. 测试字符设备驱动程序使用以下命令查看`/dev/mychar`设备文件:```bashls -l /dev/mychar```使用`cat`命令测试读取和写入操作:```bashcat /dev/mycharecho "Hello, world!" > /dev/mychar```观察系统日志,确认驱动程序的打开、读取、写入和关闭操作。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
如何编写Linux设备驱动程序Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。
在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。
本文是在编写一块多媒体卡编制的驱动程序后的总结,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。
以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正.一、Linux device driver 的概念系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。
设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。
设备驱动程序是内核的一部分,它完成以下的功能:1)对设备初始化和释放;2)把数据从内核传送到硬件和从硬件读取数据;3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据;4)检测和处理设备出现的错误。
在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备。
字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。
块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.已经提到,用户进程是通过设备文件来与实际的硬件打交道。
每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。
另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。
设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。
也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。
如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。
二、实例剖析我们来写一个最简单的字符设备驱动程序。
虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序。
不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过。
这一段定义了一些版本信息,虽然用处不是很大,但也必不可少。
Johnsonm说所有的驱动程序的开头都要包含<linux/config.h>,但我看倒是未必。
由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如open,read,write,close....,注意,不是fopen,fread,但是如何把系统调用这个结构的每一个成员的名字都对应着一个系统调用.用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。
这是linux的设备驱动程序工作的基本原理。
既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。
相当简单,不是吗?这个函数是为read调用准备的。
当调用read时,read_test()被调用,它把用户的缓冲区全部写1。
buf 是read调用的一个参数,它是用户进程空间的一个地址,但是在read_test 被调用时,系统进入核心态.所以不能使用buf这个地址,必须用__put_user(),这是kernel 提供的一个函数,用于向用户传送数据。
另外还有很多类似功能的函数,请参考。
在向用户空间拷贝数据之前,必须验证buf是否可用,这就用到函数verify_area.这几个函数都是空操作,实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。
设备驱动程序的主体可以说是写好了。
现在要把驱动程序嵌入内核。
驱动程序可以按照两种方式编译:一种是编译进kernel,另一种是编译成模块(modules)。
如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐在用insmod命令将编译好的模块调入内存时,init_module 函数被调用。
在这里,init_module只做了一件事,就是向系统的字符设备表登记了一个字符设备。
register_chrdev 需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一个没有被占用的设备号返回。
参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针。
在用rmmod卸载模块时,cleanup_module函数被调用,它释放字符设备test在系统字符设备表中占有的表项。
一个极其简单的字符设备可以说写好了,文件名就叫test.c吧。
下面的命令编译该设备驱动程序:$ gcc -O2-DMODULE-D__KERNEL__-c test.c得到文件test.o就是一个设备驱动程序。
如果设备驱动程序有多个文件,把每个文件按上面的命令行编译,然后执行:ld–r file1.o file2.o–o modulename驱动程序已经编译好了,现在把它安装到系统中去:$ insmod -f test.o如果安装成功,在/proc/devices文件中就可以看到设备test,并可以看到它的主设备号。
要卸载的话,运行命令:$ rmmod test下一步要创建设备文件:#mknod /dev/test c major minorc是指字符设备,major是主设备号,就是在/proc/devices里看到的。
用shell命令:$ cat/proc/devices | awk"\\$2==\"test\" {print \\$1}"就可以获得主设备号,可以把上面的命令行加入你的shell script中去。
minor是从设备号,设置成0就可以了。
编译运行,看看是不是打印出全1 。
以上只是一个简单的演示。
真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。
这些才是真正的难点。
请看下节,实际情况的处理。
三、设备驱动程序中的一些具体问题1. I/O Port和硬件打交道离不开I/O Port,老的ISA设备经常是占用实际的I/O端口,在linux下,操作系统没有对I/O口屏蔽,也就是说,任何驱动程序都可对任意的I/O口操作,这样就很容易引起混乱。
每个驱动程序应该自己避免误用端口。
有两个重要的kernel函数可以保证驱动程序做到这一点:1)check_region(int io_port,int off_set)这个函数察看系统的I/O表,看是否有别的驱动程序占用某一段I/O口。
参数1:io端口的基地址,参数2:io端口占用的范围。
返回值:0 没有占用,非0,已经被占用。
2)request_region(int io_port,int off_set,char *devname)如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。
在使用之前,必须向系统登记,以防止被其他程序占用。
登记后,在/proc/ioports文件中可以看到你登记的io 口。
参数1:io端口的基地址。
参数2:io端口占用的范围。
参数3:使用这段io地址的设备名。
在对I/O口登记后,就可以放心地用inb(),outb()之类的函来访问了。
在一些pci设备中,I/O端口被映射到一段内存中去,要访问这些端口就相当于访问一段内存。
经常性的,我们要获得一块内存的物理地址。
在dos环境下,(之所以不说是dos 操作系统是因为我认为DOS根本就不是一个操作系统,它实在是太简单,太不安全了)只要用段:偏移就可以了。
在windows中,95ddk提供了一个vmm 调用_MapLinearToPhys,用以把线性地址转化为物理地址。
但在Linux中是怎样做的呢?2. 内存操作在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages 直接申请页。
释放内存用的是kfree,或free_pages. 请注意,kmalloc等函数返回的是物理地址!而malloc等返回的是线性地址!关于kmalloc返回的是物理地址这一点本人有点不太明白:既然从线性地址到物理地址的转换是由386cpu硬件完成的,那样汇编指令的操作数应该是线性地址,驱动程序同样也不能直接使用物理地址而是线性地址。
但是事实上kmalloc 返回的确实是物理地址,而且也可以直接通过它访问实际的RAM,我想这样可以由两种解释,一种是在核心态禁止分页,但是这好像不太现实;另一种是linux的页目录和页表项设计得正好使得物理地址等同于线性地址。
我的想法不知对不对,还请高手指教。
言归正传,要注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。
kmalloc用法参见khg.内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。
在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址。
另外,很多硬件需要一块比较大的连续内存用作DMA传送。
这块内存需要一直驻留在内存,不能被交换到文件中去。
但是kmalloc最多只能开辟128k的内存。
这可以通过牺牲一些系统内存的方法来解决。
具体做法是:比如说你的机器由32M的内存,在lilo.conf的启动参数中加上mem=30M,这样linux就认为你的机器只有30M的内存,剩下的2M内存在vremap之后就可以为DMA 所用了。
请记住,用vremap映射后的内存,不用时应用unremap释放,否则会浪费页表。
3. 中断处理同处理I/O端口一样,要使用一个中断,必须先向系统登记。
int request_irq(unsigned int irq, void(*handle)(int,void *,struct pt_regs *),unsigned int long flags,const char *device);其中:irq: 是要申请的中断。