Deleted Added
full compact
sound.c (77269) sound.c (77843)
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/dev/sound/pcm/sound.c 77269 2001-05-27 17:22:00Z cg $
27 * $FreeBSD: head/sys/dev/sound/pcm/sound.c 77843 2001-06-06 22:17:08Z peter $
28 */
29
30#include <dev/sound/pcm/sound.h>
31#include <dev/sound/pcm/vchan.h>
32#include <sys/sysctl.h>
33#include <sys/sbuf.h>
34
35#include "feeder_if.h"
36
37#undef SNDSTAT_VERBOSE
38
39static dev_t status_dev = 0;
40static int do_status(int action, struct uio *buf);
41
42static d_open_t sndopen;
43static d_close_t sndclose;
44static d_ioctl_t sndioctl;
45static d_read_t sndread;
46static d_write_t sndwrite;
47static d_mmap_t sndmmap;
48static d_poll_t sndpoll;
49
50#define CDEV_MAJOR 30
51static struct cdevsw snd_cdevsw = {
52 /* open */ sndopen,
53 /* close */ sndclose,
54 /* read */ sndread,
55 /* write */ sndwrite,
56 /* ioctl */ sndioctl,
57 /* poll */ sndpoll,
58 /* mmap */ sndmmap,
59 /* strategy */ nostrategy,
60 /* name */ "snd",
61 /* maj */ CDEV_MAJOR,
62 /* dump */ nodump,
63 /* psize */ nopsize,
64 /* flags */ D_TRACKCLOSE,
65};
66
67/*
68PROPOSAL:
69each unit needs:
70status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
71dspW and audio are deprecated.
72dsp needs min 64 channels, will give it 256
73
74minor = (unit << 20) + (dev << 16) + channel
75currently minor = (channel << 16) + (unit << 4) + dev
76
77nomenclature:
78 /dev/pcmX/dsp.(0..255)
79 /dev/pcmX/dspW
80 /dev/pcmX/audio
81 /dev/pcmX/status
82 /dev/pcmX/mixer
83 [etc.]
84*/
85
86#define PCMMINOR(x) (minor(x))
87#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
88#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
89#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
90#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
91
92static devclass_t pcm_devclass;
93
94#ifdef USING_DEVFS
28 */
29
30#include <dev/sound/pcm/sound.h>
31#include <dev/sound/pcm/vchan.h>
32#include <sys/sysctl.h>
33#include <sys/sbuf.h>
34
35#include "feeder_if.h"
36
37#undef SNDSTAT_VERBOSE
38
39static dev_t status_dev = 0;
40static int do_status(int action, struct uio *buf);
41
42static d_open_t sndopen;
43static d_close_t sndclose;
44static d_ioctl_t sndioctl;
45static d_read_t sndread;
46static d_write_t sndwrite;
47static d_mmap_t sndmmap;
48static d_poll_t sndpoll;
49
50#define CDEV_MAJOR 30
51static struct cdevsw snd_cdevsw = {
52 /* open */ sndopen,
53 /* close */ sndclose,
54 /* read */ sndread,
55 /* write */ sndwrite,
56 /* ioctl */ sndioctl,
57 /* poll */ sndpoll,
58 /* mmap */ sndmmap,
59 /* strategy */ nostrategy,
60 /* name */ "snd",
61 /* maj */ CDEV_MAJOR,
62 /* dump */ nodump,
63 /* psize */ nopsize,
64 /* flags */ D_TRACKCLOSE,
65};
66
67/*
68PROPOSAL:
69each unit needs:
70status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
71dspW and audio are deprecated.
72dsp needs min 64 channels, will give it 256
73
74minor = (unit << 20) + (dev << 16) + channel
75currently minor = (channel << 16) + (unit << 4) + dev
76
77nomenclature:
78 /dev/pcmX/dsp.(0..255)
79 /dev/pcmX/dspW
80 /dev/pcmX/audio
81 /dev/pcmX/status
82 /dev/pcmX/mixer
83 [etc.]
84*/
85
86#define PCMMINOR(x) (minor(x))
87#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
88#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
89#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
90#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
91
92static devclass_t pcm_devclass;
93
94#ifdef USING_DEVFS
95static int snd_unit;
96TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit);
95static int snd_unit = 0;
96TUNABLE_INT("hw.snd.unit", &snd_unit);
97#endif
98
99SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
100
101void *
102snd_mtxcreate(const char *desc)
103{
104#ifdef USING_MUTEX
105 struct mtx *m;
106
107 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
108 if (m == NULL)
109 return NULL;
110 mtx_init(m, desc, MTX_RECURSE);
111 return m;
112#else
113 return (void *)0xcafebabe;
114#endif
115}
116
117void
118snd_mtxfree(void *m)
119{
120#ifdef USING_MUTEX
121 struct mtx *mtx = m;
122
123 mtx_assert(mtx, MA_OWNED);
124 mtx_destroy(mtx);
125 free(mtx, M_DEVBUF);
126#endif
127}
128
129void
130snd_mtxassert(void *m)
131{
132#ifdef USING_MUTEX
133 struct mtx *mtx = m;
134
135 mtx_assert(mtx, MA_OWNED);
136#endif
137}
138
139void
140snd_mtxlock(void *m)
141{
142#ifdef USING_MUTEX
143 struct mtx *mtx = m;
144
145 mtx_lock(mtx);
146#endif
147}
148
149void
150snd_mtxunlock(void *m)
151{
152#ifdef USING_MUTEX
153 struct mtx *mtx = m;
154
155 mtx_unlock(mtx);
156#endif
157}
158
159int
160snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
161{
162#ifdef USING_MUTEX
163 flags &= INTR_MPSAFE;
164 flags |= INTR_TYPE_TTY;
165#else
166 flags = INTR_TYPE_TTY;
167#endif
168 return bus_setup_intr(dev, res, flags, hand, param, cookiep);
169}
170
171struct pcm_channel *
172pcm_chnalloc(struct snddev_info *d, int direction)
173{
174 struct pcm_channel *c;
175 struct snddev_channel *sce;
176
177 SLIST_FOREACH(sce, &d->channels, link) {
178 c = sce->channel;
179 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
180 c->flags |= CHN_F_BUSY;
181 return c;
182 }
183 }
184 return NULL;
185}
186
187int
188pcm_chnfree(struct pcm_channel *c)
189{
190 c->flags &= ~CHN_F_BUSY;
191 return 0;
192}
193
194int
195pcm_chnref(struct pcm_channel *c, int ref)
196{
197 c->refcount += ref;
198 return c->refcount;
199}
200
201#ifdef USING_DEVFS
202static void
203pcm_makelinks(void *dummy)
204{
205 int unit;
206 dev_t pdev;
207 static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
208 struct snddev_info *d;
209
210 if (pcm_devclass == NULL || devfs_present == 0)
211 return;
212 if (dsp) {
213 destroy_dev(dsp);
214 dsp = 0;
215 }
216 if (dspW) {
217 destroy_dev(dspW);
218 dspW = 0;
219 }
220 if (audio) {
221 destroy_dev(audio);
222 audio = 0;
223 }
224 if (mixer) {
225 destroy_dev(mixer);
226 mixer = 0;
227 }
228
229 unit = snd_unit;
230 if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
231 return;
232 d = devclass_get_softc(pcm_devclass, unit);
233 if (d == NULL || d->chancount == 0)
234 return;
235
236 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0));
237 dsp = make_dev_alias(pdev, "dsp");
238 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0));
239 dspW = make_dev_alias(pdev, "dspW");
240 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
241 audio = make_dev_alias(pdev, "audio");
242 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
243 mixer = make_dev_alias(pdev, "mixer");
244}
245
246static int
247sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
248{
249 int error, unit;
250
251 unit = snd_unit;
252 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
253 if (error == 0 && req->newptr != NULL) {
254 snd_unit = unit;
255 pcm_makelinks(NULL);
256 }
257 return (error);
258}
259SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
260 0, sizeof(int), sysctl_hw_sndunit, "I", "");
261#endif
262
263struct pcm_channel *
264pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
265{
266 struct pcm_channel *ch;
267 char *dirs;
268 int err;
269
270 switch(dir) {
271 case PCMDIR_PLAY:
272 dirs = "play";
273 break;
274 case PCMDIR_REC:
275 dirs = "record";
276 break;
277 case PCMDIR_VIRTUAL:
278 dirs = "virtual";
279 dir = PCMDIR_PLAY;
280 break;
281 default:
282 return NULL;
283 }
284
285 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
286 if (!ch)
287 return NULL;
288
289 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
290 if (!ch->methods) {
291 free(ch, M_DEVBUF);
292 return NULL;
293 }
294
295 ch->pid = -1;
296 ch->parentsnddev = d;
297 ch->parentchannel = parent;
298 snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
299
300 err = chn_init(ch, devinfo, dir);
301 if (err) {
302 device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
303 kobj_delete(ch->methods, M_DEVBUF);
304 free(ch, M_DEVBUF);
305 return NULL;
306 }
307
308 return ch;
309}
310
311int
312pcm_chn_destroy(struct pcm_channel *ch)
313{
314 int err;
315
316 err = chn_kill(ch);
317 if (err) {
318 device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
319 return err;
320 }
321
322 kobj_delete(ch->methods, M_DEVBUF);
323 free(ch, M_DEVBUF);
324
325 return 0;
326}
327
328int
329pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
330{
331 struct snddev_channel *sce;
332 int unit = device_get_unit(d->dev);
333
334 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
335 if (!sce) {
336 free(ch, M_DEVBUF);
337 return ENOMEM;
338 }
339
340 sce->channel = ch;
341 SLIST_INSERT_HEAD(&d->channels, sce, link);
342
343 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
344 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
345 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
346 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
347 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
348 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
349 /* XXX SND_DEV_NORESET? */
350
351#ifdef USING_DEVFS
352 if (d->chancount++ == 0)
353 pcm_makelinks(NULL);
354#endif
355
356 return 0;
357}
358
359int
360pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
361{
362 struct snddev_channel *sce;
363 int unit = device_get_unit(d->dev);
364 dev_t pdev;
365
366 SLIST_FOREACH(sce, &d->channels, link) {
367 if (sce->channel == ch)
368 goto gotit;
369 }
370 return EINVAL;
371gotit:
372 d->chancount--;
373 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
374 free(sce, M_DEVBUF);
375
376#ifdef USING_DEVFS
377 if (d->chancount == 0)
378 pcm_makelinks(NULL);
379#endif
380 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
381 destroy_dev(pdev);
382 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
383 destroy_dev(pdev);
384 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
385 destroy_dev(pdev);
386
387 return 0;
388}
389
390int
391pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
392{
393 struct snddev_info *d = device_get_softc(dev);
394 struct pcm_channel *ch;
395 int err;
396
397 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
398 if (!ch) {
399 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
400 return ENODEV;
401 }
402 err = pcm_chn_add(d, ch);
403 if (err) {
404 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
405 pcm_chn_destroy(ch);
406 }
407
408 return err;
409}
410
411static int
412pcm_killchan(device_t dev)
413{
414 struct snddev_info *d = device_get_softc(dev);
415 struct snddev_channel *sce;
416
417 sce = SLIST_FIRST(&d->channels);
418
419 return pcm_chn_remove(d, sce->channel);
420}
421
422int
423pcm_setstatus(device_t dev, char *str)
424{
425 struct snddev_info *d = device_get_softc(dev);
426 strncpy(d->status, str, SND_STATUSLEN);
427 return 0;
428}
429
430u_int32_t
431pcm_getflags(device_t dev)
432{
433 struct snddev_info *d = device_get_softc(dev);
434 return d->flags;
435}
436
437void
438pcm_setflags(device_t dev, u_int32_t val)
439{
440 struct snddev_info *d = device_get_softc(dev);
441 d->flags = val;
442}
443
444void *
445pcm_getdevinfo(device_t dev)
446{
447 struct snddev_info *d = device_get_softc(dev);
448 return d->devinfo;
449}
450
451/* This is the generic init routine */
452int
453pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
454{
455 int sz, unit = device_get_unit(dev);
456 struct snddev_info *d = device_get_softc(dev);
457
458 if (!pcm_devclass) {
459 pcm_devclass = device_get_devclass(dev);
460 status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
461 UID_ROOT, GID_WHEEL, 0444, "sndstat");
462 }
463
464 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
465 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
466
467 d->dev = dev;
468 d->devinfo = devinfo;
469 d->chancount = 0;
470 d->maxchans = numplay + numrec;
471 sz = d->maxchans * sizeof(struct pcm_channel *);
472
473 if (sz > 0) {
474 d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
475 d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
476 if (!d->arec || !d->aplay) goto no;
477
478 if (numplay == 0 || numrec == 0)
479 d->flags |= SD_F_SIMPLEX;
480
481 d->fakechan = fkchan_setup(dev);
482 chn_init(d->fakechan, NULL, 0);
483 }
484
485#ifdef SND_DYNSYSCTL
486 sysctl_ctx_init(&d->sysctl_tree);
487 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
488 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
489 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
490 if (d->sysctl_tree_top == NULL) {
491 sysctl_ctx_free(&d->sysctl_tree);
492 goto no;
493 }
494#endif
495#if 0
496 vchan_initsys(d);
497#endif
498 return 0;
499no:
500 if (d->aplay) free(d->aplay, M_DEVBUF);
501 if (d->arec) free(d->arec, M_DEVBUF);
502 return ENXIO;
503}
504
505int
506pcm_unregister(device_t dev)
507{
508 int unit = device_get_unit(dev);
509 struct snddev_info *d = device_get_softc(dev);
510 struct snddev_channel *sce;
511 dev_t pdev;
512
513 SLIST_FOREACH(sce, &d->channels, link) {
514 if (sce->channel->refcount > 0) {
515 device_printf(dev, "unregister: channel busy");
516 return EBUSY;
517 }
518 }
519 if (mixer_isbusy(d->mixer)) {
520 device_printf(dev, "unregister: mixer busy");
521 return EBUSY;
522 }
523
524#ifdef SND_DYNSYSCTL
525 d->sysctl_tree_top = NULL;
526 sysctl_ctx_free(&d->sysctl_tree);
527#endif
528
529 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
530 destroy_dev(pdev);
531 mixer_uninit(dev);
532
533 while (d->chancount > 0)
534 pcm_killchan(dev);
535
536 if (d->aplay) free(d->aplay, M_DEVBUF);
537 if (d->arec) free(d->arec, M_DEVBUF);
538
539 chn_kill(d->fakechan);
540 fkchan_kill(d->fakechan);
541
542 return 0;
543}
544
545/*
546 * a small utility function which, given a device number, returns
547 * a pointer to the associated struct snddev_info struct, and sets the unit
548 * number.
549 */
550static struct snddev_info *
551get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
552{
553 struct snddev_info *sc;
554 int u, d, c;
555
556 u = PCMUNIT(i_dev);
557 d = PCMDEV(i_dev);
558 c = PCMCHAN(i_dev);
559 if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
560 if (unit) *unit = u;
561 if (dev) *dev = d;
562 if (chan) *chan = c;
563 if (u < 0) return NULL;
564
565 sc = devclass_get_softc(pcm_devclass, u);
566 if (sc == NULL) return NULL;
567
568 switch(d) {
569 case SND_DEV_CTL: /* /dev/mixer handled by pcm */
570 case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
571 case SND_DEV_DSP:
572 case SND_DEV_DSP16:
573 case SND_DEV_AUDIO:
574 return sc;
575
576 case SND_DEV_SEQ: /* XXX when enabled... */
577 case SND_DEV_SEQ2:
578 case SND_DEV_MIDIN:
579 case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */
580 default:
581 printf("unsupported subdevice %d\n", d);
582 return NULL;
583 }
584}
585
586static int
587sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
588{
589 int dev, unit, chan;
590 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
591
592 DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
593 unit, dev, flags, mode));
594
595 switch(dev) {
596 case SND_DEV_STATUS:
597 return do_status(0, NULL);
598
599 case SND_DEV_CTL:
600 return d? mixer_busy(d->mixer, 1) : ENXIO;
601
602 case SND_DEV_AUDIO:
603 case SND_DEV_DSP:
604 case SND_DEV_DSP16:
605 case SND_DEV_NORESET:
606 return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO;
607
608 default:
609 return ENXIO;
610 }
611}
612
613static int
614sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
615{
616 int dev, unit, chan;
617 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
618
619 DEB(printf("close snd%d subdev %d\n", unit, dev));
620
621 switch(dev) { /* only those for which close makes sense */
622 case SND_DEV_STATUS:
623 return do_status(1, NULL);
624
625 case SND_DEV_CTL:
626 return d? mixer_busy(d->mixer, 0) : ENXIO;
627
628 case SND_DEV_AUDIO:
629 case SND_DEV_DSP:
630 case SND_DEV_DSP16:
631 return d? dsp_close(d, chan, dev) : ENXIO;
632
633 default:
634 return ENXIO;
635 }
636}
637
638static int
639sndread(dev_t i_dev, struct uio *buf, int flag)
640{
641 int dev, unit, chan;
642 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
643 DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
644
645 switch(dev) {
646 case SND_DEV_STATUS:
647 return do_status(2, buf);
648
649 case SND_DEV_AUDIO:
650 case SND_DEV_DSP:
651 case SND_DEV_DSP16:
652 return d? dsp_read(d, chan, buf, flag) : EBADF;
653
654 default:
655 return ENXIO;
656 }
657}
658
659static int
660sndwrite(dev_t i_dev, struct uio *buf, int flag)
661{
662 int dev, unit, chan;
663 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
664
665 DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
666
667 switch(dev) { /* only writeable devices */
668 case SND_DEV_DSP:
669 case SND_DEV_DSP16:
670 case SND_DEV_AUDIO:
671 return d? dsp_write(d, chan, buf, flag) : EBADF;
672
673 default:
674 return EPERM; /* for non-writeable devices ; */
675 }
676}
677
678static int
679sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
680{
681 int dev, chan;
682 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
683
684 if (d == NULL) return ENXIO;
685
686 switch(dev) {
687 case SND_DEV_CTL:
688 return mixer_ioctl(d, cmd, arg);
689
690 case SND_DEV_AUDIO:
691 case SND_DEV_DSP:
692 case SND_DEV_DSP16:
693 if (IOCGROUP(cmd) == 'M')
694 return mixer_ioctl(d, cmd, arg);
695 else
696 return dsp_ioctl(d, chan, cmd, arg);
697
698 default:
699 return ENXIO;
700 }
701}
702
703static int
704sndpoll(dev_t i_dev, int events, struct proc *p)
705{
706 int dev, chan;
707 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
708
709 DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
710
711 if (d == NULL) return ENXIO;
712
713 switch(dev) {
714 case SND_DEV_AUDIO:
715 case SND_DEV_DSP:
716 case SND_DEV_DSP16:
717 return dsp_poll(d, chan, events, p);
718
719 default:
720 return (events &
721 (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
722 }
723}
724
725/*
726 * The mmap interface allows access to the play and read buffer,
727 * plus the device descriptor.
728 * The various blocks are accessible at the following offsets:
729 *
730 * 0x00000000 ( 0 ) : write buffer ;
731 * 0x01000000 (16 MB) : read buffer ;
732 * 0x02000000 (32 MB) : device descriptor (dangerous!)
733 *
734 * WARNING: the mmap routines assume memory areas are aligned. This
735 * is true (probably) for the dma buffers, but likely false for the
736 * device descriptor. As a consequence, we do not know where it is
737 * located in the requested area.
738 */
739static int
740sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
741{
742 int unit, dev, chan;
743 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
744
745 DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
746 d, dev, offset, nprot));
747
748 if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */
749
750 switch(dev) {
751 case SND_DEV_AUDIO:
752 case SND_DEV_DSP:
753 case SND_DEV_DSP16:
754 return dsp_mmap(d, chan, offset, nprot);
755
756 default:
757 return -1;
758 }
759}
760
761static int
762status_init(struct sbuf *s)
763{
764 int i, pc, rc, vc;
765 device_t dev;
766 struct snddev_info *d;
767 struct snddev_channel *sce;
768 struct pcm_channel *c;
769 struct pcm_feeder *f;
770
771 sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n",
772 __DATE__, __TIME__);
773
774 for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
775 d = devclass_get_softc(pcm_devclass, i);
776 if (!d)
777 continue;
778 dev = devclass_get_device(pcm_devclass, i);
779 sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
780 if (d->chancount > 0) {
781 pc = rc = vc = 0;
782 SLIST_FOREACH(sce, &d->channels, link) {
783 c = sce->channel;
784 if (c->direction == PCMDIR_PLAY) {
785 if (c->flags & CHN_F_VIRTUAL)
786 vc++;
787 else
788 pc++;
789 } else
790 rc++;
791 }
792 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
793 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
794#ifdef USING_DEVFS
795 (i == snd_unit)? " default" : ""
796#else
797 ""
798#endif
799 );
800#ifdef SNDSTAT_VERBOSE
801 SLIST_FOREACH(sce, &d->channels, link) {
802 c = sce->channel;
803 sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
804 c->parentchannel? c->parentchannel->name : "",
805 c->name, c->speed, c->format, c->flags);
806 if (c->pid != -1)
807 sbuf_printf(s, ", pid %d", c->pid);
808 sbuf_printf(s, "\n\t");
809 f = c->feeder;
810 while (f) {
811 sbuf_printf(s, "%s", f->class->name);
812 if (f->desc->type == FEEDER_FMT)
813 sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
814 if (f->desc->type == FEEDER_RATE)
815 sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
816 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
817 sbuf_printf(s, "(%08x)", f->desc->out);
818 if (f->source)
819 sbuf_printf(s, " <- ");
820 f = f->source;
821 }
822 sbuf_printf(s, "\n");
823 }
824#endif
825 } else
826 sbuf_printf(s, " (mixer only)\n");
827 }
828 sbuf_finish(s);
829 return sbuf_len(s);
830}
831
832static int
833do_status(int action, struct uio *buf)
834{
835 static struct sbuf s;
836 static int bufptr = 0;
837 static int status_open = 0;
838 int l, err;
839
840 switch(action) {
841 case 0: /* open */
842 if (status_open)
843 return EBUSY;
844 if (sbuf_new(&s, NULL, 4096, 0))
845 return ENXIO;
846 bufptr = 0;
847 err = (status_init(&s) > 0)? 0 : ENOMEM;
848 if (!err)
849 status_open = 1;
850 return err;
851
852 case 1: /* close */
853 if (!status_open)
854 return EBADF;
855 sbuf_delete(&s);
856 status_open = 0;
857 return 0;
858
859 case 2:
860 if (!status_open)
861 return EBADF;
862 l = min(buf->uio_resid, sbuf_len(&s) - bufptr);
863 err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0;
864 bufptr += l;
865 return err;
866
867 case 3:
868 return status_open;
869 }
870
871 return EBADF;
872}
873
874static int
875sndpcm_modevent(module_t mod, int type, void *data)
876{
877
878 switch (type) {
879 case MOD_LOAD:
880 break;
881 case MOD_UNLOAD:
882 if (do_status(3, NULL))
883 return EBUSY;
884 if (status_dev)
885 destroy_dev(status_dev);
886 status_dev = 0;
887 break;
888 default:
889 break;
890 }
891 return 0;
892}
893
894static moduledata_t sndpcm_mod = {
895 "snd_pcm",
896 sndpcm_modevent,
897 NULL
898};
899DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
900MODULE_VERSION(snd_pcm, PCM_MODVER);
97#endif
98
99SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
100
101void *
102snd_mtxcreate(const char *desc)
103{
104#ifdef USING_MUTEX
105 struct mtx *m;
106
107 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
108 if (m == NULL)
109 return NULL;
110 mtx_init(m, desc, MTX_RECURSE);
111 return m;
112#else
113 return (void *)0xcafebabe;
114#endif
115}
116
117void
118snd_mtxfree(void *m)
119{
120#ifdef USING_MUTEX
121 struct mtx *mtx = m;
122
123 mtx_assert(mtx, MA_OWNED);
124 mtx_destroy(mtx);
125 free(mtx, M_DEVBUF);
126#endif
127}
128
129void
130snd_mtxassert(void *m)
131{
132#ifdef USING_MUTEX
133 struct mtx *mtx = m;
134
135 mtx_assert(mtx, MA_OWNED);
136#endif
137}
138
139void
140snd_mtxlock(void *m)
141{
142#ifdef USING_MUTEX
143 struct mtx *mtx = m;
144
145 mtx_lock(mtx);
146#endif
147}
148
149void
150snd_mtxunlock(void *m)
151{
152#ifdef USING_MUTEX
153 struct mtx *mtx = m;
154
155 mtx_unlock(mtx);
156#endif
157}
158
159int
160snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
161{
162#ifdef USING_MUTEX
163 flags &= INTR_MPSAFE;
164 flags |= INTR_TYPE_TTY;
165#else
166 flags = INTR_TYPE_TTY;
167#endif
168 return bus_setup_intr(dev, res, flags, hand, param, cookiep);
169}
170
171struct pcm_channel *
172pcm_chnalloc(struct snddev_info *d, int direction)
173{
174 struct pcm_channel *c;
175 struct snddev_channel *sce;
176
177 SLIST_FOREACH(sce, &d->channels, link) {
178 c = sce->channel;
179 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
180 c->flags |= CHN_F_BUSY;
181 return c;
182 }
183 }
184 return NULL;
185}
186
187int
188pcm_chnfree(struct pcm_channel *c)
189{
190 c->flags &= ~CHN_F_BUSY;
191 return 0;
192}
193
194int
195pcm_chnref(struct pcm_channel *c, int ref)
196{
197 c->refcount += ref;
198 return c->refcount;
199}
200
201#ifdef USING_DEVFS
202static void
203pcm_makelinks(void *dummy)
204{
205 int unit;
206 dev_t pdev;
207 static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
208 struct snddev_info *d;
209
210 if (pcm_devclass == NULL || devfs_present == 0)
211 return;
212 if (dsp) {
213 destroy_dev(dsp);
214 dsp = 0;
215 }
216 if (dspW) {
217 destroy_dev(dspW);
218 dspW = 0;
219 }
220 if (audio) {
221 destroy_dev(audio);
222 audio = 0;
223 }
224 if (mixer) {
225 destroy_dev(mixer);
226 mixer = 0;
227 }
228
229 unit = snd_unit;
230 if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
231 return;
232 d = devclass_get_softc(pcm_devclass, unit);
233 if (d == NULL || d->chancount == 0)
234 return;
235
236 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0));
237 dsp = make_dev_alias(pdev, "dsp");
238 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0));
239 dspW = make_dev_alias(pdev, "dspW");
240 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
241 audio = make_dev_alias(pdev, "audio");
242 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
243 mixer = make_dev_alias(pdev, "mixer");
244}
245
246static int
247sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
248{
249 int error, unit;
250
251 unit = snd_unit;
252 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
253 if (error == 0 && req->newptr != NULL) {
254 snd_unit = unit;
255 pcm_makelinks(NULL);
256 }
257 return (error);
258}
259SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
260 0, sizeof(int), sysctl_hw_sndunit, "I", "");
261#endif
262
263struct pcm_channel *
264pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
265{
266 struct pcm_channel *ch;
267 char *dirs;
268 int err;
269
270 switch(dir) {
271 case PCMDIR_PLAY:
272 dirs = "play";
273 break;
274 case PCMDIR_REC:
275 dirs = "record";
276 break;
277 case PCMDIR_VIRTUAL:
278 dirs = "virtual";
279 dir = PCMDIR_PLAY;
280 break;
281 default:
282 return NULL;
283 }
284
285 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
286 if (!ch)
287 return NULL;
288
289 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
290 if (!ch->methods) {
291 free(ch, M_DEVBUF);
292 return NULL;
293 }
294
295 ch->pid = -1;
296 ch->parentsnddev = d;
297 ch->parentchannel = parent;
298 snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
299
300 err = chn_init(ch, devinfo, dir);
301 if (err) {
302 device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
303 kobj_delete(ch->methods, M_DEVBUF);
304 free(ch, M_DEVBUF);
305 return NULL;
306 }
307
308 return ch;
309}
310
311int
312pcm_chn_destroy(struct pcm_channel *ch)
313{
314 int err;
315
316 err = chn_kill(ch);
317 if (err) {
318 device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
319 return err;
320 }
321
322 kobj_delete(ch->methods, M_DEVBUF);
323 free(ch, M_DEVBUF);
324
325 return 0;
326}
327
328int
329pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
330{
331 struct snddev_channel *sce;
332 int unit = device_get_unit(d->dev);
333
334 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
335 if (!sce) {
336 free(ch, M_DEVBUF);
337 return ENOMEM;
338 }
339
340 sce->channel = ch;
341 SLIST_INSERT_HEAD(&d->channels, sce, link);
342
343 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
344 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
345 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
346 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
347 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
348 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
349 /* XXX SND_DEV_NORESET? */
350
351#ifdef USING_DEVFS
352 if (d->chancount++ == 0)
353 pcm_makelinks(NULL);
354#endif
355
356 return 0;
357}
358
359int
360pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
361{
362 struct snddev_channel *sce;
363 int unit = device_get_unit(d->dev);
364 dev_t pdev;
365
366 SLIST_FOREACH(sce, &d->channels, link) {
367 if (sce->channel == ch)
368 goto gotit;
369 }
370 return EINVAL;
371gotit:
372 d->chancount--;
373 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
374 free(sce, M_DEVBUF);
375
376#ifdef USING_DEVFS
377 if (d->chancount == 0)
378 pcm_makelinks(NULL);
379#endif
380 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
381 destroy_dev(pdev);
382 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
383 destroy_dev(pdev);
384 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
385 destroy_dev(pdev);
386
387 return 0;
388}
389
390int
391pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
392{
393 struct snddev_info *d = device_get_softc(dev);
394 struct pcm_channel *ch;
395 int err;
396
397 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
398 if (!ch) {
399 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
400 return ENODEV;
401 }
402 err = pcm_chn_add(d, ch);
403 if (err) {
404 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
405 pcm_chn_destroy(ch);
406 }
407
408 return err;
409}
410
411static int
412pcm_killchan(device_t dev)
413{
414 struct snddev_info *d = device_get_softc(dev);
415 struct snddev_channel *sce;
416
417 sce = SLIST_FIRST(&d->channels);
418
419 return pcm_chn_remove(d, sce->channel);
420}
421
422int
423pcm_setstatus(device_t dev, char *str)
424{
425 struct snddev_info *d = device_get_softc(dev);
426 strncpy(d->status, str, SND_STATUSLEN);
427 return 0;
428}
429
430u_int32_t
431pcm_getflags(device_t dev)
432{
433 struct snddev_info *d = device_get_softc(dev);
434 return d->flags;
435}
436
437void
438pcm_setflags(device_t dev, u_int32_t val)
439{
440 struct snddev_info *d = device_get_softc(dev);
441 d->flags = val;
442}
443
444void *
445pcm_getdevinfo(device_t dev)
446{
447 struct snddev_info *d = device_get_softc(dev);
448 return d->devinfo;
449}
450
451/* This is the generic init routine */
452int
453pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
454{
455 int sz, unit = device_get_unit(dev);
456 struct snddev_info *d = device_get_softc(dev);
457
458 if (!pcm_devclass) {
459 pcm_devclass = device_get_devclass(dev);
460 status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
461 UID_ROOT, GID_WHEEL, 0444, "sndstat");
462 }
463
464 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
465 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
466
467 d->dev = dev;
468 d->devinfo = devinfo;
469 d->chancount = 0;
470 d->maxchans = numplay + numrec;
471 sz = d->maxchans * sizeof(struct pcm_channel *);
472
473 if (sz > 0) {
474 d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
475 d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
476 if (!d->arec || !d->aplay) goto no;
477
478 if (numplay == 0 || numrec == 0)
479 d->flags |= SD_F_SIMPLEX;
480
481 d->fakechan = fkchan_setup(dev);
482 chn_init(d->fakechan, NULL, 0);
483 }
484
485#ifdef SND_DYNSYSCTL
486 sysctl_ctx_init(&d->sysctl_tree);
487 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
488 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
489 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
490 if (d->sysctl_tree_top == NULL) {
491 sysctl_ctx_free(&d->sysctl_tree);
492 goto no;
493 }
494#endif
495#if 0
496 vchan_initsys(d);
497#endif
498 return 0;
499no:
500 if (d->aplay) free(d->aplay, M_DEVBUF);
501 if (d->arec) free(d->arec, M_DEVBUF);
502 return ENXIO;
503}
504
505int
506pcm_unregister(device_t dev)
507{
508 int unit = device_get_unit(dev);
509 struct snddev_info *d = device_get_softc(dev);
510 struct snddev_channel *sce;
511 dev_t pdev;
512
513 SLIST_FOREACH(sce, &d->channels, link) {
514 if (sce->channel->refcount > 0) {
515 device_printf(dev, "unregister: channel busy");
516 return EBUSY;
517 }
518 }
519 if (mixer_isbusy(d->mixer)) {
520 device_printf(dev, "unregister: mixer busy");
521 return EBUSY;
522 }
523
524#ifdef SND_DYNSYSCTL
525 d->sysctl_tree_top = NULL;
526 sysctl_ctx_free(&d->sysctl_tree);
527#endif
528
529 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
530 destroy_dev(pdev);
531 mixer_uninit(dev);
532
533 while (d->chancount > 0)
534 pcm_killchan(dev);
535
536 if (d->aplay) free(d->aplay, M_DEVBUF);
537 if (d->arec) free(d->arec, M_DEVBUF);
538
539 chn_kill(d->fakechan);
540 fkchan_kill(d->fakechan);
541
542 return 0;
543}
544
545/*
546 * a small utility function which, given a device number, returns
547 * a pointer to the associated struct snddev_info struct, and sets the unit
548 * number.
549 */
550static struct snddev_info *
551get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
552{
553 struct snddev_info *sc;
554 int u, d, c;
555
556 u = PCMUNIT(i_dev);
557 d = PCMDEV(i_dev);
558 c = PCMCHAN(i_dev);
559 if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
560 if (unit) *unit = u;
561 if (dev) *dev = d;
562 if (chan) *chan = c;
563 if (u < 0) return NULL;
564
565 sc = devclass_get_softc(pcm_devclass, u);
566 if (sc == NULL) return NULL;
567
568 switch(d) {
569 case SND_DEV_CTL: /* /dev/mixer handled by pcm */
570 case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
571 case SND_DEV_DSP:
572 case SND_DEV_DSP16:
573 case SND_DEV_AUDIO:
574 return sc;
575
576 case SND_DEV_SEQ: /* XXX when enabled... */
577 case SND_DEV_SEQ2:
578 case SND_DEV_MIDIN:
579 case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */
580 default:
581 printf("unsupported subdevice %d\n", d);
582 return NULL;
583 }
584}
585
586static int
587sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
588{
589 int dev, unit, chan;
590 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
591
592 DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
593 unit, dev, flags, mode));
594
595 switch(dev) {
596 case SND_DEV_STATUS:
597 return do_status(0, NULL);
598
599 case SND_DEV_CTL:
600 return d? mixer_busy(d->mixer, 1) : ENXIO;
601
602 case SND_DEV_AUDIO:
603 case SND_DEV_DSP:
604 case SND_DEV_DSP16:
605 case SND_DEV_NORESET:
606 return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO;
607
608 default:
609 return ENXIO;
610 }
611}
612
613static int
614sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
615{
616 int dev, unit, chan;
617 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
618
619 DEB(printf("close snd%d subdev %d\n", unit, dev));
620
621 switch(dev) { /* only those for which close makes sense */
622 case SND_DEV_STATUS:
623 return do_status(1, NULL);
624
625 case SND_DEV_CTL:
626 return d? mixer_busy(d->mixer, 0) : ENXIO;
627
628 case SND_DEV_AUDIO:
629 case SND_DEV_DSP:
630 case SND_DEV_DSP16:
631 return d? dsp_close(d, chan, dev) : ENXIO;
632
633 default:
634 return ENXIO;
635 }
636}
637
638static int
639sndread(dev_t i_dev, struct uio *buf, int flag)
640{
641 int dev, unit, chan;
642 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
643 DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
644
645 switch(dev) {
646 case SND_DEV_STATUS:
647 return do_status(2, buf);
648
649 case SND_DEV_AUDIO:
650 case SND_DEV_DSP:
651 case SND_DEV_DSP16:
652 return d? dsp_read(d, chan, buf, flag) : EBADF;
653
654 default:
655 return ENXIO;
656 }
657}
658
659static int
660sndwrite(dev_t i_dev, struct uio *buf, int flag)
661{
662 int dev, unit, chan;
663 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
664
665 DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
666
667 switch(dev) { /* only writeable devices */
668 case SND_DEV_DSP:
669 case SND_DEV_DSP16:
670 case SND_DEV_AUDIO:
671 return d? dsp_write(d, chan, buf, flag) : EBADF;
672
673 default:
674 return EPERM; /* for non-writeable devices ; */
675 }
676}
677
678static int
679sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
680{
681 int dev, chan;
682 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
683
684 if (d == NULL) return ENXIO;
685
686 switch(dev) {
687 case SND_DEV_CTL:
688 return mixer_ioctl(d, cmd, arg);
689
690 case SND_DEV_AUDIO:
691 case SND_DEV_DSP:
692 case SND_DEV_DSP16:
693 if (IOCGROUP(cmd) == 'M')
694 return mixer_ioctl(d, cmd, arg);
695 else
696 return dsp_ioctl(d, chan, cmd, arg);
697
698 default:
699 return ENXIO;
700 }
701}
702
703static int
704sndpoll(dev_t i_dev, int events, struct proc *p)
705{
706 int dev, chan;
707 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
708
709 DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
710
711 if (d == NULL) return ENXIO;
712
713 switch(dev) {
714 case SND_DEV_AUDIO:
715 case SND_DEV_DSP:
716 case SND_DEV_DSP16:
717 return dsp_poll(d, chan, events, p);
718
719 default:
720 return (events &
721 (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
722 }
723}
724
725/*
726 * The mmap interface allows access to the play and read buffer,
727 * plus the device descriptor.
728 * The various blocks are accessible at the following offsets:
729 *
730 * 0x00000000 ( 0 ) : write buffer ;
731 * 0x01000000 (16 MB) : read buffer ;
732 * 0x02000000 (32 MB) : device descriptor (dangerous!)
733 *
734 * WARNING: the mmap routines assume memory areas are aligned. This
735 * is true (probably) for the dma buffers, but likely false for the
736 * device descriptor. As a consequence, we do not know where it is
737 * located in the requested area.
738 */
739static int
740sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
741{
742 int unit, dev, chan;
743 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
744
745 DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
746 d, dev, offset, nprot));
747
748 if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */
749
750 switch(dev) {
751 case SND_DEV_AUDIO:
752 case SND_DEV_DSP:
753 case SND_DEV_DSP16:
754 return dsp_mmap(d, chan, offset, nprot);
755
756 default:
757 return -1;
758 }
759}
760
761static int
762status_init(struct sbuf *s)
763{
764 int i, pc, rc, vc;
765 device_t dev;
766 struct snddev_info *d;
767 struct snddev_channel *sce;
768 struct pcm_channel *c;
769 struct pcm_feeder *f;
770
771 sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n",
772 __DATE__, __TIME__);
773
774 for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
775 d = devclass_get_softc(pcm_devclass, i);
776 if (!d)
777 continue;
778 dev = devclass_get_device(pcm_devclass, i);
779 sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
780 if (d->chancount > 0) {
781 pc = rc = vc = 0;
782 SLIST_FOREACH(sce, &d->channels, link) {
783 c = sce->channel;
784 if (c->direction == PCMDIR_PLAY) {
785 if (c->flags & CHN_F_VIRTUAL)
786 vc++;
787 else
788 pc++;
789 } else
790 rc++;
791 }
792 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
793 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
794#ifdef USING_DEVFS
795 (i == snd_unit)? " default" : ""
796#else
797 ""
798#endif
799 );
800#ifdef SNDSTAT_VERBOSE
801 SLIST_FOREACH(sce, &d->channels, link) {
802 c = sce->channel;
803 sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
804 c->parentchannel? c->parentchannel->name : "",
805 c->name, c->speed, c->format, c->flags);
806 if (c->pid != -1)
807 sbuf_printf(s, ", pid %d", c->pid);
808 sbuf_printf(s, "\n\t");
809 f = c->feeder;
810 while (f) {
811 sbuf_printf(s, "%s", f->class->name);
812 if (f->desc->type == FEEDER_FMT)
813 sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
814 if (f->desc->type == FEEDER_RATE)
815 sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
816 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
817 sbuf_printf(s, "(%08x)", f->desc->out);
818 if (f->source)
819 sbuf_printf(s, " <- ");
820 f = f->source;
821 }
822 sbuf_printf(s, "\n");
823 }
824#endif
825 } else
826 sbuf_printf(s, " (mixer only)\n");
827 }
828 sbuf_finish(s);
829 return sbuf_len(s);
830}
831
832static int
833do_status(int action, struct uio *buf)
834{
835 static struct sbuf s;
836 static int bufptr = 0;
837 static int status_open = 0;
838 int l, err;
839
840 switch(action) {
841 case 0: /* open */
842 if (status_open)
843 return EBUSY;
844 if (sbuf_new(&s, NULL, 4096, 0))
845 return ENXIO;
846 bufptr = 0;
847 err = (status_init(&s) > 0)? 0 : ENOMEM;
848 if (!err)
849 status_open = 1;
850 return err;
851
852 case 1: /* close */
853 if (!status_open)
854 return EBADF;
855 sbuf_delete(&s);
856 status_open = 0;
857 return 0;
858
859 case 2:
860 if (!status_open)
861 return EBADF;
862 l = min(buf->uio_resid, sbuf_len(&s) - bufptr);
863 err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0;
864 bufptr += l;
865 return err;
866
867 case 3:
868 return status_open;
869 }
870
871 return EBADF;
872}
873
874static int
875sndpcm_modevent(module_t mod, int type, void *data)
876{
877
878 switch (type) {
879 case MOD_LOAD:
880 break;
881 case MOD_UNLOAD:
882 if (do_status(3, NULL))
883 return EBUSY;
884 if (status_dev)
885 destroy_dev(status_dev);
886 status_dev = 0;
887 break;
888 default:
889 break;
890 }
891 return 0;
892}
893
894static moduledata_t sndpcm_mod = {
895 "snd_pcm",
896 sndpcm_modevent,
897 NULL
898};
899DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
900MODULE_VERSION(snd_pcm, PCM_MODVER);