嵌入式Linux驱动开发 02:将驱动程序添加到内核中

您所在的位置:网站首页 驱动注入源码怎么弄 嵌入式Linux驱动开发 02:将驱动程序添加到内核中

嵌入式Linux驱动开发 02:将驱动程序添加到内核中

2024-07-16 13:41:44| 来源: 网络整理| 查看: 265

文章目录 目的基础说明添加到内核中KconfigMakefile驱动程序 编译与测试模块方式编译到内核中 总结

目的

在上一篇文章 《嵌入式Linux驱动开发 01:基础开发与使用》 中我们已经实现了最基础的驱动功能。在那篇文章中我们的驱动代码是独立于内核代码存放的,并且我们的驱动编译后也是一个独立的模块。在实际使用中将驱动代码放在内核代码中,并将驱动编译到内核中也是比较常见的选择,这篇文章将此进行介绍。

这篇文章中内容均在下面的开发板上进行测试: 《新唐NUC980使用记录:自制开发板(基于NUC980DK61YC)》

这篇文章主要是在下面文章基础上进行的: 《新唐NUC980使用记录:访问以太网(LAN8720A) & 启用SSH》

基础说明

将驱动程序添加到内核中可以分为两层含义来理解:

将驱动程序源码等放到 Linux Kernel 源码目录下 Linux Kernel 源码中通常将驱动放到 drivers/ 下,本文中也将驱动源码放到这个目录下;在 Linux Kernel 配置管理工具中统一管理与编译 这条主要指可以在 menuconfig 中进行配置管理来选择编译(这里先不讨论使用设备树的情况); menuconfig 界面中各个菜单和选项都是由 Kconfig 文件定义的,所以我们需要修改和编写相关文件; 在 menuconfig 中配置最终改变的是 make 时 Makefile 文件中各个变量,我们的自己的驱动也需要 Makefile 文件来指定编译规则,并结合 Kconfig 文件中定义的变量来控制编译过程; 添加到内核中

本文中演示中涉及目录与文件结构组织如下: 在这里插入图片描述 其中 char_dev 就是本文中要添加的驱动。 user/ 目录用于统一存放自己编写的驱动,如果没有这个需求这一层可以去掉,这样结构上会更简单些,当然推荐还是留着。各目录下的 Kconfig 是一层层应用的, Makefile 同理。

进入源码目录并建立相关目录和文件:

cd ~/nuc980-sdk/NUC980-linux-4.4.y/ mkdir -p drivers/user touch drivers/user/Kconfig touch drivers/user/Makefile mkdir -p drivers/user/char_dev touch drivers/user/char_dev/char_dev.c touch drivers/user/char_dev/Kconfig touch drivers/user/char_dev/Makefile Kconfig

首先修改drivers目录下Kconfig文件:

gedit drivers/Kconfig

在其中添加下面一行,用来引用drivers/user目录下的Kconfig文件:

source "drivers/user/Kconfig"

接着编辑drivers/user目录下的Kconfig文件:

gedit drivers/user/Kconfig

写入下面内容,用来引用drivers/user/char_dev目录下的Kconfig文件:

menu "User drivers" source "drivers/user/char_dev/Kconfig" endmenu

最后编辑drivers/user/char_dev目录下的Kconfig文件:

gedit drivers/user/char_dev/Kconfig

写入下面内容:

config USER_CHAR_DEV tristate "char_dev" default n help char_dev driver test.

上面内容中 config USER_CHAR_DEV 表示设置一个可配置的变量,名称为 USER_CHAR_DEV (保存后实际的变量名会在头部添加 CONFIG_ 即 CONFIG_USER_CHAR_DEV ,这个变量可以在Makefile中使用)。 tristate 表示该变量可取值为 n/y/m ,后面的字符串为该条目在 menuconfig 中显示的文本。 default n 表示该变量默认值。 help 表示其下面的内容是可在 menuconfig 中查看帮助信息。

经过上面处理后就可以在 menuconfig 中看到相关选项并进行操作了: 在这里插入图片描述

Makefile

首先修改drivers目录下Makefile文件:

gedit drivers/Makefile

在其中添加下面一行,这样编译时会进入drivers/user目录下:

obj-y += user/

接着编辑drivers/user目录下的Makefile文件:

gedit drivers/user/Makefile

写入下面内容,这样编译时会进入drivers/user/char_dev目录下:

obj-y += char_dev/

最后编辑drivers/user/char_dev目录下的Makefile文件:

gedit drivers/user/char_dev/Makefile

写入下面内容:

obj-$(CONFIG_USER_CHAR_DEV) += char_dev.o

上面就是最终编译驱动程序过程了,这里的 CONFIG_USER_CHAR_DEV 变量就是由前面配置来产生的。根据变量的值,其最终可能产生 obj-n 、 obj-y 、 obj-m 几个结果,这几个是 Linux Kernel 源码总的Makefile中定义的变量,添加到 obj-y 的内容会编译到内核中,添加到 obj-m 的内容会编译成单独的模块。

驱动程序

最后编辑下进行测试用的驱动程序:

gedit drivers/user/char_dev/char_dev.c

直接使用上一篇文章的程序即可:

#include #include #include #include static int major = 0; static const char *char_dev_name = "char_dev"; static struct class *char_dev_class; static struct device *char_dev_device; static char dev_buf[4096]; #define MIN(a, b) ((a) return 0; } static ssize_t char_dev_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { int ret; ret = copy_to_user(buf, dev_buf, MIN(size, 4096)); // 从内核空间拷贝数据到用户空间 return ret; } static ssize_t char_dev_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { int ret; ret = copy_from_user(dev_buf, buf, MIN(size, 4096)); // 从用户空间拷贝数据到内核空间 return ret; } static const struct file_operations char_dev_fops = { .owner = THIS_MODULE, .open = char_dev_open, .release = char_dev_close, .read = char_dev_read, .write = char_dev_write, }; static int __init char_dev_init(void) { printk("modlog: func %s, line %d.\n", __FUNCTION__, __LINE__); major = register_chrdev(0, char_dev_name, &char_dev_fops); // 注册字符设备,第一个参数0表示让内核自动分配主设备号 char_dev_class = class_create(THIS_MODULE, "char_dev_class"); // if (IS_ERR(char_dev_class)) { unregister_chrdev(major, char_dev_name); return -1; } char_dev_device = device_create(char_dev_class, NULL, MKDEV(major, 0), NULL, char_dev_name); // 创建设备节点创建设备节点,成功后就会出现/dev/char_dev_name的设备文件 if (IS_ERR(char_dev_device)) { device_destroy(char_dev_class, MKDEV(major, 0)); unregister_chrdev(major, char_dev_name); return -1; } return 0; } static void __exit char_dev_exit(void) { printk("modlog: func %s, line %d.\n", __FUNCTION__, __LINE__); device_destroy(char_dev_class, MKDEV(major, 0)); // 销毁设备节点,销毁后/dev/下设备节点文件就会删除 class_destroy(char_dev_class); unregister_chrdev(major, char_dev_name); // 注销字符设备 } module_init(char_dev_init); // 模块入口 module_exit(char_dev_exit); // 模块出口 MODULE_LICENSE("GPL"); // 模块许可

这个驱动程序在安装和卸载时打印了一些消息,可以通过此判断驱动程序是否工作。

编译与测试 模块方式

在 menuconfig 将控制驱动的选项选择为模块后进行编译:

# make menuconfig export PATH=$PATH:/home/nx/nuc980-sdk/arm_linux_4.8/bin # 可以使用make整体编译或使用make modules编译单独模块 # make make modules

在这里插入图片描述 最后编译生成的模块默认在模块源码目录下,可以拷贝到开发板中进行测试:

# scp drivers/user/char_dev/char_dev.ko [email protected]:/root/

在这里插入图片描述

编译到内核中

在 menuconfig 将控制驱动的选项选择为y后进行编译:

# make menuconfig # export PATH=$PATH:/home/nx/nuc980-sdk/arm_linux_4.8/bin make uImage

在这里插入图片描述

编译完成后拷贝内核文件到开发板boot分区:

# 在开发板中挂载启动分区 # mount /dev/mmcblk0p1 /mnt/ # 在虚拟机中拷贝编译生成的内核到开发板 # scp ../image/980uimage [email protected]:/mnt/

在这里插入图片描述 开发板重启后可以看到驱动程序在内核启动时自动启动了,可以看到打印的信息以及 /dev/ 下的设备文件。

总结

将驱动程序添加到内核中还是比较简单的,按照内核源码本身的组织方式来进行就行了。更多的示例可以参考内核源码 drivers/ 目录下各个驱动。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭