企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
驱动程序freg的目录结构如下: ~~~ ~/Android/kernel/goldfish ----drivers ----freg ----freg.h ----freg.c ----Kconfig ----Makefile ~~~ 它由四个文件组成,其中freg.h和freg.c是源代码文件,Kconfig是编译选项配置文件,Makefile是编译脚本文件。下面我们就分别介绍这四个文件的实现。 **freg.h** ~~~ #ifndef _FAKE_REG_H_ #define _FAKE_REG_H_ #include <linux/cdev.h> #include <linux/semaphore.h> #define FREG_DEVICE_NODE_NAME "freg" #define FREG_DEVICE_FILE_NAME "freg" #define FREG_DEVICE_PROC_NAME "freg" #define FREG_DEVICE_CLASS_NAME "freg" struct fake_reg_dev { int val; struct semaphore sem; struct cdev dev; }; #endif ~~~ 这个文件定义了四个字符串常量,分别用来描述虚拟硬件设备freg在设备文件系统中的名称。此外,此文件还定义了一个结构体fake_reg_dev,用来描述虚拟硬件设备freg。在结构体fake_reg_dev中,成员变量val用来描述一个虚拟寄存器,它的类型为int;成员变量sem是一个信号量,用来同步访问虚拟寄存器val;成员变量dev是一个标准的Linux字符设备结构体变量,用来标志该虚拟硬件设备freg的类型为字符设备。 **freg.c** 这个文件是驱动程序freg的实现文件,它向用户空间提供了三个接口来访问虚拟硬件设备freg中的寄存器val。第一个是proc文件系统接口,第二个是传统的设备文件系统接口,第三个是devfs文件系统接口。下面我们就分段介绍该驱动程序的实现。 文件开头包含了必要的头文件,以及定义了一些相关的变量和函数原型,它们的含义可以参考代码中的注释。 ~~~ #include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include "freg.h" /*主设备号和从设备号变量*/ static int freg_major = 0; static int freg_minor = 0; /*设备类别和设备变量*/ static struct class* freg_class = NULL; static struct fake_reg_dev* freg_dev = NULL; /*传统的设备文件操作方法*/ static int freg_open(struct inode* inode, struct file* filp); static int freg_release(struct inode* inode, struct file* filp); static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos); static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos); /*传统的设备文件操作方法表*/ static struct file_operations freg_fops = { .owner = THIS_MODULE, .open = freg_open, .release = freg_release, .read = freg_read, .write = freg_write, }; /*devfs文件系统的设备属性操作方法*/ static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf); static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count); /*devfs文件系统的设备属性*/ static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, freg_val_show, freg_val_store); ~~~ 接下来,定义用来访问虚拟硬件设备freg的传统设备文件系统接口,主要是实现freg_open、freg_release、freg_read和freg_write四个函数,其中,前面两个函数用来打开和关闭虚拟硬件设备freg,而后面两个函数用来读取和写入虚拟硬件设备freg中的寄存器val。这些函数的实现细节可以参考代码中的注释,如下所示。 ~~~ /*打开设备方法*/ static int freg_open(struct inode* inode, struct file* filp) { struct fake_reg_dev* dev; /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时可以直接拿来用*/ dev = container_of(inode->i_cdev, struct fake_reg_dev, dev); filp->private_data = dev; return 0; } /*设备文件释放时调用,空实现*/ static int freg_release(struct inode* inode, struct file* filp) { return 0; } /*读取设备的寄存器val的值*/ static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) { ssize_t err = 0; struct fake_reg_dev* dev = filp->private_data; /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count < sizeof(dev->val)) { goto out; } /*将寄存器val的值拷贝到用户提供的缓冲区中*/ if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; } /*写设备的寄存器val的值*/ static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) { struct fake_reg_dev* dev = filp->private_data; ssize_t err = 0; /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count != sizeof(dev->val)) { goto out; } /*将用户提供的缓冲区的值写到设备寄存器中*/ if(copy_from_user(&(dev->val), buf, count)) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; } ~~~ 接下来,我们继续定义用来访问虚拟硬件设备freg的devfs文件系统接口。这种硬件访问接口将虚拟硬件设备freg的寄存器val当作设备的一个属性,通过读写这个属性就可以达到访问设备的目的,这是通过调用freg_val_show和freg_val_store这两个函数来实现的。为了方便后面编写proc文件系统接口来访问虚拟硬件设备freg,我们定义了两个内部使用的函数__freg_get_val和__freg_set_val,它们分别用来读写虚拟硬件设备freg的寄存器val。它们的实现如下所示。 ~~~ /*将寄存器val的值读取到缓冲区buf中,内部使用*/ static ssize_t __freg_get_val(struct fake_reg_dev* dev, char* buf) { int val = 0; /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } val = dev->val; up(&(dev->sem)); return snprintf(buf, PAGE_SIZE, "%d\n", val); } /*把缓冲区buf的值写到设备寄存器val中,内部使用*/ static ssize_t __freg_set_val(struct fake_reg_dev* dev, const char* buf, size_t count) { int val = 0; /*将字符串转换成数字*/ val = simple_strtol(buf, NULL, 10); /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } dev->val = val; up(&(dev->sem)); return count; } /*读设备属性val的值*/ static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf) { struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev); return __freg_get_val(hdev, buf); } /*写设备属性val的值*/ static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev); return __freg_set_val(hdev, buf, count); } ~~~ 接下来,我们继续定义用来访问虚拟硬件设备freg的proc文件系统接口,主要是实现freg_proc_read和freg_proc_write这两个函数。同时,我们还定义了用来在proc文件系统中创建和删除/proc/freg文件的函数freg_create_proc和freg_remove_proc。它们的实现如下所示。 ~~~ /*读取设备寄存器val的值,保存到page缓冲区中*/ static ssize_t freg_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) { if(off > 0) { *eof = 1; return 0; } return __freg_get_val(freg_dev, page); } /*把缓冲区的值buff保存到设备寄存器val中*/ static ssize_t freg_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) { int err = 0; char* page = NULL; if(len > PAGE_SIZE) { printk(KERN_ALERT"The buff is too large: %lu.\n", len); return -EFAULT; } page = (char*)__get_free_page(GFP_KERNEL); if(!page) { printk(KERN_ALERT"Failed to alloc page.\n"); return -ENOMEM; } /*先把用户提供的缓冲区的值拷贝到内核缓冲区中*/ if(copy_from_user(page, buff, len)) { printk(KERN_ALERT"Failed to copy buff from user.\n"); err = -EFAULT; goto out; } err = __freg_set_val(freg_dev, page, len); out: free_page((unsigned long)page); return err; } /*创建/proc/freg文件*/ static void freg_create_proc(void) { struct proc_dir_entry* entry; entry = create_proc_entry(FREG_DEVICE_PROC_NAME, 0, NULL); if(entry) { entry->owner = THIS_MODULE; entry->read_proc = freg_proc_read; entry->write_proc = freg_proc_write; } } /*删除/proc/freg文件*/ static void freg_remove_proc(void) { remove_proc_entry(FREG_DEVICE_PROC_NAME, NULL); } ~~~ 最后,我们定义驱动程序freg的模块加载与卸载函数freg_init和freg_exit。函数freg_init主要用来注册和初始化虚拟硬件设备freg,而函数freg_exit用来反注册和释放虚拟硬件设备freg。它们的实现如下所示。 ~~~ /*初始化设备*/ static int __freg_setup_dev(struct fake_reg_dev* dev) { int err; dev_t devno = MKDEV(freg_major, freg_minor); memset(dev, 0, sizeof(struct fake_reg_dev)); /*初始化字符设备*/ cdev_init(&(dev->dev), &freg_fops); dev->dev.owner = THIS_MODULE; dev->dev.ops = &freg_fops; /*注册字符设备*/ err = cdev_add(&(dev->dev),devno, 1); if(err) { return err; } /*初始化信号量和寄存器val的值*/ init_MUTEX(&(dev->sem)); dev->val = 0; return 0; } /*模块加载方法*/ static int __init freg_init(void) { int err = -1; dev_t dev = 0; struct device* temp = NULL; printk(KERN_ALERT"Initializing freg device.\n"); /*动态分配主设备号和从设备号*/ err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME); if(err < 0) { printk(KERN_ALERT"Failed to alloc char dev region.\n"); goto fail; } freg_major = MAJOR(dev); freg_minor = MINOR(dev); /*分配freg设备结构体*/ freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL); if(!freg_dev) { err = -ENOMEM; printk(KERN_ALERT"Failed to alloc freg device.\n"); goto unregister; } /*初始化设备*/ err = __freg_setup_dev(freg_dev); if(err) { printk(KERN_ALERT"Failed to setup freg device: %d.\n", err); goto cleanup; } /*在/sys/class/目录下创建设备类别目录freg*/ freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME); if(IS_ERR(freg_class)) { err = PTR_ERR(freg_class); printk(KERN_ALERT"Failed to create freg device class.\n"); goto destroy_cdev; } /*在/dev/目录和/sys/class/freg目录下分别创建设备文件freg*/ temp = device_create(freg_class, NULL, dev,NULL,"%s", FREG_DEVICE_FILE_NAME); if(IS_ERR(temp)) { err = PTR_ERR(temp); printk(KERN_ALERT"Failed to create freg device.\n"); goto destroy_class; } /*在/sys/class/freg/freg目录下创建属性文件val*/ err = device_create_file(temp, &dev_attr_val); if(err < 0) { printk(KERN_ALERT"Failed to create attribute val of freg device.\n"); goto destroy_device; } dev_set_drvdata(temp, freg_dev); /*创建/proc/freg文件*/ freg_create_proc(); printk(KERN_ALERT"Succeeded to initialize freg device.\n"); return 0; destroy_device: device_destroy(freg_class, dev); destroy_class: class_destroy(freg_class); destroy_cdev: cdev_del(&(freg_dev->dev)); cleanup: kfree(freg_dev); unregister: unregister_chrdev_region(MKDEV(freg_major, freg_minor), 1); fail: return err; } /*模块卸载方法*/ static void __exit freg_exit(void) { dev_t devno = MKDEV(freg_major, freg_minor); printk(KERN_ALERT"Destroy freg device.\n"); /*删除/proc/freg文件*/ freg_remove_proc(); /*销毁设备类别和设备*/ if(freg_class) { device_destroy(freg_class, MKDEV(freg_major, freg_minor)); class_destroy(freg_class); } /*删除字符设备和释放设备内存*/ if(freg_dev) { cdev_del(&(freg_dev->dev)); kfree(freg_dev); } /*释放设备号资源*/ unregister_chrdev_region(devno, 1); } MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Fake Register Driver"); module_init(freg_init); module_exit(freg_exit); ~~~ 驱动程序freg的源文件准备好之后,接下来就要为它编写编译选项配置文件和编译脚本文件了。 **Kconfig** ~~~ config FREG tristate "Fake Register Driver" default n help This is the freg driver for android system. ~~~ 这个文件定义了驱动程序freg的编译选项。在编译驱动程序freg之前,我们可以通过执行make menuconfig命令来设置这些编译选项,以便可以指定驱动程序freg的编译方式。从这个配置文件就可以看出,驱动程序freg可以以三种方式来编译。第一种方式是直接内建在内核中;第二种方式是编译成内核模块;第三种方式是不编译到内核中。默认的编译方式为n,即不编译到内核中,因此,在编译驱动程序freg之前,我们需要执行make menuconfig命令来修改它的编译选项,以便可以将驱动程序freg内建到内核中或者以模块的方式来编译。 **Makefile** `obj-$(CONFIG_FREG) += freg.o` 这是驱动程序freg的编译脚本文件,其中,$(CONFIG_FREG)是一个变量,它的值与驱动程序freg的编译选项有关。如果选择将驱动程序freg内建到内核中,那么变量$(CONFIG_FREG)的值为y;如果选择以模块的方式来编译驱动程序freg,那么变量$(CONFIG_FREG)的值为m;如果变量$(CONFIG_FREG)的值既不为y,也不为m,那么驱动程序freg就不会被编译。 至此,我们就为虚拟硬件设备freg开发了一个驱动程序。接下来,我们还需要修改内核中的Kconfig和Makefile文件来支持驱动程序freg的编译。