Linux驱动入门必须get的知识点-01.基本框架与操作

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 		//包含了入口函数init_module 和出口函数 cleanup_module 的原型声明
#include 	//包含了printk的原型声明
#include 	//现在没用,但后面会用到

//为避免重名问题,驱动所有函数要尽可能声明为静态
//__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 
#include 
#include 

int my_add(int a, int b)
{
	return a+b;
}
//导出声明
EXPORT_SYMBOL(my_add);

MODULE_LICENSE("GPL");

/***************************************led.c***************************************/
#include 
#include 
#include 

#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 
#include 
#include 

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"

你可能感兴趣的:(#,linux驱动)