C语言读取循环FIFO(Buffer)

问题描述 投票:0回答:1

我有一个硬件,可以通过硬件将输入数据写入某个地址 基地址或起始循环 FIFO 类似于此地址 0xc6dc9cf0 缓冲区(FIFO)基址 并有一个用于循环缓冲区设置的寄存器 缓冲区(FIFO)设置寄存器 并且有一个用于写入循环 FIFO 的指针和一个用于从循环 FIFO 读取的指针 每次写入时,写入指针都会由硬件递增。 读写指针寄存器 请看图片 当写指针的大小超过设定限制时调用中断 如何从循环 FIFO 中读取而不重叠?

我使用了下面的代码,但是它不能很好地工作,有时会从内存中将错误的数据写入文件中,并且这部分数据不是由硬件写入内存中的。 注意:如果写指针 >= BUFFER_MAX_SIZE+16,则写指针返回零

#include <linux/module.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/iommu-helper.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/vmalloc.h>
#include <linux/kthread.h>
#include <linux/socket.h>
#include <net/sock.h>

#define TSC_BASE_ADDRESS    0x01C06000
#define TSG_BASE_ADDRESS    0x01C06040
#define TSF_BASE_ADDRESS    0x01C06080
#define TSD_BASE_ADDRESS    0x01C06180

#define BUFFER_MAX_SIZE     1048576
void __iomem* tscBaseAddress;

volatile u8 *bufferBaseAddress;

volatile u32 *TSF_CBWPR;
volatile u32 *TSF_CBRPR;
volatile u32 *TSF_CBBAR;
volatile u32 *TSF_CBSZR

volatile int flag=0;
volatile int record_flag=1;
volatile int read_pointer;
volatile int write_pointer;

int fbuffer_len=0;
unsigned long long offset=0;
struct file *f;

struct task_struct *thread_st;

int thread_fn(void *unused) 
{
    int i=0,len;
    unsigned char *fbuffer=kmalloc(BUFFER_MAX_SIZE+16,GFP_KERNEL);
    while (record_flag)
    {
        while(!flag){};
        flag=0;
        len=write_pointer-read_pointer;
        if(len<0)
            len=(BUFFER_MAX_SIZE-read_pointer)+write_pointer;
        fbuffer_len=len;
        while(len>0)
        {
            fbuffer[i++]=*(bufferBaseAddress+read_pointer);
            len--;
            read_pointer++;
            if(read_pointer>=(BUFFER_MAX_SIZE+16))
                read_pointer=0;
        }
        *TSF_CBRPR=read_pointer;
        file_write(f,offset,fbuffer,fbuffer_len);
        offset+=fbuffer_len;
        i=0;
    }
    record_flag=1;
    return 0;
}

static irqreturn_t tsc_irq_handler(int irq,void *dev_id) 
{
    read_pointer  = *TSF_CBRPR;
    write_pointer = *TSF_CBWPR;
    *TSF_DISR=0x00000001;//clear interrupt
    flag=1;
    return IRQ_HANDLED;
}

static int tsc_init(void)
{
    int ret;
    u32 tmp;
    unsigned long value;
    tscBaseAddress = ioremap(TSC_BASE_ADDRESS, 4096);
    if (tscBaseAddress == NULL)
    {
        printk("Failed to map memory to TSC\n");
        return -1;
    }
    
    TSF_CBBAR     = (volatile u32 *)(tscBaseAddress+0x80+0x50);
    TSF_CBSZR     = (volatile u32 *)(tscBaseAddress+0x80+0x54);
    TSF_CBWPR     = (volatile u32 *)(tscBaseAddress+0x80+0x58);
    TSF_CBRPR     = (volatile u32 *)(tscBaseAddress+0x80+0x5C);
    
    printk("TSC Successfully mapped in memory\n");
    
    ret = request_irq(113, tsc_irq_handler, IRQ_NONE, "TSC Driver", (void *)(tsc_irq_handler));
    if (ret < 0) 
    {
        printk(KERN_ALERT "%s: request_irg failed with %d\n",__func__, ret);
    }
    printk("IRQ TSC done\n");
    //--------------------------------------------------------------------------------
    printk("TSF_CBWPR:%x\n",*TSF_CBWPR);
    *TSF_CBWPR=0x00000000;
    mdelay(100);
    printk("TSF_CBWPR:%x\n",*TSF_CBWPR);
    
    printk("TSF_CBRPR:%x\n",*TSF_CBRPR);
    *TSF_CBRPR=0x00000000;
    mdelay(100);
    printk("TSF_CBRPR:%x\n",*TSF_CBRPR);

    printk("TSF_CBSZR:%x\n",*TSF_CBSZR);
    *TSF_CBSZR=0x02100000;
    mdelay(100);
    printk("TSF_CBSZR:%x\n",*TSF_CBSZR);
    //--------------------------------------------------------------------------------
    printk("TSF_CBBAR:%x\n",*TSF_CBBAR);
    value=(*TSF_CBBAR);
    bufferBaseAddress     = (volatile u8 *)(value);
    //--------------------------------------------------------------------------------
    thread_st = kthread_run(thread_fn, NULL, "my_kthread");
        if (IS_ERR(thread_st)) 
    {
            printk(KERN_ERR "Error creating thread\n");
            return PTR_ERR(thread_st);
        }
    printk(KERN_INFO "Kernel Thread Created\n");
    f=file_open("/root/Desktop/a.ts",O_WRONLY|O_CREAT, 0644);
    if (IS_ERR(f)) 
    {
            printk(KERN_ERR "Failed to create file: %ld\n", PTR_ERR(f));
            return PTR_ERR(f);
        }
    printk("Create file ok\n");
    offset=0;
    return 0;
}

static void tsc_exit(void)
{
    free_irq(113,(void *)(tsc_irq_handler));
    if (client_socket) 
        sock_release(client_socket);
        if (listen_socket) 
        sock_release(listen_socket);
    flag=1;
    record_flag=0;
    file_close(f);
}
c linux-kernel circular-buffer
1个回答
0
投票

这个驱动程序有很多设计问题。

你的驱动程序的初始化不连贯。使用输出文件的线程是在文件打开之前创建的。

线程和 ISR 之间基于全局

flag
变量设置的同步是危险的。您不能假设变量的设置和检查是原子的。因此,可能会出现很多问题。一些例子:

  • 当线程使用
    read_pointer
    write_pointer
    运行循环时,ISR 可能会被触发并并行修改它们;
  • 当 ISR 将
    flag
    设置为 1 时,线程可能在将标志设置为 0 之前的行运行。因此,它不会看到标志设置为 1。

您应该使用工作队列 API,它使 ISR 触发器成为读取和记录传入数据的函数。

© www.soinside.com 2019 - 2024. All rights reserved.