ipbuf.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 334 行

C
334
字号
/* * linux/arch/arm/mach-omap/dsp/ipbuf.c * * IPBUF handler * * Copyright (C) 2002-2004 Nokia Corporation * * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: ipbuf.c * $Revision: 3.0.1 * $Date: 2004/06/29 * */#include <linux/init.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <asm/signal.h>#include <asm/arch/dsp.h>#include "dsp.h"#include "ipbuf.h"struct ipbuf **ipbuf;struct ipbcfg ipbcfg = {	.ln = 0,};struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad;static struct ipblink ipb_free = {	.top  = OMAP_DSP_BID_NULL,	.tail = OMAP_DSP_BID_NULL,};/* * Global IPBUF operations */int omap_dsp_ipbuf_config(unsigned short ln, unsigned short lsz,			 unsigned long adr, unsigned short dspkp){	void *base;	unsigned long lsz_byte = ((unsigned long)lsz) << 1;	size_t size;	int ret = 0;	int i;	/*	 * global IPBUF	 */	if (adr & 0x1) {		printk(KERN_ERR		       "mailbox: global ipbuf address (0x%08lx) is "		       "odd number!\n", adr);		return -EINVAL;	}	size = lsz_byte * ln;	if (adr + size > DSPSPACE_SIZE) {		printk(KERN_ERR		       "mailbox: ipbuf address (0x%08lx) and "		       "size (0x%08x) is illegal!\n", adr, size);		return -EINVAL;	}	base = dspword_to_virt(adr);	ipbuf = kmalloc(sizeof(void *) * ln, GFP_KERNEL);	if (ipbuf == NULL) {		printk(KERN_ERR		       "mailbox: memory allocation for ipbuf failed.\n");		return -ENOMEM;	}	for (i = 0; i < ln; i++) {		void *top, *btm;		top = base + (sizeof(struct ipbuf) + lsz_byte) * i;		btm = base + (sizeof(struct ipbuf) + lsz_byte) * (i+1) - 1;		ipbuf[i] = (struct ipbuf *)top;		if (((unsigned long)top & 0xfffe0000) !=		    ((unsigned long)btm & 0xfffe0000)) {			/*			 * an ipbuf line should not cross			 * 64k-word boundary.			 */			printk(KERN_ERR			       "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n"			       "  @0x%p, size=0x%08lx\n", i, top, lsz_byte);			ret = -EINVAL;		}	}	ipbcfg.ln       = ln;	ipbcfg.lsz      = lsz;	ipbcfg.adr      = adr;	ipbcfg.dspkp    = dspkp;	ipbcfg.bsycnt   = ln;	/* DSP holds all ipbufs initially. */	ipbcfg.cnt_full = 0;	INIT_IPBLINK(&ipb_free);	return ret;}void omap_dsp_ipbuf_unconfig(void){	ipbcfg.ln = 0;	INIT_IPBLINK(&ipb_free);}void omap_dsp_ipbuf_pcfg(void){	printk(KERN_INFO	       "omapdsp: IPBUF configuration\n"	       "           %d words * %d lines at 0x%p\n"	       "           %d lines are reserved for DSP.\n",	       ipbcfg.lsz, ipbcfg.ln, dspword_to_virt(ipbcfg.adr),	       ipbcfg.dspkp);}int is_ipbuf_internal_mem(void){	return is_dspword_internal_mem(ipbcfg.adr);}unsigned short get_free_ipbuf(unsigned char tid){	unsigned short bid;	if (ipblink_empty(&ipb_free)) {		/*		 * FIXME: wait on queue when not available,		 * but note that balance_ipbuf() also uses this routine		 * and in that case we should return immediately.		 */		return OMAP_DSP_BID_NULL;	}	/*	 * FIXME: omap_dsp_enable_dspmem!	 */	preempt_disable();	disable_irq(INT_D2A_MB1);	bid = ipb_free.top;	ipbuf[bid]->la = tid;	/* lock */	ipblink_del_top(&ipb_free, ipbuf);	enable_irq(INT_D2A_MB1);	preempt_enable();	return bid;}void release_ipbuf(unsigned short bid){	if (ipbuf[bid]->la == OMAP_DSP_TID_FREE) {		printk(KERN_WARNING		       "omapdsp: attempt to release unlocked IPBUF[%d].\n",		       bid);		/*		 * FIXME: re-calc bsycnt		 */		return;	}	ipbuf[bid]->la = OMAP_DSP_TID_FREE;	ipbuf[bid]->sa = OMAP_DSP_TID_FREE;	preempt_disable();	disable_irq(INT_D2A_MB1);	ipblink_add_tail(&ipb_free, bid, ipbuf);	enable_irq(INT_D2A_MB1);	preempt_enable();}static int try_yld(unsigned short bid, int sendtype){	int status;	ipbuf[bid]->sa = OMAP_DSP_TID_ANON;	status = __omap_dsp_mbsend(MBCMD(BKYLD), 0, bid, 0, 0, NULL, sendtype);	if (status < 0) {		/* DSP is busy and ARM keeps this line. */		release_ipbuf(bid);		return status;	}	ipb_bsycnt_inc(&ipbcfg);	return 0;}/* * balancing ipbuf lines with DSP */void balance_ipbuf(int sendtype){	while (ipbcfg.bsycnt < ipbcfg.dspkp) {		unsigned short bid;		bid = get_free_ipbuf(OMAP_DSP_TID_ANON);		if (bid == OMAP_DSP_BID_NULL)			return;		if (try_yld(bid, sendtype) < 0)			return;	}}/* * interrupt routine should call this function with * sendtype = MBSENDTYPE_NOWAIT */void unuse_ipbuf(unsigned short bid, int sendtype){	if (ipbcfg.bsycnt >= ipbcfg.dspkp) {		release_ipbuf(bid);		return;	}	/* try to return this line */	ipbuf[bid]->la = OMAP_DSP_TID_ANON;	if (try_yld(bid, sendtype) < 0)		return;	if (sendtype != MBSENDTYPE_NOWAIT)		balance_ipbuf(sendtype);}/* * functions called from mailbox1 interrupt routine */void mailbox1_err_ipbfull(void){	ipbcfg.cnt_full++;}#ifdef CONFIG_PROC_FS/* * proc file entry */static int omap_dsp_proc_ipbuf_read(char *page, char **start, off_t off,				    int count, int *eof, void *data){	char *out;	int len;	unsigned short bid;	/*	 * disable process's old pmd entry	 */	omap_dsp_map_update(current);	omap_dsp_cur_users_add(current);	out = page;	for (bid = 0; bid < ipbcfg.ln; bid++) {		unsigned short la = ipbuf[bid]->la;		unsigned short ld = ipbuf[bid]->ld;		unsigned short c  = ipbuf[bid]->c;		out += sprintf(out, "ipbuf[%d]: adr = 0x%p\n", bid, ipbuf[bid]);		if (la == OMAP_DSP_TID_FREE) {			out += sprintf(out,				       "  DSPtask[%d]->Linux "				       "(already read and now free for Linux)\n",				       ld);		} else if (ld == OMAP_DSP_TID_FREE) {			out += sprintf(out,				       "  Linux->DSPtask[%d] "				       "(already read and now free for DSP)\n",				       la);		} else if (omap_dsp_ipbuf_held(ld, bid)) {			out += sprintf(out,				       "  DSPtask[%d]->Linux "				       "(waiting to be read)\n"				       "  count = %d\n", ld, c);		} else {			out += sprintf(out,				       "  Linux->DSPtask[%d] "				       "(waiting to be read)\n"				       "  count = %d\n", la, c);		}	}	out += sprintf(out, "\nFree IPBUF link: ");	preempt_disable();	ipblink_for_each(bid, &ipb_free, ipbuf) {		out += sprintf(out, "%d ", bid);	}	preempt_enable();	out += sprintf(out, "\n");	out += sprintf(out, "IPBFULL error count: %ld\n", ipbcfg.cnt_full);	len = out - page;	len -= off;	if (len < count) {		*eof = 1;		if (len <= 0) {			len = 0;			goto finish;		}	} else {		len = count;	}	*start = page + off;finish:	omap_dsp_cur_users_del(current);	return len;}void omap_dsp_create_ipbuf_proc(void){	struct proc_dir_entry *ent;	ent = create_proc_read_entry("ipbuf", 0, procdir_dsp,				     omap_dsp_proc_ipbuf_read, NULL);	if (ent == NULL) {		printk(KERN_ERR		       "omapdsp: failed to register proc device: ipbuf\n");	}}void omap_dsp_remove_ipbuf_proc(void){	remove_proc_entry("ipbuf", procdir_dsp);}#endif /* CONFIG_PROC_FS */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?