Linux字符设备的驱动开发

我们平时用到的很多设备都是字符串设备,比如鼠标、键盘等等。

所以现在来编写一个字符串设备的驱动模版。

1.模块的入口和出口

       因为我们为了方便调试代码,所以采用模块的方式来进行注册和卸载。既然用到了模块,那么我们就需要包含

#include<linux/module.h>

       而且需要在c_cpp_properties.json中指定内核的路径,如果不指定那么它会报错,或者默认为ubunut的/usr/include下的内核头文件。但是ubuntu的kernel文件和我们开发板使用的kernel是不同的。我们要用到的kernel文件是要作为zImage烧写到系统中的,开发板内存着自己的kernel文件,但运行的是ubuntu的文件,结果会很可怕。

然后是模块的注册和模块的卸载

#include<linux/module.h>

static int __init chardev_init(void){
return 0;
}
static void __exit chardev_exit(void){

}

module_init(chardev_init);
module_exit(chardev_exit);

/—————————————————题外知识————————————————————/

       在这其中使用static关键字来声明一个函数可以使该函数仅在定义它的文件内部可见,而不会被其他文件访问。换句话说,它限制了函数的可见性和可访问性。

而使用static关键字来声明一个变量可以改变该变量的存储方式和可见性。具体来说:

①对于在函数内部用static声明的变量,其生命周期被延长至程序的整个运行期间,而不是在函数调用期间。意味着比如在子函数中声明一个变量,这个变量在子函数结束时不会被释放,而且每一次调用该子函数,它只会被最开始初始化一次。

void func() {
static int count = 0; // 此变量只初始化一次,函数每次调用时保持其值
printf("%d\n", count++);
}

② 对于在函数外部用static声明的变量,其作用范围被限制在该文件内,其他文件无法访问。

/*—————————————————题外知识————————————————————*/

__init 是一个函数修饰符,用于标记函数是否在初始化阶段被调用。当一个内核模块被加载时,__init 修饰的函数会被自动调用。在大多数情况下,__init 修饰的函数在模块加载后就不再存在,因此它们不能在运行时被调用。

module_init()是一个宏,用于指定模块初始化的函数。它接受一个函数名作为参数,并自动调用该函数来进行模块初始化。

__init chardev_init(void) 定义了一个名为 chardev_init 的初始化函数。 exit同理。

2.Makefile编写

接下来编写Makefile文件

KERNELDIR := /home/lcc/linux/I.MUX6ULL/Linux_YM/linux-imx-rel_imx_4.1.15_2.1.0_ga

CURRENT_PATH := $(shell pwd)
obj-m := chardev.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

KERNELDIR 表示开发板所使用的 Linux 内核源码目录。

CURRENT_PATH表示当前目录。

obj-m表示将chardev.c编译为模块。

build: kernel_modules表示编译的是模块。

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules表示执行make操作。-C 转移工作目录到内核目录,因为我们这个程序用到了内核中的程序。 M表示模块源码目录, modules表示模块。

最后执行make命令,生成模块。

3.加载、卸载模块 (以下内容都是在开发板终端中执行)

       正点原子是把uboot下载到sd卡中,然后开发板从SD卡启动。因为SD卡中有uboot的存在,所以就能进入uboot命令行模式。然后使用tftp把zImage和dtb直接烧写到内存中(可以试一试)。先配置开发板的IP地址:(按照自己的配置)

setenv ipaddr xxx.xx.xx.xx  开发板的IP地址

setenv ethaddr xxx.xxx.xxx.xx

setenv gatewayip xx.xx.xx 网关地址

setenv netmask xxx.xxx.xxx.xxx子网掩码

setenv serverip xx.xx.xx.xx 虚拟机IP地址 

saveenv

再用tftp加载zImage和dtb(已开通tftp服务,网上有很多教程)

然后再通过nfs把ubuntu上编写的根文件系统挂载到开发板上。(命令中的ip地址按照自己之前setenv中设置的输入,对应的)

setenv bootargs 'console=ttymxc0,115200 rw root=/dev/nfs \ nfsroot=ipaddr:/home/lcc/linux/nfs/roofts/ \

roofts ip=ethaddr:ipaddr:gatewayip:netmask::eth0:off'

生成.ko文件后。有两种命令加载驱动模块:insmod modprobe。

insomd不能解决模块的依赖问题。比如第一个模块要依赖第二个模块。加载的时候就应该先加载第二个模块。modprobe更智能,它会分析模块的依赖关系,然后将所有的模块加载到内核中。

我们要使用modprobe命令,前提条件是我们在/lib/modules/下有一个4.1.15文件(文件的名称对应开发板使用的内核版本,由我们自己创建即可)。然后使用cp命名把我们刚生成的.ko文件拷贝到4.1.15文件中。

再depmod命令。它会生成

然后就可以modprobe了。最后使用rmmod卸载模块。我刚才不小心使用了rm。它的坏处在于,模块是加载到内核中的,我们可能操作失败,因为用户没有权限去动内核东西,即使操作成功,可能会导致内核崩溃。而且使用rm后,lsmod仍然显示模块存在。

  • 31
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值