内核模块读写文件

在移植南方硅谷的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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您想了解关于内核模块如何读写串口设备的信息。在Linux系统中,串口设备被视为一种字符设备,使用串口设备需要通过打开设备文件并使用标准的读写函数进行数据传输。在内核模块中,可以使用串口驱动程序提供的函数来实现对串口设备的读写操作。下面是一个简单的例子,展示了如何在内核模块中读取串口设备: ```c #include <linux/module.h> #include <linux/serial.h> static struct uart_port *my_port; static int my_module_init(void) { int ret; /* 打开串口设备文件,并设置串口参数 */ my_port = kzalloc(sizeof(struct uart_port), GFP_KERNEL); uart_get_config(my_port); my_port->baud_rate = 9600; my_port->flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; my_port->irq = 4; /* 初始化并注册串口设备 */ ret = uart_add_one_port(&my_serial_driver, my_port); if (ret) return ret; /* 读取串口数据 */ while (1) { char buf[10]; int len; len = uart_read(my_port, buf, sizeof(buf)); if (len > 0) printk(KERN_INFO "Read %d bytes from serial port\n", len); } return 0; } static void my_module_exit(void) { /* 关闭并注销串口设备 */ uart_remove_one_port(&my_serial_driver, my_port); kfree(my_port); } module_init(my_module_init); module_exit(my_module_exit); ``` 需要注意的是,以上代码只是一个简单的例子,实际情况可能更加复杂,需要根据具体的需求进行调整。另外,在实际应用中,还需要考虑串口设备的并发访问和异常处理等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值