task.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,381 行 · 第 1/4 页

C
2,381
字号
/* * linux/arch/arm/mach-omap/dsp/task.c * * OMAP DSP task device driver * * Copyright (C) 2002-2004 Nokia Corporation * * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com> * mmap function by Hiroo Ishikawa <ext-hiroo.ishikawa@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: task.c * $Revision: 3.0.1 * $Date: 2004/10/04 * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/major.h>#include <linux/fs.h>#include <linux/poll.h>#include <linux/devfs_fs_kernel.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/proc_fs.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/signal.h>#include <asm/irq.h>#include <asm/ioctls.h>#include <asm/arch/dsp.h>#include "uaccess_dsp.h"#include "dsp.h"#include "ipbuf.h"#include "fifo.h"#include "proclist.h"struct taskdev {	int enable;	int openstat;	unsigned int usecount;	char name[OMAP_DSP_TNM_LEN];	struct file_operations fops;	wait_queue_head_t open_wait_q;	struct semaphore dld_sem;	struct dsptask *task;	struct proc_dir_entry *procent_dir;};struct rcvdt_bk_struct {	struct ipblink link;	unsigned int rp;	struct ipbuf_p *ipbuf_pvt_r;};struct dsptask {	int cfgstat;	struct list_head proc_list;	unsigned char tid;	char name[OMAP_DSP_TNM_LEN];	unsigned short ttyp;	/* read stuff */	union {		struct fifo_struct fifo;	/* for active word */		struct rcvdt_bk_struct bk;	} rcvdt;	wait_queue_head_t read_wait_q;#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	struct semaphore read_sem;#endif	/* write stuff */	size_t wsz;	struct ipbuf_p *ipbuf_pvt_w;	/* for private block */	wait_queue_head_t write_wait_q;	/* for active */#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	struct semaphore write_sem;#endif	/* ioctl stuff */	int tctl_stat;	wait_queue_head_t ioctl_wait_q;#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	struct semaphore ioctl_sem;#endif	/* mmap stuff */	void *map_base;	size_t map_length;#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	struct semaphore task_sem;	pid_t lock_pid;#endif};#define sndtyp_acv(ttyp)	((ttyp) & OMAP_DSP_TTYP_ASND)#define sndtyp_psv(ttyp)	(!((ttyp) & OMAP_DSP_TTYP_ASND))#define sndtyp_bk(ttyp)		((ttyp) & OMAP_DSP_TTYP_BKDM)#define sndtyp_wd(ttyp)		(!((ttyp) & OMAP_DSP_TTYP_BKDM))#define sndtyp_pvt(ttyp)	((ttyp) & OMAP_DSP_TTYP_PVDM)#define sndtyp_gbl(ttyp)	(!((ttyp) & OMAP_DSP_TTYP_PVDM))#define rcvtyp_acv(ttyp)	((ttyp) & OMAP_DSP_TTYP_ARCV)#define rcvtyp_psv(ttyp)	(!((ttyp) & OMAP_DSP_TTYP_ARCV))#define rcvtyp_bk(ttyp)		((ttyp) & OMAP_DSP_TTYP_BKMD)#define rcvtyp_wd(ttyp)		(!((ttyp) & OMAP_DSP_TTYP_BKMD))#define rcvtyp_pvt(ttyp)	((ttyp) & OMAP_DSP_TTYP_PVMD)#define rcvtyp_gbl(ttyp)	(!((ttyp) & OMAP_DSP_TTYP_PVMD))static __inline__ int down_tasksem_interruptible(struct dsptask *task,						 struct semaphore *sem){	int sem_st;	if (task->lock_pid == current->pid) {		/* this process has lock */		sem_st = down_interruptible(sem);	} else {		if ((sem_st = down_interruptible(&task->task_sem)) < 0)			return sem_st;		sem_st = down_interruptible(sem);		up(&task->task_sem);	}	return sem_st;}static int omap_dsp_rmdev_minor(unsigned char minor);static int omap_dsp_taskdev_init(struct taskdev *dev, char *name,				 unsigned char minor);static void omap_dsp_taskdev_delete(unsigned char minor);static void omap_dsp_taskdev_attach_task(struct taskdev *dev,					 struct dsptask *task);static void omap_dsp_taskdev_detach_task(struct taskdev *dev);#ifdef CONFIG_PROC_FSstatic void omap_dsp_taskdev_create_proc(struct taskdev *dev);static void omap_dsp_taskdev_remove_proc(struct taskdev *dev);#endifstatic struct taskdev *taskdev[TASKDEV_MAX];static struct dsptask *dsptask[TASKDEV_MAX];/* * tcfg_wait_q is used by tcfg, tadd and tdel (i.e. in ctl device * context).  It is reasonable because it is guaranteed that the ctl * device is not opened by two or more processes at any moment. */static wait_queue_head_t tcfg_wait_q;static unsigned short tcfg_wait_cmd = 0;static int taskmod_cfgstat = CFGSTAT_ERR;static unsigned char tadd_tid = OMAP_DSP_TID_ANON;static unsigned char tdel_tid = OMAP_DSP_TID_ANON;static unsigned char n_task = 0;static void *heap = NULL;#ifdef CONFIG_PROC_FSstruct proc_dir_entry *procdir_dsptask = NULL;#endifstatic int omap_dsp_task_flush_buf(struct dsptask *task){	unsigned short ttyp = task->ttyp;	preempt_disable();	if (rcvtyp_wd(ttyp)) {		/* word receiving */		flush_fifo(&task->rcvdt.fifo);	} else {		/* block receiving */		struct rcvdt_bk_struct *rcvdt = &task->rcvdt.bk;		if (rcvtyp_gbl(ttyp)) {			/* global IPBUF */			while (!ipblink_empty(&rcvdt->link)) {				unsigned short bid = rcvdt->link.top;				disable_irq(INT_D2A_MB1);				ipblink_del_top(&rcvdt->link, ipbuf);				enable_irq(INT_D2A_MB1);				unuse_ipbuf(bid, MBSENDTYPE_NORMAL);			}		} else {			/* private IPBUF */			if (!ipblink_empty(&rcvdt->link)) {				ipblink_del_pvt(&rcvdt->link);				release_ipbuf_pvt(rcvdt->ipbuf_pvt_r);			}		}	}	preempt_enable();	return 0;}static int omap_dsp_task_set_fifosz(struct dsptask *task, unsigned long sz){	unsigned short ttyp = task->ttyp;	struct fifo_struct *fifo = &task->rcvdt.fifo;	int stat;	if (!(sndtyp_wd(ttyp) && sndtyp_acv(ttyp))) {		printk(KERN_ERR		       "omapdsp: buffer size can be changed only for "		       "active word sending task.\n");		return -EINVAL;	}	if (sz == 0) {		printk(KERN_ERR "omapdsp: buffer size shouldn't be zero!\n");		return -EINVAL;	}	preempt_disable();	if (!fifo_empty(fifo)) {		printk(KERN_ERR "omapdsp: buffer is not empty!\n");		preempt_enable();		return -EINVAL;	}	if (sz & 0x1) {		/* force even value */		sz++;	}	free_fifo(fifo);	stat = alloc_fifo(fifo, sz);	preempt_enable();	if (stat < 0) {		printk(KERN_ERR		       "omapdsp: unable to change receive buffer size. "		       "(%ld bytes for %s)\n", sz, task->name);		return -ENOMEM;	}	return 0;}static int omap_dsp_task_lock(struct dsptask *task){#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	int sem_st;	if ((sem_st = down_interruptible(&task->task_sem)) < 0)		return sem_st;	task->lock_pid = current->pid;#endif	return 0;}static int omap_dsp_task_unlock(struct dsptask *task){#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	if (task->lock_pid != current->pid) {		printk(KERN_ERR		       "omapdsp: an illegal process attempted to "		       "unlock the dsptask lock!\n");		return -EINVAL;	}	task->lock_pid = 0;	up(&task->task_sem);#endif	return 0;}static int omap_dsp_task_config(struct dsptask *task, unsigned char tid){	unsigned short ttyp;	dsptask[tid] = task;	task->tid = tid;	INIT_LIST_HEAD(&task->proc_list);	init_waitqueue_head(&task->read_wait_q);	init_waitqueue_head(&task->write_wait_q);	init_waitqueue_head(&task->ioctl_wait_q);#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	init_MUTEX(&task->read_sem);	init_MUTEX(&task->write_sem);	init_MUTEX(&task->ioctl_sem);	init_MUTEX(&task->task_sem);	task->lock_pid = 0;#endif	/* TCFG request */	task->cfgstat = CFGSTAT_GOING;	tcfg_wait_cmd = MBCMD(TCFG);	omap_dsp_mbsend_and_wait(MBCMD(TCFG), tid, 0, &tcfg_wait_q);	if (task->cfgstat != CFGSTAT_DONE) {		printk(KERN_ERR "omapdsp: task %d configuration error!\n", tid);		return -EINVAL;	}	if (strlen(task->name) <= 1)		sprintf(task->name, "%d", tid);	printk(KERN_INFO "omapdsp: task %d: name %s\n", tid, task->name);	ttyp = task->ttyp;	/* task type check */	if (rcvtyp_psv(ttyp) && rcvtyp_pvt(ttyp)) {		printk(KERN_ERR		       "mailbox: illegal task type(0x%04x), tid=%d\n",		       tid, ttyp);	}	/* private buffer address check */	if (sndtyp_pvt(ttyp)) {		void *p = task->rcvdt.bk.ipbuf_pvt_r;		if ((unsigned long)p & 0x1) {			printk(KERN_ERR			       "mailbox: private ipbuf (DSP->ARM) "			       "address (0x%p) is odd number!\n", p);			return -EINVAL;		}	}	if (rcvtyp_pvt(ttyp)) {		void *p = task->ipbuf_pvt_w;		if ((unsigned long)p & 0x1) {			printk(KERN_ERR			       "mailbox: private ipbuf (ARM->DSP) "			       "address (0x%p) is odd number!\n", p);			return -EINVAL;		}	}	/* read initialization */	if (sndtyp_wd(ttyp)) {		/* word */		size_t fifosz;		fifosz = sndtyp_psv(ttyp) ? 2 :	/* passive */					    32;	/* active */		if (alloc_fifo(&task->rcvdt.fifo, fifosz) < 0) {			printk(KERN_ERR			       "omapdsp: unable to allocate "			       "receive buffer. (%d bytes for %s)\n",			       fifosz, task->name);			return -ENOMEM;		}	} else {		/* block */		INIT_IPBLINK(&task->rcvdt.bk.link);		task->rcvdt.bk.rp = 0;	}	/* write initialization */	task->wsz = rcvtyp_acv(ttyp) ? 0 :		/* active */		    rcvtyp_wd(ttyp)  ? 2 :		/* passive word */		    		       ipbcfg.lsz*2;	/* passive block */	omap_dsp_mbsend(MBCMD(TCTL), tid, OMAP_DSP_MBCMD_TCTL_TINIT);	return 0;}int omap_dsp_task_config_all(unsigned char n){	int i, ret;	struct taskdev *devheap;	struct dsptask *taskheap;	size_t devheapsz, taskheapsz;	memset(taskdev, 0, sizeof(void *) * TASKDEV_MAX);	memset(dsptask, 0, sizeof(void *) * TASKDEV_MAX);	n_task = n;	printk(KERN_INFO "omapdsp: found %d task(s)\n", n_task);	if (n_task == 0) {		taskmod_cfgstat = CFGSTAT_DONE;		return 0;	}	/*	 * reducing kmalloc!	 */	devheapsz  = sizeof(struct taskdev) * n_task;	taskheapsz = sizeof(struct dsptask) * n_task;	heap = kmalloc(devheapsz + taskheapsz, GFP_KERNEL);	if (heap == NULL) {		n_task = 0;		return -ENOMEM;	}	memset(heap, 0, devheapsz + taskheapsz);	devheap  = heap;	taskheap = heap + devheapsz;	for (i = 0; i < n_task; i++) {		struct taskdev *dev  = &devheap[i];		struct dsptask *task = &taskheap[i];		if ((ret = omap_dsp_task_config(task, i)) < 0)			return ret;		if ((ret = omap_dsp_taskdev_init(dev, task->name, i)) < 0)			return ret;		omap_dsp_taskdev_attach_task(dev, task);	}	taskmod_cfgstat = CFGSTAT_DONE;	return 0;}static void omap_dsp_task_unconfig(struct dsptask *task){	unsigned char tid = task->tid;	preempt_disable();	proc_list_flush(&task->proc_list);	if (sndtyp_wd(task->ttyp) && (task->cfgstat == CFGSTAT_DONE))		free_fifo(&task->rcvdt.fifo);	dsptask[tid] = NULL;	preempt_enable();}void omap_dsp_task_unconfig_all(void){	unsigned char minor;	unsigned char tid;	struct dsptask *task;	for (minor = 0; minor < n_task; minor++) {		/*		 * taskdev[minor] can be NULL in case of		 * configuration failure		 */		if (taskdev[minor])			omap_dsp_taskdev_delete(minor);	}	for (; minor < TASKDEV_MAX; minor++) {		if (taskdev[minor])			omap_dsp_rmdev_minor(minor);	}	for (tid = 0; tid < n_task; tid++) {		/*		 * dsptask[tid] can be NULL in case of		 * configuration failure		 */		task = dsptask[tid];		if (task)			omap_dsp_task_unconfig(task);	}	for (; tid < TASKDEV_MAX; tid++) {		task = dsptask[tid];		if (task) {			/*			 * on-demand tasks should be deleted in			 * rmdev_minor(), but just in case.			 */			omap_dsp_task_unconfig(task);			kfree(task);		}	}	if (heap) {		kfree(heap);		heap = NULL;	}	n_task = 0;	taskmod_cfgstat = CFGSTAT_ERR;}unsigned char dsp_task_count(void){	return n_task;}int omap_dsp_taskmod_busy(void){	struct taskdev *dev;	unsigned char minor;	for (minor = 0; minor < TASKDEV_MAX; minor++) {		dev = taskdev[minor];		if (dev &&		    ((dev->usecount > 0) ||		     waitqueue_active(&dev->open_wait_q)))			return 1;	}	return 0;}/* * DSP task device file operations */static ssize_t omap_dsp_task_read_wd_acv(struct file *file, char *buf,					 size_t count, loff_t *ppos){	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);	struct dsptask *task = taskdev[minor]->task;	struct fifo_struct *fifo = &task->rcvdt.fifo;#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	int sem_st;#endif	int ret = 0;	if (count == 0) {		return 0;	} else if (count == 1) {		printk(KERN_ERR		       "omapdsp: count == 1 is illegal "		       "for omap_dsp_task_read().\n");		return -EINVAL;	} else if (count & 0x1) {		/* force even value */		count--;	}#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	if ((sem_st = down_tasksem_interruptible(task, &task->read_sem)) < 0)		return sem_st;#endif	if (fifo_empty(fifo)) {		long current_state;		DECLARE_WAITQUEUE(wait, current);		add_wait_queue(&task->read_wait_q, &wait);		current_state = current->state;		set_current_state(TASK_INTERRUPTIBLE);		if (fifo_empty(fifo))	/* last check */			schedule();		set_current_state(current_state);		remove_wait_queue(&task->read_wait_q, &wait);		if (fifo_empty(fifo))			goto up_out;	}	/* data protection while copying */	disable_irq(INT_D2A_MB1);	ret = copy_to_user_fm_fifo(buf, fifo, count);	enable_irq(INT_D2A_MB1);up_out:#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	up(&task->read_sem);#endif	return ret;}static ssize_t omap_dsp_task_read_bk_acv(struct file *file, char *buf,					 size_t count, loff_t *ppos){	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);	struct dsptask *task = taskdev[minor]->task;	struct rcvdt_bk_struct *rcvdt = &task->rcvdt.bk;	int dspmem_access;#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	int sem_st;#endif	ssize_t ret = 0;	if (count == 0) {		return 0;	} else if (count == 1) {		printk(KERN_ERR		       "omapdsp: count == 1 is illegal "		       "for omap_dsp_task_read().\n");		return -EINVAL;	} else if ((int)buf & 0x1) {		printk(KERN_ERR		       "omapdsp: buf should be word aligned "		       "for omap_dsp_task_read().\n");		return -EINVAL;	} else if (count & 0x1) {		/* force even value */		count--;	}#ifdef CONFIG_OMAP_DSP_TASK_MULTIOPEN	if ((sem_st = down_tasksem_interruptible(task, &task->read_sem)) < 0)		return sem_st;#endif	if (ipblink_empty(&rcvdt->link)) {		long current_state;		DECLARE_WAITQUEUE(wait, current);		add_wait_queue(&task->read_wait_q, &wait);		current_state = current->state;		set_current_state(TASK_INTERRUPTIBLE);		if (ipblink_empty(&rcvdt->link))	/* last check */

⌨️ 快捷键说明

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