如何在linux驱动程序中保存文件

碰到问题:项目中发现ADC提取的数据,偶尔有个别好像是上次采集的数据,需要查找在哪一环节出错了,因此需要保存驱动中DMA刚完成时的数据和应用程序处理完发送包的数据。应用程序比较容易,直接保存文件就行了,驱动程序在内核层如何做呢?查找了一些资料,发现在内核中需要使用filep_open函数来获取struct file结构,然后针对struct file可以写文件,最后使用filp_close函数关闭打开的文件,初步整出以下代码:

static int kernel_file_delete(char *file_path1) 
{
    struct path path;

    int ret = kern_path(file_path1, LOOKUP_FOLLOW, &path);
    if (ret) return ret;
    
    ret = vfs_unlink(d_inode(path.dentry->d_parent), path.dentry, NULL);

    path_put(&path);
    return ret;
}

static ssize_t kernel_file_write(unsigned char *buf,int count, struct file *target_file)
{
    mm_segment_t old_fs;

    old_fs = get_fs();
    set_fs(KERNEL_DS);
    
//    ret = vfs_write(target_file, buf, count, &target_file->f_pos);
    loff_t pos = 0;
printk("targetfile=%lx,buf=%lx,count=%d\n",target_file,buf,count);
    ssize_t ret = kernel_write(target_file, buf, count, &pos);    if (ret < 0) {
        printk("Write failed: %d\n", ret);
        return ret;
    }
    
    set_fs(old_fs);
    return ret;
}

static int kernel_file_close(struct file *target_file)
{
        filp_close(target_file, NULL);
}
//========================================================================
	 struct file *target_file;
	 unsigned char *buf=(unsigned char *)p_adc_dev->dsts[0];
	buf+=0x1E84;// add 0.5S@3906 sample rate
	 sprintf(file_path,"/kernel_data%02d.bin",p_adc_dev->layout_index);
	printk("--channel%02d--1 ",p_adc_dev->current_transfer_index);
	 kernel_file_delete(file_path);
	printk("2 ");
    target_file = filp_open(file_path, O_CREAT|O_WRONLY, 0644);
    if (IS_ERR(target_file)) {
        printk("Failed to open %s\n", file_path);
    }
	printk("3 ");
	 kernel_file_write(buf,(p_adc_dev->bytes_cur_transfer-0x1E84),target_file);
	printk("4 ");
	 vfs_fsync(target_file,0);
	printk("5 ");
	 kernel_file_close(target_file);
	printk("6\n");

但是在实际使用中,发现每次运行到了都会内核崩溃,考虑可能是写文件的地方在中断的原因,因此引出第二个问题:如何把对文件的操作移到中断函数之外?
综合考虑,可以使用linux内核的工作队列方式,因此得出以下代码:
首先在设备结构体中加入work_struct,并且在probe函数中初始化work

struct axi_adc_dev
{
    。。。
	dev_t dev_node;
	bool f_on;								/* flag indicates this ADC is configured to ON */
	ktime_t startTime;
	int abort;
	int aborted;
	**struct work_struct my_work;**
};

static int axi_adc_probe(struct platform_device *pdev)
{
    。。。。
	**INIT_WORK(&aad->my_work,writefile_work_func);**

	if (dev_index == 0)
	{
      。。。。
}

然后在中断处理程序的上下文中调度这个work,以便对文件的操作放在工作队列的线程环境执行:
static void dma_rx_callback(void *completion)
{
	//中断处理程序的上下文中
    。。。
    **schedule_work(&p_adc_dev->my_work);**//调度my_work
    。。。
}

最后是my_work的工作函数writefile_work_func

#include <linux/vfs.h>
#include <linux/workqueue.h>
#include <linux/namei.h>
static void writefile_work_func(struct work_struct *work)
{
	 char file_path[19];
	 
	 struct axi_adc_dev *p_adc_dev = container_of(work, struct axi_adc_dev, my_work);

	 struct file *target_file;
	 unsigned char *buf=(unsigned char *)p_adc_dev->dsts[0];
	buf+=0x1E84;// add 0.5S@3906 sample rate
	 sprintf(file_path,"/kernel_data%02d.bin",p_adc_dev->layout_index);
	printk("--channel%02d--1 ",p_adc_dev->current_transfer_index);
	 kernel_file_delete(file_path);
	printk("2 ");
    target_file = filp_open(file_path, O_CREAT|O_WRONLY, 0644);
    if (IS_ERR(target_file)) {
        printk("Failed to open %s\n", file_path);
    }
	printk("3 ");
	 kernel_file_write(buf,(p_adc_dev->bytes_cur_transfer-0x1E84),target_file);
	printk("4 ");
	 vfs_fsync(target_file,0);
	printk("5 ");
	 kernel_file_close(target_file);
	printk("6\n");

}

这次再执行就没什么问题了,每次adc采集的DMA完成时把数据以文件的形式保存在系统根目录/下

posted @ 2025-10-21 10:25  junghooqian  阅读(13)  评论(0)    收藏  举报