Linux内核驱动编程

Linux内核驱动编程

2015-02-12

驱动程序基础的东西这儿就不罗嗦了,百度上有更好的资料,此处我们只是注重实际用处。

下面我们开始写程序:

一、初步helloword程序

首先是来一个简单的hello。

hello.c代码:

 1 /******************************
 2
 3 the first program
 4
 5 Hello World!
 6
 7 ******************************/
 8
 9 #include <linux/module.h>
10
11 #include <linux/init.h>
12
13
14
15 static int hello_init(void)
16
17 {
18
19 printk("<0>\nHello, world!\n\n");
20
21 return 0;
22
23 }
24
25
26
27 static void hello_exit(void)
28
29 {
30
31 printk("<0>\nGoodbye,world \n\n");
32
33 }
34
35
36
37 module_init(hello_init);
38
39 module_exit(hello_exit);
40
41 MODULE_LICENSE("Dual BSD/GPL"); //Any version of public GNU license

Make代码:

 1 ifeq ($(KERNELRELEASE),)
 2
 3 KERNELDIR ?= /home/study/system/linux-2.6.31
 4
 5 PWD := $(shell pwd)
 6
 7 modules:
 8
 9 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
10
11 modules_install:
12
13 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
14
15 clean:
16
17 rm -rf *.o *~ core .depend *.cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers
18
19
20
21 else
22
23 obj-m := hello.o
24
25 endif

注意:

①:Makefile中的KERNELDIR ,这个linux源代码的版本是和你的开发板的一模一样,必须修改,否侧或有错误

②:该应用程序必须在开发板上运行,千万注意别再服务器操作系统使用,否则存在破坏系统的危险。

接下来就是,编译->加载,使用以下命令

 1 make CROSS_COMPILE=arm-none-linux-gnueabi-
 2
 3 //此处为修改为你操作系统上的gcc 交叉编译
 4
 5 insmod hello.ko
 6
 7 //加载模块,此时屏幕上会打印,hello,world
 8
 9 rmmod hello.ko
10
11 //卸载模块,同样会打印 goodsbye world

接下来我们对其进行改进

二、改进helloword程序,增加输入模块参数

我们在代码中增加以下代码:

1 static char *name = "LoverXue";
2
3 static int age = 1;
4
5
6
7 module_param(name,charp,S_IRUGO);
8
9 module_param(age,int,S_IRUGO);

编译后,加载时,输入以下命令

make CROSS_COMPILE=arm-none-linux-gnueabi-

insmod hello.ko name="lihaiyan" age=20

结果如下:

附上源代码如下:

hello.c代码:

 1 /******************************
 2
 3 the first program
 4
 5 Hello World!
 6
 7 ******************************/
 8
 9
10
11 #include <linux/module.h>
12
13 #include <linux/init.h>
14
15
16
17 //module param list
18
19 static char *name = "LoverXue";
20
21 static int age = 1;
22
23
24
25 static int hello_init(void)
26
27 {
28
29     printk("<0>\nHello, %s\nI know you age is%d\n\n",name,age);
30
31     return 0;
32
33 }
34
35
36
37 static void hello_exit(void)
38
39 {
40
41     printk("<0>\nGoodbye,%s\n\n",name);
42
43 }
44
45
46
47 module_init(hello_init);
48
49 module_exit(hello_exit);
50
51 MODULE_LICENSE("Dual BSD/GPL"); //Any version of public GNU license
52
53
54
55 module_param(name,charp,S_IRUGO);
56
57 module_param(age,int,S_IRUGO);

三、改进helloword程序,增加模块之间的依赖

此时,我们的目的是:写两个模块,模块二调用模块一中的函数。

先附上代码,我们看着代码来讲解:

模块一:包含 add_sub.c add_sub.h Makefile 三个文件

add_sub.h

1 #ifndef _ADD_SUB_H_
2
3 #define _ADD_SUB_H_
4
5 long add_integer(long a,long b);//function add
6
7 long sub_integer(long a,long b);//function sub
8
9 #endif

add_sub.h 中定义声明了模块二中要调用的函数,函数具体实现,看add_sub.c

add_sub.c

 1 /*************************************
 2
 3 add_sub function
 4
 5 *************************************/
 6
 7 #include <linux/init.h>
 8
 9 #include <linux/module.h>
10
11
12
13 #include "add_sub.h" //包含函数声明的头文件
14
15
16
17 //returen sum of the two //实现函数
18
19 long add_integer(long a,long b)
20
21 {
22
23     return a+b;
24
25 }
26
27
28
29 //returen subtraction of the two
30
31 long sub_integer(long a,long b)
32
33 {
34
35     return a-b;
36
37 }
38
39 //为了让其他模块可以调用此函数,
40
41 //必须用EXPORT_SYMBOL将函数导出到内核
42
43 //export the function
44
45 EXPORT_SYMBOL(add_integer); /
46
47 EXPORT_SYMBOL(sub_integer);
48
49
50
51 MODULE_LICENSE("Dual BSD/GPL");

Makefile代码:

 1 ifeq ($(KERNELRELEASE),)
 2
 3 KERNELDIR ?= /home/study/system/linux-2.6.31
 4
 5 PWD := $(shell pwd)
 6
 7 #################################################
 8
 9 PRINT_INC = $(PWD)/
10
11 EXTRA_CFLAGS += -I $(PRINT_INC)
12
13 #################################################
14
15 modules:
16
17 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
18
19 modules_install:
20
21 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
22
23 clean:
24
25 rm -rf *.o *~ core .depend *.cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers
26
27
28
29 else
30
31 obj-m := add_sub.o
32
33 endif

修改前面的代码,必须增加#号内的代码。

此时,我们模块一全部写完。

make 编译。

模块二:包含hello.c Makefile

hello.c代码:

 1 /******************************
 2
 3 the first program
 4 Hello World!
 5 ******************************/
 6 #include <linux/module.h>
 7 #include <linux/init.h>
 8
 9 //extern module
10 #include "./add/add_sub.h" //包含头文件
11
12 //module param list
13 static char *name = "LoverXue";
14 static int age = 20;
15 static long num1 = 1;
16 static long num2 = 0;
17 static short flag = 1;
18
19 static int hello_init(void)
20 {
21     printk("<0>\nHello, %s\nI know you age is%d\n\n",name,age);
22     if(flag == 1 ){
23         printk("<0>sum of two %ld + %ld = %ld \n", num1,num2,add_integer(num1,num2));
24
25     }else{
26         printk("<0>subtraction of two %ld - %ld = %ld \n", num1,num2,sub_integer(num1,num2));
27     }
28     return 0;
29 }
30
31 static void hello_exit(void)
32 {
33     printk("<0>\nGoodbye,%s\n\n",name);
34 }
35
36 module_init(hello_init);
37 module_exit(hello_exit);
38 MODULE_LICENSE("Dual BSD/GPL"); //Any version of public GNU license
39 MODULE_VERSION("V1.0");
40
41 module_param(name,charp,S_IRUGO);
42 module_param(age,int,S_IRUGO);
43 module_param(num1,long,S_IRUGO);
44 module_param(num2,long,S_IRUGO);
45 module_param(flag,short,S_IRUGO);        

Makefile文件代码:

 1 ifeq ($(KERNELRELEASE),)
 2
 3 KERNELDIR ?= /home/study/system/linux-2.6.31
 4
 5 PWD := $(shell pwd)
 6
 7 ##########################################################
 8
 9 export-objs := /home/study/EasyARM-iMX257/module_study/02_add/add/add_sub.o
10
11 SYMBOL_INC = $(obj)/
12
13 SYMBOL_INC += /home/study/EasyARM-iMX257/module_study/02_add/add
14
15 EXTRA_CFLAGS += -I $(SYMBOL_INC)
16
17 KBUILD_EXTRA_SYMBOLS = /home/study/EasyARM-iMX257/module_study/02_add/add/Module.symvers
18
19 #########################################################
20
21 modules:
22
23 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
24
25 modules_install:
26
27 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
28
29 clean:
30
31 rm -rf *.o *~ core .depend *.cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers
32
33 else
34
35 obj-m := hello.o
36
37 endif

KBUILD_EXTRA_SYMBOLS的意思就是包含我们要调用的模块的Module.symvers,

此处最好使用绝对路径。

编译,

如果编译出错,如图所示。

解决方法是;先编译模块一,然后将模块一的Module.symvers拷贝到我们的模块二的目录下,再编译模块二。

如图所示就不会再出这样的错误了。

加载模块

接着就是在开发板中加载模块

insmod ./add/add_sub.ko

insmod hello.ko num1=50 num2=20 flag=0

结果如下:

四、将驱动使用静态编译入内核

接下来,我们来将刚刚写的代码使用静态编译,加入内核

  1. :我们进入内核的driver目录,增加目录add_sub_Kconfig
mkdir add_sub_Kconfig

  1. :将我们刚刚写的hello.c add_sub.c add_sub.h 三个文件拷贝入此文件夹

  1. :在此文件夹中增加Makefile,Kconfig两个文件,代码如下:

Makefile代码

#drivers/add_sub_Kconfig/Makefile

# Makefile for the ADD_SUB core.

#

obj-$(CONFIG_ADD_SUB) := add_sub.o

obj-$(CONFIG_TEST) := hello.o

Kconfig代码如下:

#drivers/add_sub_Kconfig/Kconfig

menu "ADD_SUB" #main menu //增加主菜单

comment "ADD_SUB"

//增加子菜单

config CONFIG_ADD_SUB #children menu, add the add_sub module

boolean "ADD_SUB support"

default y

#children menu ,add the test menu

config CONFIG_TEST

tristate "ADD_SUB test support"

depends on CONFIG_ADD_SUB #Depends on the config CONFIG_ADD_SUB

default y

endmenu
  1. :修改driver下的Makefile,Kconfig 文件代码:

Makefile中增加:

obj-$(ADD_SUB) += add_sub_Kconfig/

将上面我们自己写的makefile文件包含进来:

Kconfig中末尾增加以下代码:

将上面我们自己写的Kconfig文件包含进来

source "drivers/add_sub_Kconfig/Kconfig"

source "drivers/test//Kconfig"

  1. :修改arch/arm/ 目录下的 Makefile Kconfig

(实验证明只对,只对drivers/Kconfig中修改内容无效,drivers/Kconfig中修改内容无效,还要对arch/arm/Kconfig进行修改,很重要的一步,很多资料上都遗漏了这一步)

在Kconfig中加入代码:

source "drivers/add_sub_Kconfig/Kconfig"

  1. :配置编译

在内核的根目录下执行,make menuconfig 如图所示:

进入Device Drivers 目录

在最后面,我们看到了,我们自定义的模块:ADD_SUB模块

进入ADD_SUB,我们会看到我们写的两个模块全都默认选中了

此时,我们再次编译内核,就会把我们的模板使用静态编译进内核。

时间: 2024-08-01 22:44:54

Linux内核驱动编程的相关文章

Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2G 8. 选择网络地址转换 9.设置IO控制器 10. 选择默认磁盘类型 11.创建一个新的虚拟磁盘 12.设置磁盘大小 13.选择路径保存虚拟磁盘 14. 完成虚拟机创建 15.设置虚拟机 16.选择opensuse镜像 17.开启虚拟机 18.虚拟机启动 19.安装opensuse 20.安装程

Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" /> 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. watermark/2/text/a

你为什么看不懂Linux内核驱动源码?

学习嵌入式Linux驱动开发,最核心的技能就是能够编写Linux内核驱动.深入理解Linux内核.而做到这一步的基础,就是你要看得懂Linux内核源码,了解其基本的框架和具体实现,了解其内核API的使用方法,然后才能根据自己的需求写出高质量的内核驱动程序. 说易行难,很多新人.甚至工作1-2年的开发者刚接触Linux内核时,别说写了,看内核代码可能都是一脸懵逼:明明是C语言,但是就是看不懂是什么意思,除了根据函数名.函数参数.函数的返回值以及注释,了解整个函数的基本功能外,一旦分析其细节,你会发

第02节:Linux 内核驱动中的指定初始化

2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: int a[10] = {0,1,2,3,4,5,6,7,8}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a[9] 赋值,所以编译器会将 a[9] 默认设置为0.当数组长度比较小时,使用这种方式初始化比较方便.当数组比较大,而且数组里的非零元素并不连续时,这时候再按照固定顺序初始化就比较麻烦了. 比如,我们定义一个数组 b[100],其中 b[10].b[30] 需要初始

linux 内核驱动--Platform Device和Platform_driver注册过程

linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Platform_driver . Linux 中大部分的设备驱动,都可以使用这套机制 , 设备用 Platform_device 表示,驱动用 Platform_driver 进行注册. Linux platform driver 机制和传统的 device driver 机制 ( 通过 driver_

Linux内核驱动注册方式泛谈

Linux驱动注册有多种方式,通常是以内核提供的表征数据结构封装后按照内核子系统提供的接口函数进行注册,还有一些是比较复杂的以链表方式进行维护.以下对几种驱动注册方式进行介绍: 一.子系统有专门的驱动注册函数: 例如RTC子系统,提供rtc_device_register注册接口函数. 例如: rtc_device_register(client->name,&client->dev, &rx8025_rtc_ops, THIS_MODULE); static struct r

linux内核驱动模型

linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构成内核对象集,用kset表示,内核对象集也包含自己的内核对象,从而组成层次化的结构.2.用sysfs文件系统导出到用户空间.内核中的所有内核对象组织成树状,以对象属性为叶子.通过sysfs文件系统,将用户空间对文件的读写操作转化为对内核对象属性的显示和保存方法.从而导出内核对象信息,并提供配置接口.

linux内核驱动学习指南

目录: 1.参考链接 1. 参考链接 小白的博客 ONE_Tech 你为什么看不懂Linux内核驱动源码? 求教怎么学习linux内核驱动 原文地址:https://www.cnblogs.com/agui125/p/10071452.html

Linux内核驱动定时微秒级别实现

Linux内核驱动定时微秒级别实现 #include <linux/module.h> #include <linux/kthread.h> #define TIMEOUT_HR 1000000 /* 1us */ static struct hrtimer etx_hr_timer; ktime_t ktime; enum hrtimer_restart hrtimer_callback(struct hrtimer *timer) { static int count; pri