linux 模块驱动,linux内核模块编译

如何编译一个linux下的驱动模块

本文记录我的第一个Linux设备驱动程序的编译过程。遇到问题的解决方法。

环境:2.4.18-14的内核,Linux内核源码:2.4.18。

Linux内核源码路径:/usr/src/linux(这个源码是从kernel.org网站download的2.4.18版本)

按照《linux设备驱动开发详解》一书中的步骤实现经典例子"hello,world!"的例子。

具体步骤如下:

=============================================

1.源码如下:

/*

* hello.c-- the example of printf"hello world!" in the screen of driver program

*/

#include<linux/init.h>

#include<linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");/* declare the license of the module,it is necessary*/

static int hello_init(void)

{

printk(KERN_ALERT"Hello World enter!\n");

return 0;

}

static int hello_exit(void)

{

printk(KERN_ALERT"Hello world exit!\n");

}

module_init(hello_init);/* load the module*/

module_exit(hello_exit);/* unload the module*/

进入目录:

[root@Alex_linux/]#cd/work/jiakun_test/moduletest

[root@Alex_linux moduletest]# vi hello.c

然后拷入上面书上的源码。

2.编译代码:

1>.首先我在2.4内核的虚拟机上进行编译,编译过程如下:

[root@Alex_linux moduletest]#gcc-D__KERNEL__-I/usr/src/linux-DMODULE-Wall-O2-c-o hello.o hello.c

其中-I选项指定内河源码,也就是内核源码树路径。编译结果:

hello.c:1:22: net/sock.h: No such file or directory

hello.c: In function `hello_init':

hello.c:6: warning: implicit declaration of function `printk'

hello.c:6: `KERN_ALERT' undeclared(first use in this function)

hello.c:6:(Each undeclared identifier is reported only once

hello.c:6: for each function it appears in.)

hello.c:6: parse error before string constant

hello.c: In function `hello_exit':

hello.c:11: `KERN_ALERT' undeclared(first use in this function)

hello.c:11: parse error before string constant

hello.c: At top level:

hello.c:13: warning: type defaults to `int' in declaration of `module_init'

hello.c:13: warning: parameter names(without types) in function declaration

hello.c:13: warning: data definition has no type or storage class

hello.c:14: warning: type defaults to `int' in declaration of `module_exit'

hello.c:14: warning: parameter names(without types) in function declaration

hello.c:14: warning: data definition has no type or storage class

在网上查询有网友提示没有引入kernel.h

解决:vi hello.c

在第一行加入:#include<linux/kernel.h>

再次编译仍然报KERN_ALERT没有声明

修改编译条件-I,再次编译:

[root@Alex_linux moduletest]#gcc-D__KERNEL__-I/usr/src/linux-DMODULE-Wall-O2-c-o hello.o hello.c

[root@Alex_linux moduletest]#ls

hello.c hello.o Makefile

[root@Alex_linux moduletest]#

2>.接着我尝试在2.6内核的虚拟机上进行编译

编译过程如下:

[root@JiaKun moduletest]# ls

hello.c makefile

[root@JiaKun moduletest]# vi hello.c

[root@JiaKun moduletest]# make

make-C/mylinux/kernel/2.4.18-rmk7 M=/home/alex/test/moduletest modules

make:***/mylinux/kernel/2.4.18-rmk7: No such file or directory. Stop.

make:*** [modules] Error 2

[root@JiaKun moduletest]# vi makefile

[root@JiaKun moduletest]# make

make-C/usr/src/kernels/2.6.18-53.el5-i686 M=/home/alex/test/moduletest modules

make[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686'

scripts/Makefile.build:17:/home/alex/test/moduletest/Makefile: No such file or directory

make[2]:*** No rule to make target `/home/alex/test/moduletest/Makefile'. Stop.

make[1]:*** [_module_/home/alex/test/moduletest] Error 2

make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'

make:*** [modules] Error 2

[root@JiaKun moduletest]# mv makefile Makefile

[root@JiaKun moduletest]# make

make-C/usr/src/kernels/2.6.18-53.el5-i686 M=/home/alex/test/moduletest modules

make[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686'

CC [M]/home/alex/test/moduletest/hello.o

Building modules, stage 2.

MODPOST

CC/home/alex/test/moduletest/hello.mod.o

LD [M]/home/alex/test/moduletest/hello.ko

make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'

[root@JiaKun moduletest]# ls

hello.c hello.ko hello.mod.c hello.mod.o hello.o Makefile Module.symvers

3.执行代码,加载驱动模块:

2.4内核加载模块:

insmod./hello.o

但是此时并没有输出printk打印的信息。但是可以在/var/log/messages中看到打印的信息,这是由于KERN_ALERT优先级不够高。这里

需要修改为:KERN_EMERG。再次编译,加载模块即可以看到结果

2.6内核加载模块:

[root@JiaKun moduletest]# insmod hello.ko

[root@JiaKun moduletest]#

Message from syslogd@ at Sat Jul 26 19:52:44 2008...

JiaKun kernel: Hello, world

有的朋友可能会出现insmod命令找不到的错误,这可能有下面几个原因:

<1>你的系统没有安装module-init-tools工具,关于此问题,只需安装即可,但是一般装完系统是有这个命令的。

<2>环境变量没有添加导致不能使用该命令。使用echo$PATH即可查看PATH环境变量,发现没有/sbin这个路径,所以你当然不能使用insmod这个命令了。解决的方法很简单,只需在命令行输入:

PATH="$PATH:/sbin"即可添加。(insmod在/sbin这个目录下,你可以使用whereis insmod查看)。

<3> insmod这个命令需要在root权限下才能使用。

加载完成后你可以输入lsmod查看hello这个模块哦。

4.卸载驱动模块:rmmod hello.

加载模块后就可在屏幕上看到如下信息:Hello world enter.

卸载时就可在屏幕上看到如下信息:hello world exit.

[root@JiaKun moduletest]# rmmod hello.ko

[root@JiaKun moduletest]#

Message from syslogd@ at Sat Jul 26 19:52:58 2008...

JiaKun kernel: Goodbye, cruel world

另外,如果有多个文件,则按下列方式编写Makefile文件(file1.c、file2.c):

obj-m:= modulename.o

module-objs:= file1.o file2.o

linux系统中没有USB驱动怎么办

要启用 Linux USB支持,首先进入"USB support"节并启用"Support for USB"选项(对应模块为usbcore.o)。尽管这个步骤相当直观明了,但接下来的 Linux USB设置步骤则会让人感到糊涂。特别地,现在需要选择用于系统的正确 USB主控制器驱动程序。选项是"EHCI"(对应模块为ehci-hcd.o)、"UHCI"(对应模块为usb-uhci.o)、"UHCI(alternate driver)"和"OHCI"(对应模块为usb-ohci.o)。这是许多人对 Linux的 USB开始感到困惑的地方。

要理解"EHCI"及其同类是什么,首先要知道每块支持插入 USB设备的主板或 PCI卡都需要有 USB主控制器芯片组。这个特别的芯片组与插入系统的 USB设备进行相互操作,并负责处理允许 USB设备与系统其它部分通信所必需的所有低层次细节。

Linux USB驱动程序有三种不同的 USB主控制器选项是因为在主板和 PCI卡上有三种不同类型的 USB芯片。"EHCI"驱动程序设计成为实现新的高速 USB 2.0协议的芯片提供支持。"OHCI"驱动程序用来为非 PC系统上的(以及带有 SiS和 ALi芯片组的 PC主板上的)USB芯片提供支持。"UHCI"驱动程序用来为大多数其它 PC主板(包括 Intel和 Via)上的 USB实现提供支持。只需选择与希望启用的 USB支持的类型对应的"?HCI"驱动程序即可。如有疑惑,为保险起见,可以启用"EHCI"、"UHCI"(两者中任选一种,它们之间没有明显的区别)和"OHCI"。(赵明注:根据文档,EHCI已经包含了UHCI和OHCI,但目前就我个人的测试,单独加EHCI是不行的,通常我的做法是根据主板类型加载UHCI或OHCI后,再加载EHCI这样才可以支持USB2.0设备)。

启用了"USB support"和适当的"?HCI"USB主控制器驱动程序后,使 USB启动并运行只需再进行几个步骤。应该启用"Preliminary USB device filesystem",然后确保启用所有特定于将与 Linux一起使用的实际 USB外围设备的驱动程序。例如,为了启用对 USB游戏控制器的支持,我启用了"USB Human Interface Device(full HID) support"。我还启用了主"Input core support"节下的"Input core support"和"Joystick support"。

一旦用新的已启用 USB的内核重新引导后,若/proc/bus/usb下没有相应USB设备信息,应输入以下命令将 USB设备文件系统手动挂装到/proc/bus/usb:

# mount-t usbdevfs none/proc/bus/usb

为了在系统引导时自动挂装 USB设备文件系统,请将下面一行添加到/etc/fstab中的/proc挂装行之后:

none/proc/bus/usb usbdevfs defaults 0 0

模块的配置方法.

在很多时候,我们的USB设备驱动并不包含在内核中。其实我们只要根据它所需要使用的模块,逐一加载。就可以使它启作用。

首先要确保在内核编译时以模块方式选择了相应支持。这样我们就应该可以在/lib/modules/2.4.XX目录看到相应.o文件。在加载模块时,我们只需要运行modprobe xxx.o就可以了(modprobe主要加载系统已经通过depmod登记过的模块,insmod一般是针对具体.o文件进行加载)

对应USB设备下面一些模块是关键的。

usbcore.o要支持usb所需要的最基础模块usb-uhci.o(已经提过)usb-ohci.o(已经提过)uhci.o另一个uhci驱动程序,我也不知道有什么用,一般不要加载,会死机的ehci-hcd.o(已经提过 usb2.0)hid.oUSB人机界面设备,像鼠标呀、键盘呀都需要usb-storage.oUSB存储设备,U盘等用到

相关模块

ide-disk.oIDE硬盘ide-scsi.o把IDE设备模拟SCSI接口scsi_mod.oSCSI支持

注意kernel config其中一项:

Probe all LUNs on each SCSI device

最好选上,要不某些同时支持多个口的读卡器只能显示一个。若模块方式就要带参数安装或提前在/etc/modules.conf中加入以下项,来支持多个LUN。

add options scsi_mod max_scsi_luns=9

sd_mod.oSCSI硬盘sr_mod.oSCSI光盘sg.oSCSI通用支持(在某些探测U盘、SCSI探测中会用到)

常见USB设备及其配置

在Linux 2.4的内核中已经支持不下20种设备。它支持几乎所有的通用设备如键盘、鼠标、modem、打印机等,并不断地添加厂商新的设备象数码相机、MP3、网卡等。下面就是几个最常见设备的介绍和使用方法:

USB鼠标:

键盘和鼠标属于低速的输入设备,对于已经为用户认可的PS/2接口,USB键盘和USB鼠标似乎并没有太多更优越的地方。现在的大部分鼠标采用了PS/2接口,不过USB接口的鼠标也越来越多,两者相比,各有优势:一般来说,USB的鼠标接口的带宽大于PS/2鼠标,也就是说在同样的时间内,USB鼠标扫描次数就要多于PS/2鼠标,这样在定位上USB鼠标就更为精确;同时USB接口鼠标的默认采样率也比较高,达到125HZ,而PS/2接口的鼠标仅有40HZ(Windows 9x/Me)或是60HZ(Windows NT/2000)。

对于USB设备你当然必须先插入相应的USB控制器模块:usb-uhci.o或usb-ohci.o

modprobe usb-uhci

USB鼠标为了使其正常工作,您必须先插入模块usbmouse.o和mousedev.o

modprobe usbmouse

modprobe mousedev

若你把HID input layer支持和input core支持也作为模块方式安装,那么启动hid模块和input模块也是必要的。

modprobe hid

modprobe input

USB键盘:

一般的,我们现在使用的键盘大多是PS/2的,USB键盘还比较少见,但是下来的发展,键盘将向USB接口靠拢。使用USB键盘基本上没有太多的要求,只需在主板的BIOS设定对USB键盘的支持,就可以在各系统中完全无障碍的使用,而且更可以真正做到在即插即用和热插拔使用,并能提供两个USB连接埠:让您可以轻易地直接将具有USB接头的装置接在您的键盘上,而非计算机的后面。

同样你当然必须先插入相应的USB控制器模块:usb-uhci.o或usb-ohci.o

modprobe usb-uhci

然后您还必须插入键盘模块usbkbd.o,以及keybdev.o,这样usb键盘才能够正常工作。此时,运行的系统命令:

modprobe usbkbd

modprobe keybdev

同样若你把HID input layer支持和input core支持也作为模块方式安装,那么启动hid模块和input模块也是必要的。

U盘和USB读卡器:

数码存储设备现在对我们来说已经是相当普遍的了。CF卡、SD卡、Memory Stick等存储卡已经遍及我们的身边,通常,他们的读卡器都是USB接口的。另外,很多MP3、数码相机也都是USB接口和计算机进行数据传递。更我们的U盘、USB硬盘,作为移动存储设备,已经成为我们的必须装备。

在Linux下这些设备通常都是以一种叫做usb-storage的方式进行驱动。要使用他们必须加载此模块

modprobe usb-storage

当然,usbcore.o和usb-uhci.o或usb-ohci也肯定是不可缺少的。另外,若你系统中SCSI支持也是模块方式,那么下面的模块也要加载

modprobe scsi_mod

modprobe sd_mod

在加载完这些模块后,我们插入U盘或存储卡,就会发现系统中多了一个SCSI硬盘,通过正确地mount它,就可以使用了(SCSI硬盘一般为/dev/sd?,可参照文章后面的常见问题解答)。

mount/dev/sda1/mnt

Linux支持的其他USB设备。

MODEM--(比较常见)网络设备摄像头--(比较常见)例如ov511.o联机线--可以让你的两台电脑用USB线实现网络功能。usbnet.o显示器--(我没见过)游戏杆电视盒--(比较常见)手写板--(比较常见)扫描仪--(比较常见)刻录机--(比较常见)打印机--(比较常见)

注意:

上面所说的每个驱动模块,并不是都要手动加载,有很多系统会在启动或你的应用需要时自动加载的,写明这些模块,是便于你在不能够使用USB设备时,可以自行检查。只要用lsmod确保以上模块已经被系统加载,你的设备就应该可以正常工作了。当然注意有些模块已经以内核方式在kernel启动时存在了(这些模块文件在/lib/modules/2.4.XX中是找不到的)。

输入相关命令

linux所有驱动都可以编译成模块吗

linux下编译运行驱动

嵌入式linux下设备驱动的运行和linux x86 pc下运行设备驱动是类似的,由于手头没有嵌入式linux设备,先在vmware上的linux上学习驱动开发。

按照如下方法就可以成功编译出hello world模块驱动。

1、首先确定本机linux版本

怎么查看Linux的内核kernel版本?

'uname'是Linux/unix系统中用来查看系统信息的命令,适用于所有Linux发行版。配合使用'uname'参数可以查看当前服务器内核运行的各个状态。

#uname-a

Linux whh 3.5.0-19-generic#30-Ubuntu SMPTue Nov 13 17:49:53 UTC 2012 i686 i686 i686 GNU/Linux

只打印内核版本,以及主要和次要版本:

#uname-r

3.5.0-19-generic

要打印系统的体系架构类型,即的机器是32位还是64位,使用:

#uname-p

i686

/proc/version文件也包含系统内核信息:

# cat/proc/version

Linux version 3.5.0-19-generic(buildd@aatxe)(gcc version 4.7.2(Ubuntu/Linaro 4.7.2-2ubuntu1))#30-UbuntuSMP Tue Nov 13 17:49:53 UTC 2012

发现自己的机器linux版本是:3.5.0-19-generic

2、下载机器内核对应linux源码

到下面网站可以下载各个版本linux源码

如我的机器3.5.0版本源码下载地址为:

下载完后,找一个路径解压,如我解压到/linux-3.5/

然后很重要的一步是:执行命令uname-r,可以看到Ubuntu的版本信息是3.5.0-19-generic

。进入linux源码目录,编辑Makefile,将EXTRAVERSION=修改为EXTRAVERSION=-19-generic。

这些都是要配置源码的版本号与系统版本号,如果源码版本号和系统版本号不一致,在加载模块的时候会出现如下错误:insmod: error inserting'hello.ko':-1 Invalid module format。

原因很明确:编译时用的hello.ko的kenerl不是我的pc的kenerl版本。

执行命令cp/boot/config-3.5.0-19-generic./config,覆盖原有配置文件。

进入linux源码目录,执行make menuconfig配置内核,执行make编译内核。

3、写一个最简单的linux驱动代码hello.c

/*======================================================================

Asimple kernel module:"hello world"

======================================================================*/

#include<linux/init.h>

#include<linux/module.h>

MODULE_LICENSE("zeroboundaryBSD/GPL");

static int hello_init(void)

{

printk(KERN_INFO"Hello World enter\n");

return0;

}

static void hello_exit(void)

{

printk(KERN_INFO"Hello World exit\n");

}

module_init(hello_init);

module_exit(hello_exit);

MODULE_AUTHOR("zeroboundary");

MODULE_DESCRIPTION("A simple HelloWorld Module");

MODULE_ALIAS("a simplestmodule");

4、写一个Makefile对源码进行编译

KERN_DIR=/linux-3.5

all:

make-C$(KERN_DIR) M=`pwd` modules

clean:

make-C$(KERN_DIR) M=`pwd` clean

obj-m+= hello.o

5、模块加载卸载测试

insmod hello.ko

rmmod hello.ko

然后dmesg|tail就可以看见结果了

最后,再次编译驱动程序hello.c得到hello.ko。执行insmod./hello.ko,即可正确insert模块。

使用insmod hello.ko将该Module加入内核中。在这里需要注意的是要用 su命令切换到root用户,否则会显示如下的错误:insmod: error inserting'hello.ko':-1 Operation not permitted

内核模块版本信息的命令为modinfo hello.ko

通过lsmod命令可以查看驱动是否成功加载到内核中

通过insmod命令加载刚编译成功的time.ko模块后,似乎系统没有反应,也没看到打印信息。而事实上,内核模块的打印信息一般不会打印在终端上。驱动的打印都在内核日志中,我们可以使用dmesg命令查看内核日志信息。dmesg|tail

可能还会遇到这种问题insmod: error inserting'hello.ko':-1 Invalid module format

用dmesg|tail查看内核日志详细错误

disagrees about version of symbolmodule_layout,详细看这里。

在X86上我的办法是:

make-C/usr/src/linux-headers-3.5.0-19-generic SUBDIRS=$PWD modules

阅读剩余
THE END