T3 ko模块开发

1.ko模块传参

1.1概念

  • 之前在装载驱动时候使用insmod hello_driver.ko指令,实际上该指令可以传递参数
  • 传参示例insmod hello_driver.ko myname="makabaka" val=22
  • 通过传参可以向驱动里面传入参数,驱动可对参数做相应处理,这点类似函数传参
  • 例如在wifi驱动上面,wifi芯片在运行的时候会运行其内部原厂代码(firmware固件,为.bin文件),当我们要运行wifi驱动的时候需要告诉固件驱动固件的位置.如insmod rtxxx.ko path=/lib/modules/firmware/xxx.bin
  • 既然用到了参数,那么在内核里面就得提供相应接口来接收参数
/*
功能:接收参数
参数:
参数1:表示参数名字
参数2:表示参数类型
参数3:文件权限,针对文件
	1.可以直接写数字,如0666,0777
	2.或者使用宏,S_IRUGO|S_IWUGO|S_IXUGO
	  分别表示对所有者,同组者,其他用户有读,写,执行权限,即0777
*/

module_param(name, type, perm)
    
sudo insmod param.ko my_name="wtf" my_value=66

1.2示例代码

  • 注意:参数权限不可给太大,否则报错
#include <linux/init.h>
#include <linux/module.h>

static int my_value = 77;
static char *my_name = "makabaka"; 
/*模块装载函数实现*/
static int __init hello_drv_init(void)
{
    printk("--------%s---------\n", __FUNCTION__);
    printk("name = %s, value = %d\n", my_name, my_value);
    return 0;
}
/*模块卸载函数实现*/
static void __exit hello_drv_exit(void)
{
    printk("--------%s---------\n", __FUNCTION__);
}
module_init(hello_drv_init);
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL");
/*接收参数*/
module_param(my_value, int, 0644);
/*所有用户可读写执行,然权限太大会报错*/
//module_param(my_name, charp, 0777);
//module_param(my_name, charp, S_IRUGO|S_IWUGO|S_IXUGO);
/*所有用户有读权限,拥有着有写权限*/
module_param(my_name, charp, S_IRUGO|S_IWUSR);
  • 调试
# 没加参数前
pi@raspberrypi:~/my_drivers $ sudo insmod param.ko
pi@raspberrypi:~/my_drivers $ sudo rmmode param
pi@raspberrypi:~/my_drivers $ dmesg
...
[ 4782.525509] --------hello_drv_init---------
[ 4782.525516] name = makabaka, value = 77
[ 5081.692546] --------hello_drv_exit---------
# 加参数后,可见传参成功
pi@raspberrypi:~/my_drivers $ sudo insmod param.ko my_name="wtf" my_value=66
pi@raspberrypi:~/my_drivers $ sudo rmmode param
...
[ 5383.877852] --------hello_drv_init---------
[ 5383.877871] name = wtf, value = 66
[ 5440.380181] --------hello_drv_exit---------

2.ko模块的符号导出

2.1概念

  • 在内核中允许很多ko文件存在,在装载文件的时候这些代码就会被装载到内核空间
  • 在内核空间里面会自动执行入口函数module.init()
  • 那么内核里面不同的ko文件可能存在相互调用的情况
  • Linux内核采用的是以模块化形式管理内核代码.内核中的每个模块相互之间是相互独立的,也就是说A模块的全局变量和函数,B模块是无法访问的
  • 有些时候,我们写一些模块代码的时候,发现部分函数功能别人已经实现了,此时我们就想如果我们可以调用他们已经实现好的函数接口就好了.那如何才能做到这点呢?符号导出了,也就是说你可以把你实现的函数接口和全局变量导出,以供其他模块使用
  • 在Linux内核里面,如果一个模块已经以静态的方式编译进的内核,那么它导出的符号就会出现在全局的内核符号表中

2.2示例

  • 编写2个驱动文件,一个是hello_driver.c,一个为math.c.前者调用后者内部的函数
  • math.c,如果你想其中的某个函数被外部调用,那么就应该将其导出
#include <linux/module.h>
#include <linux/init.h>


int my_add(int a, int b)
{
    return a + b;
}
/*导出符号表*/
EXPORT_SYMBOL(my_add);

int my_sub(int a, int b)
{
    return a - b;
}
/*导出符号表*/
EXPORT_SYMBOL(my_sub);

/*此处不需要模块的装载卸载的入口声明,直接定义函数*/
MODULE_LICENSE("GPL");
  • math.h,存在于调用者相同路径下
#ifndef __MATH_H__
#define __MATH_H__

int my_add(int a, int b);
int my_sub(int a, int b);

#endif
  • hello_driver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/stat.h>
#include "math.h"/*需引入头文件*/

/*模块装载函数实现*/
static int __init hello_drv_init(void)
{
    printk("--------%s---------\n", __FUNCTION__);
    /*调用math模块的函数*/
    printk("a+b=%d, a-b=%d\n", my_add(22, 33), my_sub(22, 33));
    return 0;
}

/*模块卸载函数实现*/
static void __exit hello_drv_exit(void)
{
    printk("--------%s---------\n", __FUNCTION__);
}

module_init(hello_drv_init);
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL");
  • 编译
hanqi@hanqi-PC:~/my_drivers$ make
make -C /home/hanqi/pi/linux-rpi-4.14.y M=/home/hanqi/my_drivers modules ARCH=arm CROSS_COMPILE=/home/hanqi/pi/toolchain/tools-master/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
make[1]: Entering directory '/home/hanqi/pi/linux-rpi-4.14.y'
  CC [M]  /home/hanqi/my_drivers/math.o
  CC [M]  /home/hanqi/my_drivers/hello_driver.o
  Building modules, stage 2.
  MODPOST 2 modules
  CC      /home/hanqi/my_drivers/hello_driver.mod.o
  LD [M]  /home/hanqi/my_drivers/hello_driver.ko		# 调用者ko
  CC      /home/hanqi/my_drivers/math.mod.o			    
  LD [M]  /home/hanqi/my_drivers/math.ko			    # 被调用者ko
make[1]: Leaving directory '/home/hanqi/pi/linux-rpi-4.14.y'
  • 运行,如果先装载调用者驱动的话(hello_driver.ko),会报错,因为被调用者的驱动没有加载到内核里面,即其符号表没有导出,故无法找到相关功能函数
# 找不到符号
pi@raspberrypi:~/my_drivers $ sudo insmod hello_driver.ko        
insmod: ERROR: could not insert module hello_driver.ko: Unknown symbol in module
  • 正确姿势应该是先加载功能驱动模块,在加载调用者驱动
# 先装载功能驱动
pi@raspberrypi:~/my_drivers $ sudo insmod math.ko   
# 查看符号表是否导出,里面存在math文件,故成功
pi@raspberrypi:~/my_drivers $ ls /sys/module/
8250          dwc_otg         lockd                    rfkill                uio
auth_rpcgss   fb              loop                     rng_core              uio_pdrv_genirq
bcm2708_fb    firmware_class  math                     scsi_mod              usbcore
bcm2835_mmc   fixed           mmcblk                   scsi_transport_iscsi  usbhid
block         fscache         module                   sdhci                 usb_storage
brcmfmac      fuse            mousedev                 smsc95xx              vc_mem
brcmutil      hid             netpoll                  snd                   vt
brd           i2c_dev         nfs                      snd_bcm2835           watchdog
cachefiles    ip_tables       nfs_layout_nfsv41_files  snd_pcm               workqueue
cfg80211      ipv6            nfsv4                    snd_timer             x_tables
configfs      kdb             printk                   spurious              xz_dec
cpufreq       kernel          random                   srcutree
cryptomgr     keyboard        rc_core                  sunrpc
debug_core    kgdboc          rcupdate                 sysrq
dns_resolver  lan78xx         rcutree                  tcp_cubic
# 再装载用户驱动
pi@raspberrypi:~/my_drivers $ sudo insmod hello_driver.ko
# 查看运行结果
pi@raspberrypi:~/my_drivers $ dmesg
[ 7806.485637] --------hello_drv_init---------
[ 7806.485650] a+b=55, a-b=-11
posted @ 2021-08-05 23:39  MHDSG  阅读(204)  评论(0)    收藏  举报