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启动的时候打印信息的控制)
  • 在内核的启动参数中设置默认的打印输出级别(推荐使用此方法)

相关文章
|
1月前
|
存储 机器学习/深度学习 人工智能
嵌入式中一文搞懂ARM处理器架构
嵌入式中一文搞懂ARM处理器架构
38 1
|
8月前
|
编译器 C语言
ARM与C语言的混合编程【嵌入式系统】
ARM与C语言的混合编程【嵌入式系统】
83 0
|
NoSQL Ubuntu Linux
arm嵌入式gdb移植和搭建远程gdb调试运行环境
arm嵌入式gdb移植和搭建远程gdb调试运行环境
499 0
arm嵌入式gdb移植和搭建远程gdb调试运行环境
|
编译器 Shell 测试技术
ARM嵌入式——制作根文件系统并使用NFS挂载运行。
ARM嵌入式——制作根文件系统并使用NFS挂载运行。
312 0
|
存储 Web App开发 Ubuntu
ARM嵌入式学习笔记——《Linux内核》
ARM嵌入式学习笔记——《Linux内核》
160 0
|
Ubuntu Linux 编译器
ARM嵌入式学习笔记——《根文件系统》
ARM嵌入式学习笔记——《根文件系统》
200 0
【各种问题处理】X86架构和ARM架构的区别
【1月更文挑战第13天】【各种问题处理】X86架构和ARM架构的区别
|
4月前
|
Web App开发 NoSQL 安全
ARM架构-银河麒麟v10-server离线安装Harbor
ARM架构-银河麒麟v10-server离线安装Harbor
326 0
|
4月前
|
边缘计算 编译器 数据中心
X86架构与Arm架构的主要区别分析
X86架构与Arm架构的主要区别分析
472 0
|
3月前
|
缓存 API Android开发
一起学点ARM的微架构二?
一起学点ARM的微架构二?
75 1