TI中文支持网
TI专业的中文技术问题搜集分享网站

DSP与ARM核通信的诡异现象

项目要求DSP不跑操作系统,arm核上linux3.3,在做双核通信的linux设备驱动时候遇到了一个诡异的问题,只要DSP通过CHIPSIG_INT0触发ARM中断,ARM中的linux内核的其他很多驱动都停止了工作,连自己的led控制驱动都失去了响应,流水灯也失去了闪烁,LCD显示也停止了刷新,但是运行GUI还是可以显示界面,就是界面不能刷新,触摸屏也不懂了。好像是其他驱动都阻塞了!!!!

我的linux设备驱动是这样实现的:使用Tasklet实现软中断,只要DSP的数据一准备好就通过CHIPSIG_INT0触发ARM中断,这时候驱动的tasklet顶半部会立刻响应中断去读取数据,然后调度tasklet的底半部,这样的流程一般是没问题才对,但是现象就是只要顶半部被触发了,其他驱动就异常了,linux也没有任何提示,而且无法恢复,触发重启内核,但是唯独这个驱动完全正常运行,数据照样读取也不丢帧,还有一个现象是DSP触发一次CHIPSIG_INT0中断,ARM核会响应两次,也就是顶半部会触发两次调用!!我的驱动实现如下,麻烦帮忙看下。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/types.h>
#include<linux/ioctl.h>
#include<linux/cdev.h>
#include<linux/kdev_t.h>
#include<mach/hardware.h>
#include<linux/gpio.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <mach/da8xx.h>
#include <mach/mux.h>
#include <mach/irqs.h>
#include <linux/interrupt.h>
#include "k_linkQueue.h"
#include "memory_map.h"
#include <linux/wait.h>//for 等待队列api
#include <linux/errno.h>

//引入其他模块函数和变量
extern Ping_Pong_Buffer res_buff;

extern char k_linkQueue_create(linkQueue *queue, unsigned int dataType, unsigned int blockNumber, unsigned int blockSize);//创建链队列
extern void k_linkQueue_release(linkQueue *queue);//释放链队列
extern unsigned int k_linkQueue_insertData(linkQueue *queue, void *data, int force);//链队列插入数据
extern unsigned int k_linkQueue_getData(linkQueue *queue, void *r_buff);//获取队列的数据

extern void SHM_ARM_Init(unsigned char* const mem_base);//共享内存初始化

static int read_quest = 0;//如果有读取数据请求且链队列无数据可读,则置1
//设置主从设备号
#define PROTOCOL_MAJOR 		1
#define PROTOCOL_MINOR 		0



//定义设备驱动的名字或设备节点的名字
#define 	DEVICE_NAME 		"protocol_driver"


//定义全局的循环队列作为数据缓冲区
k_linkQueue queue;

//寄存器地址映射全局变量
unsigned int *R_CHIPSIG = NULL;//CHIPSIG寄存器
unsigned int *R_CHIPSIG_CLR = NULL;//CHIPSIG_CLR寄存器

//物理内存映射全局变量
volatile void *mem_base = NULL;
volatile unsigned char *cur_buf_ptr = NULL;
volatile unsigned char *data_ready_ptr = NULL;



//定义读数据等待队列头,IO阻塞
DECLARE_WAIT_QUEUE_HEAD(wait_queue_head);

//定义原子变量
static atomic_t dev_available = ATOMIC_INIT(1);//原子变量的值为1,即只能一个进程打开此设备


//定义设备类
static struct class *protocol_class;
struct cdev *protocol_cdev;
dev_t protocol_dev_no;

/*定义tasklet和声明底半部函数并关联*/
void read_data_tasklet(unsigned long);

DECLARE_TASKLET(CHIPINT0_tasklet,read_data,0);
//将CHIPINT0_tasklet与read_data绑定,传入参数0

/*中断处理底半部, 拷贝内存*/
void read_data(unsigned long a)
{
	if(read_quest == 1)//如果进程调用了此驱动的read函数且链队列没有数据可读,read_quest将会被赋值为1
	{
		read_quest = 0;
		//通过底半部唤醒被阻塞的读取进程
		wake_up_interruptible(&wait_queue_head);//唤醒读等待队列
	}

}

/*中断处理顶半部*/
irqreturn_t CHIPINT0_interrupt(int irq,void *dev_id)
{
	//只要一触发进入这个程序,其他驱动会出问题,即使这个程序什么都不做也会这样
	volatile Buffer_Type *next_read;
	//如果DSP数据已经ready
	if(*(res_buff.cur_buffer->data_ready) == 1)
	{
		if(*(res_buff.bufferID_ptr) == BUFF_ID_PING)//切换读buffer
		{
			next_read = &res_buff.pong_buffer;//下一次中断读pong buffer
			//printk(KERN_ALERT"read ping\n");
		}
		else
		{
			next_read = &res_buff.ping_buffer;//下一次中断读ping buffer
			//printk(KERN_ALERT"read pong\n");
		}
		*(res_buff.bufferID_ptr) ^= BUFF_ID_PING;//切换DSP写另一个buffer
		//将数据插入链队列
		k_linkQueue_insertData(&queue, res_buff.cur_buffer->buf_ptr, 0);
		//标识位都重置
		*(res_buff.cur_buffer->data_ready) = 0;
		*(res_buff.cur_buffer->data_size) = 0;
		res_buff.cur_buffer = next_read;
	}
	//清楚中断标识
	*R_CHIPSIG_CLR = 1 << 0;//clear chipint0 status
	tasklet_schedule(&CHIPINT0_tasklet);//调度底半部return IRQ_HANDLED;

}

//文件打开函数
static int protocol_open(struct inode *inode, struct file *file)
{
	int result = 0;
	if(!atomic_dec_and_test(&dev_available))
	{
		atomic_inc(&dev_available);
		return -EBUSY;//设备已经被打开
	}
	printk (KERN_ALERT "\nprotrol driver open\n");return 0;
}

//文件释放函数
static int protocol_release(struct inode *inode, struct file *filp)
{
	atomic_inc(&dev_available);//释放设备,原子变量加1
	printk (KERN_ALERT "device released\n");
	return 0;
}

//文件读函数
static int protocol_read(struct file *filp, char *dst, size_t size, loff_t*offset)
{
	int ret = 0;
	//定义等待队列
	DECLARE_WAITQUEUE(wait_queue, current);//定义等待队列
	add_wait_queue(&wait_queue_head, &wait_queue);//添加等待队列
	if(queue.remainNumber == queue.blockNumber)//当前buffer没数据
	{
		//printk(KERN_ALERT"\nbuffer no data\n");
		//如果是非阻塞方式读取,则直接跳出
		if(filp->f_flags & O_NONBLOCK)
		{
			ret = -EAGAIN;
			goto out;
		}
		//阻塞当前进程,放弃cpu资源
		read_quest = 1;
		__set_current_state(TASK_INTERRUPTIBLE);//改变进程状态为睡眠
		schedule();//调度其他进程运行
		if(signal_pending(current))
		{
			//如果是因为信号被唤醒,则返回到系统调用之前的地方
			ret = -ERESTARTSYS;
			goto out;
		}
	}
	//将数据拷贝到用户空间
	ret = k_linkQueue_getData(&queue, dst);
	if(ret == 0)
	{
		//printk(KERN_ALERT"\ncopy data to user space failed :%d\n", ret);
	}
out:
	remove_wait_queue(&wait_queue_head, &wait_queue);//移除等待队列
	set_current_state(TASK_RUNNING);//设置当前进程为运行状态
	return ret;
}


static long protocol_ioctl(struct file *file, unsigned int cmd, unsigned long value)
{
	return 0;
}
/*驱动文件操作结构体,file_operations结构体中的成员函数会在应用程序进行
open()、release()、ioctl()协同调用时被调用*/
static const struct file_operations protocol_fops =
{.owner	= THIS_MODULE,.open		= protocol_open,.release	= protocol_release,.read		= protocol_read,
//  .write	= protocol_write,.unlocked_ioctl=protocol_ioctl,
};

/*设备驱动模块加载函数*/
int __init protocol_init(void)
{
	int ret = 0;
	int result = 0;
	//申请注册设备号(动态)ret=alloc_chrdev_region(&protocol_dev_no, PROTOCOL_MINOR, 1, DEVICE_NAME);if(ret < 0){printk(KERN_EMERG "alloc_chrdev_region failed\n");return 0;}//分配cdevprotocol_cdev = cdev_alloc();if(protocol_cdev == NULL){printk(KERN_EMERG "Cannot alloc cdev\n");return 0;}//初始化cdevcdev_init(protocol_cdev,&protocol_fops);protocol_cdev->owner=THIS_MODULE;//注册cdevcdev_add(protocol_cdev, protocol_dev_no, 1);//创建一个类protocol_class = class_create(THIS_MODULE, DEVICE_NAME);//创建设备节点device_create(protocol_class, NULL, protocol_dev_no, NULL, DEVICE_NAME);//申请链式循环队列作为缓冲区DSP数据帧的缓冲区
	k_linkQueue_create(&queue, sizeof(double), 1000, DATA_BLOCK_SIZE);//申请1000个blocksize的队列空间作为帧缓冲区

//映射ARM的核间通讯寄存器
	R_CHIPSIG = ioremap(SOC_SYSCFG_0_REGS + SYSCFG0_CHIPSIG, 4);
	R_CHIPSIG_CLR = ioremap(SOC_SYSCFG_0_REGS + SYSCFG0_CHIPSIG_CLR, 4);
	//将物理地址映射到内核空间
	mem_base = ioremap(SHARED_BUFFER_ADDR, SHARED_BUFFER_SIZE);
	//共享内存初始化
	SHM_ARM_Init((unsigned char *)mem_base);
	/*申请中断*/

/*
	参数说明:
	IRQ_DA8XX_CHIPINT0:要申请的中断号
	CHIPINT0_interrupt:向内核登记的中断处理程序(顶半部),为一个回调
	IRQF_DISABLED:表明中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断
	"DSP Signal":名称
	NULL:在中断共享时会用到,这里设置为NULL
*/
	result = request_irq(IRQ_DA8XX_CHIPINT0, CHIPINT0_interrupt,IRQF_SHARED,"DSP Signal",&protocol_cdev);
	if(result != 0)
	{
		if(result == -EINVAL)
		{
			printk(KERN_ALERT "irq request err:-EINVAL\n");
		}
		else if(result == -EBUSY)
		{
			printk(KERN_ALERT "irq request err:--EBUSY\n");
		}
		else
		{
			printk(KERN_ALERT "irq request err: unknown\n");
		}
		return result;
	}


	return 0;
}

/*设备驱动模块卸载函数*/
void __exit protocol_exit(void)
{
		/*释放中断*/
	/*
		参数详解:
				IRQ_DA8XX_CHIPINT0:中断号
				NULL:void *dev_id,这个参数是中断共享的时候用到的,此驱动不共享中断,因此为NULL
	*/
	free_irq(IRQ_DA8XX_CHIPINT0, NULL);
	//释放帧缓冲的内存
	k_linkQueue_release(&queue);
	//释放寄存器映射
	iounmap(R_CHIPSIG);
	iounmap(R_CHIPSIG_CLR);
	cdev_del(protocol_cdev);							//删除cdev	unregister_chrdev_region(protocol_dev_no, 1);	//注销设备号	device_destroy(protocol_class, protocol_dev_no);			//销毁设备节点	class_destroy(protocol_class);						//销毁设备类	printk(KERN_ALERT "exit success\n");

}

//驱动被加载或被卸载自动执行的函数
module_init(protocol_init);
module_exit(protocol_exit);

MODULE_LICENSE("GPL");
MODULE_VERSION ("v0.9");
MODULE_AUTHOR("None");
MODULE_DESCRIPTION("driver");
Joker Wu:

没人遇到过吗

Joker Wu:

没有人吗

Joker Wu:

回复 Shine:

非常感谢,触发两次中断的问题已解决,至于其他的问题不是驱动的问题,我自己已经解决并找到原因,是因为我在DSP端调用了DelayTimerSetup();函数,这个函数会改变定时器的一些配置,导致arm linux依赖于此定时器的驱动无法正常工作,比如显示设备等等,请问你们设置startware的时候是否有考虑过这个问题,还是说这是不可避免的,需要程序员自己去控制?

Shine:

回复 Joker Wu:

starterware是裸机程序,不用操作系统的。

赞(0)
未经允许不得转载:TI中文支持网 » DSP与ARM核通信的诡异现象
分享到: 更多 (0)