一、对linux驱动文件编写时结构认识与记录
驱动文件常见的内容认识,
查看当前内核中已有的模块
lsmod
cat /proc/modules
module安装
insmod
使用modprobe安装 可识别依赖关系
int __init xxx_init();
module_init(xxx_init);
module卸载
rmmod
modprobe -r
int __exit xxx_exit();
module_exit(xxx_exit);
module模块安装时添加参数
module_param(变量名,类型,权限属性);
module_param_array(变量名,类型,个数,权限属性);
编译多个内核模块时,obj-m表示编译生成可加载模块。
相对应的,obj-y表示直接将模块编译进内核。
obj-m +=xx.ko
obj-m +=xxx.ko
存在a.c b.c 组成xxxx.ko时,其依赖关系
obj-m +=xxxx.ko
xxxx-objs=a.o b.oKERNEL_DIR:=xxxxx/kernel
CROSS_COMPILE:=xxxxx/bin/arm-eabi-
PWD:=$(shell pwd)
default:
例子:make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
标准的make指令是这样的:make -C $KDIR M=$PWD [target],下面分别介绍每个字段的含义。
-C选项:此选项指定内核源码的位置,make在编译时将会进入内核源码目录,执行编译,编译完成时返回。
$KDIR:/lib/modules/$(shell uname -r)/build/,指定内核源码的位置。
直接在目标板上编译时,内核头文件默认存放在/lib/modules/$(shell uname -r)/build/中,这个build/目录是一个软连接,链接到源码头文件的安装位置。而内核真正的源码库则直接引用正在运行的内核镜像。
当跨平台编译时,就需要指定相应的内核源码目录,而不是系统中的源码目录,但是交叉编译时,需不需要指定架构平台和交叉编译工具链呢?我们接着往下看;
M=$(PWD):需要编译的模块源文件地址$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:rm *.o *.order .*.cmd *.mod.c *.symvers.tmp_versions -rf
内核中符号表(其中符号包含函数和变量,而查看路径 /proc/kallsyms)的使用
在 *.ok中添加
extern 函数
EXPROT_SYMBOL_GBL(函数名/变量名)在任意模块中使用符号表,安装和卸载时存在依赖关系,卸载时先卸载本体,后卸载依赖。(安装时反之)
设备号由32位的数据组成=主设备(占0xfff00000位,用于区分不同类设备)+次设备(占0xfffff位吗,用于区分同一类设备);
参考内核源码中fs文件相关内容。
字符设备的注册
register_chrdev(设备号,设备名,设备操作函数的地址);
设备操作函数结构体
static struct file_operations xxx_fops =
{.owner = xxx_MODULE, //所有者.open = xxx_open, //打开.read = xxx_read, //读取.write = xxx_write, //写入.release = xxx_release,//释放...
}
————————————————————————————————————————————————————————————
struct cdev 字符设备结构体
int cdev_init(struct cdev *cdev, const struct file_operations *fops)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
void cdev_del(struct cdev *p)动态创建设备号
alloc_chrdev_region
指定创建设备号
MKDEV
register_chrdev_region
创建类
class_create
创建设备
device_create查看申请的设备
cat /proc/devices
MAJOR()获取当前主设备号
MINOR()获取当前次设备号
字符设备的注销
unregister_chrdev(设备号,设备名);
一般字符设备的注册在驱动模块的入口函数 xxx_init 中进行,字符设备的注销在驱动模块的出口函数 xxx_exit 中进行。————————————————————————————————————————————————————————————
注销设备号
unregister_chrdev_region(dev_t from, unsigned count)
删除设备
device_destroy(
删除类
class_destroy
物理内存和虚拟内存之间的转换,需要用到
两个函数:ioremap 和 iounmap。*ioremap(要映射的物理起始地址, 要映射的内存空间大小);
得到虚拟地址空间首地址卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射
iounmap (取消映射的虚拟地址空间首地址);