设备驱动---第二课(字符型设备)

    xiaoxiao2021-12-14  21

    一、流程框架

    1.头文件

    linux/cdev.h

    2.封装结构体

    struct cdev { /*共有的部分*/ struct module *owner; //所有者 const struct file_operations *ops; //方法集(开关读写) dev_t dev; unsigned int count; //使用计数 /*特有的部分*/ }

    3.系统提供的一些接口

    /* * 功能:在堆区申请空间,构造cdev设备对象 * 返回值:成功:对象首地址 失败:NULL */ struct cdev *cdev_alloc(void); /* * 功能:释放cdev设备对象 */ void cdev_put(struct cdev *p); /* * 功能: cdev初始化 */ void cdev_init(struct cdev *, const struct file_operations *); /* *功能:注册字符设备 *参数:struct cdev *:设备 * dev_t: 设备号 * int: 设备数量 * 返回值: 成功:0 失败: -1 */ int cdev_add(struct cdev *, dev_t, unsgned int); /* *功能:注销字符设备 *参数: struct cdev *:设备 */ voidcdev_del(struct cdev *);

    4.自定函数

    /*初始化*/ static int __init demo_init(void) { //1.构造设备/驱动对象 obj = cdev_alloc(); //2.初始化设备/驱动对象 cdev_init(obj,&fops); //3.获取设备号 alloc_chrdev_region(); //4.注册设备/驱动对象 cdev_add(obj,dev_t); //5.必要的硬件初始化 return 0; } /*卸载*/ static void __exit demo_exit(void) { //1.回收清理 void cdev_put(struct cdev *); //2.注销 void cdev_del(struct cdev *); //3.关闭相关,或者一些收场工作 } 这里写代码片

    二、 一些术语

    【设备号】

    标识一个具体设备 无符号整型 类型别名dev_t 设备号 = 主设备号+次设备号 主设备号:表一类设备 次设备号:表示某一类设备中的一个具体的设备 通常用一个宏将其拼凑 dev_t MKDEV(int major, int minor); 也有一个宏,可以从中提取主次设备号 int MAJOR(dev_t); int MINOR(dev_t);
    函数中也有提取出主设备号和次设备号的函数: imajor();iminor();

    设备号怎么得来? A.静态申请:

    /* * 功能:静态分配设备号 * 输入参数: dev_t *: 想要申请到的主设备号 * unsigned int: 数量 * const char *: 设备名 * 返回值: 成功: 0 失败:负数,其绝对值是错误号 */ int register_chrdev_region(dev_t *, unsigned int, const char *);

    B:动态分配

    /* * 功能:动态分配设备号 * 输出参数: dev_t *: 申请到的主设备号 * 输入参数: unsigned int first_minor: 第一个次设备号 * unsigned int: 数量 * const char *: 设备名 * 返回值: 成功: 0 失败:负数,其绝对值是错误号 */ int alloc_chrdev_region(dev_t *, unsigned int first_minor, unsigned int, const char *);

    设备号怎么释放?

    /* * 功能:释放设备 * 输入参数: dev_t *: 要释放的设备 * unsigned int: 数量 * 返回值: none */ void unregister_chrdev_region(dev_t *, unsigned int);

    三、操作方法集

    /*include/linux/fs.h*/ 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 *); int (*iterate_shared) (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 (*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 ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64); ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, u64); };

    四、设备文件

    1.作用: 用来找设备的 2、内容 设备号+设备类型 3、设备文件怎么生成? A、手工生成: #mknod /设备文件名 设备类型 主设备号 次设备号

    mknod /dev/demo0 c 250 0

    B、自动生成: 内核有函数可用,man 2 mknod 看详情。

    #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int mknod(const char *pathname, mode_t mode, dev_t dev);

    五、工程实例

    #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/errno.h> static int major = 0; static int minor = 0; const int count = 3; #define DEVNAME "demo" static struct cdev *demop = NULL; /*打开设备 static int demo_open(struct inode *inode, struct file *filp) { printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__); return 0; } //关闭设备 static int demo_release(struct inode *inode, struct file *filp) { printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__); return 0; } //读设备 //ssize_t read(int fd, void *buf, size_t count) static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset) { printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__); printk(KERN_INFO "%p : %d\n", buf, size); return 123; } //写设备 static ssize_t demo_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset) { printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__); printk(KERN_INFO "%p : %d\n", buf, size); return 321; } static struct file_operations fops = { .owner = THIS_MODULE, .open = demo_open, .release= demo_release, .read = demo_read, .write = demo_write, }; static int __init demo_init(void) { dev_t devnum; int ret; printk(KERN_INFO "%s : %s : %d --- entry\n", __FILE__, __func__, __LINE__); //1. alloc cdev obj demop = cdev_alloc(); if(NULL == demop){ return -ENOMEM; } //2. init cdev obj cdev_init(demop, &fops); ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME); if(ret){ goto ERR_STEP; } major = MAJOR(devnum); //3. register cdev obj ret = cdev_add(demop, devnum, count); if(ret){ goto ERR_STEP1; } printk(KERN_INFO "%s : %s : %d --- done.\n", __FILE__, __func__, __LINE__); return 0; ERR_STEP1: unregister_chrdev_region(devnum, count); ERR_STEP: cdev_del(demop); printk(KERN_INFO "%s : %s : %d --- fail.\n", __FILE__, __func__, __LINE__); return ret; } static void __exit demo_exit(void) { printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__); unregister_chrdev_region(MKDEV(major, minor), count); cdev_del(demop); } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("xxg"); MODULE_DESCRIPTION("Demo for kernel module");

    六、本课相关的一些linux命令

    1、将模块加载入内核:

    sudo insmod xxgdemo.ko

    2、查看已加载的模块:

    lsmod

    3、查看加载时内核有什么打印信息:

    sudo dmesg | tail -9

    4、查看内核符号表:

    sudo cat /proc/kallsyms

    5、卸载模块:

    sudo rmmod xxgdemo

    6、手动创建设备:

    sudo mknod /dev/xxgdemo1 c 250 0

    7、查看设备对应的设备号:

    sudo cat /proc/devices
    转载请注明原文地址: https://ju.6miu.com/read-968114.html

    最新回复(0)