Deleted Added
full compact
dsp.c (50724) dsp.c (50733)
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id$
26 * $FreeBSD: head/sys/dev/sound/pcm/dsp.c 50733 1999-09-01 06:58:27Z peter $
27 */
28
29#include <sys/param.h>
30#include <sys/queue.h>
31#include <sys/kernel.h>
32
33#include <dev/pcm/sound.h>
34
35static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch);
36
37static pcm_channel *
38allocchn(snddev_info *d, int direction)
39{
40 pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec;
41 int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount;
42 for (i = 0; i < cnt; i++) {
43 if (!(chns[i].flags & CHN_F_BUSY)) {
44 chns[i].flags |= CHN_F_BUSY;
45 return &chns[i];
46 }
47 }
48 return NULL;
49}
50
51static int
52getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch)
53{
54 if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
55 panic("read and write both prioritised");
56 if (d->flags & SD_F_SIMPLEX) {
57 *rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan;
58 *wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan;
59 } else {
60 *rdch = d->arec[chan];
61 *wrch = d->aplay[chan];
62 }
63 return 0;
64}
65
66static void
67setchns(snddev_info *d, int chan)
68{
69 if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
70 panic("read and write both prioritised");
71 d->flags |= SD_F_DIR_SET;
72 if (d->flags & SD_F_EVILSB16) {
73 if ((d->flags & SD_F_PRIO_RD) && (d->aplay[chan])) {
74 pcm_channel *tmp;
75 tmp = d->arec[chan];
76 d->arec[chan] = d->aplay[chan];
77 d->aplay[chan] = tmp;
78 }
79 if (d->aplay[chan]) chn_setdir(d->aplay[chan], PCMDIR_PLAY);
80 if (d->arec[chan]) chn_setdir(d->arec[chan], PCMDIR_REC);
81 }
82}
83
84int
85dsp_open(snddev_info *d, int chan, int oflags, int devtype)
86{
87 pcm_channel *rdch = NULL, *wrch = NULL;
88 u_int32_t fmt;
89
90 if (chan >= d->chancount) return ENODEV;
91 if (d->aplay[chan] || d->arec[chan]) return EBUSY;
92 if (oflags & FREAD) {
93 rdch = allocchn(d, PCMDIR_REC);
94 if (!rdch) return EBUSY;
95 }
96 if (oflags & FWRITE) {
97 wrch = allocchn(d, PCMDIR_PLAY);
98 if (!wrch) {
99 if (rdch) rdch->flags &= ~CHN_F_BUSY;
100 return EBUSY;
101 }
102 }
103 d->aplay[chan] = wrch;
104 d->arec[chan] = rdch;
105 switch (devtype) {
106 case SND_DEV_DSP16:
107 fmt = AFMT_S16_LE;
108 break;
109
110 case SND_DEV_DSP:
111 fmt = AFMT_U8;
112 break;
113
114 case SND_DEV_AUDIO:
115 fmt = AFMT_MU_LAW;
116 break;
117
118 default:
119 return ENXIO;
120 }
121
122 if (rdch) {
123 chn_reset(rdch);
124 if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO;
125 rdch->volume = (100 << 8) | 100;
126 rdch->format = fmt;
127 rdch->speed = DSP_DEFAULT_SPEED;
128 rdch->blocksize = 2048;
129 }
130 if (wrch) {
131 chn_reset(wrch);
132 if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO;
133 wrch->volume = (100 << 8) | 100;
134 wrch->format = fmt;
135 wrch->speed = DSP_DEFAULT_SPEED;
136 wrch->blocksize = 2048;
137 }
138 return 0;
139}
140
141int
142dsp_close(snddev_info *d, int chan, int devtype)
143{
144 pcm_channel *rdch, *wrch;
145
146 d->flags &= ~SD_F_TRANSIENT;
147 getchns(d, chan, &rdch, &wrch);
148
149 if (rdch) {
150 chn_abort(rdch);
151 rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
152 }
153 if (wrch) wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
154 d->aplay[chan] = NULL;
155 d->arec[chan] = NULL;
156 return 0;
157}
158
159int
160dsp_read(snddev_info *d, int chan, struct uio *buf, int flag)
161{
162 pcm_channel *rdch, *wrch;
163
164 if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
165 if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
166 getchns(d, chan, &rdch, &wrch);
167 if (!rdch || !(rdch->flags & CHN_F_BUSY))
168 panic("dsp_read: non%s channel", rdch? "busy" : "existant");
169 if (rdch->flags & CHN_F_MAPPED) return EINVAL;
170 if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING;
171 return chn_read(rdch, buf);
172}
173
174int
175dsp_write(snddev_info *d, int chan, struct uio *buf, int flag)
176{
177 pcm_channel *rdch, *wrch;
178
179 if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
180 if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
181 getchns(d, chan, &rdch, &wrch);
182 if (!wrch || !(wrch->flags & CHN_F_BUSY))
183 panic("dsp_write: non%s channel", wrch? "busy" : "existant");
184 if (wrch->flags & CHN_F_MAPPED) return EINVAL;
185 if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING;
186 return chn_write(wrch, buf);
187}
188
189int
190dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
191{
192 int ret = 0, *arg_i = (int *)arg;
193 u_long s;
194 pcm_channel *wrch = NULL, *rdch = NULL;
195
196 getchns(d, chan, &rdch, &wrch);
197
198 /*
199 * all routines are called with int. blocked. Make sure that
200 * ints are re-enabled when calling slow or blocking functions!
201 */
202 s = spltty();
203 switch(cmd) {
204
205 /*
206 * we start with the new ioctl interface.
207 */
208 case AIONWRITE: /* how many bytes can write ? */
209 if (wrch && wrch->buffer.dl) chn_dmaupdate(wrch);
210 *arg_i = wrch? wrch->buffer.fl : 0;
211 break;
212
213 case AIOSSIZE: /* set the current blocksize */
214 {
215 struct snd_size *p = (struct snd_size *)arg;
216 splx(s);
217 if (wrch) chn_setblocksize(wrch, p->play_size);
218 if (rdch) chn_setblocksize(rdch, p->rec_size);
219 }
220 /* FALLTHROUGH */
221 case AIOGSIZE: /* get the current blocksize */
222 {
223 struct snd_size *p = (struct snd_size *)arg;
224 if (wrch) p->play_size = wrch->blocksize;
225 if (rdch) p->rec_size = rdch->blocksize;
226 }
227 break;
228
229 case AIOSFMT:
230 {
231 snd_chan_param *p = (snd_chan_param *)arg;
232 splx(s);
233 if (wrch) {
234 chn_setformat(wrch, p->play_format);
235 chn_setspeed(wrch, p->play_rate);
236 }
237 if (rdch) {
238 chn_setformat(rdch, p->rec_format);
239 chn_setspeed(rdch, p->rec_rate);
240 }
241 }
242 /* FALLTHROUGH */
243
244 case AIOGFMT:
245 {
246 snd_chan_param *p = (snd_chan_param *)arg;
247 p->play_rate = wrch? wrch->speed : 0;
248 p->rec_rate = rdch? rdch->speed : 0;
249 p->play_format = wrch? wrch->format : 0;
250 p->rec_format = rdch? rdch->format : 0;
251 }
252 break;
253
254 case AIOGCAP: /* get capabilities */
255 {
256 snd_capabilities *p = (snd_capabilities *)arg;
257 pcmchan_caps *pcaps = NULL, *rcaps = NULL;
258 if (rdch) rcaps = chn_getcaps(rdch);
259 if (wrch) pcaps = chn_getcaps(wrch);
260 p->rate_min = max(rcaps? rcaps->minspeed : 0,
261 pcaps? pcaps->minspeed : 0);
262 p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
263 pcaps? pcaps->maxspeed : 1000000);
264 p->bufsize = min(rdch? rdch->buffer.bufsize : 1000000,
265 wrch? wrch->buffer.bufsize : 1000000);
266 /* XXX bad on sb16 */
267 p->formats = (rcaps? rcaps->formats : 0xffffffff) &
268 (pcaps? pcaps->formats : 0xffffffff);
269 p->mixers = 1; /* default: one mixer */
270 p->inputs = d->mixer.devs;
271 p->left = p->right = 100;
272 }
273 break;
274
275 case AIOSTOP:
276 if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch);
277 else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch);
278 else {
279 splx(s);
280 printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
281 *arg_i = 0;
282 }
283 break;
284
285 case AIOSYNC:
286 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
287 ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
288 break;
289 /*
290 * here follow the standard ioctls (filio.h etc.)
291 */
292 case FIONREAD: /* get # bytes to read */
293 if (rdch && rdch->buffer.dl) chn_dmaupdate(rdch);
294 *arg_i = rdch? rdch->buffer.rl : 0;
295 break;
296
297 case FIOASYNC: /*set/clear async i/o */
298 DEB( printf("FIOASYNC\n") ; )
299 break;
300
301 case SNDCTL_DSP_NONBLOCK:
302 case FIONBIO: /* set/clear non-blocking i/o */
303 if (rdch) rdch->flags &= ~CHN_F_NBIO;
304 if (wrch) wrch->flags &= ~CHN_F_NBIO;
305 if (*arg_i) {
306 if (rdch) rdch->flags |= CHN_F_NBIO;
307 if (wrch) wrch->flags |= CHN_F_NBIO;
308 }
309 break;
310
311 /*
312 * Finally, here is the linux-compatible ioctl interface
313 */
314 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
315 case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
316 case SNDCTL_DSP_GETBLKSIZE:
317 *arg_i = wrch? wrch->blocksize : 0; /* XXX rdch? */
318 break ;
319
320 case SNDCTL_DSP_SETBLKSIZE:
321 splx(s);
322 if (wrch) chn_setblocksize(wrch, *arg_i);
323 if (rdch) chn_setblocksize(rdch, *arg_i);
324 break;
325
326 case SNDCTL_DSP_RESET:
327 DEB(printf("dsp reset\n"));
328 if (wrch) chn_abort(wrch);
329 if (rdch) chn_abort(rdch);
330 break;
331
332 case SNDCTL_DSP_SYNC:
333 printf("dsp sync\n");
334 splx(s);
335 if (wrch) chn_sync(wrch, wrch->buffer.bufsize - 4);
336 break;
337
338 case SNDCTL_DSP_SPEED:
339 splx(s);
340 if (wrch) chn_setspeed(wrch, *arg_i);
341 if (rdch) chn_setspeed(rdch, *arg_i);
342 /* fallthru */
343
344 case SOUND_PCM_READ_RATE:
345 *arg_i = wrch? wrch->speed : rdch->speed;
346 break;
347
348 case SNDCTL_DSP_STEREO:
349 splx(s);
350 if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
351 ((*arg_i)? AFMT_STEREO : 0));
352 if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
353 ((*arg_i)? AFMT_STEREO : 0));
354 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
355 break;
356
357 case SOUND_PCM_WRITE_CHANNELS:
358 splx(s);
359 if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
360 ((*arg_i == 2)? AFMT_STEREO : 0));
361 if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
362 ((*arg_i == 2)? AFMT_STEREO : 0));
363 /* fallthru */
364
365 case SOUND_PCM_READ_CHANNELS:
366 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
367 break;
368
369 case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
370 *arg_i = wrch? chn_getcaps(wrch)->formats : chn_getcaps(rdch)->formats;
371 break ;
372
373 case SNDCTL_DSP_SETFMT: /* sets _one_ format */
374 splx(s);
375 if (wrch) chn_setformat(wrch, *arg_i);
376 if (rdch) chn_setformat(rdch, *arg_i);
377 *arg_i = wrch? wrch->format : rdch->format;
378 break;
379
380 case SNDCTL_DSP_SUBDIVIDE:
381 /* XXX watch out, this is RW! */
382 DEB(printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");)
383 break;
384
385 case SNDCTL_DSP_SETFRAGMENT:
386 /* XXX watch out, this is RW! */
387 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
388 {
389 int bytes = 1 << min(*arg_i & 0xffff, 16);
390 int count = (*arg_i >> 16) & 0xffff;
391 pcm_channel *c = wrch? wrch : rdch;
392 splx(s);
393 if (rdch) chn_setblocksize(rdch, bytes);
394 if (wrch) chn_setblocksize(wrch, bytes);
395
396 /* eg: 4dwave can only interrupt at buffer midpoint, so
397 * it will force blocksize == bufsize/2
398 */
399 count = c->buffer.bufsize / c->blocksize;
400 bytes = ffs(c->blocksize) - 1;
401 *arg_i = (count << 16) | bytes;
402 }
403 break;
404
405 case SNDCTL_DSP_GETISPACE:
406 /* return space available in the input queue */
407 {
408 audio_buf_info *a = (audio_buf_info *)arg;
409 if (rdch) {
410 snd_dbuf *b = &rdch->buffer;
411 if (b->dl) chn_dmaupdate(rdch);
412 a->bytes = b->fl;
413 a->fragments = 1;
414 a->fragstotal = b->bufsize / rdch->blocksize;
415 a->fragsize = rdch->blocksize;
416 }
417 }
418 break;
419
420 case SNDCTL_DSP_GETOSPACE:
421 /* return space available in the output queue */
422 {
423 audio_buf_info *a = (audio_buf_info *)arg;
424 if (wrch) {
425 snd_dbuf *b = &wrch->buffer;
426 if (b->dl) chn_dmaupdate(wrch);
427 a->bytes = b->fl;
428 a->fragments = 1;
429 a->fragstotal = b->bufsize / wrch->blocksize;
430 a->fragsize = wrch->blocksize;
431 }
432 }
433 break;
434
435 case SNDCTL_DSP_GETIPTR:
436 {
437 count_info *a = (count_info *)arg;
438 if (rdch) {
439 snd_dbuf *b = &rdch->buffer;
440 if (b->dl) chn_dmaupdate(rdch);
441 a->bytes = b->total;
442 a->blocks = (b->total - b->prev_total) / rdch->blocksize;
443 a->ptr = b->fp;
444 b->prev_total += a->blocks * rdch->blocksize;
445 } else ret = EINVAL;
446 }
447 break;
448
449 case SNDCTL_DSP_GETOPTR:
450 {
451 count_info *a = (count_info *)arg;
452 if (wrch) {
453 snd_dbuf *b = &wrch->buffer;
454 if (b->dl) chn_dmaupdate(wrch);
455 a->bytes = b->total;
456 a->blocks = (b->total - b->prev_total) / wrch->blocksize;
457 a->ptr = b->rp;
458 b->prev_total += a->blocks * wrch->blocksize;
459 } else ret = EINVAL;
460 }
461 break;
462
463 case SNDCTL_DSP_GETCAPS:
464 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
465 if (rdch && wrch && !(d->flags & SD_F_SIMPLEX))
466 *arg_i |= DSP_CAP_DUPLEX;
467 break;
468
469 case SOUND_PCM_READ_BITS:
470 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
471 break;
472
473 case SNDCTL_DSP_SETTRIGGER:
474 if (rdch) {
475 rdch->flags &= ~CHN_F_TRIGGERED;
476 if (*arg_i & PCM_ENABLE_INPUT)
477 rdch->flags |= CHN_F_TRIGGERED;
478 chn_intr(rdch);
479 }
480 if (wrch) {
481 wrch->flags &= ~CHN_F_TRIGGERED;
482 if (*arg_i & PCM_ENABLE_OUTPUT)
483 wrch->flags |= CHN_F_TRIGGERED;
484 chn_intr(wrch);
485 }
486 break;
487
488 case SNDCTL_DSP_GETTRIGGER:
489 *arg_i = 0;
490 if (wrch && wrch->flags & CHN_F_TRIGGERED)
491 *arg_i |= PCM_ENABLE_OUTPUT;
492 if (rdch && rdch->flags & CHN_F_TRIGGERED)
493 *arg_i |= PCM_ENABLE_INPUT;
494 break;
495
496 case SNDCTL_DSP_MAPINBUF:
497 case SNDCTL_DSP_MAPOUTBUF:
498 case SNDCTL_DSP_SETSYNCRO:
499 /* undocumented */
500
501 case SNDCTL_DSP_POST:
502 case SOUND_PCM_WRITE_FILTER:
503 case SOUND_PCM_READ_FILTER:
504 /* dunno what these do, don't sound important */
505 default:
506 DEB(printf("default ioctl snd%d fn 0x%08x fail\n", unit, cmd));
507 ret = EINVAL;
508 break;
509 }
510 splx(s);
511 return ret;
512}
513
514int
515dsp_poll(snddev_info *d, int chan, int events, struct proc *p)
516{
517 int ret = 0, e;
518 pcm_channel *wrch = NULL, *rdch = NULL;
519
520 getchns(d, chan, &rdch, &wrch);
521 e = events & (POLLOUT | POLLWRNORM);
522 if (wrch && e) ret |= chn_poll(wrch, e, p);
523 e = events & (POLLIN | POLLRDNORM);
524 if (rdch && e) ret |= chn_poll(rdch, e, p);
525 return ret;
526}
527
528int
529dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot)
530{
531 pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL;
532
533 getchns(d, chan, &rdch, &wrch);
534 /* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */
535 if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch;
536 else if (rdch && (nprot & PROT_READ)) c = rdch;
537 if (c) {
538 c->flags |= CHN_F_MAPPED;
539 return atop(vtophys(c->buffer.buf + offset));
540 }
541 return -1;
542}
543
27 */
28
29#include <sys/param.h>
30#include <sys/queue.h>
31#include <sys/kernel.h>
32
33#include <dev/pcm/sound.h>
34
35static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch);
36
37static pcm_channel *
38allocchn(snddev_info *d, int direction)
39{
40 pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec;
41 int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount;
42 for (i = 0; i < cnt; i++) {
43 if (!(chns[i].flags & CHN_F_BUSY)) {
44 chns[i].flags |= CHN_F_BUSY;
45 return &chns[i];
46 }
47 }
48 return NULL;
49}
50
51static int
52getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch)
53{
54 if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
55 panic("read and write both prioritised");
56 if (d->flags & SD_F_SIMPLEX) {
57 *rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan;
58 *wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan;
59 } else {
60 *rdch = d->arec[chan];
61 *wrch = d->aplay[chan];
62 }
63 return 0;
64}
65
66static void
67setchns(snddev_info *d, int chan)
68{
69 if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
70 panic("read and write both prioritised");
71 d->flags |= SD_F_DIR_SET;
72 if (d->flags & SD_F_EVILSB16) {
73 if ((d->flags & SD_F_PRIO_RD) && (d->aplay[chan])) {
74 pcm_channel *tmp;
75 tmp = d->arec[chan];
76 d->arec[chan] = d->aplay[chan];
77 d->aplay[chan] = tmp;
78 }
79 if (d->aplay[chan]) chn_setdir(d->aplay[chan], PCMDIR_PLAY);
80 if (d->arec[chan]) chn_setdir(d->arec[chan], PCMDIR_REC);
81 }
82}
83
84int
85dsp_open(snddev_info *d, int chan, int oflags, int devtype)
86{
87 pcm_channel *rdch = NULL, *wrch = NULL;
88 u_int32_t fmt;
89
90 if (chan >= d->chancount) return ENODEV;
91 if (d->aplay[chan] || d->arec[chan]) return EBUSY;
92 if (oflags & FREAD) {
93 rdch = allocchn(d, PCMDIR_REC);
94 if (!rdch) return EBUSY;
95 }
96 if (oflags & FWRITE) {
97 wrch = allocchn(d, PCMDIR_PLAY);
98 if (!wrch) {
99 if (rdch) rdch->flags &= ~CHN_F_BUSY;
100 return EBUSY;
101 }
102 }
103 d->aplay[chan] = wrch;
104 d->arec[chan] = rdch;
105 switch (devtype) {
106 case SND_DEV_DSP16:
107 fmt = AFMT_S16_LE;
108 break;
109
110 case SND_DEV_DSP:
111 fmt = AFMT_U8;
112 break;
113
114 case SND_DEV_AUDIO:
115 fmt = AFMT_MU_LAW;
116 break;
117
118 default:
119 return ENXIO;
120 }
121
122 if (rdch) {
123 chn_reset(rdch);
124 if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO;
125 rdch->volume = (100 << 8) | 100;
126 rdch->format = fmt;
127 rdch->speed = DSP_DEFAULT_SPEED;
128 rdch->blocksize = 2048;
129 }
130 if (wrch) {
131 chn_reset(wrch);
132 if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO;
133 wrch->volume = (100 << 8) | 100;
134 wrch->format = fmt;
135 wrch->speed = DSP_DEFAULT_SPEED;
136 wrch->blocksize = 2048;
137 }
138 return 0;
139}
140
141int
142dsp_close(snddev_info *d, int chan, int devtype)
143{
144 pcm_channel *rdch, *wrch;
145
146 d->flags &= ~SD_F_TRANSIENT;
147 getchns(d, chan, &rdch, &wrch);
148
149 if (rdch) {
150 chn_abort(rdch);
151 rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
152 }
153 if (wrch) wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
154 d->aplay[chan] = NULL;
155 d->arec[chan] = NULL;
156 return 0;
157}
158
159int
160dsp_read(snddev_info *d, int chan, struct uio *buf, int flag)
161{
162 pcm_channel *rdch, *wrch;
163
164 if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
165 if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
166 getchns(d, chan, &rdch, &wrch);
167 if (!rdch || !(rdch->flags & CHN_F_BUSY))
168 panic("dsp_read: non%s channel", rdch? "busy" : "existant");
169 if (rdch->flags & CHN_F_MAPPED) return EINVAL;
170 if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING;
171 return chn_read(rdch, buf);
172}
173
174int
175dsp_write(snddev_info *d, int chan, struct uio *buf, int flag)
176{
177 pcm_channel *rdch, *wrch;
178
179 if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
180 if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
181 getchns(d, chan, &rdch, &wrch);
182 if (!wrch || !(wrch->flags & CHN_F_BUSY))
183 panic("dsp_write: non%s channel", wrch? "busy" : "existant");
184 if (wrch->flags & CHN_F_MAPPED) return EINVAL;
185 if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING;
186 return chn_write(wrch, buf);
187}
188
189int
190dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
191{
192 int ret = 0, *arg_i = (int *)arg;
193 u_long s;
194 pcm_channel *wrch = NULL, *rdch = NULL;
195
196 getchns(d, chan, &rdch, &wrch);
197
198 /*
199 * all routines are called with int. blocked. Make sure that
200 * ints are re-enabled when calling slow or blocking functions!
201 */
202 s = spltty();
203 switch(cmd) {
204
205 /*
206 * we start with the new ioctl interface.
207 */
208 case AIONWRITE: /* how many bytes can write ? */
209 if (wrch && wrch->buffer.dl) chn_dmaupdate(wrch);
210 *arg_i = wrch? wrch->buffer.fl : 0;
211 break;
212
213 case AIOSSIZE: /* set the current blocksize */
214 {
215 struct snd_size *p = (struct snd_size *)arg;
216 splx(s);
217 if (wrch) chn_setblocksize(wrch, p->play_size);
218 if (rdch) chn_setblocksize(rdch, p->rec_size);
219 }
220 /* FALLTHROUGH */
221 case AIOGSIZE: /* get the current blocksize */
222 {
223 struct snd_size *p = (struct snd_size *)arg;
224 if (wrch) p->play_size = wrch->blocksize;
225 if (rdch) p->rec_size = rdch->blocksize;
226 }
227 break;
228
229 case AIOSFMT:
230 {
231 snd_chan_param *p = (snd_chan_param *)arg;
232 splx(s);
233 if (wrch) {
234 chn_setformat(wrch, p->play_format);
235 chn_setspeed(wrch, p->play_rate);
236 }
237 if (rdch) {
238 chn_setformat(rdch, p->rec_format);
239 chn_setspeed(rdch, p->rec_rate);
240 }
241 }
242 /* FALLTHROUGH */
243
244 case AIOGFMT:
245 {
246 snd_chan_param *p = (snd_chan_param *)arg;
247 p->play_rate = wrch? wrch->speed : 0;
248 p->rec_rate = rdch? rdch->speed : 0;
249 p->play_format = wrch? wrch->format : 0;
250 p->rec_format = rdch? rdch->format : 0;
251 }
252 break;
253
254 case AIOGCAP: /* get capabilities */
255 {
256 snd_capabilities *p = (snd_capabilities *)arg;
257 pcmchan_caps *pcaps = NULL, *rcaps = NULL;
258 if (rdch) rcaps = chn_getcaps(rdch);
259 if (wrch) pcaps = chn_getcaps(wrch);
260 p->rate_min = max(rcaps? rcaps->minspeed : 0,
261 pcaps? pcaps->minspeed : 0);
262 p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
263 pcaps? pcaps->maxspeed : 1000000);
264 p->bufsize = min(rdch? rdch->buffer.bufsize : 1000000,
265 wrch? wrch->buffer.bufsize : 1000000);
266 /* XXX bad on sb16 */
267 p->formats = (rcaps? rcaps->formats : 0xffffffff) &
268 (pcaps? pcaps->formats : 0xffffffff);
269 p->mixers = 1; /* default: one mixer */
270 p->inputs = d->mixer.devs;
271 p->left = p->right = 100;
272 }
273 break;
274
275 case AIOSTOP:
276 if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch);
277 else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch);
278 else {
279 splx(s);
280 printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
281 *arg_i = 0;
282 }
283 break;
284
285 case AIOSYNC:
286 printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
287 ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
288 break;
289 /*
290 * here follow the standard ioctls (filio.h etc.)
291 */
292 case FIONREAD: /* get # bytes to read */
293 if (rdch && rdch->buffer.dl) chn_dmaupdate(rdch);
294 *arg_i = rdch? rdch->buffer.rl : 0;
295 break;
296
297 case FIOASYNC: /*set/clear async i/o */
298 DEB( printf("FIOASYNC\n") ; )
299 break;
300
301 case SNDCTL_DSP_NONBLOCK:
302 case FIONBIO: /* set/clear non-blocking i/o */
303 if (rdch) rdch->flags &= ~CHN_F_NBIO;
304 if (wrch) wrch->flags &= ~CHN_F_NBIO;
305 if (*arg_i) {
306 if (rdch) rdch->flags |= CHN_F_NBIO;
307 if (wrch) wrch->flags |= CHN_F_NBIO;
308 }
309 break;
310
311 /*
312 * Finally, here is the linux-compatible ioctl interface
313 */
314 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
315 case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
316 case SNDCTL_DSP_GETBLKSIZE:
317 *arg_i = wrch? wrch->blocksize : 0; /* XXX rdch? */
318 break ;
319
320 case SNDCTL_DSP_SETBLKSIZE:
321 splx(s);
322 if (wrch) chn_setblocksize(wrch, *arg_i);
323 if (rdch) chn_setblocksize(rdch, *arg_i);
324 break;
325
326 case SNDCTL_DSP_RESET:
327 DEB(printf("dsp reset\n"));
328 if (wrch) chn_abort(wrch);
329 if (rdch) chn_abort(rdch);
330 break;
331
332 case SNDCTL_DSP_SYNC:
333 printf("dsp sync\n");
334 splx(s);
335 if (wrch) chn_sync(wrch, wrch->buffer.bufsize - 4);
336 break;
337
338 case SNDCTL_DSP_SPEED:
339 splx(s);
340 if (wrch) chn_setspeed(wrch, *arg_i);
341 if (rdch) chn_setspeed(rdch, *arg_i);
342 /* fallthru */
343
344 case SOUND_PCM_READ_RATE:
345 *arg_i = wrch? wrch->speed : rdch->speed;
346 break;
347
348 case SNDCTL_DSP_STEREO:
349 splx(s);
350 if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
351 ((*arg_i)? AFMT_STEREO : 0));
352 if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
353 ((*arg_i)? AFMT_STEREO : 0));
354 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
355 break;
356
357 case SOUND_PCM_WRITE_CHANNELS:
358 splx(s);
359 if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
360 ((*arg_i == 2)? AFMT_STEREO : 0));
361 if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
362 ((*arg_i == 2)? AFMT_STEREO : 0));
363 /* fallthru */
364
365 case SOUND_PCM_READ_CHANNELS:
366 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
367 break;
368
369 case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
370 *arg_i = wrch? chn_getcaps(wrch)->formats : chn_getcaps(rdch)->formats;
371 break ;
372
373 case SNDCTL_DSP_SETFMT: /* sets _one_ format */
374 splx(s);
375 if (wrch) chn_setformat(wrch, *arg_i);
376 if (rdch) chn_setformat(rdch, *arg_i);
377 *arg_i = wrch? wrch->format : rdch->format;
378 break;
379
380 case SNDCTL_DSP_SUBDIVIDE:
381 /* XXX watch out, this is RW! */
382 DEB(printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");)
383 break;
384
385 case SNDCTL_DSP_SETFRAGMENT:
386 /* XXX watch out, this is RW! */
387 DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
388 {
389 int bytes = 1 << min(*arg_i & 0xffff, 16);
390 int count = (*arg_i >> 16) & 0xffff;
391 pcm_channel *c = wrch? wrch : rdch;
392 splx(s);
393 if (rdch) chn_setblocksize(rdch, bytes);
394 if (wrch) chn_setblocksize(wrch, bytes);
395
396 /* eg: 4dwave can only interrupt at buffer midpoint, so
397 * it will force blocksize == bufsize/2
398 */
399 count = c->buffer.bufsize / c->blocksize;
400 bytes = ffs(c->blocksize) - 1;
401 *arg_i = (count << 16) | bytes;
402 }
403 break;
404
405 case SNDCTL_DSP_GETISPACE:
406 /* return space available in the input queue */
407 {
408 audio_buf_info *a = (audio_buf_info *)arg;
409 if (rdch) {
410 snd_dbuf *b = &rdch->buffer;
411 if (b->dl) chn_dmaupdate(rdch);
412 a->bytes = b->fl;
413 a->fragments = 1;
414 a->fragstotal = b->bufsize / rdch->blocksize;
415 a->fragsize = rdch->blocksize;
416 }
417 }
418 break;
419
420 case SNDCTL_DSP_GETOSPACE:
421 /* return space available in the output queue */
422 {
423 audio_buf_info *a = (audio_buf_info *)arg;
424 if (wrch) {
425 snd_dbuf *b = &wrch->buffer;
426 if (b->dl) chn_dmaupdate(wrch);
427 a->bytes = b->fl;
428 a->fragments = 1;
429 a->fragstotal = b->bufsize / wrch->blocksize;
430 a->fragsize = wrch->blocksize;
431 }
432 }
433 break;
434
435 case SNDCTL_DSP_GETIPTR:
436 {
437 count_info *a = (count_info *)arg;
438 if (rdch) {
439 snd_dbuf *b = &rdch->buffer;
440 if (b->dl) chn_dmaupdate(rdch);
441 a->bytes = b->total;
442 a->blocks = (b->total - b->prev_total) / rdch->blocksize;
443 a->ptr = b->fp;
444 b->prev_total += a->blocks * rdch->blocksize;
445 } else ret = EINVAL;
446 }
447 break;
448
449 case SNDCTL_DSP_GETOPTR:
450 {
451 count_info *a = (count_info *)arg;
452 if (wrch) {
453 snd_dbuf *b = &wrch->buffer;
454 if (b->dl) chn_dmaupdate(wrch);
455 a->bytes = b->total;
456 a->blocks = (b->total - b->prev_total) / wrch->blocksize;
457 a->ptr = b->rp;
458 b->prev_total += a->blocks * wrch->blocksize;
459 } else ret = EINVAL;
460 }
461 break;
462
463 case SNDCTL_DSP_GETCAPS:
464 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
465 if (rdch && wrch && !(d->flags & SD_F_SIMPLEX))
466 *arg_i |= DSP_CAP_DUPLEX;
467 break;
468
469 case SOUND_PCM_READ_BITS:
470 *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
471 break;
472
473 case SNDCTL_DSP_SETTRIGGER:
474 if (rdch) {
475 rdch->flags &= ~CHN_F_TRIGGERED;
476 if (*arg_i & PCM_ENABLE_INPUT)
477 rdch->flags |= CHN_F_TRIGGERED;
478 chn_intr(rdch);
479 }
480 if (wrch) {
481 wrch->flags &= ~CHN_F_TRIGGERED;
482 if (*arg_i & PCM_ENABLE_OUTPUT)
483 wrch->flags |= CHN_F_TRIGGERED;
484 chn_intr(wrch);
485 }
486 break;
487
488 case SNDCTL_DSP_GETTRIGGER:
489 *arg_i = 0;
490 if (wrch && wrch->flags & CHN_F_TRIGGERED)
491 *arg_i |= PCM_ENABLE_OUTPUT;
492 if (rdch && rdch->flags & CHN_F_TRIGGERED)
493 *arg_i |= PCM_ENABLE_INPUT;
494 break;
495
496 case SNDCTL_DSP_MAPINBUF:
497 case SNDCTL_DSP_MAPOUTBUF:
498 case SNDCTL_DSP_SETSYNCRO:
499 /* undocumented */
500
501 case SNDCTL_DSP_POST:
502 case SOUND_PCM_WRITE_FILTER:
503 case SOUND_PCM_READ_FILTER:
504 /* dunno what these do, don't sound important */
505 default:
506 DEB(printf("default ioctl snd%d fn 0x%08x fail\n", unit, cmd));
507 ret = EINVAL;
508 break;
509 }
510 splx(s);
511 return ret;
512}
513
514int
515dsp_poll(snddev_info *d, int chan, int events, struct proc *p)
516{
517 int ret = 0, e;
518 pcm_channel *wrch = NULL, *rdch = NULL;
519
520 getchns(d, chan, &rdch, &wrch);
521 e = events & (POLLOUT | POLLWRNORM);
522 if (wrch && e) ret |= chn_poll(wrch, e, p);
523 e = events & (POLLIN | POLLRDNORM);
524 if (rdch && e) ret |= chn_poll(rdch, e, p);
525 return ret;
526}
527
528int
529dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot)
530{
531 pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL;
532
533 getchns(d, chan, &rdch, &wrch);
534 /* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */
535 if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch;
536 else if (rdch && (nprot & PROT_READ)) c = rdch;
537 if (c) {
538 c->flags |= CHN_F_MAPPED;
539 return atop(vtophys(c->buffer.buf + offset));
540 }
541 return -1;
542}
543