Linux Device Drivers 笔记 内核模块框架
最简单的内核模块
#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> static int __init init_testko(void) { printk("test ko init\n"); return 0; } static void __exit exit_testko(void) { printk("test ko exit\n"); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("KEVIN"); module_init(init_testko); module_exit(exit_testko);Makefile写法
obj-m = test.o #模块源代码 KER_DIR = /root/linux-3.0 #内核源码路径 CUR_DIR = $(shell pwd) all: make -C $(KER_DIR) M=$(CUR_DIR) modules clean: rm -rf *.o *.ko *.mod.o *.mod.c *.symvers *.order cp: cp *.ko /root/rootfs/ 分配设备号 设备号在内核中设备号用一个32位数dev_t(linux/types.h)表示,高十二位表示主设备号,低二十位表示次设备号。
#include<linux/kdev_t.h> MAJOR(dev_t dev); MINOR(dev_t dev); 以上来两个宏分别从dev_t中提取主设备号和次设备号。下边的宏用来合成一个设备号。 MKDEV(int major, int minor); 分配设备号的方法 静态分配 int register_chrdev_region(dev_t first, unsigned int count, char * name); 动态分配 int alloc_chrdev_region(dev_t *dev, unsigned int dirstminor, unsigned int count, char * name); 这两个函数在分配成功的时候返回0,分配失败的时候返回一个错误码。 释放占用的设备号 void unregister_chrdev_region(dev_t first, unsigned int count); 参数: first 要分配的设备编号范围的启始值 count 所请求的连续设备编号的个数 name 是和该编号范围关联的设备名,将出现在/proc/devices和sysfs中 dev 是用来保存分配的设备号的内存地址关于错误码,内核定义了一组宏来表示,这些宏保存在./include/asm-generic/errno-base.h中
可以在写驱动代码的时候使用,例如
return -EPERM; 表示操作没有权限,使用的时候需要加上‘-’符号。
关于设备号的分配
内核中有块设备表和字符设备表,根据设备的类型和主设备号可通过这两个表之一找到对应的驱动程序函数跳转结构,而次设备号则一般只用做同类型设备中具体设备项的编号。
linux2.6之前的额版本采用8位的数据类型存储主设备号,因此将块设备和字符设备的种类都限制到了256种。
内核源代码中关于设备号分配最重要的一个函数是__register_chrdev_region,此函数主要维护了一个指针数组chrdevs,以散列表的形式管理设备。而register_chrdev_region和alloc_chrdev_region都间接的调用了这个函数,其中alloc_chrdev_region更是直接以第一个参数为0的方式调用,具体的讲解见
此处还有一个疑问:在以动态分配形式分配设备号时__register_chrdev_region从255向前搜索chrdevs数组,找到一个指向NULL的指针时就返回此指针的索引(数组下标)作为动态分配的主设备号,但是如果数组chrdevs中的指针全不为NULL,是不是会分配失败,也就是说动态分配只能分配0-255的主设备号?(先Mark一下,以后探究)
注册设备 ldd3的方法–关于cdev结构 #include <linux/cdev.h> struct cdev * cdev_alloc(void); void cdev_init(struct cdev * dev, struct file_operations * fops); int cdev_add(struct cdev * dev, dev_t num, unsigned int count); void cdev_del(struct cdev * dev);每一个字符设备都对应一个cdev结构,注册设备的过程就是分配和初始化cdev结构的过程,因此常使用如下代码完成
动态创建
静态创建
struct cdev mcdev; struct file_operations fops; cdev_init(&mcdev, &fops); mcdev.owner = THIS_MODULE; result = cdev_add(&mycdev, devno, count); 在设备注销的时候调用 cdev_del(&mcdev); 老方法 int register_chrdev(unsigned int major, const char * name, struct file_operations * fops); major : 主设备号,为0时表示自动分配 name : 驱动程序的名字,将在/proc/devices中显示 fops : 默认的file_operations结构 int unregister_chrdev(unsigned int major, const char * name);