免费制作二维码的网站,html网站可以做访问统计吗,网站模板受法律版权保护吗,网站免费的正能量漫画字符设备
Linux 将系统设备分为#xff1a;字符设备、块设备、网络设备。工作原理 字符设备是 Linux 驱动中最基本的一类设备驱动#xff0c;字符设备就是一个一个字节#xff0c;
按照字节流进行读写操作的设备#xff0c;读写数据是分先后顺序的。在Linux的世界里面一切…字符设备
Linux 将系统设备分为字符设备、块设备、网络设备。工作原理 字符设备是 Linux 驱动中最基本的一类设备驱动字符设备就是一个一个字节
按照字节流进行读写操作的设备读写数据是分先后顺序的。在Linux的世界里面一切皆文件所有的硬件设备操作到应用层都会被抽象成文件
的操作。我们知道如果应用层要访问硬件设备它必定要调用到硬件对应的驱动程序。1.在Linux文件系统中每个文件都用一个struct inode结构体来描述这个结构体里面记录了 这个文件的所有信息例如文件类型访问权限等。struct inode信息
2.在Linux操作系统中每个驱动程序在应用层的/dev目录下都会有一个设备文件和它对应并且该文件会有对应的主设备号和次设备号。
3.在Linux操作系统中每个驱动程序都要分配一个主设备号字符设备的设备号保存在struct cdev结构体中。struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;//接口函数集合struct list_head list;//内核链表dev_t dev; //设备号unsigned int count//次设备号个数};
4.在Linux操作系统中每打开一次文件Linux操作系统在VFS层都会分配一个struct file结构体来描述打开的这个文件。该结构体用于维护文件打开权限、文件指针偏移值、私有内存地址等信息。strcut file有两个非常重要的字段文件描述符和缓冲区字符驱动相关函数
注册 内核共提供了三个函数来注册一组字符设备编号这三个函数分别是
register_chrdev_region()、alloc_chrdev_region()和 register_chrdev()。注意事项
1.使用register_chrdev注册字符设备其内部申请struct cdev 结构并调用cdev_add函数添加设备。
2.使用register_chrdev_region/alloc_chrdev_region注册字符设备需要在外部事先定义struct cdev 结构然后使用函数cdev_init初始化它最后还需在外部调用cdev_add函数添加设备。//比较老的内核注册的形式 早期的驱动
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
功能注册或者分配设备号并注册fops到cdev结构体如果major0功能为注册该主设备号如果major0功能为动态分配主设备号。
参数major : 主设备号name : 设备名称执行 cat /proc/devices显示的名称fops : 文件系统的接口指针
返回值如果major0 成功返回0失败返回负的错误码如果major0 成功返回主设备号失败返回负的错误码int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能注册一个范围)的设备号
参数from 设备号count 注册的设备个数name 设备的名字
返回值成功返回0,失败返回错误码负数int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
功能注册一个主设备号由内核动态分配次设备号为baseminor~baseminorcount的设备驱动
参数dev: 用来获取设备号baseminor:次设备号起始值count: 次设备号个数name: 设备名称
返回值成功返回0,失败返回错误码负数 //注销
static inline void unregister_chrdev(unsigned int major, const char *name)void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能初始化cdev结构体
参数cdev cdev结构体地址fops 操作字符设备的函数接口地址
返回值无int cdev_add(struct cdev *p, dev_t dev, unsigned count)
功能添加一个字符设备到操作系统
参数p cdev结构体地址dev 设备号count 次设备号个数
返回值成功返回0,失败返回错误码负数void cdev_del(struct cdev *p)
功能从系统中删除一个字符设备
参数p cdev结构体地址
返回值无struct file_operations { struct module *owner;//拥有该结构的模块的指针一般为THIS_MODULES 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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读取操作 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写入操作 int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录对于设备文件该字段为NULL unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数判断目前是否可以进行非阻塞的读写或写入 int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统将使用此种函数指针代替ioctl long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上32位的ioctl调用将使用此函数指针代替 int (*mmap) (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 *, struct dentry *, int datasync); //刷新待处理的数据 int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据 int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化 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 **); };实例
#include linux/module.h
#include linux/kernel.h
#include linux/fs.h
#include linux/init.h
#include linux/delay.h
#include linux/irq.h
#include asm/uaccess.h
#include asm/irq.h
#include asm/io.h
#include asm/arch/regs-gpio.h
#include asm/hardware.h
#include linux/poll.h
#include linux/cdev.h/* 1. 确定主设备号 */
static int major;static int hello_open(struct inode *inode, struct file *file)
{printk(hello_open\n);return 0;
}static int hello2_open(struct inode *inode, struct file *file)
{printk(hello2_open\n);return 0;
}/* 2. 构造file_operations */
static struct file_operations hello_fops {.owner THIS_MODULE,.open hello_open,
};static struct file_operations hello2_fops {.owner THIS_MODULE,.open hello2_open,
};#define HELLO_CNT 2static struct cdev hello_cdev;
static struct cdev hello2_cdev;
static struct class *cls;static int hello_init(void)
{dev_t devid;/* 3. 告诉内核 */
#if 0major register_chrdev(0, hello, hello_fops); /* (major, 0), (major, 1), ..., (major, 255)都对应hello_fops */
#elseif (major) {// 事先知道可用的主设备号和起始次设备号devid MKDEV(major, 0);register_chrdev_region(devid, HELLO_CNT, hello); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */} else {// 事先不知道可用的主设备号由内核动态分配alloc_chrdev_region(devid, 0, HELLO_CNT, hello); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */major MAJOR(devid); }cdev_init(hello_cdev, hello_fops);cdev_add(hello_cdev, devid, HELLO_CNT);devid MKDEV(major, 2);register_chrdev_region(devid, 1, hello2);cdev_init(hello2_cdev, hello2_fops);cdev_add(hello2_cdev, devid, 1);#endifcls class_create(THIS_MODULE, hello);// 使用register_chrdev注册下面的四个设备节点都将对应该设备驱动都能调用hello_open// 使用 register_chrdev_region/alloc_chrdev_region 60/63注册设备节点/dev/hello0、/dev/hello1对应hello_fops设备驱动调用hello_open打开// 使用 register_chrdev_region/alloc_chrdev_region 71注册设备节点/dev/hello2对应hello2_fops设备驱动调用hello2_open打开// /dev/hello3节点未注册到设备驱动无法打开设备。class_device_create(cls, NULL, MKDEV(major, 0), NULL, hello0); /* /dev/hello0 */class_device_create(cls, NULL, MKDEV(major, 1), NULL, hello1); /* /dev/hello1 */class_device_create(cls, NULL, MKDEV(major, 2), NULL, hello2); /* /dev/hello2 */class_device_create(cls, NULL, MKDEV(major, 3), NULL, hello3); /* /dev/hello3 */return 0;
}static void hello_exit(void)
{class_device_destroy(cls, MKDEV(major, 0));class_device_destroy(cls, MKDEV(major, 1));class_device_destroy(cls, MKDEV(major, 2));class_device_destroy(cls, MKDEV(major, 3));class_destroy(cls);cdev_del(hello_cdev);unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT);cdev_del(hello2_cdev);unregister_chrdev_region(MKDEV(major, 2), 1);
}module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE(GPL);