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); //向系统添加一个cdev int cdev_add(struct cdev *, dev_t, unsigned); //向系统删除一个cdev void 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_MMU
unsigned (*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_CNT 1 #define DEMO_NAME "demo" #define MEM_BUFFER_SIZE 1000 struct 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 的操作函数等。



