Linux 驱动模块

Waring:
   字符设备、块设备和网络设备是相对的,如SPI驱动可写成字符设备驱动或块设备驱动、CAN设备驱动可写成字符设备驱动或网络设备驱动,具体实现方式应依据设备类型来定。当SPI连接存储设备时,由于块设备传输单位为 512B, 故应将其编写成块设备驱动,应用空间通过块操作来提高读写效率。块设备特性具体内容:参考块设备驱动框架详解


设备分类

字符设备

字符设备     >>>>   字符设备驱动    >>>>   字符设备文件 (/dev/生成字符设备文件)
	(鼠标、键盘、显示……)

块设备

块设备       >>>>   块设备驱动     >>>>   块设备文件   (/dev/生成块设备文件)
	(内存卡、SD卡、RAM……)

网络设备

网络设备     >>>>   网络设备驱动    (ifconfig -a 查看设备信息) 	
	(以太网、WiFi、蓝牙、CAN……)  无设备文件

字符设备驱动实现步骤

	1. 驱动编写

    2. 驱动编译

    3. 驱动使用

模块编写

(Linux内核以一定的格式进行驱动的编写)
三要素:

   1. 入口(加载)
		module_init(入口函数名);    static int __init xxx_func(void){}
		
	2. 出口(卸载)
		module_exit(卸载函数名);	   static void __exit xxx_func(void){}     //进口函数名和出口函数名不需要相同
		
	3. GPL协议声明
		MODULE_LICENSE("GPL");

	4. 描述驱动的而一些信息(不必要)
		MODULE_AUTHOR("zsj");                            //驱动程序的作者
		MODULE_VERSION("demo_1");						 //模块版本
		MODULE_DESCRIPTION("Fist_drive demo test");      //一些描述信息
		MODULE_ALIAS("demo");							 //模块别称
		……
		
		module_param(name, type, perm);
		name:表示参数的名字;
		type:表示参数的类型;
		perm:表示参数的访问权限;

		声明一个数组参数:
		module_param_array(name, type, num, perm);
		name:表示数组的名字;
	    type:表示参数的类型;
	    num :表示数组中元素数量;
		perm:表示参数的访问权限;
		
		【type支持的基本类型有】
		bool   :布尔类型
		invbool:颠倒了值的bool类型;
		charp  :字符指针类型,内存为用户提供的字符串分配;
		int    :整型
		long   :长整型
		short  :短整型
		uint   :无符号整型
		ulong  :无符号长整型
		ushort :无符号短整型
		
		【perm参数 设定访问权限】
		modlue_param和module_param_array中的perm用于设定该参数的访问权限;
		perm表示该参数在sysfs文件系统中所对应的文件节点的属性;你用该使用<linux/stat.h>中定义的权限值;这个值控制
		谁可以存取这些模块参数在sysfs文件系统中的表示;当perm为0时,表示此参数不存在sysfs文件系统下对应的文件节点;
		否则,模块被加载后,在/sys/module/目录下将会出现以此模块名命名的目录,带有给定的权限;
		比如:
		#define S_IRWXU 00700
		#define S_IRUSR 00400
		#define S_IWUSR 00200
		#define S_IXUSR 00100
		#define S_IRWXG 00070
		#define S_IRGRP 00040
		#define S_IWGRP 00020
		#define S_IXGRP 00010
		#define S_IRWXO 00007
		#define S_IROTH 00004
		#define S_IWOTH 00002
		#define S_IXOTH 00001
		使用 S_IRUGO 作为参数可以被所有人读取, 但是不能改变; S_IRUGO|S_IWUSR 允许 root 来改变参数. 

上述模块的宏定义包含的头文件在Linux内核:
#include <linux/init.h>
#include <linux/module.h>


EXPORT_SYMBOL_GPL() 用来声明驱动之间的全局变量和函数


编译内核模块:

  编译器: gcc/交叉编译工具
	
	编写编译内核模块的Makefile
	
	内部编译:将内核模块源文件放在内核源码中进行编译   ==>>  相关文件(Konfig,Makefile,make menuconfig)
	静态编译:将内核模块编译进uImage中
	
	
	外部编译:将内核模块源文件放在内核源码外进行编译
	动态编译:编译生成动态模块 XXX.ko

模块编译makefile实例:

	KERNDIR:= /lib/modules/'uname -r'/build/       //自己放置源码的目录,编译成对应的架构
	PWD:=$(shell pwd)                              //编写的驱动所在路径
	
	obj-m:= module_test.o                          //目标文件依赖所有 *.o 文件生成
	
	all:
		make -C $(KERNDIR) M=$(PWD) modules     //去指定的路径编译成目标modules
		
	clean:
		make -C $(KERNDIR) M=$(PWD) clean	    //去指定的路径清除目标clean

(开始因 obj-m 错误操作,编译时导致提示没有头文件,谨慎每一步操作)
如果一个模块包含多个.c文件:
obj-m:= module_name.o
module_name-objs := file1.o file2.o …

Note:
  嵌入式系统编译模块时,需下载好所需的内核源码,配置好,并编译一次(否则无法成功编译模块,及KERNDIR下的内核已经编译过一次)


原因:
  编译驱动只编译了你的驱动代码,而且里面引用了内核的头文件,如果没编译内核,驱动程序链接时会找不到内核的二进制代码,会链接失败。


模块的使用:

	查看内核模块信息的命令:
		modinfo (作者,功能描述)
		
	查看当前内核工作的模块:
		lsmod
		
	查看内核日志信息命令:
		dmesg	
		-C 清除已有的内核日志信息
		
	插入模块进入内核:
		insmod  /  modprobe(可指定个别模块或一组相依的模块)
		
	卸载内核模块:
		rmmod                  
	
	内核模块加载时,执行对应的加载函数,并只会执行一次
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值