我有一个硬件,可以通过硬件将输入数据写入某个地址 基地址或起始循环 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);
}
这个驱动程序有很多设计问题。
你的驱动程序的初始化不连贯。使用输出文件的线程是在文件打开之前创建的。
线程和 ISR 之间基于全局
flag
变量设置的同步是危险的。您不能假设变量的设置和检查是原子的。因此,可能会出现很多问题。一些例子:
read_pointer
和 write_pointer
运行循环时,ISR 可能会被触发并并行修改它们;flag
设置为 1 时,线程可能在将标志设置为 0 之前的行运行。因此,它不会看到标志设置为 1。您应该使用工作队列 API,它使 ISR 触发器成为读取和记录传入数据的函数。