一个简单字符设备驱动实例
【嵌入式LINUX操作系统】字符设备驱动程序编写举例
![【嵌入式LINUX操作系统】字符设备驱动程序编写举例](https://img.taocdn.com/s3/m/8df13e8977232f60dccca174.png)
《嵌入式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结构体。
字符设备驱动开发实验
![字符设备驱动开发实验](https://img.taocdn.com/s3/m/209ce12a83c4bb4cf7ecd149.png)
字符设备驱动实验实验步骤: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; }。
简单的虚拟字符设备驱动的实现
![简单的虚拟字符设备驱动的实现](https://img.taocdn.com/s3/m/2b1ad6a7f242336c1fb95e03.png)
简单的虚拟字符设备驱动的实现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。
一个简单字符设备驱动实例
![一个简单字符设备驱动实例](https://img.taocdn.com/s3/m/235bb6c005087632311212ff.png)
如何编写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时间来等待.已经提到,用户进程是通过设备文件来与实际的硬件打交道。
课件:实验教案-简单字符设备驱动
![课件:实验教案-简单字符设备驱动](https://img.taocdn.com/s3/m/40554e4ef524ccbff0218430.png)
四、实验步骤:
➢ 参考《嵌入式系统设计》课程第04讲内容,根据实验 内容要求设计编写驱动程序及应用程序
➢ 编译和加载驱动程序 ➢ 运行测试程序进行驱动程序测试 ➢ 卸载驱动程序
定义一个全局结构指针,初始值为NULL,该数据结构中包 含一个大小为1024的buffer和一个count整形变量
在open中对该全局结构进行NULL判断,为NULL则为其分配 内存,并将buffer初始化为0,将count自加
在release中如果count为0,则释放,否则进行count自减 在read里面对该buffer进行读取 在write里面对该buffer进行赋值(任意赋值即可) 写测试程序进行测试
第一次上机实验
1
一、实验目的
➢ 掌握简单字符设备驱动程序编写方法。 ➢ 编写应用程序对驱动程序进行测试,学习应用程序与、实验要求
➢ 按实验内容编写驱动程序及测试程序 ➢ 编译驱动程序和应用程序 ➢ 在嵌入式设备上加载驱动程序并进行测试
3
三、实验内容:
➢ 写一个简单的字符设备驱动程序,要求:
实验二:字符设备驱动实验
![实验二:字符设备驱动实验](https://img.taocdn.com/s3/m/815e061b83c4bb4cf6ecd11c.png)
实验二:字符设备驱动实验一、实验目的通过本实验的学习,了解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()五个基本操作,并编写一个测试程序来测试所编写的字符设备驱动程序。
最简单的字符设备驱动程序
![最简单的字符设备驱动程序](https://img.taocdn.com/s3/m/a68d839d85868762caaedd3383c4bb4cf6ecb75b.png)
最简单的字符设备驱动程序⾸先,先理清⼀下简单字符设备驱动程序的思路:(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篇)
![字符设备驱动实验报告(3篇)](https://img.taocdn.com/s3/m/dfa56847182e453610661ed9ad51f01dc28157b6.png)
第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```观察系统日志,确认驱动程序的打开、读取、写入和关闭操作。
实现一个简单的linux字符设备驱动
![实现一个简单的linux字符设备驱动](https://img.taocdn.com/s3/m/7cefcf11866fb84ae45c8da5.png)
实现一个简单的linux字符设备驱动步骤1:编写驱动程序view plaincopy to clipboardprint?1. #include <linux/module.h>2. #include <linux/init.h>3. #include <linux/kernel.h>4. #include <linux/cdev.h>5. #include <linux/fs.h>6. #include <linux/kdev_t.h>7. #include <asm/uaccess.h>8. #include <linux/device.h>9. #define DEVICE_NAME "cdev_zhangwei"10. int number_of_devices = 1;11. struct cdev mydev;12. dev_t dev = 0;13. char data[128] = "/0"; // the data of my device14. struct class *myclass;15. static int mydev_open(struct inode *inode, struct file *file)16. {17. pr_info("mydev driver open!/n");18. return 0;19. }20. static int mydev_release(struct inode *inode, struct file *file)21. {22. pr_info("mydev driver released!/n");23. return 0;24. }25. ssize_t mydev_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)26. {27. ssize_t ret = 0;28. pr_info("mydev_write!/n");29. pr_info("writing %d bytes/n", count);30. if (count > 127)31. return -ENOMEM;32. if (count < 0)33. return -EINVAL;34. if (copy_from_user(data, buf, count)) {35. ret = -EFAULT;36. }37. else {38. data[127] = ''/0'';39. pr_info("kernel received: %s/n", data);40. ret = count;41. }42. return ret;43. }44. static ssize_t mydev_read(struct file* filp, char* buf, size_t len,loff_t* off)45. {46. if( copy_to_user(buf,data,len) )47. {48. return -EFAULT;49. }50.51. return len;52. }53. struct file_operations mydev_fops = {54. .owner = THIS_MODULE,55. .open = mydev_open,56. .read = mydev_read,57. .write = mydev_write,58. .release = mydev_release59.60. };61. static int __init mydev_init(void)62. {63. int result, error;64. result = register_chrdev(0, DEVICE_NAME, &mydev_fops);65. pr_info("udev_cdev: get major number: %d/n", result);66. dev = MKDEV(result, 0);67. myclass = class_create(THIS_MODULE, "mydev_class");68. device_create(myclass, NULL, dev, NULL, DEVICE_NAME);69. return 0;70. }71. static void __exit mydev_exit(void)72. {73. cdev_del(&mydev);74. unregister_chrdev_region(dev, number_of_devices);75. device_destroy(myclass, dev);76. class_destroy(myclass);77. pr_info("Goodbye cdev!/n");78. }79. module_init(mydev_init);80. module_exit(mydev_exit);81. MODULE_LICENSE("GPL");82. MODULE_DESCRIPTION("Simple cdev udev driver test");Makefile:obj-m := mydrive.oKDIR := /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)default:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modulesclean:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) cleanrm -rf Module.markers modules.order Module.symversinsmod mydrive.kolsmod查看步骤3:创建设备节点:mknod /dev/mydriver c 主设备号次设备号次设备号这里填0,主设备号可以利用cat /proc/devices 查看ls –l /dev/mydriver用df看看/dev/mydriver的使用情况了步骤4:编写用户程序(测试咱们的驱动是否可行),如以下代码,这个嘛,简单的用gcc 命令编译就好了1. #include <stdio.h>2. #include <sys/types.h>3. #include <unistd.h>4. #include <stdlib.h>5. #include <fcntl.h>6. i nt main (void)7. {8. int fd,len;9. pid_t pid;10. char buff[] = "This is from userspace zhangwei fight it!";11. char buff_read[100] ;12. fd = open ("/dev/cdev_zhangwei", O_RDWR);13. if (fd < 0) {14. perror("open failed");15. exit(0);16. }17. pid = fork();18. if(pid>0)19. {20. len = write (fd, buff, sizeof(buff));21. printf ("son Write returns %d/n",len );22. }23. else // parent24. {25. //waitpid(pid);26. printf ("read returns %d/n", read(fd,buff_read,len) );27. printf("buff_read = %s/n",buff_read);28. }29. close (fd);30. return 0;31. }。
linux设备驱动程序之简单字符设备驱动
![linux设备驱动程序之简单字符设备驱动](https://img.taocdn.com/s3/m/e146bdd25122aaea998fcc22bcd126fff6055d55.png)
linux设备驱动程序之简单字符设备驱动⼀、linux系统将设备分为3类:字符设备、块设备、⽹络设备。
使⽤驱动程序:1、字符设备:是指只能⼀个字节⼀个字节读写的设备,不能随机读取设备内存中的某⼀数据,读取数据需要按照先后数据。
字符设备是⾯向流的设备,常见的字符设备有⿏标、键盘、串⼝、控制台和LED设备等。
2、块设备:是指可以从设备的任意位置读取⼀定长度数据的设备。
块设备包括硬盘、磁盘、U盘和SD卡等。
每⼀个字符设备或块设备都在/dev⽬录下对应⼀个设备⽂件。
linux⽤户程序通过设备⽂件(或称设备节点)来使⽤驱动程序操作字符设备和块设备。
⼆、字符设备驱动程序基础:1、主设备号和次设备号(⼆者⼀起为设备号): ⼀个字符设备或块设备都有⼀个主设备号和⼀个次设备号。
主设备号⽤来标识与设备⽂件相连的驱动程序,⽤来反映设备类型。
次设备号被驱动程序⽤来辨别操作的是哪个设备,⽤来区分同类型的设备。
linux内核中,设备号⽤dev_t来描述,2.6.28中定义如下: typedef u_long dev_t; 在32位机中是4个字节,⾼12位表⽰主设备号,低12位表⽰次设备号。
可以使⽤下列宏从dev_t中获得主次设备号: 也可以使⽤下列宏通过主次设备号⽣成dev_t: MAJOR(dev_t dev); MKDEV(int major,int minor);MINOR(dev_t dev);View Code//宏定义:#define MINORBITS 20#define MINORMASK ((1U << MINORBITS) - 1)#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))2、分配设备号(两种⽅法):(1)静态申请:int register_chrdev_region(dev_t from, unsigned count, const char *name);View Code/*** register_chrdev_region() - register a range of device numbers* @from: the first in the desired range of device numbers; must include* the major number.* @count: the number of consecutive device numbers required* @name: the name of the device or driver.** Return value is zero on success, a negative error code on failure.*/(2)动态分配:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);View Codeint alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);/*** alloc_chrdev_region() - register a range of char device numbers* @dev: output parameter for first assigned number* @baseminor: first of the requested range of minor numbers* @count: the number of minor numbers required* @name: the name of the associated device or driver** Allocates a range of char device numbers. The major number will be* chosen dynamically, and returned (along with the first minor number)* in @dev. Returns zero or a negative error code.*/注销设备号:void unregister_chrdev_region(dev_t from, unsigned count);创建设备⽂件:利⽤cat /proc/devices查看申请到的设备名,设备号。
简单字符设备驱动程序的设计精简版
![简单字符设备驱动程序的设计精简版](https://img.taocdn.com/s3/m/b54461bbaff8941ea76e58fafab069dc502247d5.png)
简单字符设备驱动程序的设计1. 简介2. 设计思路2.1 初始化设备初始化设备的过程包括分配设备号、注册字符设备驱动程序和初始化字符设备驱动程序等步骤。
2.1.1 分配设备号设备号用于唯一标识一个字符设备。
可以使用alloc_chrdev_region()函数动态分配一个设备号,也可以使用register_chrdev_region()函数静态分配一个设备号。
2.1.2 注册字符设备驱动程序注册字符设备驱动程序需要使用cdev_init()函数来初始化字符设备结构,并使用cdev_add()函数将字符设备添加到内核中。
2.1.3 初始化字符设备驱动程序在cdev_init()函数中,需要设置字符设备驱动程序的操作函数,包括读取数据的read()函数、写入数据的write()函数和释放设备的release()函数。
2.2 读取数据读取数据的过程包括打开文件、调用字符设备驱动程序的read()函数和关闭文件等步骤。
2.2.1 打开文件打开文件需要使用系统调用的open()函数。
在打开文件时,可以进行一些初始化的操作。
2.2.2 调用read()函数调用字符设备驱动程序的read()函数读取数据。
在read()函数中,可以从字符设备的缓冲区中读取数据,并将数据发送给应用程序。
2.2.3 关闭文件关闭文件需要使用系统调用的close()函数。
在关闭文件时,可以进行一些资源的释放操作。
2.3 写入数据写入数据的过程包括打开文件、调用字符设备驱动程序的write()函数和关闭文件等步骤。
2.3.1 打开文件打开文件需要使用系统调用的open()函数。
在打开文件时,可以进行一些初始化的操作。
2.3.2 调用write()函数调用字符设备驱动程序的write()函数写入数据。
在write()函数中,可以将应用程序发送的数据写入字符设备的缓冲区中。
2.3.3 关闭文件关闭文件需要使用系统调用的close()函数。
简单字符设备驱动程序的设计[1]简版
![简单字符设备驱动程序的设计[1]简版](https://img.taocdn.com/s3/m/9b935416657d27284b73f242336c1eb91a373309.png)
简单字符设备驱动程序的设计简单字符设备驱动程序的设计1. 引言字符设备驱动程序是一种用于管理与操作字符设备的软件模块。
字符设备是指每次读写以字符为单位进行的设备,如终端设备、串口设备等。
本文将介绍如何设计一个简单的字符设备驱动程序。
2. 设计目标本文所设计的字符设备驱动程序具备以下目标:- 支持对字符设备的打开、关闭、读取和写入操作- 实现对字符设备的基本管理功能- 提供简单的错误处理机制3. 设计概述为了实现上述目标,我们将分为以下几个步骤来设计简单字符设备驱动程序。
步骤一:注册字符设备在设计字符设备驱动程序之前,我们首先需要在内核中注册字符设备。
在Linux系统中,可以使用`register_chrdev`函数来注册字符设备。
该函数将分配一个主设备号,并将字符设备驱动程序与该主设备号关联起来。
步骤二:编写设备打开函数设备打开函数是字符设备驱动程序的入口函数,它在应用程序打开设备文件时被调用。
在设备打开函数中,我们可以完成设备的初始化工作,并分配资源给设备。
步骤三:编写设备关闭函数设备关闭函数在应用程序关闭设备文件时被调用。
在设备关闭函数中,我们可以释放设备所占用的资源,并做一些清理工作。
步骤四:编写设备读取函数设备读取函数用于从设备中读取数据。
在设备读取函数中,我们可以读取设备缓冲区中的数据,并将数据返回给应用程序。
步骤五:编写设备写入函数设备写入函数用于向设备中写入数据。
在设备写入函数中,我们可以将应用程序传递的数据写入设备缓冲区,以供后续读取。
步骤六:添加文件操作结构体为了将设备驱动程序与设备文件相关联,我们需要在字符设备驱动程序中定义一个文件操作结构体。
该结构体中包含了与设备操作相关的函数指针,如打开函数、关闭函数、读取函数和写入函数。
步骤七:注册字符设备驱动程序完成上述步骤后,我们需要将字符设备驱动程序注册到内核中。
可以使用`cdev_init`函数来初始化字符设备,然后使用`cdev_add`函数将字符设备添加到内核的字符设备列表中。
实验二:字符设备驱动实验
![实验二:字符设备驱动实验](https://img.taocdn.com/s3/m/8b6254a6cc7931b765ce15d7.png)
实验二:字符设备驱动实验一、实验目的通过本实验的学习,了解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()五个基本操作,并编写一个测试程序来测试所编写的字符设备驱动程序。
linux设备驱动第三篇:写一个简单的字符设备驱动
![linux设备驱动第三篇:写一个简单的字符设备驱动](https://img.taocdn.com/s3/m/453a1ccba48da0116c175f0e7cd184254b351b77.png)
linux设备驱动第三篇:写⼀个简单的字符设备驱动在linux设备驱动第⼀篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写⼀个简单的字符设备驱动。
本篇借鉴LDD中的源码,实现⼀个与硬件设备⽆关的字符设备驱动,仅仅操作从内核中分配的⼀些内存。
下⾯就开始学习如何写⼀个简单的字符设备驱动。
⾸先我们来分解⼀下字符设备驱动都有那些结构或者⽅法组成,也就是说实现⼀个可以使⽤的字符设备驱动我们必须做些什么⼯作。
1、主设备号和次设备号对于字符设备的访问是通过⽂件系统中的设备名称进⾏的。
他们通常位于/dev⽬录下。
如下:xxx@ubuntu:~$ ls -l /dev/total 0brw-rw---- 1 root disk 7, 0 3⽉ 25 10:34 loop0brw-rw---- 1 root disk 7, 1 3⽉ 25 10:34 loop1brw-rw---- 1 root disk 7, 2 3⽉ 25 10:34 loop2crw-rw-rw- 1 root tty 5, 0 3⽉ 25 12:48 ttycrw--w---- 1 root tty 4, 0 3⽉ 25 10:34 tty0crw-rw---- 1 root tty 4, 1 3⽉ 25 10:34 tty1crw--w---- 1 root tty 4, 10 3⽉ 25 10:34 tty10其中b代表块设备,c代表字符设备。
对于普通⽂件来说,ls -l会列出⽂件的长度,⽽对于设备⽂件来说,上⾯的7,5,4等代表的是对应设备的主设备号,⽽后⾯的0,1,2,10等则是对应设备的次设备号。
那么主设备号和次设备号分别代表什么意义呢?⼀般情况下,可以这样理解,主设备号标识设备对应的驱动程序,也就是说1个主设备号对应⼀个驱动程序。
当然,现在也有多个驱动程序共享主设备号的情况。
⽽次设备号有内核使⽤,⽤于确定/dev下的设备⽂件对应的具体设备。
简单字符设备驱动程序的设计本月修正2023简版
![简单字符设备驱动程序的设计本月修正2023简版](https://img.taocdn.com/s3/m/20114e9381eb6294dd88d0d233d4b14e85243e12.png)
简单字符设备驱动程序的设计简单字符设备驱动程序的设计摘要本文介绍了简单字符设备驱动程序的设计,包括驱动程序的层次结构、关键功能模块的实现以及编写驱动程序的一般步骤。
通过阅读本文,读者可以了解字符设备驱动程序的基本原理和设计方法,为开发自己的驱动程序奠定基础。
1. 引言字符设备驱动程序是操作系统中的一个重要组成部分,它负责处理和管理字符设备的读写请求。
字符设备包括键盘、显示器等基本输入输出设备,也包括串口、并口等其他字符设备。
本文将以一个简单的字符设备驱动程序为例,介绍其设计和实现过程。
2. 设计思路2.1 驱动程序的层次结构字符设备驱动程序通常具有以下三个层次结构:- 应用层:负责与用户空间进行交互,接收和处理用户的读写请求。
- 中间层:负责驱动程序的逻辑控制,将用户请求传递给底层驱动程序。
- 底层层:与硬件设备进行交互,负责真正的数据读写操作。
2.2 关键功能模块的实现字符设备驱动程序的关键功能模块包括初始化、打开、关闭、读取和写入等。
下面分别介绍这些功能模块的实现。
- 初始化:在驱动程序加载时进行初始化操作,包括申请资源、注册字符设备等。
- 打开:当用户打开字符设备时,内核会调用驱动程序的打开函数,可以在该函数中进行一些必要的初始化操作。
- 关闭:当用户关闭字符设备时,内核会调用驱动程序的关闭函数,可以在该函数中进行一些必要的资源释放操作。
- 读取:当用户从字符设备中读取数据时,内核会调用驱动程序的读取函数,可以在该函数中实现数据的读取操作。
- 写入:当用户向字符设备中写入数据时,内核会调用驱动程序的写入函数,可以在该函数中实现数据的写入操作。
3. 编写驱动程序的步骤编写字符设备驱动程序的一般步骤包括以下几个方面:1. 定义并注册字符设备:在代码中定义一个字符设备结构体,并在初始化函数中调用 `register_chrdev` 函数进行注册。
2. 实现初始化函数:在初始化函数中进行资源的申请、字符设备结构体的初始化和注册等操作。
简单字符设备驱动程序的设计
![简单字符设备驱动程序的设计](https://img.taocdn.com/s3/m/a0f8335b2379168884868762caaedd3383c4b535.png)
简单字符设备驱动程序的设计1. 引言字符设备驱动程序是操作系统内核与硬件之间的接口,用于实现对字符设备的访问和控制。
设计一个简单的字符设备驱动程序,可以帮助我们深入理解操作系统的工作原理,也是学习驱动程序开发的重要一步。
2. 设计目标本文旨在设计一个简单的字符设备驱动程序,具备以下目标:实现字符设备的读取和写入操作;支持多个进程对字符设备进行读写;实现等待队列,当设备忙碌时,读写操作能够进入等待状态,待设备空闲后再进行处理;提供用户空间与内核空间之间的数据传输。
3. 设计思路3.1 设册,我们需要将设册到Linux内核中。
设册是指将设备信息添加到内核的设备列表中,以便内核能够对其进行管理和访问。
设册需要提供设备的主设备号和次设备号。
3.2 设备打开和关闭设备打开是指用户空间应用程序请求访问设备时,内核需要分配设备资源,并建立应用程序与设备之间的连接。
设备关闭是指用户空间应用程序不再需要访问设备时,内核需要释放设备资源,并断开应用程序与设备之间的连接。
3.3 设备读取和写入设备读取是指用户空间应用程序从设备中读取数据,设备写入是指用户空间应用程序向设备中写入数据。
设备读取和写入需要设计缓冲区以存储数据,并通过相应的操作函数实现数据的传输。
3.4 设备等待队列为了实现设备的并发访问,我们需要设计设备的等待队列,即当设备忙碌时,读写操作能够进入等待状态。
设备等待队列需要设计相应的等待队列头,并通过等待队列头为读写操作设置等待条件。
3.5 用户空间与内核空间数据传输设备的读取和写入操作需要涉及用户空间和内核空间之间的数据传输。
为此,我们需要设计合适的接口函数,用于实现用户空间与内核空间数据的传输。
4. 设计实现基于上述设计思路,我们可以开始编写简单字符设备驱动程序的实现代码。
具体实现过程略过,可以参考相关的驱动程序开发资料和示例代码。
5. 测试与验证在代码编写完成后,我们需要进行测试和验证,以确保驱动程序能够正常运行并满足设计目标。
简单字符设备驱动程序的设计
![简单字符设备驱动程序的设计](https://img.taocdn.com/s3/m/dc14effafc0a79563c1ec5da50e2524de418d049.png)
简单字符设备驱动程序的设计简单字符设备驱动程序的设计简介字符设备驱动程序是操作系统中的一种驱动程序,用于与用户空间中的字符设备进行交互。
字符设备是一种提供字节流访问的设备,如终端、串口等。
设计步骤步骤一:创建设备文件在Linux系统中,字符设备驱动程序通过设备文件与用户空间进行通信。
我们需要在/dev目录下创建设备文件,例如/dev/my_device。
通过命令`mknod /dev/my_device c <major><minor>`可以创建设备文件,其中<major>和<minor>分别是设备的主设备号和次设备号。
步骤二:编写驱动程序代码驱动程序代码主要包括以下几个部分:头文件引用:需要包含linux/module.h、linux/fs.h等头文件。
定义设备结构:可以使用struct cdev来定义字符设备结构,结构中包括设备编号、设备操作函数等信息。
实现设备操作函数:字符设备驱动程序需要实现open、release、read和write等设备操作函数,用于处理设备的打开、关闭、读取和写入操作。
注册设备:字符设备驱动程序需要在初始化时调用函数register_chrdev_region或alloc_chrdev_region来分配设备号,并通过函数cdev_add将设备添加到系统中。
步骤三:构建和安装驱动程序在编写完驱动程序代码后,需要进行构建和安装。
使用Makefile文件可以方便地进行构建和安装操作。
通过make命令可以编译.ko文件,然后使用insmod命令将.ko文件加载到内核中。
步骤四:驱动程序驱动程序安装完成后,可以通过编写程序来驱动程序的功能。
在用户空间中,通过打开设备文件、读取和写入设备数据等操作,来与驱动程序进行交互。
简单字符设备驱动程序的设计分为创建设备文件、编写驱动程序代码、构建和安装驱动程序以及驱动程序等几个步骤。
在设计过程中,需要了解字符设备驱动程序的结构和操作函数的实现。
一个简单的演示用的Linux字符设备驱动程序
![一个简单的演示用的Linux字符设备驱动程序](https://img.taocdn.com/s3/m/12dba48d647d27284b7351f3.png)
实现如下的功能:--字符设备驱动程序的结构及驱动程序需要实现的系统调用--可以使用cat命令或者自编的readtest命令读出"设备"里的内容--以8139网卡为例,演示了I/O端口和I/O内存的使用本文中的大部分内容在Linux Device Driver这本书中都可以找到,这本书是Linux驱动开发者的唯一圣经。
================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数如果不指定MODULE_LICENSE("GPL", 在模块插入内核的时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的"GPL"属性。
module_init(char8139_init;module_exit(char8139_exit;MODULE_LICENSE("GPL";MODULE_AUTHOR("ypixunil";MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init(static int __init char8139_init(void{int result;PDBG("hello. init.\n";/* register our char device */result=register_chrdev(char8139_major, "char8139", &char8139_fops;if(result<0{PDBG("Cannot allocate major device number!\n";return result;}/* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */if(char8139_major == 0char8139_major=result;/* allocate some kernel memory we need */buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL;if(!buffer{PDBG("Cannot allocate memory!\n";result= -ENOMEM;可以通过"cat /proc/devices"命令来查看系统中已经使用的主设备号。
简单字符设备驱动程序的设计
![简单字符设备驱动程序的设计](https://img.taocdn.com/s3/m/a4192d9b51e2524de518964bcf84b9d528ea2c18.png)
简单字符设备驱动程序的设计简单字符设备驱动程序的设计简介在操作系统中,设备驱动程序是用来管理和控制硬件设备的软件模块。
其中,字符设备驱动程序是一种用来管理和控制字符设备的驱动程序。
字符设备是一种以字节流的形式进行输入和输出的设备,例如键盘、打印机等。
设计步骤步骤一:设册,我们需要将设册到系统中,这样操作系统就能够管理并使用该设备。
设备的注册可以通过调用`register_chrdev`函数来完成。
在注册设备时,需要指定设备的主设备号和设备的名称。
步骤二:初始化设备设册完成后,我们需要对设备进行初始化。
设备的初始化可以在驱动程序的`init`函数中完成。
在初始化函数中,我们可以进行一些必要的设备设置,例如分配内存空间、设置设备的属性等。
步骤三:实现文件操作函数文件操作函数是驱动程序的核心部分。
在字符设备驱动程序中,常见的文件操作函数有`open`、`release`、`read`和`write`。
这些函数负责打开设备、关闭设备、从设备读取数据和向设备写入数据。
步骤四:实现字符设备控制函数字符设备控制函数是驱动程序的另一个重要模块。
在字符设备驱动程序中,常见的字符设备控制函数有`ioctl`。
`ioctl`函数可以用来控制设备的一些特殊操作,例如设置设备的状态、获取设备的信息等。
步骤五:清理函数在驱动程序退出时,需要进行一些清理工作,例如释放分配的内存空间、注销设备等。
这些清理工作可以在驱动程序的`exit`函数中完成。
设计一个简单的字符设备驱动程序需要完成设备的注册、初始化设备、实现文件操作函数和字符设备控制函数、清理函数等步骤。
通过以上步骤可以构建一个简单的字符设备驱动程序,实现对字符设备的管理和控制。
这个驱动程序可以用作学习和理解设备驱动程序的基础,并为后续更复杂的驱动程序开发打下基础。
- 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: 是要申请的中断。