0. 编写编译驱动的Makefile
#指定驱动的测试的路径
ROOM_DIR = /nfs/rootfs/home/2.study/
#指定测试Demo的交叉编译工具链
DEMO_DIR = /home/linux/1-DataShare/0.交叉编译工具链/0.exynos-4412/gcc-4.6.4/bin/arm-arm1176jzfssf-linux-gnueabi-
GCC = $(DEMO_DIR)gcc
#$(KERNELRELEASE)是内核Makefile里的一个变量,但我们没有定义这个变量
ifeq ($(KERNELRELEASE), )
#所以该Makefile第一执行的时候会执行这里,在这里首先指定内核路径和当前路径
KERNEL_DIR = /home/linux/1-DataShare/1-系统移植/2-内核移植/1.完成版1_3.14.28/
CUR_DIR = $(shell pwd)
#该目标为Makefile的第一个目标,所以也是默认目标
all:
#进入内核目录编译内核目录以外的一个目标,该目标通过M参数指定。此时内核目录中的Makefiel会调用该Makefile
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
#编译测试文件
$(GCC) ./main.c -o main
#清除掉所有的编译文件
clean:
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
#移动驱动和测试文件到板子上
install:
sudo cp -raf *.ko main $(ROOM_DIR)
else
#当该Makefile被内核Makefiel调用的时候会执行这里,-m 表示编译成模块
obj-m += led.o
endif
//运行驱动基本操作
insmod xxx.ko //加载模块
lsmod xxx.ko //查看已加载模块
rmmod xxx //注意没有.ko,卸载已加载的模块
1.Linux 驱动基本框架
//3个最基本的头文件,几乎是所有驱动编写时必备的玩意儿
#include <linux/init.h> //包含了入口函数init_module 和出口函数 cleanup_module 的原型声明
#include <linux/kernel.h> //包含了printk的原型声明
#include <linux/module.h> //现在没用,但后面会用到
//为避免重名问题,驱动所有函数要尽可能声明为静态
//__init 和 __exit表示该代码被调用一次后,所在的存储空间将被释放掉
static int __init led_dev_init(void)
{
// __FUNCTION__ 这个宏表示的是当前的函数名
printk("_____%s_____\n", __FUNCTION__);
return 0;
}
static void __exit led_dev_exit(void){
printk("_____%s_____\n", __FUNCTION__);
}
//下面两个宏的作用是为默认的入口函数和出口函数取别名
module_init(led_dev_init); //入口函数 init_module 的别名为 led_dev_init
module_exit(led_dev_exit); //出口函数 cleanup_module 的别名为 led_dev_exit
MODULE_LICENSE("GPL"); //开源协议声明
//上面三个声明是一个驱动不可缺少的部分,下面这一堆根据需求而定,可有可无
MODULE_AUTHOR("Dallan"); //作者声明
MODULE_DESCRIPTION("Demo"); //模块功能声明
MODULE_ALIAS("First"); //给模块取一个更合适的别名
2.外部函数的调用
/**************************************math.h***************************************/
#ifndef __MATH_H__
#define __MATH_H__
int my_add(int a, int b);
#endif
/**************************************math.c***************************************/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
int my_add(int a, int b)
{
return a+b;
}
//导出声明
EXPORT_SYMBOL(my_add);
MODULE_LICENSE("GPL");
/***************************************led.c***************************************/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "math.h"
static int id = 1;
//调用外部函数和应用编程时区别在于,在函数实现的位置需要进行导出声明
static int __init led_dev_init(void)
{
printk("_____%s_____\n", __FUNCTION__);
printk("id+1 = %d \n", my_add(id, 1));
return 0;
}
static void __exit led_dev_exit(void){
printk("_____%s_____\n", __FUNCTION__);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
3.内核模块传参
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
static int id = 88;
static char *name = "Dream";
//模块参数声明,可以驱动加载时进行外部赋值。
//参数分别为 变量名,变量类型,变量权限。charp 等价于 char *。
//权限 UGO 分别为用户权限、组权限、外部权限,值 RWX 分别为4、2、1,权限的宏的命名就是这么来的。
module_param(a, int, 0644);
module_param(buf, charp, S_IRUGO | S_IWUSR);
static int __init led_dev_init(void)
{
printk("_____%s_____\n", __FUNCTION__);
printk("%s:id+1 = %d \n", name, my_add(id, 1));
return 0;
}
static void __exit led_dev_exit(void){
printk("_____%s_____\n", __FUNCTION__);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
//运行时指定参数,替换变量原本的值
insmod xxx.ko a=1 name="Mark"