目录
1.驱动模块(驱动程序的框架)
2.内核中的打印函数(编写第一个驱动程序)
Source Insight 使用:
打印函数编写
分析
3.驱动的多文件编译
4.模块传递参数
安装好驱动之后如何传参?
多驱动之间调用(导出符号表)
具体过程
5.字符设备驱动
字符设备驱动的注册
总结归纳
具体过程
1.驱动模块(驱动程序的框架)
入口(安装):资源的申请
出口(卸载):资源的释放
许可证:GPL
#include <linux/init.h>
#include <linux/module.h>
//声明 //static 是为了防止别人的驱动和你重名
static int __init hello_init(void) //入口
{}
static void __exit hello_exit(void) //出口
{}
//告诉内核驱动的入口
module_init(hello_init);
//告诉内核驱动的出口
module_exit(hello_exit);
MODULE_LICENSE("GPL"); //许可证
Makefile
KERNELDIR:= /lib/modules/$(shell uname -r)/build/ //ubuntu 内核目录
#KERNELDIR:= /home/linux/kernel/kernel-3.4.39/ //开发板内核目录
PWD:=$(shell pwd)
all: make -C $(KERNELDIR) M=$(PWD) modules
clean:make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o
具体过程:
vi Makefile
ls
更改
vi hello.c
make
SI3US-361500-17409
2.内核中的打印函数(编写第一个驱动程序)
Source Insight 使用:
打印函数编写
分析
--------------------------------------打印级别--------------------------------------
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
y@y-virtual-machine:~/linux6818/demo$ cat /proc/sys/kernel/printk4 4 1 7 终端的级别 消息的默认级别 终端的最大级别 终端的最小级别
#define console_loglevel (console_printk[0])
#define default_message_loglevel (console_printk[1])
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
--------------------------------打印函数---------------------------------------------
printk(KERN_ERR "BFS-fs: %s(): " format, __func__, ## args)
功能:消息打印
参数:第一个参数:打印的级别第二个参数:打印的内容第三个参数:和printf一样,需要打印的参数
ubuntu级别
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
static int __init hello_init(void)//入口
{printk(KERN_ERR "hello word\n");return 0;
}
static void __exit hello_exit(void)//出口
{printk(KERN_ERR "baibai\n");
}
module_init(hello_init);//告诉内核驱动的入口
module_exit(hello_exit);//告诉内核驱动的出口
MODULE_LICENSE("GPL");
安装卸载无反应,因为ubuntu基于linux内核做的开发
PM:
dmesg (查看消息的回显) dmesg -c (查看回显并清空)dmesg -C (清空回显)
可以不写KERN _ERR 以默认的级别显示
重点:
ctrl+Alt+F5 (进入虚拟控制台)ctrl+Alt+F7(退出虚拟控制台)-》有的退出是F2
sudo insmod hello.ko(安装驱动)sudo rmmod hello(卸载驱动)
dmesg (查看消息的回显) dmesg -c (查看回显并清空)dmesg -C (清空回显)
cat /prod/sys/kernel/printk
(查看ubuntu终端显示级别 消息默认级别 消息最大级别 消息最小级别)
su root echo 4 3 1 7 > /proc/sys/kernel/printk
(修改ubuntu终端显示级别 消息默认级别 消息最大级别 消息最小级别)
echo 4 3 1 7 > /proc/sys/kernel/printk
(修改开发板)
= 赋值 需要等待其他文件全部执行完,才执行调用的
:= 立即赋值
+= 附加赋值
?= 询问变量之前是否被赋值过 如果赋值过,本次赋值不成立,否则成立
3.驱动的多文件编译
hello.c add.cMakefileobj-m:=demo.odemo-y+=hello.o add.o最终生成demo.ko文件
vi makefile
增添完后 make
然后make clean
4.模块传递参数
module_param(name, type, perm) //驱动安装时候的安装路径,或者安装时配置的参数功能:接收命令行传递的参数参数:name: 变量的名字 type:变量的类型 perm:权限 0664 0775
#include <linux/init.h>
#include <linux/module.h>
int a=10;
module_param(a,int,0664);
static int __init hello_init(void)
{printk("sum= %d\n",a);return 0;
}
static void __exit hello_exit(void)
{printk(KERN_ERR"bai");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
替换Linux6818/demo的hello.c文件 然后进行修改之前的双编译Makefile 即vi Makefile
完事后 进行make
完事后 卸载驱动 sudo rmmod hello
传递两个参数程序
int a=10;
module_param(a,int,0664);
int b=10;
module_param(b,int,0664);
问:安装时怎么区分a和b的作用,自己写的知道那如果移植其他厂商的呢比如LCD屏
使用modinfo hello.ko查看,且需要程序里使用如下函数进行配置
无法看出哪个是什么功能 无法提示
MODULE_PARM_DESC(_parm, desc)
功能:对变量的功能进行描述
参数:@_parm:变量@desc :描述字段
int a=10;
module_param(a,int,0664);
MODULE_PARM_DESC(a,"light");
short b=123;
module_param(b,short,0664);
MODULE_PARM_DESC(b,"clour");
char c='c';module_param(c,byte,0664);MODULE_PARM_DESC(c,"light_c");char *d=null;module_param(d,charp,0664);MODULE_PARM_DESC(d,"clour");static int __init hello_init(void)
{ printk("sum= %d\n",a);printk("sum= %d\n",b);printk("sum= %c\n",c);printk("sum= %s\n",d);
return 0;
}
static void __exit hello_exit(void){printk(KERN_ERR"bai");
}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");
vi hello.c
int a=10;
int b=0;
module_param(a, int, 0664) ;
module_param(b, int, 0664) ;
MODULE_PARM_dESC(a,"light");
MODULE_PARM_dESC(b,"color");
sudo insmod hello.ko a=20
make
modinfo hello.ko
sudo insomd hello.ko a=30 b=24
sudo dmesg -c
数组传参:
module_param_array(name,type,nump,perm)
功能:接收命令行传递的数组
参数:
name:数组名 type :数组类型 nump:参数的个数,变量的地址 perm 权限
int ww[10]={0};
int num;
module_param_array(ww,int,&num,0664)
static int __init hello_init(void)
{int i;for(i=0;i<num;i++){printk("ww[%d]=%d\n",i,ww[i]);}
}
sudo insmod hello.ko ww=1,2,3,4,5
具体过程:
int ww[10]={0};
int num;
module_param_array(ww,int,&num,0664)
static int __init hello_init(void)
{int i;for(i=0;i<num;i++){printk("ww[%d]=%d\n",i,ww[i]);}
}
make
sudo insmod hello.ko ww=1,2,3,4,5
安装好驱动之后如何传参?
1.lsmod查看驱动名字
2.找路径 /sys/module/驱动模块的名字/parameters
3.修改 -》su root -》echo 需要改为多少>需要修改的参数名
4.cat 需要修改的参数名(查看是否修改成功)
多驱动之间调用(导出符号表)
假如有两个驱动模块,modul1和modu2 。这两个是可以调用的
cp ../day1/module . -a 拷贝module到当前目录下
makdir export mv module/ export/
mv module/ A 重命名moudule为A
cp A B -a 拷贝一份并命名为B
#include <linux/intt.h>
#include <linux/module.h>
int add(int a,int b)
{return (a+b);
}
EXPORT_SYMBOL_GPL(add);//导出符号表
static int __init hello_init(void)
{return 0;
}
staic int __exit hello_exit(void)
{
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
进入B,mv hello.c demo.c改下名字
然后vi打开
extern int add(int a,int b);
static int __init hello_init(void)
{printk("sum = %d\n",add(10,345));
}
编译
make
发现报错,提示add没有定义
所以在执行之前把A里面大M开头文件复制到当前目录下
mv cp ../A/Module.symvers
然后再make
安装:
先安装提供者 sudo insmod hello.ko
再安装调用者 sudo insmod demo.ko
查看信息:dmesg
卸载:
先卸载 demo.ko 再卸载 hello.ko
具体过程
cp -r /
vi add.c
make
vi hello.c
5.字符设备驱动
APP层 ---- 读写操作 ----→
open打开一个文件 --→{ open=hello、read=buf、write=ubuf、close=hello}
内核层 ---- 读写文件 ----→
|
v
硬件层 ---- GPIO输出 ----→ LED灯
字符设备驱动的注册
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
功能:注册一个字符设备驱动
参数:
@major:主设备号
:如果你填写的值大于0,它认为这个就是主设备号
:如果你填写的值为0,操作系统给你分配一个主设备号
@name :名字 cat /proc/devices 查看设备名和主设备号
@fops :操作方法结构体
返回值:major>0 ,成功返回0,失败返回错误码(负数) (vi -t EIO 可以查看错误码)
major=0,成功主设备号,失败返回错误码(负数)
void unregister_chrdev(unsigned int major, const char *name)
功能:注销一个字符设备驱动
参数:
@major:主设备号
@name:名字
返回值:无
总结归纳:
字符设备驱动:
1、注册驱动register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
major=0 自动分配设备号 name:驱动的名字 fops:结构体
2、声明结构体-》注册驱动时第三个参数
3、open、read、write、release-》按照内核的格式自己写的
4、把自己写的这些函数->给到结构体里-》.open .read .write .release
5、注销设备驱动
具体过程:
开始注册,起一个变量
1.注册驱动 register_chrdev
major=0 (自动分配设备号) name:驱动名字 fops:结构体
2.声明结构体-》注册驱动时第三个参数
3.open、read、write、release-》按照内核的格式自己写的
4.把自己写的函数给到结构体里-》.open .read .write .release
5.注销设备驱动