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 return snd_seq_oss_synth_load_patch(dp, rec.s.dev, 118 fmt, buf, 0, count); 119 } 120 if (ev_is_long(&rec)) { 121 /* extended code */ 122 if (rec.s.code == SEQ_EXTENDED && 123 dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { 124 err = -EINVAL; 125 break; 126 } 127 ev_size = LONG_EVENT_SIZE; 128 if (count < ev_size) 129 break; 130 /* copy the reset 4 bytes */ 131 if (copy_from_user(rec.c + SHORT_EVENT_SIZE, 132 buf + SHORT_EVENT_SIZE, 133 LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) { 134 err = -EFAULT; 135 break; 136 } 137 } else { 138 /* old-type code */ 139 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { 140 err = -EINVAL; 141 break; 142 } 143 ev_size = SHORT_EVENT_SIZE; 144 } 145 146 /* insert queue */ 147 if ((err = insert_queue(dp, &rec, opt)) < 0) 148 break; 149 150 result += ev_size; 151 buf += ev_size; 152 count -= ev_size; 153 } 154 return result > 0 ? result : err; 155} 156 157 158/* 159 * insert event record to write queue 160 * return: 0 = OK, non-zero = NG 161 */ 162static int 163insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) 164{ 165 int rc = 0; 166 struct snd_seq_event event; 167 168 /* if this is a timing event, process the current time */ 169 if (snd_seq_oss_process_timer_event(dp->timer, rec)) 170 return 0; /* no need to insert queue */ 171 172 /* parse this event */ 173 memset(&event, 0, sizeof(event)); 174 /* set dummy -- to be sure */ 175 event.type = SNDRV_SEQ_EVENT_NOTEOFF; 176 snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client); 177 178 if (snd_seq_oss_process_event(dp, rec, &event)) 179 return 0; /* invalid event - no need to insert queue */ 180 181 event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer); 182 if (dp->timer->realtime || !dp->timer->running) { 183 snd_seq_oss_dispatch(dp, &event, 0, 0); 184 } else { 185 if (is_nonblock_mode(dp->file_mode)) 186 rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0); 187 else 188 rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0); 189 } 190 return rc; 191} 192 193 194/* 195 * select / poll 196 */ 197 198unsigned int 199snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait) 200{ 201 unsigned int mask = 0; 202 203 /* input */ 204 if (dp->readq && is_read_mode(dp->file_mode)) { 205 if (snd_seq_oss_readq_poll(dp->readq, file, wait)) 206 mask |= POLLIN | POLLRDNORM; 207 } 208 209 /* output */ 210 if (dp->writeq && is_write_mode(dp->file_mode)) { 211 if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait)) 212 mask |= POLLOUT | POLLWRNORM; 213 } 214 return mask; 215} 216