ARM嵌入式学习笔记——《设备驱动基础》(二)

简介: ARM嵌入式学习笔记——《设备驱动基础》

Linux内核程序的命令传递参数

  • 明确:要想给Linux内核传递参数,必须遵循以下三个原则:
  • 接收参数的内核变量必须是全局变量。
  • 内核变量:此变量定义在内核空间,也就是此变量定义在内核程序内,也就是此变量的地址一定位于0xC0000000~0xFFFFFFFF.
  • 接收参数的内核全局变量的数据类型一定是以下类型:
  • 内核全局变量:明确其生命周期,当insmod安装内核程序时,内核会给他分配内存空间,永驻内存,直到rmmod从内核卸载。
  • bool 、invbool:布尔类型。
  • char、uchar
  • short、ushort
  • int、uint
  • long、ulong
  • charp(char*)
  • 切记:内核要求变量不允许是浮点数,CPU核处理浮点数的速度特别慢,所以不能使用浮点数。
  • 接收参数的内核全局变量接收参数之前必须用以下宏进行传参声明:
  • module_param(name, type, perm);
  • name:内核全局变量名。
  • type:内核全局变量的数据类型。
  • perm:内核全局变量的访问权限,一般用8进制来表示权限,不允许用可执行权限。

案例:编写内核程序,实现Linux内核程序的命令行传参。

  • 上位机执行:
mkdir /opt/drivers/day01/3.0
cd /opt/drivers/day01/3.0
vim helloworld.c
vim Makefile
make
cp helloworld.ko /opt/rootfs/home/drivers/


  • 下位机测试:
cd /home/drivers
echo 8 > /proc/sys/kernel/printk
insmod helloworld.ko
rmmod helloworld


安装内核程序时,传递参数

#include <linux/init.h>
#include <linux/module.h>
static int irq;
static char *pstring;
//传参声明
module_param(irq, int, 0664);
module_param(pstring, charp, 0);
//insmod时执行
static int helloworld_init(void)
{
    printk("%s: irq = %d, pstring = %s \n", __func__, irq, pstring):
    return 0;
}
//rmmod时执行
static void helloworld_exit(void)
{
    printk("%s: irq = %d, pstring = %s\n", __func__, irq, pstring);
}
module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_LICENSE("GPL");
#编写Makefile
obj-m += helloworld.o
all:
    make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:
    make -C /opt/kernel SUBDIRS=$(PWD) clean
#使用执行:
insmod helloworld.ko irq-250 pstring=china


//安装内核程序后,传递参数

#安装内核后,传递参数执行:
#查看当前参数值:
cat /sys/module/helloworld/parameters/irq
#修改参数值:
echo 2555 > /sys/module/helloworld/parameters/irq


总结:

  • 如果权限非零,将来在/sys/…/目录下会生成一个跟变量名同名的文件,将来通过修改文件的值来间接的修改变量的值,将来安装以后也能再次传递参数。
  • 如果权限为0,不会有同名的文件,此变量的传参只能在安装时传递参数,安装后不能再次传递参数。

Linux内核符合导出

  • 明确:何为符号(symbol)

  • 符合就是变量名或者函数名。
  • 符号导出目的:为了让别人的程序能够访问调用。
  • 回顾应用程序符号导出(多文件之间的访问,多文件,使用头文件声明和包含)

Linux内核符号导出(内核多文件之间的访问调用)

  • 明确:Linux内核程序多文件之间的访问和应用程序一模一样,都需要声明、定义和调用,但是内核要求导出的变量或者函数需要显示的导出一下。
  • 用以下宏导出即可:
  • EXPORT_SYMBOL(变量名或者函数名);
  • 或者——EXPORT_SYMBOL_GPL(变量名或者函数名);
  • 前者导出的函数或者变量,不管调用的内核程序是否遵循GPL协议,这个内核程序都可以调用。
  • 后者导出的函数或者变量只能给那些遵循GPL协议的内核程序访问。
  • 内核程序遵循GPL协议的代码:MODULE_LICENSE(“GPL”);

案例:helloworld调用test的变量

  • test.h
#ifndef __TEST_H
#define __TEST_H
//函数声明
extern void my_test(void);
#endif


  • test.c
#include <linux/init.h>
#include <linux/module.h>
//函数定义
void my_test(void)
{
    printk("%s\n", __FUNCTION__);
}
//显式将函数进行导出
EXPORT_SYMBOL(my_test);
MODULE_LICENSE("GPL");


  • helloworld.c
#include <linux/init.h>
#include <linux/module.h>
#include "test.h"
static int helloworld_init(void)
{
    //调用test
    my_test();
    printk("%s\n", __func__);
    return 0;
}
static void helloworld_exit(void)
{
    //调用test
    my_test();
    printk("%s\n", __func__);
}
module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_LICENSE("GPL");


  • 编译调用:
vim Makefile
obj-m += helloworld.o
obj-m += test.o
all:
    make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:
    make -C /opt/kernel SUBDIRS=$(PWD) clean
#复制到下位机上:
cp test.ko helloworld.ko /opt/rootfs/home/drivers


printk vs printf

  • 相同点:

  • 都是用于打印调试信息的。
  • 用法一模一样。
  • 不同点:

  • printf 只能用于用户空间

printk 只能用于内核空间

printk 特点

  • 能够指定打印输出级别,共8级。分别是:

  • KERN_EMERG <0> 系统奔溃
  • KERN_ALERT <1> 情况紧急必须立刻处理
  • KERN_CRIT <2> 严重情况
  • KERN_ERR <3> 错误情况
  • KERN_WARNING <4> 警告信息
  • KERN_NOTICE <5> 正常情况但是需要注意
  • KERN_INFO <6> 信息
  • KERN_DEBUG <7> 调试信息
  • 结论:数字越小,危险系数越高。此信息越应该打印输出。

  • 数字越小,打印级别越高。
  • 用法:

  • printk(KERN_ERR “this is a error msg!\n”);
  • printk("<3>" “this is a error msg!\n”);

实例:测试打印

test_printk.c


#include <linux/init.h>
#include <linux/module.h>
static int printk_all_init(void)
{
    printk("<0>", "level 0\n");
    printk("<1>", "level 1\n");
    printk("<2>", "level 2\n");
    printk("<3>", "level 3\n");
    printk("<4>", "level 4\n");
    printk("<5>", "level 5\n");
    printk("<6>", "level 6\n");
    printk("<7>", "level 7\n");
    return 0;
}
static void printk_all_exit(void)
{
    printk("<0>", "level 0\n");
    printk("<1>", "level 1\n");
    printk("<2>", "level 2\n");
    printk("<3>", "level 3\n");
    printk("<4>", "level 4\n");
    printk("<5>", "level 5\n");
    printk("<6>", "level 6\n");
    printk("<7>", "level 7\n");
}
module_init(printk_all_init);
module_exit(printk_all_exit);
MODULE_LICENSE("GPL");


printk 函数的默认打印输出级别的配置

  • printk 函数默认的打印阈值的配置
  • 如果printk函数指定的打印输出级别对应的数字小于默认的打印阈值的数据,此信息输出,否则屏蔽。
  • 问:内核默认的打印输出级别(阈值)如果配置?
  • 两种方法:
  • 通过修改配置文件/proc/sys/kernel/printk来修改默认的打印输出级别。(此方法解决不了Linux启动的时候打印信息的控制)
  • 在内核的启动参数中设置默认的打印输出级别(推荐使用此方法)

相关文章
|
2月前
|
数据处理
基于ARM的嵌入式原理与应用:ALU的功能与特点
基于ARM的嵌入式原理与应用:ALU的功能与特点
181 0
|
6月前
|
存储 Ubuntu 编译器
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(三)
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(三)
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(三)
|
6月前
|
Ubuntu Unix Linux
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(一)
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(一)
|
6月前
|
Linux 编译器 语音技术
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(二)
合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(二)
|
5月前
|
机器学习/深度学习 人工智能 计算机视觉
好的资源-----打卡机+Arm+Qt+OpenCV嵌入式项目-基于人脸识别的考勤系统-----B站神经网络与深度学习,商城
好的资源-----打卡机+Arm+Qt+OpenCV嵌入式项目-基于人脸识别的考勤系统-----B站神经网络与深度学习,商城
|
6月前
|
存储 算法 Linux
详细解读ARM嵌入式整理
详细解读ARM嵌入式整理
45 0
|
7月前
|
安全 Unix Linux
【ARM】在NUC977上搭建基于boa的嵌入式web服务器
【ARM】在NUC977上搭建基于boa的嵌入式web服务器
190 0
|
23天前
|
机器学习/深度学习 弹性计算 人工智能
阿里云服务器架构有啥区别?X86计算、Arm、GPU异构、裸金属和高性能计算对比
阿里云ECS涵盖x86、ARM、GPU/FPGA/ASIC、弹性裸金属及高性能计算等多种架构。x86架构采用Intel/AMD处理器,适用于广泛企业级应用;ARM架构低功耗,适合容器与微服务;GPU/FPGA/ASIC专为AI、图形处理设计;弹性裸金属提供物理机性能;高性能计算则针对大规模并行计算优化。
|
2月前
|
编解码 弹性计算 应用服务中间件
阿里云服务器Arm计算架构解析:Arm计算架构云服务器租用收费标准价格参考
阿里云服务器架构分为X86计算、Arm计算、高性能计算等多种架构,其中Arm计算架构以其低功耗、高效率的特点受到广泛关注。本文将深入解析阿里云Arm计算架构云服务器的技术特点、适用场景以及包年包月与按量付费的收费标准与最新活动价格情况,以供选择参考。
|
2月前
|
机器学习/深度学习 弹性计算 编解码
阿里云服务器计算架构X86/ARM/GPU/FPGA/ASIC/裸金属/超级计算集群有啥区别?
阿里云服务器ECS提供了多种计算架构,包括X86、ARM、GPU/FPGA/ASIC、弹性裸金属服务器及超级计算集群。X86架构常见且通用,适合大多数应用场景;ARM架构具备低功耗优势,适用于长期运行环境;GPU/FPGA/ASIC则针对深度学习、科学计算、视频处理等高性能需求;弹性裸金属服务器与超级计算集群则分别提供物理机级别的性能和高速RDMA互联,满足高性能计算和大规模训练需求。