发布时间:2026-07-21阅读(1)


三种设备的定义分别如下,

更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.


Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
1.驱动初始化1.1分配cdev
/*……*//* 分配cdev*/struct cdev btn_cdev;/*……*//* 1.1 申请设备号*/if(major){//静态dev_id = MKDEV(major, 0);register_chrdev_region(dev_id, 1, "button");} else {//动态alloc_chardev_region(&dev_id, 0, 1, "button");major = MAJOR(dev_id);}/*……*/
获得:主设备号:MAJOR(dev_t dev); 次设备号:MINOR(dev_t dev); 生成:MKDEV(int major,int minor);
int register_chrdev_region(dev_t from, unsigned count, const char *name);/*功能:申请使用从from开始的count 个设备号(主设备号不变,次设备号增加)*/
1.2.初始化cdev
1 void cdev_init(struct cdev *, struct file_operations *); 2 cdev_init()函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接。
1.3.注册cdev
1 int cdev_add(struct cdev *, dev_t, unsigned);2 cdev_add()函数向系统添加一个 cdev,完成字符设备的注册。
1.4.硬件初始化
/* 地址映射 */gpx2con = ioremap(GPX2CON, 4);if (gpx2con == NULL){printk("gpx2con ioremap err\n");goto err3;}gpx2dat = ioremap(GPX2DAT, 4);if (gpx2dat == NULL){printk("gpx2dat ioremap err\n");goto err4;} gpx1con = ioremap(GPX1CON, 4);gpx1dat = ioremap(GPX1DAT, 4); gpf3con = ioremap(GPF3CON, 4);gpf3dat = ioremap(GPF3DAT, 4); writel(readl(gpx2con)&(~(0xf<<28))|(0x1<<28), gpx2con);writel((0x1<<7), gpx2dat);writel(readl(gpx1con)&(~(0xf<<0))|(0x1<<0), gpx1con);writel((0x1<<0), gpx1dat);writel(readl(gpf3con)&(~(0xf<<16))|(0x1<<16), gpf3con);writel(readl(gpf3dat)|(0x1<<4), gpf3dat);writel(readl(gpf3con)&(~(0xf<<20))|(0x1<<20), gpf3con);writel(readl(gpf3dat)|(0x1<<5), gpf3dat);
struct file_operations hello_fops = {.owner = THIS_MODULE,.open = hello_open,.release = hello_release,.read = hello_read,.write = hello_write,};
2.1. open()函数
1 int(*open)(struct inode *, struct file*); 2 /*打开*/
2.2. read( )函数
ssize_t(*read)(struct file *, char __user*, size_t, loff_t*); /*用来从设备中读取数据,成功时函数返回读取的字节数,出错时返回一个负值*/
2.3. write( )函数
1 ssize_t(*write)(struct file *, const char__user *, size_t, loff_t*);2 /*向设备发送数据,成功时该函数返回写入的字节数。如果此函数未被实现,3 当用户进行write()系统调用时,将得到-EINVAL返回值*/
2.4. close( )函数
1 int(*release)(struct inode *, struct file*); 2 /*关闭*/
2.5 ioctl( )函数
int ioctl(int fd, ind cmd, …);
MAGIC: 幻数 8位的数 0 - 255 L nr: 序数,用来区分同一类设备的不同命令 #define CMD _IO(MAGIC, nr)
#define LED_MAGIC L #define LED_ON _IO(LED_MAGIC, 1) #define LED_OFF _IO(LED_MAGIC, 2)
2.5. 补充说明
1. 在Linux字符设备驱动程序设计中,有3种非常重要的数据结构:struct file、struct inode、struct file_operations。
2. 在read( )和write( )中的buff 参数是用户空间指针。因此,它不能被内核代码直接引用,因为用户空间指针在内核空间时可能根本是无效的——没有那个地址的映射。因此,内核提供了专门的函数用于访问用户空间的指针:
1 unsigned long copy_from_user(void *to, const void __user *from, unsigned long count);2 unsigned long copy_to_user(void __user *to, const void *from, unsigned long count);
3.1. 删除cdev
在字符设备驱动模块卸载函数中通过cdev_del()函数向系统删除一个cdev,完成字符设备的注销。
/*原型:*/void cdev_del(struct cdev *);/*例:*/cdev_del(&btn_cdev);
3.2. 释放设备号
/*原型:*/void unregister_chrdev_region(dev_t from, unsigned count);/*例:*/unregister_chrdev_region(MKDEV(major, 0), 1);
struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
dev 类型是dev_t,也就是我们的设备号 ops是一个同样也是一个结构体并且是一个字符驱动实现的主体,字符驱动通常需要和应用程序交互,在学linux系统编程的时候,都会讲到linux 应用程序通过系统调用陷入到内核空间,从而执行内核代码,而驱动作为内核的一部分同样也是需要在内核空间执行的,ops也就是file_operations这个结构体就是我们的驱动为应用程序调用驱动而实现的一个操作的集合。后面会详细讲解。
MAJOR(dev_t dev) MINOR(dev_t dev) 相反地,可以通过主次设备号来生成dev_t: MKDEV(int major,int minor)
4.2 Linux 2.6内核提供一组函数用于操作cdev 结构体1:void cdev_init(struct cdev*,struct file_operations *);2:struct cdev *cdev_alloc(void);3:int cdev_add(struct cdev *,dev_t,unsigned);4:void cdev_del(struct cdev *);

4.3 Linux 2.6内核分配和释放设备号5:int register_chrdev_region(dev_t from,unsigned count,const char *name)
6:int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);
7:void unregister_chrdev_region(dev_t from,unsigned count);
4.4 cdev结构的file_operations结构体1 struct file_operations { 2 3 /*拥有该结构的模块计数,一般为THIS_MODULE*/ 4 struct module *owner; 5 6 /*用于修改文件当前的读写位置*/ 7 loff_t (*llseek) (struct file *, loff_t, int); 8 9 /*从设备中同步读取数据*/10 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);11 12 /*向设备中写数据*/13 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);14 15 16 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);17 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);18 int (*readdir) (struct file *, void *, filldir_t);19 20 /*轮询函数,判断目前是否可以进行非阻塞的读取或写入*/21 unsigned int (*poll) (struct file *, struct poll_table_struct *);22 23 /*执行设备的I/O命令*/24 int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);25 26 27 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);28 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);29 30 /*用于请求将设备内存映射到进程地址空间*/31 int (*mmap) (struct file *, struct vm_area_struct *);32 33 /*打开设备文件*/34 int (*open) (struct inode *, struct file *);35 int (*flush) (struct file *, fl_owner_t id);36 37 /*关闭设备文件*/38 int (*release) (struct inode *, struct file *);39 40 41 int (*fsync) (struct file *, struct dentry *, int datasync);42 int (*aio_fsync) (struct kiocb *, int datasync);43 int (*fasync) (int, struct file *, int);44 int (*lock) (struct file *, int, struct file_lock *);45 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);46 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);47 int (*check_flags)(int);48 int (*flock) (struct file *, int, struct file_lock *);49 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);50 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);51 int (*setlease)(struct file *, long, struct file_lock **);52 };
1 struct file{ 2 3 mode_t fmode; /*文件模式,如FMODE_READ,FMODE_WRITE*/ 4 5 ...... 6 7 loff_t f_pos; /*loff_t 是一个64位的数,需要时,须强制转换为32位*/ 8 9 unsigned int f_flags; /*文件标志,如:O_NONBLOCK*/10 11 struct file_operations *f_op;12 13 void *private_data; /*非常重要,用于存放转换后的设备描述结构指针*/14 15 .......16 17 };
1 struct inode{ 2 3 dev_t i_rdev; /*设备编号*/ 4 5 struct cdev *i_cdev; /*cdev 是表示字符设备的内核的内部结构*/ 6 7 }; 8 9 可以从inode中获取主次设备号,使用下面二个宏:10 11 /*驱动工程师一般不关心这二个宏*/12 13 unsigned int imajor(struct inode *inode);14 15 unsigned int iminor(struct inode *inode);
1/*设备结构体*/ 2 3 struct xxx_dev{ 4 5 struct cdev cdev; 6 7 char *data; 8 9 struct semaphore sem;10 11 ......12 13 };14 15 16 17 /*模块加载函数*/18 19 static int __init xxx_init(void)20 21 {22 23 .......24 25 初始化cdev结构;26 27 申请设备号;28 29 注册设备号;30 31 32 33 申请分配设备结构体的内存; /*非必须*/34 35 }36 37 38 39 /*模块卸载函数*/40 41 static void __exit xxx_exit(void)42 43 {44 45 .......46 47 释放原先申请的设备号;48 49 释放原先申请的内存;50 51 注销cdev设备;52 53 }54 55
1 /*读设备*/ 2 3 ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) 4 5 { 6 7 ...... 8 9 使用filp->private_data获取设备结构体指针;10 11 分析和获取有效的长度;12 13 /*内核空间到用户空间的数据传递*/14 15 copy_to_user(void __user *to, const void *from, unsigned long count);16 17 ......18 19 }20 21 /*写设备*/22 23 ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)24 25 {26 27 ......28 29 使用filp->private_data获取设备结构体指针;30 31 分析和获取有效的长度;32 33 /*用户空间到内核空间的数据传递*/34 35 copy_from_user(void *to, const void __user *from, unsigned long count);36 37 ......38 39 }40 41 /*ioctl函数*/42 43 static int xxx_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)44 45 {46 47 ......48 49 switch(cmd){50 51 case xxx_CMD1:52 53 ......54 55 break;56 57 case xxx_CMD2:58 59 .......60 61 break;62 63 default:64 65 return -ENOTTY; /*不能支持的命令*/66 67 }68 69 return 0;70 71 }
1 struct file_operations xxx_fops = { 2 3 .owner = THIS_MODULE, 4 5 .open = xxx_open, 6 7 .read = xxx_read, 8 9 .write = xxx_write,10 11 .close = xxx_release,12 13 .ioctl = xxx_ioctl,14 15 .lseek = xxx_llseek,16 17 };18 19 上面的写法需要注意二点,一:结构体成员之间是以逗号分开的而不是分号,结构体字段结束时最后应加上分号。
devfs_mk_bdev devfs_mk_cdev devfs_mk_symlink devfs_mk_dir devfs_remove
class_device_create_file,在2.6.26以后也不行了,现在,使用的是device_create ,从2.6.18开始可用 struct device *device_create(struct class *class, struct device *parent, dev_t devt, const char *fmt, ...) 从2.6.26起又多了一个参数drvdata: the data to be added to the device for callbacks
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
//device classes struct class { const char *name; struct module *owner; nbsp; struct kset subsys; struct list_head devices; struct list_head interfaces; struct kset class_dirs; struct semaphore sem; //lockschildren, devices, interfaces struct class_attribute *class_attrs; struct device_attribute *dev_attrs; int (*dev_uevent)(structdevice *dev, struct kobj_uevent_env *env); void (*class_release)(structclass *class); void (*dev_release)(struct device *dev); int (*suspend)(struct device*dev, pm_message_t state); int (*resume)(struct device *dev);};
// class_create - create a struct class structure// @owner: pointer to the module that is to "own"this struct class// @name: pointer to a string for the name of this class. // This is used to create a struct class pointer that canthen be used// in calls to device_create(). //Note, the pointer created here is to be destroyed whenfinished by// making a call to class_destroy(). struct class *class_create(struct module *owner, const char *name){ struct class *cls; int retval; cls = kzalloc(sizeof(*cls), GFP_KERNEL); if (!cls) { retval = -ENOMEM; goto error; } cls->name = name; cls->owner = owner; cls->class_release =class_create_release; retval =class_register(cls); if (retval) goto error; return cls;error: kfree(cls); return ERR_PTR(retval);}
// class_destroy - destroys a struct class structure//@cs: pointer to the struct class that is to be destroyed //Note, the pointer to be destroyed must have been created with a call//to class_create(). void class_destroy(struct class *cls){ if((cls == NULL) || (IS_ERR(cls))) return; class_unregister(cls);}
// device_create - creates a device and registersit with sysfs// @class: pointer to the struct class that thisdevice should be registered to// @parent: pointer to the parent struct device ofthis new device, if any// @devt: the dev_t for the char device to beadded// @fmt: string for the devices name // This function can be used by char deviceclasses. A struct device// will be created in sysfs, registered to thespecified class. // A "dev" file will be created, showingthe dev_t for the device, if// the dev_t is not 0,0.// If a pointer to a parent struct device ispassed in, the newly created// struct device will be a child of that device insysfs.// The pointer to the struct device will bereturned from the call.// Any further sysfs files that might be requiredcan be created using this// pointer. // Note: the struct class passed to this functionmust have previously// been created with a call to class_create(). struct device *device_create(struct class *class,struct device *parent, dev_tdevt, const char *fmt, ...){ va_list vargs; struct device *dev; va_start(vargs,fmt); dev =device_create_vargs(class, parent, devt, NULL, fmt, vargs); va_end(vargs); return dev;}
第一个参数指定所要创建的设备所从属的类, 第二个参数是这个设备的父设备,如果没有就指定为NULL, 第三个参数是设备号, 第四个参数是设备名称, 第五个参数是从设备号。
// device_destroy - removes a device that was created with device_create()// @class: pointer to the struct class that this device was registered with// @devt: the dev_t of the device that was previously registered // This call unregisters and cleans up a device that was created with a// call to device_create(). void device_destroy(struct class *class,dev_t devt){ structdevice *dev = NULL; structdevice *dev_tmp; down(&class->sem); list_for_each_entry(dev_tmp,&class->devices, node) { if(dev_tmp->devt == devt) { dev= dev_tmp; break; } } up(&class->sem); if(dev) device_unregister(dev);}
相比devfs,udev有很多优势,在此就不罗嗦了,提醒一点,udev是应用层的东东,不要试图在内核的配置选项里找到它;加入对udev的支持很简单,以字符设备驱动为例,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用 class_device_create创建对应的设备。大致用法如下:
struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h> int HELLO_MAJOR = 0;int HELLO_MINOR = 0;int NUMBER_OF_DEVICES = 2; struct class *my_class;//struct cdev cdev;//dev_t devno; struct hello_dev {struct device *dev;dev_t chrdev;struct cdev cdev;}; static struct hello_dev *my_hello_dev = NULL; struct file_operations hello_fops = { .owner = THIS_MODULE}; static int __init hello_init (void){int err = 0;struct device *dev; my_hello_dev = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);if (NULL == my_hello_dev) {printk("%s kzalloc failed!\n",__func__);return -ENOMEM;} devno = MKDEV(HELLO_MAJOR, HELLO_MINOR);if (HELLO_MAJOR)err= register_chrdev_region(my_hello_dev->chrdev, 2, "memdev");else{err = alloc_chrdev_region(&my_hello_dev->chrdev, 0, 2, "memdev");HELLO_MAJOR = MAJOR(devno);} if (err) {printk("%s alloc_chrdev_region failed!\n",__func__);goto alloc_chrdev_err;}printk("MAJOR IS %d\n",HELLO_MAJOR); cdev_init(&(my_hello_dev->cdev), &hello_fops);my_hello_dev->cdev.owner = THIS_MODULE;err = cdev_add(&(my_hello_dev->cdev), my_hello_dev->chrdev, 1);if (err) {printk("%s cdev_add failed!\n",__func__);goto cdev_add_err;}printk (KERN_INFO "Character driver Registered\n"); my_class =class_create(THIS_MODULE,"hello_char_class"); //类名为hello_char_classif(IS_ERR(my_class)) {err = PTR_ERR(my_class);printk("%s class_create failed!\n",__func__);goto class_err;} dev = device_create(my_class,NULL,my_hello_dev->chrdev,NULL,"memdev%d",0); //设备名为memdevif (IS_ERR(dev)) {err = PTR_ERR(dev);gyro_err("%s device_create failed!\n",__func__);goto device_err;} printk("hello module initialization\n");return 0; device_err:device_destroy(my_class, my_hello_dev->chrdev);class_err:cdev_del(my_hello_dev->chrdev);cdev_add_err:unregister_chrdev_region(my_hello_dev->chrdev, 1);alloc_chrdev_err:kfree(my_hello_dev);return err;} static void __exit hello_exit (void){cdev_del (&(my_hello_dev->cdev));unregister_chrdev_region (my_hello_dev->chrdev,1);device_destroy(my_class, devno); //delete device node under /dev//必须先删除设备,再删除class类class_destroy(my_class); //delete class created by usprintk (KERN_INFO "char driver cleaned up\n");} module_init (hello_init);module_exit (hello_exit); MODULE_LICENSE ("GPL");
[root@localhost node]# insmod node.ko [root@localhost node]# dmesg | tail -3 [23503.365316] create node success: [23503.365319] ls -l /dev/noddev* [23503.365321] ls -l /sys/class/noddev [root@localhost node]# ls -l /dev/noddev* crw------- 1 root root 66, 0 11月 26 15:02 /dev/noddev0 crw------- 1 root root 66, 20 11月 26 15:02 /dev/noddev20 [root@localhost node]# ls -l /sys/class/noddev 总用量 0 lrwxrwxrwx 1 root root 0 11月 26 15:02 noddev0 -> ../../devices/virtual/noddev/noddev0 lrwxrwxrwx 1 root root 0 11月 26 15:02 noddev20 -> ../../devices/virtual/noddev/noddev20

Copyright © 2024 有趣生活 All Rights Reserve吉ICP备19000289号-5 TXT地图HTML地图XML地图