linux中一切皆文件,驱动程序加载后会在/dev目录下生成一个相应的文件,应用程序通过对这个名为/dev/xxx的文件进行操作即可。
应用程序运行在用户空间,linux驱动运行在内核空间。用户空间不能直接对linux内核操作,必须使用系统调用的方法来实现从用户空间陷入到内核空间,这样才能实现对底层驱动的操作。open,close write和read函数都是由C库提供的,在linux系统中,系统调用作为c库的一部分。当我们调用open函数的时候流程如下
应用程序 | C库 | 内核 | 具体驱动 open() -> | C库中的open()-> | open()系统调用 -> | 驱动open()file_operations
每一个系统调用,在驱动中都有一个对应的驱动函数,这个函数的结合体就是linux内核驱动操作函数的集合,内容如下
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
};
字符设备注册和注销
注册函数
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
参数:major:主设备号
name:设备名字
fops:与设备绑定的file_operation结构体
返回值 :负数:注册失败
注销函数
static inline void unregister_chrdev(unsigned int major, const char *name)
参数:major:主设备号
name:设备名
返回值:NULL
输入命令 “cat /proc/devices”可以查看已经被使用的设备号。
Linux设备号linux每个设备都有一个自己的设备号,设备号由主设备号和次设备号组成,主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备。linux使用dev_t的数据类型表示设备号,定义在include/linux/types.h
typedef __u32 __kernel_dev_t;
dev_t本质是一个unsigned int类型,其中高12位为主设备号,低20位为次设备号。linux的主设备号范围为0-4095.在include/linux/kdev_t.h中提供了几个关于设备号的宏。
#define MINORBITS 20 //次设备号位数 #define MINORMASK ((1U << MINORBITS) - 1) //次设备号掩码 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //获取主设备号 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //获取次设备号 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //合成设备号设备号分配
注册函数
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
参数:dev:保存申请的设备号。
baseminor:次设备号起始地址
count:要申请的设备号数量
name: 设备名字
返回值:负数:分配失败
卸载函数
void unregister_chrdev_region(dev_t from, unsigned count)
参数:from:要释放的设备号
count:从from开始,要释放的设备号个数
字符设备驱动举例#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEV_NAME "led" #define DEV_CNT (1) struct chr_device { dev_t devno; //设备号 struct cdev cdev; //字符设备 struct class *class; //类 struct device *device; //设备 struct platform_device *dev; }; ssize_t para_show(struct device *dev, struct device_attribute *attr, char *buf) { printk(KERN_EMERG "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); return sprintf(buf, "I am what I amn"); } ssize_t para_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { printk(KERN_EMERG "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); printk(KERN_EMERG "%sn", buf); return count; } static DEVICE_ATTR(para, 0664, para_show, para_store); static const struct of_device_id chrdev_match_table[] = { {.compatible = "rk3399,testnode"}, {}, }; static int chr_dev_open(struct inode *inode, struct file *filp) { printk(KERN_EMERG "chrdev open!!!n"); return 0; } static ssize_t chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { printk(KERN_EMERG "chrdev write!!!n"); return cnt; } static ssize_t chr_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { printk(KERN_EMERG "chrdev write!!!n"); return 0; } static int chrdev_release(struct inode *inode, struct file *filp) { printk(KERN_EMERG "chrdev release!!!n"); return 0; } static struct file_operations chr_dev_fops = { .owner = THIS_MODULE, .open = chr_dev_open, .read = chr_dev_read, .write = chr_dev_write, .release = chrdev_release, }; int chrdev_probe(struct platform_device *dev) { int ret = 0; struct chr_device *chr_dev; printk(KERN_EMERG "chrdev probe !n"); chr_dev = (struct chr_device *)kzalloc(sizeof(struct chr_device), GFP_KERNEL); if (chr_dev == NULL) return -1; ret = alloc_chrdev_region(&chr_dev->devno, 0, DEV_CNT, DEV_NAME); if (ret < 0) { return -1; } chr_dev->cdev.owner = THIS_MODULE; cdev_init(&chr_dev->cdev, &chr_dev_fops); ret = cdev_add(&chr_dev->cdev, chr_dev->devno, DEV_CNT); if (ret < 0) { printk(KERN_EMERG "fail to add cdevn"); goto add_err; } chr_dev->class = class_create(THIS_MODULE, DEV_NAME); chr_dev->device = device_create(chr_dev->class, NULL, chr_dev->devno, NULL, DEV_NAME); if (chr_dev->device == NULL) { printk(KERN_EMERG "fail to add chr_dev->devicen"); goto add_err; } chr_dev->dev = dev; device_create_file(chr_dev->device, &dev_attr_para); dev_set_drvdata(&dev->dev, chr_dev); return 0; add_err: unregister_chrdev_region(chr_dev->devno, DEV_CNT); return -1; } int chrdev_remove(struct platform_device *dev) { struct chr_device *chr_dev = dev_get_drvdata(&dev->dev); device_destroy(chr_dev->class, chr_dev->devno); class_destroy(chr_dev->class); cdev_del(&chr_dev->cdev); unregister_chrdev_region(chr_dev->devno, DEV_CNT); printk(KERN_EMERG "chrdev remove !n"); return 0; } static struct platform_driver chrdev_driver = { .probe = chrdev_probe, .remove = chrdev_remove, .driver = { .name = "test node", .owner = THIS_MODULE, .of_match_table = chrdev_match_table, }, }; static int __init chrdev_init(void) { return platform_driver_register(&chrdev_driver); } static void __exit chrdev_exit(void) { platform_driver_unregister(&chrdev_driver); return; } module_init(chrdev_init) module_exit(chrdev_exit); MODULE_LICENSE("GPL");



