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 */ 36 static int insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt); 37 38 39 /* 40 * read interface 41 */ 42 43 int 44 snd_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 95 int 96 snd_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 */ 163 static int 164 insert_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 unsigned int 200 snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait) 201 { 202 unsigned int 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 |= POLLIN | POLLRDNORM; 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 |= POLLOUT | POLLWRNORM; 214 } 215 return mask; 216 } 217