区别于Linux4.0之前的字符设备驱动结构,4.0采用cdev注册字符设备。
一、构造一个字符设备结构体,用于cdev的初始化
struct led_dev_t{ struct cdev cdev; };
二、__init 入口函数
1.设备号的处理
dev_t led_devno= MKDEV(led_major,0);
MKDEV(led_major,0)通过主次设备号生成dev_t,在cdev的结构体里面定义了成员dev_t为,32位,12位是主设备号,20位是次设备号。如果不想自己设定主次设备号,可以使用MAJOR(dev_t dev)和MINOR(dev_t dev)生成主次设备号。
if(led_major) { ret = register_chrdev_region(led_devno, 1,"led"); } else { ret = alloc_chrdev_region(&led_devno, 0, 1, "led"); led_major = MAJOR(led_devno); }
判断主设备号是否已定义,如果已定义,则注册设备号和设备名;否则申请一个设备号空间然后使用MAJOR(led_devno)生成主设备号。
2.注册字符设备前的准备:申请所需注册设备的结构体空间
ledevp = kzalloc(sizeof(led_dev_t), GFP_KERNEL); /*申请内存空间*/ /*判断内存是否申请成功*/ if(!ledevp) { ret = -ENOMEM; unregister_chrdev_region(led_devno,1); return ret; }
3.注册字符设备
static void led_setup_cdev(struct led_dev_t *dev,int index) { int err, led_devno = MKDEV(led_major,index); /*通过主设备号和次设备号生成dev_t*/ cdev_init(&dev->cdev,&chardev_led_fops); /*初始化cdev*/ dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev,led_devno,1); /*添加一个cdev,param1:cdev,param2:主次设备号*/ if(err) { printk(KERN_NOTICE"Error %d adding led %d",err,index); } }
cdev_init初始化cdev,将本驱动的cdev添加进cdev链表
三、字符设备驱动模板
#include <linux/module.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/gpio.h> #include <linux/io.h> #include <linux/cdev.h> #include <linux/irq.h> #include <linux/uaccess.h> #include <linux/of.h> #include <linux/of_gpio.h> #define LED_MAJOR 229 static int led_major = LED_MAJOR; /*设备结构体*/ struct led_dev_t{ struct cdev cdev; }; struct led_dev_t *ledevp; static int led_drv_open(struct inode *inode,struct file *file) { /*设置LED引脚为输出模式*/ } static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { /*向引脚写入值*/ copy_from_user(..,buf,..);//从用户空间获得参数 } const static struct file_operations chardev_led_fops = { .owner = THIS_MODULE, .open = led_drv_open, .write = led_drv_write }; static void led_setup_cdev(struct led_dev_t *dev,int index) { int err, led_devno = MKDEV(led_major,index); /*通过主设备号和次设备号生成dev_t*/ cdev_init(&dev->cdev,&chardev_led_fops); /*初始化cdev*/ dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev,led_devno,1); /*添加一个cdev,param1:cdev,param2:主次设备号*/ if(err) { printk(KERN_NOTICE"Error %d adding led %d",err,index); } } static int __init chardev_led_init(void) { int ret; dev_t led_devno= MKDEV(led_major,0); /*获取字符设备号*/ if(led_major) { ret = register_chrdev_region(led_devno, 1,"led"); } else { ret = alloc_chrdev_region(&led_devno, 0, 1, "led"); } ledevp = kzalloc(sizeof(led_dev_t), GFP_KERNEL); /*申请内存空间*/ /*判断内存是否申请成功*/ if(!ledevp) { ret = -ENOMEM; unregister_chrdev_region(led_devno,1); return ret; } led_setup_cdev(ledevp,1); /*设置字符设备*/ return 0; } static void __exit chardev_led_exit(void) { cdev_del(&ledevp->cdev); /*删除cdev,释放内存空间*/ kfree(ledevp); unregister_chrdev_region(MKDEV(led_major, 0), 1); } module_init(chardev_led_init); module_exit(chardev_led_exit); MODULE_AUTHOR(" MUGGLE <1198492751@qq.com>") MODULE_LICENSE("GPL v2");