目的:实现最简单的点灯操作。
Linux一切皆文件,应用程序访问某个物理设备(文件)时,首先通过open, read, write
等库函数调用系统调用接口(System call interface
),系统调用通过传进来的系统调用号操作虚拟文件系统(Virtual File System
),VFS再根据目标文件类型去找相应的驱动程序。
应用程序和VFS之间的接口是系统调用,而VFS与文件系统以及设备文件之间的接口是file_iperations
结构体成员函数。
Linux通过注册+回调的方式将驱动程序和系统调用联系起来。file_operations
中包含对文件进行打开,关闭,读写,控制等一些成员函数。
具体的成员函数参考:Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析
static struct file_operations led_drv_fops = {
.owner = THIS_MODULE,
.open = led_drv_open,
.write = led_drv_write,
};
/* 使用insmod命令安装驱动时会调用此函数 */
int led_drv_init(void){
int minor = 0;
GPIOB = (GPIO *)ioremap(GPIOB_BASE, sizeof(GPIO)); // 地址映射
major = register_chrdev(0, "led_drv", &led_drv_fops); // 注册驱动, 0表示动态分配主设备号
leddrv_class = class_create(THIS_MODULE, "led_drv");
// leddrv_class_dev = class_device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led"); // 从3.0开始改为device_create()
leddrv_dev[minor] = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "leds");
for(minor = 1; minor < 3; minor++){
leddrv_dev[minor] = device_create(leddrv_class, NULL, MKDEV(major, minor), NULL, "led%d", minor);
}
led_init();
printk("led_drv_init\n");
end:
return 0;
}
module_init(led_drv_init);
ioremap
将物理地址映射到内核虚拟地址。register_chrdev
注册字符设备。class_create
用于动态创建设备的逻辑类。---- linux驱动的类class及其节点device_create
用于动态的创建逻辑设备。会在/sys/devices/virtual
目录下创建新的逻辑设备目录,在/dev
目录下创建与逻辑类对应的设备文件。例如上面的程序创建了/dev/leds
,/dev/led1
,/dev/led2
。主设备号相同,次设备号分别为0
,1
,2
。module_init
的修饰,将加载函数链接到.initcall
段,方便内核寻找。void led_drv_exit(void){
int minor;
unregister_chrdev(major, "led_drv"); // 卸载驱动
for(minor = 0; minor < 3; minor++){
device_unregister(leddrv_dev[minor]); // 与device_create()对应
}
class_destroy(leddrv_class); // 与class_create()对应
iounmap(GPIOB);
}
module_exit(led_drv_exit);
unregister_chrdev
删除字符设备。device_unregister
会删除/sys/devices/virtual
下对应的设备目录,以及/dev
下对应的设备文件。class_destroy
删除设备的逻辑类。module_exit
修饰。struct file
代表一个已经打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct file。struct inode
内核使用inode结构体在内核内部表示一个文件。更多:Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析
KERN_DIR = ~/linux-3.4.y
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
是内核目录,编译模块前要先编译内核。make -C $(KERN_DIR) M=`pwd` modules
->参考文章obj-m
表示生成独立的.ko
文件,obj-y
表示该模块编译到zImageobj-m
表示生成的驱动文件名,*-objs
表示所有依赖,且所有源文件不能与生成的驱动名同名,否则会报module license 'unspecified' taints kernel.
错误。->参考文章 .....
obj-m += key_drv.o
key_drv-objs := key_drv_main.o key/key.o
01_LED: first_drv
02_KEY: key_polling