驱动开发1:内核程序框架
驱动编程
.ko 文件
可加载的内核模块,包含可以动态加载到运行中的 Linux 内核的代码
程序1:内核入口和出口
#include <linux/init.h>
#include <linux/module.h>static int __init my_init(void){printk("%s\n,%s\n,%s\n,%s\n,%d\n",__DATE__,__TIME__,__FILE__,__FUNCTION__,__LINE__);return 0;//0表示insmod成功
}static void __exit my_exit(void){printk("%s\n,%s\n,%s\n,%s\n,%d\n",__DATE__,__TIME__,__FILE__,__FUNCTION__,__LINE__);}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
C/C++ 的预定义宏(Predefined Macros)
__TIME__
hh:mm:ss
__DATE__
Mmm dd yyyy"
__FILE__
当前源文件的路径
__FUNCTION__
当前函数名
__LINE__
当前行号
程序二:内核传参
内核传参的三大原则:
-
必须是全局变量
-
数据类型没有浮点数
bool invbool char uchar short ushort int uint long ulong charp(char *)
-
内核参数接受的全局变量必须用宏来进行传参说明
module_param(name,tyoe,perm)
name
→ 参数变量名(你在代码里定义的全局变量)
type
→ 参数类型(bool, int, long, charp 等)
perm
→ 权限(决定是否能通过sysfs
读写,通常写0
或0644
)
#include <linux/init.h>
#include <linux/module.h>//全局变量传参
static int irq;
static char* pstring;//传参说明
module_param(irq,int,0664);
module_param(pstring,charp,0);static int __init my_init(void){printk("函数%s:irq=%d,pstring=%s\n",__func__,irq,pstring);return 0;//0表示insmod成功
}static void __exit my_exit(void){printk("函数%s:irq=%d,pstring=%s\n",__func__,irq,pstring);
}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
内核直接传参,不需要用“”
可以在这个文件路径下修改变量值
//全局变量传参
static int irq;
static char* pstring
//传参说明
module_param(irq,int,0664);
module_param(pstring,charp,0);MODULE_PARM_DESC(irq,"this is a int");
MODULE_PARM_DESC(irq,"that is a string");
可以用:MODULE_PARM_DESC增加变量描述,用modinfo命令查看ko文件
程序三:多文件之间的混合调用
符号导出:让别的内核程序访问到变量和函数
#ifndef __MYTEST_H_
#define __MYTEST_H_
//声明一个函数
extern void mytest(void);
struct mystruct{int a;char b;
};
extern struct mystruct g_str;
#endif
#include <linux/init.h>
#include <linux/module.h>
#include "mytest.h"void mytest(void){printk("function:%s\n",__func__);
}
//符号导出
EXPORT_SYMBOL(mytest);/*导出结构体*/
struct mystruct g_str={100,'c'};EXPORT_SYMBOL(g_str);
//GPL标准
MODULE_LICENSE("GPL");
这里需要用EXPORT_SYMBOL导出的函数或变量一定不能用static修饰
因为这里相当于给其他内核程序使用该内容,不涉及硬件的申请资源等,不需要module_init和module_exit
#include <linux/init.h>
#include <linux/module.h>
#include "mytest.h"
static int __init my_init(void){printk("%s\n",__func__);mytest();printk("%d,%c\n",g_str.a,g_str.b);return 0;
}static void __exit my_exit(void){printk("%s\n",__func__);mytest();printk("%d,%c\n",g_str.a,g_str.b);
}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
符号导出就是可以借助头文件访问内容
程序运行:
一定要先加载定义内容的,在加载内核主程序入口
其实是因为在/porc/kallsyms可以找到
注意:EXPORT_SYMBOL_GPL
#include <linux/init.h> #include <linux/module.h> #include "mytest.h"void mytest(void){printk("function:%s\n",__func__); } struct mystruct g_str={100,'c'};//符号导出GPL EXPORT_SYMBOL_GPL(mytest); EXPORT_SYMBOL_GPL(g_str); //GPL标准 MODULE_LICENSE("GPL");//----------------------------------// #include <linux/init.h> #include <linux/module.h> #include "mytest.h" static int __init my_init(void){printk("%s\n",__func__);mytest();printk("%d,%c\n",g_str.a,g_str.b);return 0; }static void __exit my_exit(void){printk("%s\n",__func__);mytest();printk("%d,%c\n",g_str.a,g_str.b); }module_init(my_init); module_exit(my_exit); //MODULE_LICENSE("GPL");注释
程序四:printk使用
printk(KERN_LEVEL "format string", arguments...);
级别常量 | 数值 | 描述 |
---|---|---|
KERN_EMERG | 0 | 系统不可用,紧急情况 |
KERN_ALERT | 1 | 必须立即采取行动 |
KERN_CRIT | 2 | 临界条件 |
KERN_ERR | 3 | 错误条件 |
KERN_WARNING | 4 | i警告条件 |
KERN_NOTICE | 5 | 正常但重要的条件 |
KERN_INFO | 6 | 信息性消息 |
KERN_DEBUG | 7 | 调试级消息 |
#include <linux/init.h>
#include <linux/module.h>static int __init my_init(void){printk(KERN_EMERG"system cannot work\n");printk(KERN_ALERT"take messages immediately\n");printk(KERN_CRIT"linjie condition\n");printk(KERN_ERR"error condition\n");printk(KERN_WARNING"warning condition\n");printk(KERN_NOTICE"notice message\n");printk(KERN_INFO"information\n");printk(KERN_DEBUG"debug message\n");return 0;
}static void __exit my_exit(void){printk("<0>""system cannot work\n");printk("<1>""take messages immediately\n");printk("<2>""linjie condition\n");printk("<3>""error condition\n");printk("<4>""warning condition\n");printk("<5>""notice message\n");printk("<6>""information\n");printk("<7>""debug message\n");
}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
一定要注意:
只有一个参数,也就是多个小串拼成一个大串
可以修改内核的printk
程序五:gpio开关灯的使用
内核提供的GPIO操作函数
int gpio_request(unsigned gpio, const char *label);
//内核程序要想访问操作某个GPIO硬件,必须利用此函数先向内核申请这个GPIO资源
GPIO硬件 | GPIO编号 |
---|---|
GPIOC12 | PAD_GPIO_C + 12 |
GPIOB26 | PAD_GPIO_B + 26 |
void gpio_free(unsigned gpio)
//功能:如果内核程序不再使用某个GPIO硬件,记得要释放资源
//返回值:不用记,只需参考内核大神如何使用判断此函数的返回值即可
gpio_direction_output(unsigned gpio, int value);
//功能:配置GPIO为输出功能同时输出value(1高/0低)
gpio_direction_output(unsigned gpio, int value);
//功能:配置GPIO为输出功能同时输出value(1高/0低)gpio_direction_input(unsigned gpio);
//功能:配置GPIO为输入功能
gpio_set_value(unsigned gpio, int value);
//设置GPIO的输出值为value(1/0)
//此函数使用的前提是提前配置为输出功能int gpio_get_value(unsigned gpio);
//获取GPIO的电平状态,返回值保存状态
//此函数对输入还是输出无要求
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h>struct led_resource{char* name;int gpio;
};
static struct led_resource led_info[]={{.name="LED1",.gpio=PAD_GPIO_C+12},{.name="LED2",.gpio=PAD_GPIO_C+11}
};
static int __init led_init(void){int i;for(i=0;i<ARRAY_SIZE(led_info);++i){struct led_resource* p=&led_info[i];gpio_request(p->gpio,p->name);gpio_direction_output(p->gpio,0);printk("%s:第%d的灯亮了\n",__func__,i+1);}return 0;
}
static void __exit led_exit(void){int i;for(i=0;i<ARRAY_SIZE(led_info);++i){struct led_resource* p=&led_info[i];gpio_set_value(p->gpio,1);gpio_free(p->gpio);//必须在setValue后再释放 printk("%s第%d的灯灭了\n",__func__,i+1);}
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");