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 + -
显示快捷键?