-
2021-05-15 10:36:27
Linux驱动程序编写演示----编译驱动程序
1驱动编写:
#include #include static int __init test_driver_init(void)
{
/* register this driver */
printk("Hello test_driver , init this driver");
return 0;
}
static void __exit test_driver_exit(void)
{
/* deregister this driver*/
printk("Good bye test_driver, exit this driver");
}
module_init(test_driver_init);
module_exit(test_driver_exit);
MODULE_LICENSE("GPL");
编译驱动程序
编译驱动程序,有两种方法,一是在代码目录下,进行单独编译;二是加入内核编译的菜单里。针对我们所写的驱动程序test_driver,分别来使用两种方法进行编译。
2、单独编译驱动文件
当内核已经稳定的运行后,以模块的方式运行驱动时,可以采用单独编译驱动的方式。
需要自己动手写Makefile文件,内容如下:
obj-m := test_driver.o #告诉make在编译的时候,编译成模块,得到test_driver.ko
KERNELDIR := /usr/local/arm/kernel/linux-2.6.30 #把源码的路径设置好
defualt:
$(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules #make不带参数时,运行这里
install:
insmod test_driver.ko
uninstall:
rmmod test_driver.ko
clean:
$(MAKE) -C $(KERNELDIR) M=$(shell pwd) clean
这个Makefile文件很简单,保存后,回到命令行,直接键入make回车,就可以编译了。编译完成,得到test_driver.ko文件,和源码是一个路径(这是用M=$(shell pwd)指定的。
错误:
make -C /usr/local/arm/kernel/linux-2.6.30 M=/home/driver_test modules
make[1]: Entering directory `/usr/local/arm/kernel/linux-2.6.30'
CC [M] /home/driver_test/test_driver.o
In file included from include/linux/gfp.h:4,
from include/linux/kmod.h:22,
from include/linux/module.h:13,
from /home/driver_test/test_driver.c:2:
include/linux/mmzone.h:18:26: error: linux/bounds.h: No such file or directory
include/linux/mmzone.h:256:5: warning: "MAX_NR_ZONES" is not defined
In file included from include/linux/gfp.h:4,
from include/linux/kmod.h:22,
from include/linux/module.h:13,
from /home/driver_test/test_driver.c:2:
include/linux/mmzone.h:290: error: 'MAX_NR_ZONES' undeclared here (not in a function)
make[2]: *** [/home/driver_test/test_driver.o] 错误 1
make[1]: *** [_module_/home/driver_test] 错误 2
make[1]: Leaving directory `/usr/local/arm/kernel/linux-2.6.30'
make: *** [defualt] 错误 2
解决方法:
在/usr/local/arm/kernel/linux-2.6.30目录下执行
# make prepare
驱动文件与内核一起编译
1、加入到menuconfig中编译
这个方式,是编译内核时,在源码顶层目录下运行make menuconfig命令时,在菜单中可以看到我们自己的驱动程序,
这样可以采取与内核及其他驱动程序统一进行编译。采用以下几个步骤来完成:
(1)修改我们自己的Makefile文件
obj-m := test_driver.o
obj-$(CONFIG_TEST_DRIVER) +=test_driver.o
在原来的基础上,只保留上面两行内容。
(2)增加一个Kconfig文件
这个文件在我们自己的源码路径下创建,内容如下:
config TEST_DRIVER
tristate " Test Driver for Linux"
default n
help
This item is just for linux driver demo and
test linux menuconfig, do nothing
简单说明:
config是Kconfig文件语言的关键字,TEST_DRIVER是我们自己定义的变量,它的值是y,n,M三个值中的一个。我们上面缺省的语句,default n,就是默认情况下,CONFIG_TEST_DRIVER=n,前缀CONFIG_,这是系统自己加上的。
tristate是关键字,其后面是我们自己写的菜单上出现的提示。整句的含义是," Test Driver for Linux "这个选择项,是3态的,即:y,n,M。
(3)修改arch/arm下的Kconfig文件
经过前面2个步骤的修改,内核配置菜单,还是找不到我们的代码路径,还需要在架构路径下进行添加,方法是在endmenu之前,加入:source "/home/driver_test/kconfig",这样做之后,架构下arm的Kconfig就可以找到我们自己的Kconfig了。类似这样:
#this is test_driver
source "/home/driver_test/Kconfig"
(4)修改drivers/Makefile文件
Kconfig文件相当于给Makefile文件提供一个目录上路由功能,具体的编译工作,还是必须由Makefile来指定规则的。该Makefile文件上添加一句:
#this is driver_test
obj-$(CONFIG_TEST_DRIVER) +=/home/driver_test
经过上面四个步骤的修改,我们的驱动程序代码就加入了内核统一编译的菜单中去了。如图:
配置完成后,命令行运行make开始编译,之后可以在我们的源码路径下找到编译成功的test_driver.ko文件。
更多相关内容 -
以内核模块加载的linux驱动程序编写
2021-01-19 17:53:11这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载。Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难。为了解决这个问题引入了内核机制。 ... -
如何在嵌入式LINUX中添加设备驱动程序
2020-08-04 01:27:39下面介绍的是在linux环境中添加设备驱动程序的方法以及实例介绍。 -
linux 添加字符设备驱动程序及测试程序.zip
2022-01-07 16:02:26linux 添加字符设备驱动程序及测试程序,linux下采用模块方法,添加一个新的设备驱动程序。要求添加字符设备的驱动。另附一个应用程序,测试添加的驱动程序. int main(void) { int fd; char buf[MAX_SIZE]; char ... -
ioctl_driver:有关如何编写Linux驱动程序的示例
2021-05-13 10:33:09关于如何为Linux创建IOCTL驱动程序的简单示例。 这可以用于简单的测试目的: 从内核模式访问特殊寄存器以在用户模式下获得结果。 例如,CP15处理器的Arm寄存器。 访问某些内存映射区域 ... 此仓库包含以下文件夹... -
linux驱动加载 动态加载 静态加载 自动加载
2021-02-04 14:54:48在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载。 静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。动态加载利用了Linux的module特性,可以在系统启动后用insmod命令添加模块(.ko)...概述
在咱们工作中,编译驱动,加载驱动时最常见的事。但是内核是如何加载驱动的,有些事编译到内核里面,有些事编译成ko,有些还能放到root下,让系统自动加载。
总的说来,在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载。
静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。动态加载利用了Linux的module特性,可以在系统启动后用insmod命令添加模块(.ko),在不需要的时候用rmmod命令卸载模块。
驱动加载
静态加载过程
将模块的程序编译到Linux内核中,也就是咱们在编译内核时选择Y的模块,静态由do_initcall函数加载。先来看看initcall在哪里:
核心进程(/init/main.c)
start_kernel()//内核启动的入口,负责初始化调度,中断,内存,最后启动1号进程 和2号进程
rest_init()
kernel_init()//这是谁? linux 1号进程,top一下系统就能找到1号进程了。
do_basic_setup()
do_initcalls()
do_initcalls中会定义的各个模块加载顺序,加载顺序分为16个等级,加载时按照16个等级依次加载内核驱动。关于每个等级的定义参考/include/linux/init.h。举个例子,在2.6.24的内核 中:gianfar_device使用的是arch_initcall,而gianfar_driver使用的是module_init,因为 arch_initcall的优先级大于module_init,所以gianfar设备驱动的device先于driver在总线上添加。
动态加载过程
将模块的程序编译成KO,也就是咱们在编译内核时选择M的模块,通过insmod
、modprobe 或者udev动态加载到内核中。 insmod加绝对路径/××.ko,而modprobe ××即可,不用加.ko或.o后缀,也不用加路径;最重要的一点是:modprobe同时会加载当前模块所依赖的其它模块。
来看看insmod都做了什么:
Insmod
insmod_main
bb_init_module
init_module
sys_init_module(进入系统调用)
sys_init_module()系统调用会调用module_init指定的函数进行模块的初始化,至此ko加载完成。
驱动匹配
硬件固件设计
以PCI为例,所有PCI硬件上必须设计一系列寄存器,PCI通过这些寄存器获取硬件信息,称为PCI配置空间,PCI配置空间格式如下:
PCI标准规定每个设备的配置寄存器组最多可以有256字节的连续空间,其中开头的64字节的用途和格式是标准的,成为配置寄存器组的“头部”,这样的头部又有两种,“0型”头部用于一般的PCI设备,“1型”头部用于PCI桥,无论是“0型”还是“1型”,其开头的16个字节的用途和格式是共同的。其中Device ID 和 Vendor ID 位于前4个字节。
在系统中运行LSPCI -NN,会得到类似如下输出:
Ethernet controller [0200]: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) [8086:100f] (rev 01)
其中的8086是vendor ID,100f是Device ID。所以咱们常用的LSCPI实质上就是去硬件上读取硬件的寄存器值,并且显示出来。
软件驱动设计
每一个硬件设备都有Verdon ID, Device ID, SubVendor ID等信息。所以每一个设备驱动程序,必须说明自己能够为哪些Verdon ID, DevieceID, SubVendor ID的设备提供服务。以PCI设备为例,它是通过一个pci_device_id的数据结构来实现这个功能的。例如:RTL8139驱动的pci_device_id定义为:
Verdon ID可以理解为厂商ID,8086 代表Intel,1249代表三星等,Device ID可以理解为产品ID,每款产品的ID不同。上面的信息说明,Verdon ID为0x10EC, Device ID为0x8139, 0x8138的PCI设备(SubVendor ID和SubDeviceID为PCI_ANY_ID,表示不限制。),都可以使用这个驱动程序(8139too)。
下面是内核设备与驱动匹配的代码:
自动加载过程
自动加载属于动态加载,加载的是ko文件,许多同学也没搞清楚,系统启动之后ko在什么时候,通过什么程序自动加载上的。
构建自动加载环境
编译内核时,通过make modules_install INSTALL_MOD_PATH=XXX,会将所有选项为M的模块编译为KO, 并且发布到xxx/lib/modules/uname-r/下面。在模块安装的时候,depmod会根据模块中的rtl8139_pci_tbl的信息,生成下面的信息,保存到/lib/modules/uname-r/modules.alias文件中,其内容如下:
alias pci:v000010ECd00008138sv*sd*bc*sc*i* 8139too alias pci:v000010ECd00008139sv*sd*bc*sc*i* 8139too ......
v后面的000010EC说明其Vendor ID为10EC,d后面的00008138说明Device ID为8139,而sv,和sd为SubVendor ID和SubDevice ID,后面的星号表示任意匹配。
另外在/lib/modules/uname-r/modules.dep文件中还保存这模块之间的依赖关系,其内容如下:
8139too.ko:mii.ko
modules.dep由depmod工具生成,在使用 modprobe xxx加载驱动时, modprobe需要借助modules.dep文件来分析模块之间的依赖关系,先加载依赖的ko,再加载真正需要加载的ko。
PCI扫描自动加载驱动
在内核启动过程中,总线驱动程序会会总线协议进行总线枚举(总线驱动程序总是集成在内核之中,不能够按模块方式加载,你可以通过make menuconfig进入Busoptions,这里面的各种总线,你只能够选择Y或N,而不能选择M.),并且为每一个设备建立一个设备对象。每一个总线对象有一个kset对象,每一个设备对象嵌入了一个kobject对象,kobject连接在kset对象上,这样总线和总线之间,总线和设备设备之间就组织成一颗树状结构。当总线驱动程序为扫描到的设备建立设备对象时,会初始化kobject对象,并把它连接到设备树中,同时会调用kobject_uevent()把这个(添加新设备的)事件,以及相关信息(包括设备的VendorID,DeviceID等信息)通过netlink发送到用户态中。
来看看这个过程:
subsys_initcall (前面讲到过驱动的16级加载机制,subsys_initcall处于16级中的第8级,arch\x86\pci\legacy.c 参照内核4.19)
pci_legacy_init
pcibios_scan_root
pci_scan_root_bus
pci_scan_slot
pci_scan_single_device
pci_device_add
device_add
device_add有两个流程:
- bus_probe_device 匹配内核中已有驱动
- kobject_uevent 发消息到udev,让udev去/lib/modules/下面加载驱动
在用户态的udevd检测到这个事件,就可以根据这些信息,打开/lib/modules/uname-r/modules.alias文件,根据
alias pci:v000010ECd00008138sv*sd*bc*sc*i* 8139too
得知这个新扫描到的设备驱动模块为8139too。于是modprobe就知道要加载8139too这个模块了,同时modprobe根据 modules.dep文件发现,8139too依赖于mii.ko,如果mii.ko没有加载,modprobe就先加载mii.ko,接着再加载 8139too.ko。
-
嵌入式系统/ARM技术中的以内核模块加载的linux驱动程序编写
2020-10-21 22:05:14这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载。Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难。为了解决这个问题引入了内核机制。 ... -
Linux系统下PCI设备驱动程序的开发
2020-10-22 18:27:03摘要:以一个具体的PCI设备的驱动开发过程为基础,总结了与PCI设备驱动开发的相关问题,详细阐述了基本开发步骤、具体实现、驱动程序内核块的加载以及用户进程和驱动程序的协同工作问题。 1 Linux 系统下设备驱动... -
linux下静态/动态加载驱动的两种方式
2021-05-12 01:32:43在LINUX下加载驱动程序...动态加载利用了LINUX的module特性,可以在系统启动后用insmod命令把驱动程序(.o文件)添加上去,在不需要的时候用rmmod命令来卸载。在台式机上一般采用动态加载的方式。在嵌入式产品里可以...在LINUX下加载驱动程序可以采用动态和静态两种方式。
静态加载就是把驱动程序直接编译到内核里,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译下载内核,效率较低。
动态加载利用了LINUX的module特性,可以在系统启动后用insmod命令把驱动程序(.o文件)添加上去,在不需要的时候用rmmod命令来卸载。在台式机上一般采用动态加载的方式。
在嵌入式产品里可以先用动态加载的方式来调试,调试完毕后再编译到内核里。下面以我们的nHD板卡为例讲述一下加载驱动程序的方法。
假设我们需要添加一个名为mydrv的字符型设备驱动,主设备号为254,次设备号为0(只有一个从设备),静态加载的步骤如下:
1、编写自己的驱动程序源文件mydrv.c,并放在firmware\uClinux-Samsung-2500\linux-2.4.x\drivers\char下面。一个典型的字符型驱动的最后一般包括如下内容:
static int mydrv_init(void)
{
int ret;
ret = register_chrdev(mydrv_major, " mydrv ",
&my_fops);
if(ret == 0) printk("register_chrdev succeed!\n");
else printk("register_chrdev fail!\n");
return 0;
}
static __exit void mydrv _cleanup(void)
{
unregister_chrdev(mydrv _major, " mydrv ");
printk("register_chrdev succeed!\n");
return ;
}
module_init(mydrv _init);
module_exit (mydrv _cleanup);
函数mydrv_init的任务是注册设备,mydrv_cleanup的任务是取消注册。
Module_init和module_exit的作用后面会讲到。
2.在firmware\uClinux-Samsung-2500\vendors\Samsung\2500\Makefile中添加如下语句(以刚才的设备为例,实际添加时当然要根据你自己的设备名称和设备号来添加):
mknod $(ROMFSDIR) /dev/mydrv c 254 0
这句话的目的是在内核中创建一个与你的驱动程序对应的设备节点。
3.在firmware\uClinux-Samsung-2500\linux-2.4.x\drivers\char\Makefile
中添加如下语句:
obj-$(CONFIG_CHAR_MYDRV) +=mydrv.o
这句话的目的是根据编译选项$(CONFIG_CHAR_MYDRV)来决定是否要添加该设备驱动。
4.在firmware\uClinux-Samsung-2500\linux-2.4.x\drivers\char\config.in
中添加:
if [“$CONFIG_ARCH_SAMSUNG”=”y”]; then
tristate ' ,MYDRV driver module '
CONFIG_CHAR_MYDRV
这句话的目的是在运行make menuconfig时产生与你的设备对应的编译选项。
5.运行make menuconfgi,应该能看到你自己的设备的选项,选中就可以了。
6.编译内核,下载,运行自己的测试程序。
如果你觉得上述步骤比较麻烦,可以把4、5两条都省去,把第3条中的
obj-$(CONFIG_CHAR_MYDRV) +=mydrv.o
改为
obj-y +=mydrv.o
这样在menuconfig就没有与你的设备对应的选项了,编译内核时直接会把你的驱动编译进去。
还有一个问题需要说明一下。在…/drivers/char下有一个mem.c文件,其中最后有一个int __init
chr_dev_init(void)函数。大家可以看到,所有字符设备的初始化函数(IDE_INT_init之类)都要添加在这里,而我们刚才的驱动程序的初始化函数并没有添加到这里。这个问题涉及到系统启动时的do_initcall函数,详细讲述起来比较烦琐,大家有兴趣可以看一下《情景分析》下册P726~P729。这里简单介绍一下。如果对一个函数(通常都是一些初始化函数)作如下处理(仍以我们的mydrv_init函数为例):
__initcall(mydrv_init)
那么在编译内核时会生成一个指向mydrv_init的函数指针__initcall_mydrv_int,系统启动时,在运行do_initcall函数时,会依次执行这些初始化函数,并且会在初始化结束后把这些函数所占用的内存释放掉。
回到mem.c文件,在最后有一行:
__initcall(chr_dev_init)
这句话的作用就显而易见了,在系统启动时自动执行chr_dev_init函数。所以我们完全可以不用在mem.c/chr_dev_init中添加我们自己的初始化函数,而是在我们自己的设备文件中(mydrv.c)添加如下一行:
__initcall(mydrv_init).
在我们前面说到的我们自己的设备文件mydrv.c中,最后有一句:
module_init(mydrv _init);
大家可以看一下module_init的定义,在…linux.-2.4.x\include\linux\init.h中。如果定义了宏MODULE时,module_init是作为模块初始化函数,如果没有定义MODULE,则
module_init(fn)就被定义为__initcall(fn)。静态编译时是不定义MODULE的,所以我们的驱动中的module_init就等于是:
__initcall(mydrv_init).
这样我们的初始化函数就会在启动时被执行了。
至于究竟是在mem.c/chr_dev_init中添加你的设备初始化函数,还是在设备文件中通过module_init来完成,完全取决于你的喜好,没有任何差别。如果你的初始化函数只是注册一个设备(没有申请内存等操作),那即使你在两个地方都加上(等于初始化了两次)也没关系,不会出错(有兴趣可以看一下内核里注册设备的函数实现,
…linux-2.4.x\fs\devices.c\register_chrdev)。不过为了规范起见,还是不建议这样作。
最后一个问题。在静态加载驱动的时候,我们那个mydrv_cleanup和module_exit函数永远不会被执行,所以去掉是完全可以的,不过为了程序看起来结构清晰,也为了与动态加载的程序兼容,还是建议保留着。
下面讲一下动态加载驱动的方法。
1、运行make menuconfgi,在内核配置中进入Loadable module support,选择Enable
loadable module support和Kernel module
loader(NEW)两个选项。在应用程序配置中进入busybox,选择insmod, rmmod,
lsmod三个选项。
2、在…vendors\Samsung\2500\Makefile中添加相应的设备节点,方法与静态加载时完全一样。
3、编写自己的驱动程序文件,在文件开始处加一句:
#define MODULE
文件最后的
mydrv_init
mydrv_cleanup
module_init(mydrv _init)
module_exit (mydrv _cleanup)
这四项必须保留。
4、仿照如下的格式写自己的Makefile文件:
KERNELDIR=
/home/hexf/hardware/nHD/Design/firmware/uClinux-Samsung-2500/linux-2.4.x
CFLAGS = -D__KERNEL__ -I$(KERNELDIR)/include -Wall
-Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing
-fno-common -fno-common -pipe -fno-builtin -D__linux__ -DNO_MM
-mapcs-32 -mshort-load-bytes -msoft-float
CC = arm-elf-gcc
all: mydrv.o
clean:
rm -f *.o
5、编译自己的驱动程序文件。注意在动态加载时只编译不连接,所以得到的是.o文件。
6、把编译后的驱动程序的.o
文件,连同自己的测试程序(假设叫mytest,注意这个是可执行文件)一起放在编译服务器的/exports/自己的目录下。测试程序就是一个普通的应用程序,其编写和编译的步骤这里就不讲了。
7、启动nHD板卡,用nfs的方法把编译服务器上/exports/自己的目录mount上来(假设mount 到
/mnt下)。Nfs的使用大家都很熟悉了,这里就不再说。
8、
cd /mnt
/bin/insmod mydrv.o
现在你的设备就已经被动态加载到系统里了。可以用lsmod命令查看当前已挂接的模块。
9、运行你的测试程序
10、调试完毕后用 rmmod mydrv把你的设备卸载掉。
补充几点:
1、关于建立设备节点的问题,因为大家所使用的系统不太一样,所以不需要按照我说的方法。总之只要在你自己的系统的dev目录下建立了自己的驱动程序的设备节点就可以了。
2、没有考虑动态分配主设备号的问题。所以注册设备那个地方稍微有点不严密。
3、模块加载时要把自己的.c文件编译成.o文件,CFLAGS后面那一串编译选项有时可能有点烦人,如果你没搞定,最简单的办法就是重新编译一遍内核并重定向到一个文件中(别忘了先make
clean一下):make > out。
然后在out文件里随便找一个字符驱动程序的编译过程,把它的编译选项找出来,拷贝到你自己的Makefile里就可以了。我就是这么作的。
下面是一个最简单的字符设备驱动的例子。实际的驱动千差万别,但其实也就是“填充”自己的open,close,read,write,ioctl几个函数而已。
#ifndef __KERNEL__
#define __KERNEL__
#endif
#define MODULE
#define drvtest_major 254
#include
#include
#include
#include
#include // printk()
#include // kmalloc()
#include // error codes
#include // size_t
#include // mark_bh
#include
#include
#include
#include
#include
#include
#include
static int mytest_open(struct inode *inode,struct file
*filp)
{
MOD_INC_USE_COUNT;
printk("mytest open!\n");
return 0;
}
static ssize_t mytest_read(struct file *flip,char * buff,size_t
count,
loff_t * f_pos)
{
char buf[10] ={0x1,0x2,0x3,0x4,0x5};
memcpy(buff,buf,5);
return 5;
}
static int mytest_close(struct inode *inode,struct file
*filp)
{ MOD_DEC_USE_COUNT;
printk("mytest close!\n");
return 0;
}
static struct file_operations my_fops = {
read: mytest_read,
// write: mytest_write,
open: mytest_open,
release: mytest_close,
// ioctl: mytest_ioctl,
};
static int mytest_init(void)
{
int ret;
ret = register_chrdev(drvtest_major, "drvtest",
&my_fops);
if(ret == 0) printk("register_chrdev succeed!\n");
else printk("register_chrdev fail!\n");
return 0;
}
static __exit void mytest_cleanup(void)
{
unregister_chrdev(drvtest_major, "drvtest");
printk("register_chrdev succeed!\n");
printk("bye!\n");
return ;
}
module_init(mytest_init);
module_exit(mytest_cleanup);
-
简单的Linux驱动程序以及如何加载/卸载驱动
2021-05-15 08:35:04今天记录一下简单的Linux驱动程序怎么写以及如何加载/卸载驱动以hello.c为例:hello.c#ifndef __KERNEL__# define __KERNEL__#endif#ifndef MODULE# define MODULE#endif#include #include #include MODULE_LICENSE...今天记录一下简单的Linux驱动程序怎么写以及如何加载/卸载驱动
以hello.c为例:
hello.c
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include
#include
#include
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void){
printk(KERN_ALERT "HELLO WORLD\n");
return 0;
}
static void hello_exit(void){
printk(KERN_ALERT "GOODBYE CRUEL WORLD\n");
}
module_init(hello_init);
module_exit(hello_exit);
/*
3.5.0-23-generic
make -C /lib/modules/3.5.0-23-generic/build M=`pwd` modules
*/
用Makefile来编译:
Makefile
obj-m :=hello.o
all :
$(MAKE)-C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE)-C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
以下终端是编译命令,下图所示编译成功:
加载和移除驱动:
sudo insmod ./hello.ko
sudo rmmod hello
查看日志
tail /var/log/kern.log
参考博客https://www.cnblogs.com/QuLory/archive/2012/10/23/2736339.html
原文:https://www.cnblogs.com/GotWindy/p/11067015.html
-
Linux 驱动程序开发
2021-01-12 17:44:01Linux 驱动程序开发 设备驱动概述 设备驱动在操作系统中的位置 设备驱动程序是 内核 代码的一部分 , 驱动程序的地址空间是内核的地址空间 驱动程序的代码直接对设备硬件 直接对设备硬件(实际是设 实际是设备的各种... -
把设备驱动程序编译进嵌入式Linux内核
2020-03-04 11:20:26由于uClinux不支持模块动态加载,而且嵌入式Linux不能够象桌面Linux那样灵活的使用insmod/rmmod加载卸载设备驱动程序, 因而这里只介绍将设备驱动程序静态编译进uClinux内核的方法。本文以uClinux为例,介绍在一个以... -
Linux驱动程序之NOR FLASH
2021-06-09 06:39:36norflash驱动程序的步骤:(1)分配map_info结构体,并设置:物理地址,位宽,虚拟地址等(2)设置读写函数,用默认函数即可(3)调用NOR FLASH协议层提供的函数来识别:do_map_probe(4)添加分区:add_mtd_... -
Linux系统以太网卡驱动程序加载与配置.pdf
2021-09-07 00:58:00Linux系统以太网卡驱动程序加载与配置.pdf -
linux 添加字符设备驱动程序及测试程序
2012-02-24 20:47:27linux下采用模块方法,添加一个新的设备驱动程序。 要求添加字符设备的驱动。 另附一个应用程序,测试添加的驱动程序 -
详细讲解Linux驱动程序
2021-05-08 23:40:04编写Linux驱动程序1.建立Linux驱动骨架? Linux内核在使用驱动时需要装载与卸载驱动?装载驱动:建立设备文件、分配内存地址空间等;module_init 函数处理驱动初始化?卸载驱动:删除设备文件、释放内存地址空间等;... -
《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf
2013-02-17 15:12:26本书是linux设备驱动程序开发领域的权威著作。全书基于2.6内核,不仅透彻讲解了基本概念和技术,更深入探讨了其他书没有涵盖或浅尝辄止的许多重要主题和关键难点,如pcmcia、i2c和usb等外部总线以及视频、音频、... -
linux下中断处理程序示例,含驱动和应用程序
2018-08-19 09:55:12该示例提供了中断处理的详细代码,含驱动程序和应用程序。用户可以直接调用接口进行中断函数的处理。 -
基于块的Linux驱动程序 操作系统课程设计 最详细教程 块设备驱动 linux内核编译
2021-07-18 17:01:54增加一个驱动程序(使用内存模拟设备),使用模块编译方式 动态加载和卸载新的驱动 通过程序或命令行使用该驱动。 (至少能通过该驱动保存1MB的数据,还能将这些数据读取出来。可以模仿ramdisk的实现方式。) 1.... -
Linux驱动的两种加载方式过程分析
2021-05-10 01:43:30一、概念简述在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载。静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译和... -
Linux驱动程序框架以及概述
2021-01-13 15:52:54设备驱动程序功能: 1、对设备初始化和释放 2、把数据从内核传送到硬件和从硬件读取数据 3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据 4、检测和处理设备出现的错误 访问特定硬件: 访问特定硬件... -
linux驱动动态与静态加载
2019-10-02 16:40:31在Linux中驱动的加载方式有动态加载和静态加载。动态加载,即驱动不添加到内核中,在内核启动完成后,仅在用到这一驱动时才会进行加载静态加载,驱动编译进内核中,随内核的启动而完成驱动的加载。添加字符驱动代码... -
CAN总线在嵌入式Linux下驱动程序的实现
2021-02-03 13:17:12CAN总线在嵌入式Linux下驱动程序的实现、电子技术,开发板制作交流 -
linux驱动模块开机自动加载,以及应用程序开机自启动
2021-05-13 22:00:51启动脚本的一般存放路径是 /etc/init.d/rcS (rcS即脚本文件),自启动应用程序一般放置于/etc/rc.d/init.d 目录下。我们需要用vi命令对rcS进行编辑,当然也可以通过其他方式对其进行覆盖。在脚本中自启动应用程序示例... -
基于μCLinux的USB驱动程序实现
2020-11-04 04:24:20在此介绍FTDI公司的USB芯片FT245BL的主要性能、工作原理,并将其应用在Blackfin ADSP-BF533微处理器的嵌入式开发平台上,说明在μClinux下编写与加载USB接口芯片FT245BL的驱动程序方法,实现了DSP主板的 USB端口通信... -
基于Linux的设备驱动程序的加载方法.pdf
2021-09-06 23:30:20基于Linux的设备驱动程序的加载方法.pdf -
如何在Linux上安装设备驱动程序
2021-05-11 16:37:03了解Linux设备驱动程序如何工作以及如何使用它们.对于想要切换到Linux的熟悉Windows或MacOS的人,他们将面临如何安装和配置设备驱动程序的难题. 这是可以理解的,因为Windows和MacOS都有使该过程非常友好的机制. ...