app: open , read , write
驱动: led_open,led_read,led_write
驱动框架:
一、写出:led_open,led_read
二、怎么告诉内核?
a、定义一个file_operations
b、把这个结构体告诉内核:
register_chrdev(major,name,file_operations)
c、谁来调用它 (register_chrdev)
驱动的入口函数 first_drv_init
d、修饰:module_init(first_drv_init) (怎么知道是哪个入口函数)
module_init:(入口函数)定义了一个结构体,结构体有个函数指针,指向入口函数xxx_init。加载一个驱动的时候,内核就会找到这个结构体里面的函数指针,指向入口函数。
加载一个驱动时,内核就会找到这个结构体里面的函数指针,指向入口函数。入口函数把file_operations告诉内核。
介绍一个内核里的数组chrdevs(字符设备的file_operations和major都包含在这个数组中):
static struct char_device_struct { struct char_device_struct *next; unsigned int major; unsigned int baseminor; int minorct; char name[64]; struct file_operations *fops; struct cdev *cdev; /* will die */ } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
app:open("/dev/xxx")
打开文件的属性:c _ _ _, _ _ _, _ _ _, major, minor
设备类型,主设备号
内核数组chrdevs:
1 | 2 | …… | …… | …… | …… | major | …… | …… | …… |
★★VFS系统根据打开的这个文件的属性(设备类型:字符设备,主设备号major),就会找到注册进去的file_operations结构。
★★register_chrdev的实现:在一个内核数组chrdevs里,以major为索引,找到一项,在这一项里把file_operations填充进去,挂进去。
总结:
怎么根据打开的设备(open),找到驱动程序具体的实现(led_open)?
在内核定义了一个chrdevs数组,在这个数组里面,根据主设备号,找到了那一项的file_operations,这个file_operations结构是驱动程序里面实现的。
驱动程序:1、定义了led_open、led_write、led_read
2、定义了file_operations结构体,.open=led_open,.write=led_write
3、入口函数里,用register_chrdev(major,name,file_operations)把这个结构体放到内核数组chrdevs,对应的主设备号里面。
入口函数(注册):把file_operations结构体挂到chrdevs数组对应的设备号那一项
出口函数(卸载):把file_operations结构体从对应的设备号那一项拖出来。
第一个驱动程序 first_drv.c:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static int first_drv_open(struct inode *inode, struct file *file) { printk("first_drv_open\n"); return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { printk("first_drv_write\n"); return 0; } static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = first_drv_open, .write = first_drv_write, }; int first_drv_init(void) { register_chrdev(111, "first_drv", &first_drv_fops); //注册驱动程序,告诉内核 return 0; } void first_drv_exit(void) { unregister_chrdev(111, "first_drv"); //卸载驱动 } module_init(first_drv_init); module_exit(first_drv_exit);
Makefile文件:
KERN_DIR = /work/system/linux-2.6.22.6 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += first_drv.o
KERN_DIR表示内核的目录,编一个驱动程序,依赖于内核。
-C:转到这个KERN_DIR这个目录去,用这个目录的Makefile来进行编译。
M=:当前目录是什么。modules:目标。