linux/sound/core/seq/oss/seq_oss_rw.c
<<
>>
Prefs
   1/*
   2 * OSS compatible sequencer driver
   3 *
   4 * read/write/select interface to device file
   5 *
   6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  21 */
  22
  23#include "seq_oss_device.h"
  24#include "seq_oss_readq.h"
  25#include "seq_oss_writeq.h"
  26#include "seq_oss_synth.h"
  27#include <sound/seq_oss_legacy.h>
  28#include "seq_oss_event.h"
  29#include "seq_oss_timer.h"
  30#include "../seq_clientmgr.h"
  31
  32
  33/*
  34 * protoypes
  35 */
  36static int insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt);
  37
  38
  39/*
  40 * read interface
  41 */
  42
  43int
  44snd_seq_oss_read(struct seq_oss_devinfo *dp, char __user *buf, int count)
  45{
  46        struct seq_oss_readq *readq = dp->readq;
  47        int result = 0, err = 0;
  48        int ev_len;
  49        union evrec rec;
  50        unsigned long flags;
  51
  52        if (readq == NULL || ! is_read_mode(dp->file_mode))
  53                return -ENXIO;
  54
  55        while (count >= SHORT_EVENT_SIZE) {
  56                snd_seq_oss_readq_lock(readq, flags);
  57                err = snd_seq_oss_readq_pick(readq, &rec);
  58                if (err == -EAGAIN &&
  59                    !is_nonblock_mode(dp->file_mode) && result == 0) {
  60                        snd_seq_oss_readq_unlock(readq, flags);
  61                        snd_seq_oss_readq_wait(readq);
  62                        snd_seq_oss_readq_lock(readq, flags);
  63                        if (signal_pending(current))
  64                                err = -ERESTARTSYS;
  65                        else
  66                                err = snd_seq_oss_readq_pick(readq, &rec);
  67                }
  68                if (err < 0) {
  69                        snd_seq_oss_readq_unlock(readq, flags);
  70                        break;
  71                }
  72                ev_len = ev_length(&rec);
  73                if (ev_len < count) {
  74                        snd_seq_oss_readq_unlock(readq, flags);
  75                        break;
  76                }
  77                snd_seq_oss_readq_free(readq);
  78                snd_seq_oss_readq_unlock(readq, flags);
  79                if (copy_to_user(buf, &rec, ev_len)) {
  80                        err = -EFAULT;
  81                        break;
  82                }
  83                result += ev_len;
  84                buf += ev_len;
  85                count -= ev_len;
  86        }
  87        return result > 0 ? result : err;
  88}
  89
  90
  91/*
  92 * write interface
  93 */
  94
  95int
  96snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count, struct file *opt)
  97{
  98        int result = 0, err = 0;
  99        int ev_size, fmt;
 100        union evrec rec;
 101
 102        if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
 103                return -ENXIO;
 104
 105        while (count >= SHORT_EVENT_SIZE) {
 106                if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) {
 107                        err = -EFAULT;
 108                        break;
 109                }
 110                if (rec.s.code == SEQ_FULLSIZE) {
 111                        /* load patch */
 112                        if (result > 0) {
 113                                err = -EINVAL;
 114                                break;
 115                        }
 116                        fmt = (*(unsigned short *)rec.c) & 0xffff;
 117                        /* FIXME the return value isn't correct */
 118                        return snd_seq_oss_synth_load_patch(dp, rec.s.dev,
 119                                                            fmt, buf, 0, count);
 120                }
 121                if (ev_is_long(&rec)) {
 122                        /* extended code */
 123                        if (rec.s.code == SEQ_EXTENDED &&
 124                            dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
 125                                err = -EINVAL;
 126                                break;
 127                        }
 128                        ev_size = LONG_EVENT_SIZE;
 129                        if (count < ev_size)
 130                                break;
 131                        /* copy the reset 4 bytes */
 132                        if (copy_from_user(rec.c + SHORT_EVENT_SIZE,
 133                                           buf + SHORT_EVENT_SIZE,
 134                                           LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) {
 135                                err = -EFAULT;
 136                                break;
 137                        }
 138                } else {
 139                        /* old-type code */
 140                        if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
 141                                err = -EINVAL;
 142                                break;
 143                        }
 144                        ev_size = SHORT_EVENT_SIZE;
 145                }
 146
 147                /* insert queue */
 148                if ((err = insert_queue(dp, &rec, opt)) < 0)
 149                        break;
 150
 151                result += ev_size;
 152                buf += ev_size;
 153                count -= ev_size;
 154        }
 155        return result > 0 ? result : err;
 156}
 157
 158
 159/*
 160 * insert event record to write queue
 161 * return: 0 = OK, non-zero = NG
 162 */
 163static int
 164insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
 165{
 166        int rc = 0;
 167        struct snd_seq_event event;
 168
 169        /* if this is a timing event, process the current time */
 170        if (snd_seq_oss_process_timer_event(dp->timer, rec))
 171                return 0; /* no need to insert queue */
 172
 173        /* parse this event */
 174        memset(&event, 0, sizeof(event));
 175        /* set dummy -- to be sure */
 176        event.type = SNDRV_SEQ_EVENT_NOTEOFF;
 177        snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
 178
 179        if (snd_seq_oss_process_event(dp, rec, &event))
 180                return 0; /* invalid event - no need to insert queue */
 181
 182        event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
 183        if (dp->timer->realtime || !dp->timer->running) {
 184                snd_seq_oss_dispatch(dp, &event, 0, 0);
 185        } else {
 186                if (is_nonblock_mode(dp->file_mode))
 187                        rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
 188                else
 189                        rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
 190        }
 191        return rc;
 192}
 193                
 194
 195/*
 196 * select / poll
 197 */
 198  
 199__poll_t
 200snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait)
 201{
 202        __poll_t mask = 0;
 203
 204        /* input */
 205        if (dp->readq && is_read_mode(dp->file_mode)) {
 206                if (snd_seq_oss_readq_poll(dp->readq, file, wait))
 207                        mask |= EPOLLIN | EPOLLRDNORM;
 208        }
 209
 210        /* output */
 211        if (dp->writeq && is_write_mode(dp->file_mode)) {
 212                if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
 213                        mask |= EPOLLOUT | EPOLLWRNORM;
 214        }
 215        return mask;
 216}
 217