字符设备驱动步骤
编写字符设备驱动框架的步骤(简要

/* alloc_chrdev_region() - register a range of char device numbers
mknod /dev/yourname c major minor
其中“yourname”可以是任意符合unix下路径名的名字,不一定要是你代码里定义的驱动或设备的名字;c 表示创建字符设备节点,major是你成功申请的主设备号,minor是次设备号,这个可以是任意的(在次设备号范围内)
*
* Return value is zero on success, a negative error code on failure.*/
这种方式主要用于,驱动开发者事先知道该驱动主设备号的情况。
⑵动态申请
通过下面这个函数实现:
另外一种方法是通过udev自动生成。这种方法需要在你的代码里创建一个设备类,然后在这个设备类的基础上,创建一个设备;另外应用程序需要跑一个udevd的后台程序。
struct class* class_create(owner, name);
struct device *device_create(struct class *class, struct device *parent,
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
linux字符设备驱动框架流程

linux字符设备驱动框架流程Linux字符设备驱动框架流程一、引言字符设备驱动是Linux系统中的一种设备驱动类型,用于对字符设备的操作和管理。
本文将介绍Linux字符设备驱动框架的流程,包括驱动的注册、设备的初始化、文件操作接口的实现以及驱动的注销。
二、驱动的注册1. 驱动的初始化:驱动的初始化是在模块加载时进行的,通过定义init函数来进行初始化操作。
在初始化函数中,需要进行一些准备工作,如分配主设备号、创建设备类等。
2. 分配主设备号:主设备号是用来标识设备驱动的唯一标识符,通过调用函数alloc_chrdev_region来分配主设备号。
分配成功后,可以通过主设备号和次设备号来唯一标识一个设备。
3. 创建设备类:设备类用于将具有相同属性和行为的设备分为一组,通过调用函数class_create来创建设备类。
设备类的创建需要指定设备类的名字和设备的回调函数。
4. 注册字符设备驱动:注册字符设备驱动是通过调用函数cdev_init和cdev_add来实现的。
cdev_init用于初始化字符设备结构,cdev_add用于将字符设备添加到系统中。
三、设备的初始化1. 设备的创建:设备的创建是通过调用函数device_create来实现的。
设备的创建需要指定设备类、父设备、设备号和设备名。
2. 设备的初始化:设备的初始化是在设备创建后进行的,通过定义probe函数来进行初始化操作。
在probe函数中,需要进行一些设备的特定初始化工作,如申请资源、初始化设备寄存器等。
四、文件操作接口的实现1. 文件操作接口的定义:文件操作接口是用于对设备进行读写操作的接口,包括打开设备、关闭设备、读取设备和写入设备等操作。
文件操作接口需要定义在字符设备结构的file_operations成员中。
2. 文件操作接口的实现:文件操作接口的实现是通过定义对应的函数来实现的。
在函数中,需要进行一些设备操作的具体实现,如读取设备数据、写入设备数据等。
最简单的字符设备驱动程序

最简单的字符设备驱动程序⾸先,先理清⼀下简单字符设备驱动程序的思路:(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;}。
字符设备驱动开发流程

字符设备驱动开发流程
字符设备驱动开发流程如下:
1. 确定所需的驱动功能:确定设备的主要功能和操作系统的要求。
2. 分配设备编号:在Linux中,字符设备驱动需要分配一个设备编号,可以使用Linux自带的编号分配机制,也可以手动分配。
3. 编写设备驱动程序:使用C语言或者汇编语言编写设备驱动程序,包含初始化、读写操作、中断处理和卸载等函数。
4. 编译驱动程序:使用Makefile等工具编译生成驱动程序。
5. 安装驱动程序:将驱动程序安装到操作系统中,可以使用insmod命令进行安装。
6. 测试驱动程序:使用测试工具或者编写测试程序测试驱动程序的功能和正确性。
7. 调试和优化:对驱动程序进行调试和优化,保证其稳定性和性能。
8. 发布和维护:将驱动程序发布到Linux社区,接受反馈和维护驱动程序。
注:以上流程仅供参考,实际开发过程中可能会因为具体情况而有所不同。
第5章 字符设备驱动

第5章字符设备驱动现在,你已经准备就绪了,可以尝试去写一个简单、但实用的设备驱动了。
在这一章,我们将深入字符设备驱动的内幕:顺序存取设备数据的内核代码。
字符设备驱动能从如下几类设备获取原始的数据:如打印机、鼠标、看门狗、键盘、内存、实时时钟等,但它不适合用于以块方式存储的、随机访问的设备,如硬盘、软盘和光盘。
字符设备驱动基础让我们以自顶向下的方式开始字符设备驱动学习之旅。
为了访问一个字符设备,系统用户需要调用相应的应用程序。
此应用程序负责和设备交互,为了实现此目的,需要得到相应驱动的标识符。
驱动通过/dev目录给用户提供接口:bash> ls -l /devtotal 0crw------- 1 root root 5, 1 Jul 16 10:02 console...lrwxrwxrwx 1 root root 3 Oct 6 10:02 cdrom -> hdc...brw-rw---- 1 root disk 3, 0 Oct 6 2007 hdabrw-rw---- 1 root disk 3, 1 Oct 6 2007 hda1...crw------- 1 root tty 4, 1 Oct 6 10:20 tty1crw------- 1 root tty 4, 2 Oct 6 10:02 tty2ls命令输出结果的每一行的第一个字符表示驱动的类型:c表示字符设备驱动,b代表块设备驱动,l表示符号链接。
第五列的数字是主设备号,第六列是次设备号。
主设备号通常标识设备对应的驱动程序,次设备号用于确定驱动程序所服务的设备。
例如,IDE块存储设备驱动/dev/had的主设备号为3,负责处理系统的硬盘;当进一步指明其次设备号为1时(/dev/hda1),它指向第一个硬盘分区。
字符设备驱动与块设备驱动占用不同空间,因此可以将同一个主设备号分配给字符设备和块设备驱动。
让我们进一步深入字符设备驱动。
字符设备驱动实验报告(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.流程说明在上一节中已经提到,设备驱动程序可以使用模块的方式动态加载到内核中去。
加载模块的方式与以往的应用程序开发有很大的不同。
以往在开发应用程序时都有一个main函数作为程序的入口点,而在驱动开发时却没有main 函数,模块在调用insmod命令时被加载,此时的入口点是init_module函数,通常在该函数中完成设备的注册。
同样,模块在调用rmmod 函数时被卸载,此时的入口点是cleanup_module函数,在该函数中完成设备的卸载。
在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,如read、write等,而驱动程序就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作,init_module入口点函数则不需要完成其他如read、write之类功能。
上述函数之间的关系如图11.3 所示。
内核设备注册设备卸载设备功能用户调用模块init_module()cleanup_modulermmodinsmod图11.3 设备驱动程序流程图2.重要数据结构用户应用程序调用设备的一些功能是在设备驱动程序中定义的,也就是设备驱动程序的入口点,它是一个在<linux/fs.h>中定义的struct file结构,这是一个内核结构,不会出现在用户空间的程序中,它定义了常见文件I/O 函数的入口。
如下所示:struct file_operations {loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *filp, char *buff, size_t count, loff_t *offp); ssize_t (*write) (struct file *filp, const char *buff, size_t count, loff_t *offp); int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsignedlong);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *);int (*fasync) (int, struct file *, int);int (*check_media_change) (kdev_t dev);int (*revalidate) (kdev_t dev);int (*lock) (struct file *, int, struct file_lock *);};这里定义的很多函数读者在第6 章中已经见到过了,当时是调用这些函数,而在这里我们将学习如何实现这些函数。
实现一个简单的linux字符设备驱动
实现一个简单的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. }。
简单字符设备驱动程序的设计[1]简版
简单字符设备驱动程序的设计简单字符设备驱动程序的设计1. 引言字符设备驱动程序是一种用于管理与操作字符设备的软件模块。
字符设备是指每次读写以字符为单位进行的设备,如终端设备、串口设备等。
本文将介绍如何设计一个简单的字符设备驱动程序。
2. 设计目标本文所设计的字符设备驱动程序具备以下目标:- 支持对字符设备的打开、关闭、读取和写入操作- 实现对字符设备的基本管理功能- 提供简单的错误处理机制3. 设计概述为了实现上述目标,我们将分为以下几个步骤来设计简单字符设备驱动程序。
步骤一:注册字符设备在设计字符设备驱动程序之前,我们首先需要在内核中注册字符设备。
在Linux系统中,可以使用`register_chrdev`函数来注册字符设备。
该函数将分配一个主设备号,并将字符设备驱动程序与该主设备号关联起来。
步骤二:编写设备打开函数设备打开函数是字符设备驱动程序的入口函数,它在应用程序打开设备文件时被调用。
在设备打开函数中,我们可以完成设备的初始化工作,并分配资源给设备。
步骤三:编写设备关闭函数设备关闭函数在应用程序关闭设备文件时被调用。
在设备关闭函数中,我们可以释放设备所占用的资源,并做一些清理工作。
步骤四:编写设备读取函数设备读取函数用于从设备中读取数据。
在设备读取函数中,我们可以读取设备缓冲区中的数据,并将数据返回给应用程序。
步骤五:编写设备写入函数设备写入函数用于向设备中写入数据。
在设备写入函数中,我们可以将应用程序传递的数据写入设备缓冲区,以供后续读取。
步骤六:添加文件操作结构体为了将设备驱动程序与设备文件相关联,我们需要在字符设备驱动程序中定义一个文件操作结构体。
该结构体中包含了与设备操作相关的函数指针,如打开函数、关闭函数、读取函数和写入函数。
步骤七:注册字符设备驱动程序完成上述步骤后,我们需要将字符设备驱动程序注册到内核中。
可以使用`cdev_init`函数来初始化字符设备,然后使用`cdev_add`函数将字符设备添加到内核的字符设备列表中。
字符设备驱动程序的基本步骤
字符设备驱动程序的基本步骤字符设备驱动程序的基本步骤一.设备号对字符设备的访问是通过文件系统内的设备名称来访问的,设备名称位于目录/dev下.为了便于系统管理,设置了和设备名称一一对应的设备号,它分为主设备号和次设备号.通常来说,主设备号标示了设备对应的驱动程序,次设备号则用来分辨拥有同一个主设备号的的各个不同设备.在内核中,设备号使用类型dev_t来保存,它包括了主设备号和次设备号.dev_t是一个32位的整数,其中的12位用来标示主设备号,其余的20位用来标示次设备号.我们可以使用两个宏来获得设备的主设备号及次设备号:MAJOR(dev_t dev_id);MINOR(dev_t dev_id);将主设备号和次设备号转换为dev_t类型,则可以使用下面的宏:MKDEV(int major, int minor);其中,major为主设备号,minor为次设备号.二.分配设备号在建立一个字符设备之前.首先要申请设备号,完成该功能的函数有两个,都包含在头文件中.下面分别来看这两个文件:1.int register_chrdev_region(dev_t first, unsigned int count, char *name);其中, first为要分配的设备编号范围的起始值,经常被置零.count则是所请求的连续设备编号的个数,这意味着只能申请连续的设备编号.2.int alloc_chrdev_region(dev_t *dev, unsigned firstminor, int count, char *name);其中dev用于保存申请成功后动态分配的第一个设备号, firstminor则是请求使用的第一个次设备号.其余与上个函数相同.三.定义并初始化file_operations结构体.file_operations结构体用于连接设备号和驱动程序的操作.在该结构体的内部包含了一组函数指针,这些函数用来实现系统调用.通常情况下,要注册如下的几个函数:1.struct module *owner:用来指向拥有该结构体的模块.2.ssize_t read(struct file *filp, char __user *buf, size_t count, loff_t *f_ops):用来从设备中读取数据.其中:filp为文件属性结构体指针.buf为用户态函数使用的字符内存缓冲.count为要读取的数据数.f_ops为文件指针的偏移量.2.ssize_t write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops):用来向设备输入数据.各函数的含义与上个函数相同.3.int open(struct inode *inode, struct file *):该函数用来打开一个设备文件.4.int release(struct inode *inode, struct file *):该函数用来关闭一个设备文件.该结构体的初始化形式如下例:struct file_operations scull_fops = {.owner = THIS_MODULE,.read = read,.write = write,.open = open,.release = release,}四.字符设备的注册.内核内部使用struct cdev结构来表示字符设备.在内核调用设备的操作之前,必须分配或注册一个或者多个该结构体.该结构体包含在头文件中.一般的步骤如下:首先定义该结构体:struct cdev my_cdev;然后即可以初始化该结构,使用如下的函数初始化:int cdev_init(struct cdev *dev, struct file_operations *fops).然后定义该结构体中的一个所有者字段:my_cdev.owner = THIS_MODULE;最后即可以向模块添加该结构体:int cdev_add(struct cdev *dev, dev_t dev_num, usigned int count).其中dev是cdev结构体,dev_num是该设备对应的第一个设备编号, count则是与该设备关联的设备编号数量.五.移除字符设备void cdev_del(struct cdev *dev);六.注销设备号.unregister_chrdev_region(dev_t first, unsigned int count);以上两个函数一般用于模块出口函数中.。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
编写字符设备驱动框架的步骤
Step 1: 申请设备号(主要是申请主设备号)
有两种方式:
⑴静态申请
通过下面这个函数实现:
int register_chrdev_region(dev_t from, unsigned count, const char *name);
/* 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.*/
这种方式主要用于,驱动开发者事先知道该驱动主设备号的情况。
⑵动态申请
通过下面这个函数实现:
int 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.*/
这种方式由系统动态分配一个设备号,返回的设备号保存在参数dev中。
Step 2 :注册字符设备
在linux 内核中用struct cdev表示一个字符设备。
字符设备的注册与注销分别通过下面的两个函数来实现:
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
void cdev_del(struct cdev *p);
不过,在注册一个字符设备之前,要调用下面这个函数来初始化struct cdev结构体:
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
另外,struct cdev结构体变量可以声明为一个指针,内核提供了一个函数来申请:struct cdev *cdev_alloc(void);
step 3:创建设备节点
有两种方法:
一是通过mknod命令来创建。
如:
mknod /dev/yourname c major minor
其中“yourname”可以是任意符合unix下路径名的名字,不一定要是你代码里定义的驱动或设备的名字;c 表示创建字符设备节点,major是你成功申请的主设备号,minor是次设备号,这个可以是任意的(在次设备号范围内)
另外一种方法是通过udev自动生成。
这种方法需要在你的代码里创建一个设备类,然后在这个设备类的基础上,创建一个设备;另外应用程序需要跑一个udevd的后台程序。
struct class*class_create(owner, name);
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)。