cdev结构体cdev操作函数分配和释放设备号自动创建设备节点file_operations结构体字符设备驱动模板实例
实例说明 总结 cdev结构体
在include/linux/cdev.h
struct cdev {struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list;dev_t dev;unsigned int count;};
cdev操作函数//用于初始化cdev成员,并建立文件操作和cdev之间的联系void cdev_init(struct cdev *cdev, struct file_operations *fops)//用于动态申请一个cdev内存struct cdev *cdev_alloc(void)//减少使用计数void cdev_put(struct cdev *p);//向系统添加一个cdevint cdev_add(struct cdev *, dev_t, unsigned);//向系统删除一个cdevvoid cdev_del(struct cdev *);
分配和释放设备号在调用cdev_add之前应该先申请设备号
//用于已知设备号情况int register_chrdev_region(dev_t from, unsigned count, const char *name);//用于未知设备号情况(可以自动避开重复的设备号)int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);//释放掉已经申请的设备号void unregister_chrdev_region(dev_t from, unsigned count);
自动创建设备节点1.创建类和删除类
struct class *class_create (struct module *owner, const char *name);void class_destroy(struct class *cls);
2.创建设备和删除设备
struct device *device_create(struct class *class,struct device *parent,dev_t devt,void *drvdata,const char *fmt, ...);void device_destroy(struct class *class, dev_t devt);
file_operations结构体struct file_operations {struct module *owner;//用来修该文件当前读写位置,返回新的为止loff_t (*llseek) (struct file *, loff_t, int);//用来从设备中读取数据, 成功时函数返回读取的字节数, 出错时返回一个负值。ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//向设备发送数据, 成功时该函数返回写入的字节数。ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//异步读ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);//异步写ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);int (*iterate) (struct file *, struct dir_context *);//函数一般用于询问设备是否可被非阻塞地立即读写。unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*mremap)(struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **, void **);long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);void (*show_fdinfo)(struct seq_file *m, struct file *f);#ifndef CONFIG_MMUunsigned (*mmap_capabilities)(struct file *);#endif};
字符设备驱动模板struct xxx_dev_t {struct cdev cdev;...} xxx_dev;int xxx_release(struct inode *inode, struct file *filp){//printk("chrdevbase release!rn");return 0;}int xxx_open(struct inode *inode, struct file *filp){//printk("chrdevbase open!rn");return 0;}ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos){...copy_from_user(..., buf, ...);//用户空间缓冲区到内核空间的复制...}ssize_t xxx_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){copy_to_user(buf,...,...);}long xxx_ioctl(struct file *filp, unsigned int cmd,unsigned long arg){...switch (cmd) {case XXX_CMD1:...break;case XXX_CMD2:...break;default:return - ENOTTY;}return 0;}//字符设备驱动文件操作结构体模板struct file_operations xxx_fops = {.owner = THIS_MODULE,.read = xxx_read,.write = xxx_write,.unlocked_ioctl= xxx_ioctl,.open = xxx_open;.release = xxx_release;...};static int __init xxx_init(void){...cdev_init(&xxx_dev.cdev, &xxx_fops); xxx_dev.cdev.owner = THIS_MODULE;if (xxx_major) {register_chrdev_region(xxx_dev_no, 1, DEV_NAME);} else {alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);}ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); ...}static void __exit xxx_exit(void){unregister_chrdev_region(xxx_dev_no, 1); cdev_del(&xxx_dev.cdev); ...}
实例#include #include #include #include #include #include #include #include #include #define MEM_CLEAR (_IO(0xEF, 0x1)) #define MEM_READ (_IOR(0xEF, 0x2)) #define MEM_WRITE (_IOW(0xEF, 0x3)) #define MEM_READ_WRITE (_IOWR(0xEF, 0x4)) #define DEMO_CNT1 #define DEMO_NAME"demo"#define MEM_BUFFER_SIZE 1000struct demo_dev{dev_t devid;struct cdev cdev;struct class *class;struct class val_class;struct device *device;int major;int minor;unsigned char mem[MEM_BUFFER_SIZE];};struct demo_dev demo;static ssize_t demo_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offset){unsigned long p = *offset;unsigned int count = cnt;int ret = 0;struct demo_dev *dev = filp->private_data;if (p >= MEM_BUFFER_SIZE)return 0;if (count > MEM_BUFFER_SIZE - p)count = MEM_BUFFER_SIZE - p;if (copy_to_user(buf, dev->mem + p, count)) {ret = -EFAULT;} else {*offset += count;ret = count;printk("read %u bytes(s) from %lun",cnt,p);}return ret;}static ssize_t demo_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offset){unsigned long p = *offset;unsigned int count = cnt;int ret = 0;struct demo_dev *dev = filp->private_data;if (p >= MEM_BUFFER_SIZE)return 0;if (count > MEM_BUFFER_SIZE - p)count = MEM_BUFFER_SIZE - p;if (copy_from_user(dev->mem + p, buf, count))ret = -EFAULT;else {*offset += count;ret = count;printk("written %u bytes(s) from %lun", count, p);}return ret;}static loff_t demo_llseek(struct file *filp, loff_t offset, int orig){loff_t ret = 0;switch (orig) {case 0: if (offset< 0) {ret = -EINVAL;break;}if ((unsigned int)offset > MEM_BUFFER_SIZE) {ret = -EINVAL;break;}filp->f_pos = (unsigned int)offset;ret = filp->f_pos;break;case 1: if ((filp->f_pos + offset) > MEM_BUFFER_SIZE) {ret = -EINVAL;break;}if ((filp->f_pos + offset) < 0) {ret = -EINVAL;break;}filp->f_pos += offset;ret = filp->f_pos;break;default:ret = -EINVAL;break;}return ret;}static long demo_ioctl(struct file *filp, unsigned int cmd,unsigned long arg){struct demo_dev *dev = filp->private_data;switch (cmd) {case MEM_CLEAR:memset(dev->mem, 0, MEM_BUFFER_SIZE);printk(KERN_INFO "demo is set to zeron");break;default:return -EINVAL;}return 0;}static int demo_open(struct inode *inode, struct file *filp){struct demo_dev *dev = container_of(inode->i_cdev,struct demo_dev, cdev);filp->private_data = dev;return 0;}static int demo_release(struct inode *inode, struct file *filp){return 0;}static struct file_operations demo_fops = {.owner = THIS_MODULE,.llseek = demo_llseek,.read = demo_read,.write = demo_write,.unlocked_ioctl = demo_ioctl,.open = demo_open,.release = demo_release,};char* strcpy(char* dest, const char* src) {char* tmp = dest;while ((*dest++ = *src++) != '0');return tmp;}static ssize_t std_show(struct device *dev,struct device_attribute *attr, char *buf){return sprintf(buf, "%sn",demo.mem);}static ssize_t std_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){strcpy(demo.mem,buf);return len;}static DEVICE_ATTR_RW(std);static struct attribute *led_class_attrs[] = {&dev_attr_std.attr,NULL,};static const struct attribute_group led_group = {.attrs = led_class_attrs,};static const struct attribute_group *led_groups[] = {&led_group,NULL,};static int __init demo_init(void){if (demo.major) {demo.devid = MKDEV(demo.major, 0);register_chrdev_region(demo.devid, DEMO_CNT, DEMO_NAME);} else {alloc_chrdev_region(&demo.devid, 0, DEMO_CNT, DEMO_NAME);demo.major = MAJOR(demo.devid);demo.minor = MINOR(demo.devid);}printk("major=%d,minor=%drn",demo.major, demo.minor);demo.cdev.owner = THIS_MODULE;cdev_init(&demo.cdev, &demo_fops);cdev_add(&demo.cdev, demo.devid, DEMO_CNT);demo.val_class.owner = THIS_MODULE;demo.val_class.name = DEMO_NAME;demo.val_class.class_groups = led_groups;class_register(&demo.val_class);demo.device = device_create(&demo.val_class, NULL, demo.devid, NULL, DEMO_NAME);if (IS_ERR(demo.device)) {return PTR_ERR(demo.device);}return 0;}static void __exit demo_exit(void){cdev_del(&demo.cdev);unregister_chrdev_region(demo.devid, DEMO_CNT); device_destroy(&demo.val_class, demo.devid);class_unregister(&demo.val_class);//class_destroy(demo.class);}//将上面两个函数指定为驱动的入口和出口函数 module_init(demo_init);module_exit(demo_exit);//LICENSE和作者信息MODULE_LICENSE("GPL");MODULE_AUTHOR("bin");
container_of() 的作用是通过结构体成员的指针找到对应结构体的指针,这个技巧在Linux内核编程中十分常用。
在struct demo_dev *dev = container_of(inode->i_cdev,struct demo_dev, cdev); 语句
中, 传给container_of() 的第1个参数是结构体成员的指针, 第2个参数为整个结构体的类型, 第3个参数
为传入的第1个参数即结构体成员的类型, container_of() 返回值为整个结构体的指针。
本实例以两方式进行和应用层通信 分别是file_operations , sysfs
编译加载到内核中后,可以在/dev节点下找到demo这个文件,或者在/sys/class/demo中找到std文件 可对应进行读写操作
字符设备驱动主要工作就是初始化,添加删除结构体,申请释放设备号,编写file_operations 的操作函数等。