在移植南方硅谷的WiFi模块时需要自定义修改MAC的地址,采用在加载模块的时候读取外部文件的方式去修改MAC地址,这就涉及到在内核层去读写文件操作。主要用到了filp_open()、filp_close()、vfs_read()、vfs_write()这几个函数,这些函数在linux/fs.h文件中定义。
函数介绍
1.filp_open
函数原型如下:
struct file *filp_open(const char *filename, int flags, umode_t mode);
参数说明:
filename:要打开或创建文件的名称,包括路径部分;
flags:文件的打开方式,该取值与open()函数类似,可以取O_CREATE、O_RDWR、O_RDONLY;
mode:创建文件时使用该参数,设置文件的读写权限,其它情况可以设置为0。
返回值:文件打开成功返回正确的struct file *指针,失败返回错误的指针。
注意:
函数filp_open()将返回struct file *结构指针,将会提供给后继的函数进行使用,可以使用IS_ERR()来校验指针的有效性。
2.filp_close
函数的原型如下:
int filp_close(struct file *filp, fl_owner_t id);
参数说明:
filp:使用filp_opne()函数返回的struct file *结构指针;
id:一般设置为NULL。
返回值:文件成功关闭时返回0,失败时返回负的错误号。
3.vfs_read
函数的原型如下:
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos);
参数说明:
file:函数filp_open()调用后返回的struct file *结构指针;
buf:buf缓冲区,用来存储读取到的数据,该参数具有__user进行修饰,表明buf指向用户空间地址,如果传入内核空间地址时,就会出错,并返回-EFAULT;
count:要读取的数据字节个数;
pos:返回数据读取后的文件指针。
返回值:文件读取成功时,返回读取到字节数,如果失败,返回负的错误号。
4、vfs_write
函数的原型如下:
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
参数说明:
file:函数filp_open()调用后返回的struct file *结构指针;
buf:buf缓冲区,用来存储要写入文件中的数据,该参数具有__user进行修饰,表明buf指向用户空间地址,如果传入内核空间地址时,就会出错,并返回-EFAULT;
count:要写入的数据字节个数;
pos:返回数据写入后的文件指针。
返回值:文件写入成功时,返回写入到的字节个数,如果失败,返回负的错误号。
附加函数
对于vfs_read()和vfs_write()函数,参数buf是使用__user进行修饰的,表明buf指向用户空间地址,由于函数是在内核态中使用,因此,需要使用函数改变kernel对内存地址检查的处理方式。
1、set_fs
原型如下:
static inline void set_fs(mm_segment_t fs)
该函数是设置kernel对内存地址检查方式,共有两个取值分别是USER_DS和KERNEL_DS。
2、get_fs
该函数是获取kernel对内存地址检查方式。
举例
1、一个读文件的模块
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/dcache.h>
#include <asm/fcntl.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
static int __init hello_init(void)
{
int ret;
struct file *fp;
mm_segment_t old_fs;
loff_t pos;
char buf[30];
printk(KERN_INFO "=====hello_init=====\n");
fp = filp_open("/oem/test",O_RDONLY, 0);
if (IS_ERR(fp)) {
ret = PTR_ERR(fp);
printk(KERN_INFO "/oem/test open failed,err = %d\n", ret);
return ret;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
pos = fp->f_pos;
ret = vfs_read(fp, buf, 128, &pos);
if (!ret) {
ret = -EINVAL;
printk(KERN_INFO "vfs read failed\n");
return ret;
}
printk(KERN_INFO "f_pos = %lld,pos = %lld,buf = %s\n",
fp->f_pos, pos, buf);
set_fs(old_fs);
filp_close(fp, NULL);
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "hello_exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile
CROSS_COMPILE=/home/zhub/rtl8821/rockchip_rk3308b_sdk/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
ARCH=arm64
KSRC=/home/zhub/rtl8821/rockchip_rk3308b_sdk/kernel
ifneq ($(KERNELRELEASE),)
obj-m := test_kernel.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
#$(MAKE) -C $(KDIR) M=$(PWD) modules
$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KSRC) M=$(shell pwd) modules
clean:
rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.order *.a
endif