Deleted Added
full compact
sound.c (124617) sound.c (124740)
1/*
2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
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
28#include <dev/sound/pcm/sound.h>
29#include <dev/sound/pcm/vchan.h>
1/*
2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
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
28#include <dev/sound/pcm/sound.h>
29#include <dev/sound/pcm/vchan.h>
30#include <dev/sound/pcm/dsp.h>
30#include <sys/sysctl.h>
31
32#include "feeder_if.h"
33
31#include <sys/sysctl.h>
32
33#include "feeder_if.h"
34
34SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 124617 2004-01-17 10:37:11Z phk $");
35SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 124740 2004-01-20 03:58:57Z matk $");
35
36devclass_t pcm_devclass;
37
38int pcm_veto_load = 1;
39
40#ifdef USING_DEVFS
41int snd_unit = 0;
42TUNABLE_INT("hw.snd.unit", &snd_unit);
43#endif
44
45int snd_maxautovchans = 0;
46TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
47
48SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
49
50static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
51
52struct sysctl_ctx_list *
53snd_sysctl_tree(device_t dev)
54{
55 struct snddev_info *d = device_get_softc(dev);
56
57 return &d->sysctl_tree;
58}
59
60struct sysctl_oid *
61snd_sysctl_tree_top(device_t dev)
62{
63 struct snddev_info *d = device_get_softc(dev);
64
65 return d->sysctl_tree_top;
66}
67
68void *
69snd_mtxcreate(const char *desc, const char *type)
70{
71#ifdef USING_MUTEX
72 struct mtx *m;
73
74 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
75 if (m == NULL)
76 return NULL;
77 mtx_init(m, desc, type, MTX_DEF | MTX_RECURSE);
78 return m;
79#else
80 return (void *)0xcafebabe;
81#endif
82}
83
84void
85snd_mtxfree(void *m)
86{
87#ifdef USING_MUTEX
88 struct mtx *mtx = m;
89
90 /* mtx_assert(mtx, MA_OWNED); */
91 mtx_destroy(mtx);
92 free(mtx, M_DEVBUF);
93#endif
94}
95
96void
97snd_mtxassert(void *m)
98{
99#ifdef USING_MUTEX
100#ifdef INVARIANTS
101 struct mtx *mtx = m;
102
103 mtx_assert(mtx, MA_OWNED);
104#endif
105#endif
106}
107/*
108void
109snd_mtxlock(void *m)
110{
111#ifdef USING_MUTEX
112 struct mtx *mtx = m;
113
114 mtx_lock(mtx);
115#endif
116}
117
118void
119snd_mtxunlock(void *m)
120{
121#ifdef USING_MUTEX
122 struct mtx *mtx = m;
123
124 mtx_unlock(mtx);
125#endif
126}
127*/
128int
129snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
130{
131#ifdef USING_MUTEX
132 flags &= INTR_MPSAFE;
133 flags |= INTR_TYPE_AV;
134#else
135 flags = INTR_TYPE_AV;
136#endif
137 return bus_setup_intr(dev, res, flags, hand, param, cookiep);
138}
139
140#ifndef PCM_DEBUG_MTX
141void
142pcm_lock(struct snddev_info *d)
143{
144 snd_mtxlock(d->lock);
145}
146
147void
148pcm_unlock(struct snddev_info *d)
149{
150 snd_mtxunlock(d->lock);
151}
152#endif
153
154struct pcm_channel *
155pcm_getfakechan(struct snddev_info *d)
156{
157 return d->fakechan;
158}
159
160/* return a locked channel */
161struct pcm_channel *
162pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
163{
164 struct pcm_channel *c;
165 struct snddev_channel *sce;
166 int err;
167
168 snd_mtxassert(d->lock);
169
170 /* scan for a free channel */
171 SLIST_FOREACH(sce, &d->channels, link) {
172 c = sce->channel;
173 CHN_LOCK(c);
174 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
175 if (chnum == -1 || c->num == chnum) {
176 c->flags |= CHN_F_BUSY;
177 c->pid = pid;
178 return c;
179 }
180 }
181 CHN_UNLOCK(c);
182 }
183
184 /* no channel available */
185 if (direction == PCMDIR_PLAY) {
186 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
187 /* try to create a vchan */
188 SLIST_FOREACH(sce, &d->channels, link) {
189 c = sce->channel;
190 if (!SLIST_EMPTY(&c->children)) {
191 err = vchan_create(c);
192 if (!err)
193 return pcm_chnalloc(d, direction, pid, -1);
194 else
195 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
196 }
197 }
198 }
199 }
200
201 return NULL;
202}
203
204/* release a locked channel and unlock it */
205int
206pcm_chnrelease(struct pcm_channel *c)
207{
208 CHN_LOCKASSERT(c);
209 c->flags &= ~CHN_F_BUSY;
210 c->pid = -1;
211 CHN_UNLOCK(c);
212 return 0;
213}
214
215int
216pcm_chnref(struct pcm_channel *c, int ref)
217{
218 int r;
219
220 CHN_LOCKASSERT(c);
221 c->refcount += ref;
222 r = c->refcount;
223 return r;
224}
225
226int
227pcm_inprog(struct snddev_info *d, int delta)
228{
229 int r;
230
231 if (delta == 0)
232 return d->inprog;
233
234 /* backtrace(); */
235 pcm_lock(d);
236 d->inprog += delta;
237 r = d->inprog;
238 pcm_unlock(d);
239 return r;
240}
241
242static void
243pcm_setmaxautovchans(struct snddev_info *d, int num)
244{
245 struct pcm_channel *c;
246 struct snddev_channel *sce;
247 int err, done;
248
249 if (num > 0 && d->vchancount == 0) {
250 SLIST_FOREACH(sce, &d->channels, link) {
251 c = sce->channel;
252 if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
253 c->flags |= CHN_F_BUSY;
254 err = vchan_create(c);
255 if (err) {
256 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
257 c->flags &= ~CHN_F_BUSY;
258 }
259 return;
260 }
261 }
262 }
263 if (num == 0 && d->vchancount > 0) {
264 done = 0;
265 while (!done) {
266 done = 1;
267 SLIST_FOREACH(sce, &d->channels, link) {
268 c = sce->channel;
269 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
270 done = 0;
271 snd_mtxlock(d->lock);
272 err = vchan_destroy(c);
273 snd_mtxunlock(d->lock);
274 if (err)
275 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
276 break; /* restart */
277 }
278 }
279 }
280 }
281}
282
283#ifdef USING_DEVFS
284static int
285sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
286{
287 struct snddev_info *d;
288 int error, unit;
289
290 unit = snd_unit;
291 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
292 if (error == 0 && req->newptr != NULL) {
293 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
294 return EINVAL;
295 d = devclass_get_softc(pcm_devclass, unit);
296 if (d == NULL || SLIST_EMPTY(&d->channels))
297 return EINVAL;
298 snd_unit = unit;
299 }
300 return (error);
301}
302SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
303 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
304#endif
305
306static int
307sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
308{
309 struct snddev_info *d;
310 int i, v, error;
311
312 v = snd_maxautovchans;
313 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
314 if (error == 0 && req->newptr != NULL) {
315 if (v < 0 || v >= SND_MAXVCHANS)
316 return EINVAL;
317 if (v != snd_maxautovchans) {
318 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
319 d = devclass_get_softc(pcm_devclass, i);
320 if (!d)
321 continue;
322 pcm_setmaxautovchans(d, v);
323 }
324 }
325 snd_maxautovchans = v;
326 }
327 return (error);
328}
329SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
330 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
331
332struct pcm_channel *
333pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
334{
335 struct pcm_channel *ch;
336 char *dirs;
337 int err, *pnum;
338
339 switch(dir) {
340 case PCMDIR_PLAY:
341 dirs = "play";
342 pnum = &d->playcount;
343 break;
344
345 case PCMDIR_REC:
346 dirs = "record";
347 pnum = &d->reccount;
348 break;
349
350 case PCMDIR_VIRTUAL:
351 dirs = "virtual";
352 dir = PCMDIR_PLAY;
353 pnum = &d->vchancount;
354 break;
355
356 default:
357 return NULL;
358 }
359
360 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
361 if (!ch)
362 return NULL;
363
364 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
365 if (!ch->methods) {
366 free(ch, M_DEVBUF);
367
368 return NULL;
369 }
370
371 snd_mtxlock(d->lock);
372 ch->num = (*pnum)++;
373 snd_mtxunlock(d->lock);
374
375 ch->pid = -1;
376 ch->parentsnddev = d;
377 ch->parentchannel = parent;
378 ch->dev = d->dev;
379 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
380
381 err = chn_init(ch, devinfo, dir);
382 if (err) {
383 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
384 kobj_delete(ch->methods, M_DEVBUF);
385 free(ch, M_DEVBUF);
386 snd_mtxlock(d->lock);
387 (*pnum)--;
388 snd_mtxunlock(d->lock);
389
390 return NULL;
391 }
392
393 return ch;
394}
395
396int
397pcm_chn_destroy(struct pcm_channel *ch)
398{
399 struct snddev_info *d;
400 int err;
401
402 d = ch->parentsnddev;
403 err = chn_kill(ch);
404 if (err) {
405 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
406 return err;
407 }
408
409 kobj_delete(ch->methods, M_DEVBUF);
410 free(ch, M_DEVBUF);
411
412 return 0;
413}
414
415int
36
37devclass_t pcm_devclass;
38
39int pcm_veto_load = 1;
40
41#ifdef USING_DEVFS
42int snd_unit = 0;
43TUNABLE_INT("hw.snd.unit", &snd_unit);
44#endif
45
46int snd_maxautovchans = 0;
47TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
48
49SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
50
51static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
52
53struct sysctl_ctx_list *
54snd_sysctl_tree(device_t dev)
55{
56 struct snddev_info *d = device_get_softc(dev);
57
58 return &d->sysctl_tree;
59}
60
61struct sysctl_oid *
62snd_sysctl_tree_top(device_t dev)
63{
64 struct snddev_info *d = device_get_softc(dev);
65
66 return d->sysctl_tree_top;
67}
68
69void *
70snd_mtxcreate(const char *desc, const char *type)
71{
72#ifdef USING_MUTEX
73 struct mtx *m;
74
75 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
76 if (m == NULL)
77 return NULL;
78 mtx_init(m, desc, type, MTX_DEF | MTX_RECURSE);
79 return m;
80#else
81 return (void *)0xcafebabe;
82#endif
83}
84
85void
86snd_mtxfree(void *m)
87{
88#ifdef USING_MUTEX
89 struct mtx *mtx = m;
90
91 /* mtx_assert(mtx, MA_OWNED); */
92 mtx_destroy(mtx);
93 free(mtx, M_DEVBUF);
94#endif
95}
96
97void
98snd_mtxassert(void *m)
99{
100#ifdef USING_MUTEX
101#ifdef INVARIANTS
102 struct mtx *mtx = m;
103
104 mtx_assert(mtx, MA_OWNED);
105#endif
106#endif
107}
108/*
109void
110snd_mtxlock(void *m)
111{
112#ifdef USING_MUTEX
113 struct mtx *mtx = m;
114
115 mtx_lock(mtx);
116#endif
117}
118
119void
120snd_mtxunlock(void *m)
121{
122#ifdef USING_MUTEX
123 struct mtx *mtx = m;
124
125 mtx_unlock(mtx);
126#endif
127}
128*/
129int
130snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
131{
132#ifdef USING_MUTEX
133 flags &= INTR_MPSAFE;
134 flags |= INTR_TYPE_AV;
135#else
136 flags = INTR_TYPE_AV;
137#endif
138 return bus_setup_intr(dev, res, flags, hand, param, cookiep);
139}
140
141#ifndef PCM_DEBUG_MTX
142void
143pcm_lock(struct snddev_info *d)
144{
145 snd_mtxlock(d->lock);
146}
147
148void
149pcm_unlock(struct snddev_info *d)
150{
151 snd_mtxunlock(d->lock);
152}
153#endif
154
155struct pcm_channel *
156pcm_getfakechan(struct snddev_info *d)
157{
158 return d->fakechan;
159}
160
161/* return a locked channel */
162struct pcm_channel *
163pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
164{
165 struct pcm_channel *c;
166 struct snddev_channel *sce;
167 int err;
168
169 snd_mtxassert(d->lock);
170
171 /* scan for a free channel */
172 SLIST_FOREACH(sce, &d->channels, link) {
173 c = sce->channel;
174 CHN_LOCK(c);
175 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
176 if (chnum == -1 || c->num == chnum) {
177 c->flags |= CHN_F_BUSY;
178 c->pid = pid;
179 return c;
180 }
181 }
182 CHN_UNLOCK(c);
183 }
184
185 /* no channel available */
186 if (direction == PCMDIR_PLAY) {
187 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
188 /* try to create a vchan */
189 SLIST_FOREACH(sce, &d->channels, link) {
190 c = sce->channel;
191 if (!SLIST_EMPTY(&c->children)) {
192 err = vchan_create(c);
193 if (!err)
194 return pcm_chnalloc(d, direction, pid, -1);
195 else
196 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
197 }
198 }
199 }
200 }
201
202 return NULL;
203}
204
205/* release a locked channel and unlock it */
206int
207pcm_chnrelease(struct pcm_channel *c)
208{
209 CHN_LOCKASSERT(c);
210 c->flags &= ~CHN_F_BUSY;
211 c->pid = -1;
212 CHN_UNLOCK(c);
213 return 0;
214}
215
216int
217pcm_chnref(struct pcm_channel *c, int ref)
218{
219 int r;
220
221 CHN_LOCKASSERT(c);
222 c->refcount += ref;
223 r = c->refcount;
224 return r;
225}
226
227int
228pcm_inprog(struct snddev_info *d, int delta)
229{
230 int r;
231
232 if (delta == 0)
233 return d->inprog;
234
235 /* backtrace(); */
236 pcm_lock(d);
237 d->inprog += delta;
238 r = d->inprog;
239 pcm_unlock(d);
240 return r;
241}
242
243static void
244pcm_setmaxautovchans(struct snddev_info *d, int num)
245{
246 struct pcm_channel *c;
247 struct snddev_channel *sce;
248 int err, done;
249
250 if (num > 0 && d->vchancount == 0) {
251 SLIST_FOREACH(sce, &d->channels, link) {
252 c = sce->channel;
253 if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
254 c->flags |= CHN_F_BUSY;
255 err = vchan_create(c);
256 if (err) {
257 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
258 c->flags &= ~CHN_F_BUSY;
259 }
260 return;
261 }
262 }
263 }
264 if (num == 0 && d->vchancount > 0) {
265 done = 0;
266 while (!done) {
267 done = 1;
268 SLIST_FOREACH(sce, &d->channels, link) {
269 c = sce->channel;
270 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
271 done = 0;
272 snd_mtxlock(d->lock);
273 err = vchan_destroy(c);
274 snd_mtxunlock(d->lock);
275 if (err)
276 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
277 break; /* restart */
278 }
279 }
280 }
281 }
282}
283
284#ifdef USING_DEVFS
285static int
286sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
287{
288 struct snddev_info *d;
289 int error, unit;
290
291 unit = snd_unit;
292 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
293 if (error == 0 && req->newptr != NULL) {
294 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
295 return EINVAL;
296 d = devclass_get_softc(pcm_devclass, unit);
297 if (d == NULL || SLIST_EMPTY(&d->channels))
298 return EINVAL;
299 snd_unit = unit;
300 }
301 return (error);
302}
303SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
304 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
305#endif
306
307static int
308sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
309{
310 struct snddev_info *d;
311 int i, v, error;
312
313 v = snd_maxautovchans;
314 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
315 if (error == 0 && req->newptr != NULL) {
316 if (v < 0 || v >= SND_MAXVCHANS)
317 return EINVAL;
318 if (v != snd_maxautovchans) {
319 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
320 d = devclass_get_softc(pcm_devclass, i);
321 if (!d)
322 continue;
323 pcm_setmaxautovchans(d, v);
324 }
325 }
326 snd_maxautovchans = v;
327 }
328 return (error);
329}
330SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
331 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
332
333struct pcm_channel *
334pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
335{
336 struct pcm_channel *ch;
337 char *dirs;
338 int err, *pnum;
339
340 switch(dir) {
341 case PCMDIR_PLAY:
342 dirs = "play";
343 pnum = &d->playcount;
344 break;
345
346 case PCMDIR_REC:
347 dirs = "record";
348 pnum = &d->reccount;
349 break;
350
351 case PCMDIR_VIRTUAL:
352 dirs = "virtual";
353 dir = PCMDIR_PLAY;
354 pnum = &d->vchancount;
355 break;
356
357 default:
358 return NULL;
359 }
360
361 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
362 if (!ch)
363 return NULL;
364
365 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
366 if (!ch->methods) {
367 free(ch, M_DEVBUF);
368
369 return NULL;
370 }
371
372 snd_mtxlock(d->lock);
373 ch->num = (*pnum)++;
374 snd_mtxunlock(d->lock);
375
376 ch->pid = -1;
377 ch->parentsnddev = d;
378 ch->parentchannel = parent;
379 ch->dev = d->dev;
380 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
381
382 err = chn_init(ch, devinfo, dir);
383 if (err) {
384 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
385 kobj_delete(ch->methods, M_DEVBUF);
386 free(ch, M_DEVBUF);
387 snd_mtxlock(d->lock);
388 (*pnum)--;
389 snd_mtxunlock(d->lock);
390
391 return NULL;
392 }
393
394 return ch;
395}
396
397int
398pcm_chn_destroy(struct pcm_channel *ch)
399{
400 struct snddev_info *d;
401 int err;
402
403 d = ch->parentsnddev;
404 err = chn_kill(ch);
405 if (err) {
406 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
407 return err;
408 }
409
410 kobj_delete(ch->methods, M_DEVBUF);
411 free(ch, M_DEVBUF);
412
413 return 0;
414}
415
416int
416pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
417pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
417{
418 struct snddev_channel *sce, *tmp, *after;
418{
419 struct snddev_channel *sce, *tmp, *after;
419 int unit = device_get_unit(d->dev);
420 int x = -1;
420 int device = device_get_unit(d->dev);
421
421
422 /*
423 * Note it's confusing nomenclature.
424 * dev_t
425 * device -> pcm_device
426 * unit -> pcm_channel
427 * channel -> snddev_channel
428 * device_t
429 * unit -> pcm_device
430 */
431
422 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
423 if (!sce) {
424 return ENOMEM;
425 }
426
427 snd_mtxlock(d->lock);
428 sce->channel = ch;
432 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
433 if (!sce) {
434 return ENOMEM;
435 }
436
437 snd_mtxlock(d->lock);
438 sce->channel = ch;
439 sce->chan_num= d->devcount++;
429 if (SLIST_EMPTY(&d->channels)) {
430 SLIST_INSERT_HEAD(&d->channels, sce, link);
431 } else {
432 after = NULL;
433 SLIST_FOREACH(tmp, &d->channels, link) {
434 after = tmp;
435 }
436 SLIST_INSERT_AFTER(after, sce, link);
437 }
440 if (SLIST_EMPTY(&d->channels)) {
441 SLIST_INSERT_HEAD(&d->channels, sce, link);
442 } else {
443 after = NULL;
444 SLIST_FOREACH(tmp, &d->channels, link) {
445 after = tmp;
446 }
447 SLIST_INSERT_AFTER(after, sce, link);
448 }
438 if (mkdev)
439 x = d->devcount++;
440 snd_mtxunlock(d->lock);
449 snd_mtxunlock(d->lock);
450 sce->dsp_devt= make_dev(&dsp_cdevsw,
451 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
452 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
453 device, sce->chan_num);
441
454
442 if (mkdev) {
443 dsp_register(unit, x);
444 if (ch->direction == PCMDIR_REC)
445 dsp_registerrec(unit, ch->num);
446 }
455 sce->dspW_devt= make_dev(&dsp_cdevsw,
456 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
457 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
458 device, sce->chan_num);
447
459
460 sce->audio_devt= make_dev(&dsp_cdevsw,
461 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
462 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
463 device, sce->chan_num);
464
465 if (ch->direction == PCMDIR_REC)
466 sce->dspr_devt = make_dev(&dsp_cdevsw,
467 PCMMKMINOR(device, SND_DEV_DSPREC,
468 sce->chan_num), UID_ROOT, GID_WHEEL,
469 0666, "dspr%d.%d", device, sce->chan_num);
470
448 return 0;
449}
450
451int
471 return 0;
472}
473
474int
452pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
475pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
453{
454 struct snddev_channel *sce;
476{
477 struct snddev_channel *sce;
455 int unit = device_get_unit(d->dev);
456#if 0
457 int ourlock;
458
459 ourlock = 0;
460 if (!mtx_owned(d->lock)) {
461 snd_mtxlock(d->lock);
462 ourlock = 1;
463 }
464#endif
465
466 SLIST_FOREACH(sce, &d->channels, link) {
467 if (sce->channel == ch)
468 goto gotit;
469 }
470#if 0
471 if (ourlock)
472 snd_mtxunlock(d->lock);
473#endif
474 return EINVAL;
475gotit:
476 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
478#if 0
479 int ourlock;
480
481 ourlock = 0;
482 if (!mtx_owned(d->lock)) {
483 snd_mtxlock(d->lock);
484 ourlock = 1;
485 }
486#endif
487
488 SLIST_FOREACH(sce, &d->channels, link) {
489 if (sce->channel == ch)
490 goto gotit;
491 }
492#if 0
493 if (ourlock)
494 snd_mtxunlock(d->lock);
495#endif
496 return EINVAL;
497gotit:
498 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
477 if (rmdev) {
478 dsp_unregister(unit, --d->devcount);
479 if (ch->direction == PCMDIR_REC)
480 dsp_unregisterrec(unit, ch->num);
481 }
482
483 if (ch->direction == PCMDIR_REC)
484 d->reccount--;
485 else if (ch->flags & CHN_F_VIRTUAL)
486 d->vchancount--;
487 else
488 d->playcount--;
489
490#if 0
491 if (ourlock)
492 snd_mtxunlock(d->lock);
493#endif
494 free(sce, M_DEVBUF);
495
496 return 0;
497}
498
499int
500pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
501{
502 struct snddev_info *d = device_get_softc(dev);
503 struct pcm_channel *ch;
504 int err;
505
506 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
507 if (!ch) {
508 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
509 return ENODEV;
510 }
511
499
500 if (ch->direction == PCMDIR_REC)
501 d->reccount--;
502 else if (ch->flags & CHN_F_VIRTUAL)
503 d->vchancount--;
504 else
505 d->playcount--;
506
507#if 0
508 if (ourlock)
509 snd_mtxunlock(d->lock);
510#endif
511 free(sce, M_DEVBUF);
512
513 return 0;
514}
515
516int
517pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
518{
519 struct snddev_info *d = device_get_softc(dev);
520 struct pcm_channel *ch;
521 int err;
522
523 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
524 if (!ch) {
525 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
526 return ENODEV;
527 }
528
512 err = pcm_chn_add(d, ch, 1);
529 err = pcm_chn_add(d, ch);
513 if (err) {
514 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
515 snd_mtxunlock(d->lock);
516 pcm_chn_destroy(ch);
517 return err;
518 }
519
520 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
521 ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
522 ch->flags |= CHN_F_BUSY;
523 err = vchan_create(ch);
524 if (err) {
525 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
526 ch->flags &= ~CHN_F_BUSY;
527 }
528 }
529
530 return err;
531}
532
533static int
534pcm_killchan(device_t dev)
535{
536 struct snddev_info *d = device_get_softc(dev);
537 struct snddev_channel *sce;
538 struct pcm_channel *ch;
539 int error = 0;
540
541 sce = SLIST_FIRST(&d->channels);
542 ch = sce->channel;
543
530 if (err) {
531 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
532 snd_mtxunlock(d->lock);
533 pcm_chn_destroy(ch);
534 return err;
535 }
536
537 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
538 ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
539 ch->flags |= CHN_F_BUSY;
540 err = vchan_create(ch);
541 if (err) {
542 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
543 ch->flags &= ~CHN_F_BUSY;
544 }
545 }
546
547 return err;
548}
549
550static int
551pcm_killchan(device_t dev)
552{
553 struct snddev_info *d = device_get_softc(dev);
554 struct snddev_channel *sce;
555 struct pcm_channel *ch;
556 int error = 0;
557
558 sce = SLIST_FIRST(&d->channels);
559 ch = sce->channel;
560
544 error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children));
561 error = pcm_chn_remove(d, sce->channel);
545 if (error)
546 return (error);
547 return (pcm_chn_destroy(ch));
548}
549
550int
551pcm_setstatus(device_t dev, char *str)
552{
553 struct snddev_info *d = device_get_softc(dev);
554
555 snd_mtxlock(d->lock);
556 strncpy(d->status, str, SND_STATUSLEN);
557 snd_mtxunlock(d->lock);
558 return 0;
559}
560
561u_int32_t
562pcm_getflags(device_t dev)
563{
564 struct snddev_info *d = device_get_softc(dev);
565
566 return d->flags;
567}
568
569void
570pcm_setflags(device_t dev, u_int32_t val)
571{
572 struct snddev_info *d = device_get_softc(dev);
573
574 d->flags = val;
575}
576
577void *
578pcm_getdevinfo(device_t dev)
579{
580 struct snddev_info *d = device_get_softc(dev);
581
582 return d->devinfo;
583}
584
585unsigned int
586pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
587{
588 struct snddev_info *d = device_get_softc(dev);
589 int sz, x;
590
591 sz = 0;
592 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
593 x = sz;
594 RANGE(sz, min, max);
595 if (x != sz)
596 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
597 x = min;
598 while (x < sz)
599 x <<= 1;
600 if (x > sz)
601 x >>= 1;
602 if (x != sz) {
603 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
604 sz = x;
605 }
606 } else {
607 sz = deflt;
608 }
609
610 d->bufsz = sz;
611
612 return sz;
613}
614
615int
616pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
617{
618 struct snddev_info *d = device_get_softc(dev);
619
620 if (pcm_veto_load) {
621 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
622
623 return EINVAL;
624 }
625
626 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
627
628 d->flags = 0;
629 d->dev = dev;
630 d->devinfo = devinfo;
631 d->devcount = 0;
632 d->reccount = 0;
633 d->playcount = 0;
634 d->vchancount = 0;
635 d->inprog = 0;
636
637 SLIST_INIT(&d->channels);
638 SLIST_INIT(&d->channels);
639
640 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
641 d->flags |= SD_F_SIMPLEX;
642
643 d->fakechan = fkchan_setup(dev);
644 chn_init(d->fakechan, NULL, 0);
645
646#ifdef SND_DYNSYSCTL
647 sysctl_ctx_init(&d->sysctl_tree);
648 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
649 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
650 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
651 if (d->sysctl_tree_top == NULL) {
652 sysctl_ctx_free(&d->sysctl_tree);
653 goto no;
654 }
655 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
656 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
657#endif
658 if (numplay > 0)
659 vchan_initsys(dev);
660 if (numplay == 1)
661 d->flags |= SD_F_AUTOVCHAN;
662
663 sndstat_register(dev, d->status, sndstat_prepare_pcm);
664 return 0;
665no:
666 snd_mtxfree(d->lock);
667 return ENXIO;
668}
669
670int
671pcm_unregister(device_t dev)
672{
673 struct snddev_info *d = device_get_softc(dev);
674 struct snddev_channel *sce;
675 struct pcm_channel *ch;
676
677 snd_mtxlock(d->lock);
678 if (d->inprog) {
679 device_printf(dev, "unregister: operation in progress\n");
680 snd_mtxunlock(d->lock);
681 return EBUSY;
682 }
683 if (sndstat_busy() != 0) {
684 device_printf(dev, "unregister: sndstat busy\n");
685 snd_mtxunlock(d->lock);
686 return EBUSY;
687 }
562 if (error)
563 return (error);
564 return (pcm_chn_destroy(ch));
565}
566
567int
568pcm_setstatus(device_t dev, char *str)
569{
570 struct snddev_info *d = device_get_softc(dev);
571
572 snd_mtxlock(d->lock);
573 strncpy(d->status, str, SND_STATUSLEN);
574 snd_mtxunlock(d->lock);
575 return 0;
576}
577
578u_int32_t
579pcm_getflags(device_t dev)
580{
581 struct snddev_info *d = device_get_softc(dev);
582
583 return d->flags;
584}
585
586void
587pcm_setflags(device_t dev, u_int32_t val)
588{
589 struct snddev_info *d = device_get_softc(dev);
590
591 d->flags = val;
592}
593
594void *
595pcm_getdevinfo(device_t dev)
596{
597 struct snddev_info *d = device_get_softc(dev);
598
599 return d->devinfo;
600}
601
602unsigned int
603pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
604{
605 struct snddev_info *d = device_get_softc(dev);
606 int sz, x;
607
608 sz = 0;
609 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
610 x = sz;
611 RANGE(sz, min, max);
612 if (x != sz)
613 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
614 x = min;
615 while (x < sz)
616 x <<= 1;
617 if (x > sz)
618 x >>= 1;
619 if (x != sz) {
620 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
621 sz = x;
622 }
623 } else {
624 sz = deflt;
625 }
626
627 d->bufsz = sz;
628
629 return sz;
630}
631
632int
633pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
634{
635 struct snddev_info *d = device_get_softc(dev);
636
637 if (pcm_veto_load) {
638 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
639
640 return EINVAL;
641 }
642
643 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
644
645 d->flags = 0;
646 d->dev = dev;
647 d->devinfo = devinfo;
648 d->devcount = 0;
649 d->reccount = 0;
650 d->playcount = 0;
651 d->vchancount = 0;
652 d->inprog = 0;
653
654 SLIST_INIT(&d->channels);
655 SLIST_INIT(&d->channels);
656
657 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
658 d->flags |= SD_F_SIMPLEX;
659
660 d->fakechan = fkchan_setup(dev);
661 chn_init(d->fakechan, NULL, 0);
662
663#ifdef SND_DYNSYSCTL
664 sysctl_ctx_init(&d->sysctl_tree);
665 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
666 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
667 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
668 if (d->sysctl_tree_top == NULL) {
669 sysctl_ctx_free(&d->sysctl_tree);
670 goto no;
671 }
672 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
673 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
674#endif
675 if (numplay > 0)
676 vchan_initsys(dev);
677 if (numplay == 1)
678 d->flags |= SD_F_AUTOVCHAN;
679
680 sndstat_register(dev, d->status, sndstat_prepare_pcm);
681 return 0;
682no:
683 snd_mtxfree(d->lock);
684 return ENXIO;
685}
686
687int
688pcm_unregister(device_t dev)
689{
690 struct snddev_info *d = device_get_softc(dev);
691 struct snddev_channel *sce;
692 struct pcm_channel *ch;
693
694 snd_mtxlock(d->lock);
695 if (d->inprog) {
696 device_printf(dev, "unregister: operation in progress\n");
697 snd_mtxunlock(d->lock);
698 return EBUSY;
699 }
700 if (sndstat_busy() != 0) {
701 device_printf(dev, "unregister: sndstat busy\n");
702 snd_mtxunlock(d->lock);
703 return EBUSY;
704 }
705
706
688 SLIST_FOREACH(sce, &d->channels, link) {
689 ch = sce->channel;
690 if (ch->refcount > 0) {
691 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
692 snd_mtxunlock(d->lock);
693 return EBUSY;
694 }
695 }
707 SLIST_FOREACH(sce, &d->channels, link) {
708 ch = sce->channel;
709 if (ch->refcount > 0) {
710 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
711 snd_mtxunlock(d->lock);
712 return EBUSY;
713 }
714 }
715
716 SLIST_FOREACH(sce, &d->channels, link) {
717 destroy_dev(sce->dsp_devt);
718 destroy_dev(sce->dspW_devt);
719 destroy_dev(sce->audio_devt);
720 if (sce->dspr_devt)
721 destroy_dev(sce->dspr_devt);
722 }
723
696 if (mixer_uninit(dev)) {
697 device_printf(dev, "unregister: mixer busy\n");
698 snd_mtxunlock(d->lock);
699 return EBUSY;
700 }
701
702#ifdef SND_DYNSYSCTL
703 d->sysctl_tree_top = NULL;
704 sysctl_ctx_free(&d->sysctl_tree);
705#endif
706 while (!SLIST_EMPTY(&d->channels))
707 pcm_killchan(dev);
708
709 chn_kill(d->fakechan);
710 fkchan_kill(d->fakechan);
711
712 sndstat_unregister(dev);
713 snd_mtxunlock(d->lock);
714 snd_mtxfree(d->lock);
715 return 0;
716}
717
724 if (mixer_uninit(dev)) {
725 device_printf(dev, "unregister: mixer busy\n");
726 snd_mtxunlock(d->lock);
727 return EBUSY;
728 }
729
730#ifdef SND_DYNSYSCTL
731 d->sysctl_tree_top = NULL;
732 sysctl_ctx_free(&d->sysctl_tree);
733#endif
734 while (!SLIST_EMPTY(&d->channels))
735 pcm_killchan(dev);
736
737 chn_kill(d->fakechan);
738 fkchan_kill(d->fakechan);
739
740 sndstat_unregister(dev);
741 snd_mtxunlock(d->lock);
742 snd_mtxfree(d->lock);
743 return 0;
744}
745
718int
719pcm_regdevt(dev_t dev, unsigned unit, unsigned type, unsigned channel)
720{
721 struct snddev_info *d;
722 struct snddev_devt *dt;
723
724 d = devclass_get_softc(pcm_devclass, unit);
725 KASSERT((d != NULL), ("bad d"));
726 KASSERT((dev != NULL), ("bad dev"));
727
728 dt = malloc(sizeof(*dt), M_DEVBUF, M_ZERO | M_WAITOK);
729 if (dt == NULL)
730 return ENOMEM;
731 dt->dev = dev;
732 dt->type = type;
733 dt->channel = channel;
734
735 snd_mtxlock(d->lock);
736 SLIST_INSERT_HEAD(&d->devs, dt, link);
737 snd_mtxunlock(d->lock);
738
739 return 0;
740}
741
742dev_t
743pcm_getdevt(unsigned unit, unsigned type, unsigned channel)
744{
745 struct snddev_info *d;
746 struct snddev_devt *dt;
747
748 d = devclass_get_softc(pcm_devclass, unit);
749 KASSERT((d != NULL), ("bad d"));
750
751#if 0
752 snd_mtxlock(d->lock);
753#endif
754 SLIST_FOREACH(dt, &d->devs, link) {
755 if ((dt->type == type) && (dt->channel == channel))
756 return dt->dev;
757 }
758#if 0
759 snd_mtxunlock(d->lock);
760#endif
761
762 return NULL;
763}
764
765int
766pcm_unregdevt(unsigned unit, unsigned type, unsigned channel)
767{
768 struct snddev_info *d;
769 struct snddev_devt *dt;
770
771 d = devclass_get_softc(pcm_devclass, unit);
772 KASSERT((d != NULL), ("bad d"));
773
774#if 0
775 snd_mtxlock(d->lock);
776#endif
777 SLIST_FOREACH(dt, &d->devs, link) {
778 if ((dt->type == type) && (dt->channel == channel)) {
779 SLIST_REMOVE(&d->devs, dt, snddev_devt, link);
780 free(dt, M_DEVBUF);
781#if 0
782 snd_mtxunlock(d->lock);
783#endif
784 return 0;
785 }
786 }
787#if 0
788 snd_mtxunlock(d->lock);
789#endif
790
791 return ENOENT;
792}
793
794/************************************************************************/
795
796static int
797sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
798{
799 struct snddev_info *d;
800 struct snddev_channel *sce;
801 struct pcm_channel *c;
802 struct pcm_feeder *f;
803 int pc, rc, vc;
804
805 if (verbose < 1)
806 return 0;
807
808 d = device_get_softc(dev);
809 if (!d)
810 return ENXIO;
811
812 snd_mtxlock(d->lock);
813 if (!SLIST_EMPTY(&d->channels)) {
814 pc = rc = vc = 0;
815 SLIST_FOREACH(sce, &d->channels, link) {
816 c = sce->channel;
817 if (c->direction == PCMDIR_PLAY) {
818 if (c->flags & CHN_F_VIRTUAL)
819 vc++;
820 else
821 pc++;
822 } else
823 rc++;
824 }
825 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
826 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
827#ifdef USING_DEVFS
828 (device_get_unit(dev) == snd_unit)? " default" : ""
829#else
830 ""
831#endif
832 );
833
834 if (verbose <= 1) {
835 snd_mtxunlock(d->lock);
836 return 0;
837 }
838
839 SLIST_FOREACH(sce, &d->channels, link) {
840 c = sce->channel;
841 sbuf_printf(s, "\n\t");
842
843 /* it would be better to indent child channels */
844 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
845 sbuf_printf(s, "spd %d", c->speed);
846 if (c->speed != sndbuf_getspd(c->bufhard))
847 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
848 sbuf_printf(s, ", fmt 0x%08x", c->format);
849 if (c->format != sndbuf_getfmt(c->bufhard))
850 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
851 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
852 if (c->pid != -1)
853 sbuf_printf(s, ", pid %d", c->pid);
854 sbuf_printf(s, "\n\t");
855
856 if (c->bufhard != NULL && c->bufsoft != NULL) {
857 sbuf_printf(s, "interrupts %d, ", c->interrupts);
858 if (c->direction == PCMDIR_REC)
859 sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
860 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
861 else
862 sbuf_printf(s, "underruns %d, ready %d",
863 c->xruns, sndbuf_getready(c->bufsoft));
864 sbuf_printf(s, "\n\t");
865 }
866
867 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
868 sbuf_printf(s, " -> ");
869 f = c->feeder;
870 while (f->source != NULL)
871 f = f->source;
872 while (f != NULL) {
873 sbuf_printf(s, "%s", f->class->name);
874 if (f->desc->type == FEEDER_FMT)
875 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
876 if (f->desc->type == FEEDER_RATE)
877 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
878 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
879 sbuf_printf(s, "(0x%08x)", f->desc->out);
880 sbuf_printf(s, " -> ");
881 f = f->parent;
882 }
883 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
884 }
885 } else
886 sbuf_printf(s, " (mixer only)");
887 snd_mtxunlock(d->lock);
888
889 return 0;
890}
891
892/************************************************************************/
893
894#ifdef SND_DYNSYSCTL
895int
896sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
897{
898 struct snddev_info *d;
899 struct snddev_channel *sce;
900 struct pcm_channel *c;
901 int err, newcnt, cnt, busy;
902 int x;
903
904 d = oidp->oid_arg1;
905
906 x = pcm_inprog(d, 1);
907 if (x != 1) {
908 printf("x: %d\n", x);
909 pcm_inprog(d, -1);
910 return EINPROGRESS;
911 }
912
913 busy = 0;
914 cnt = 0;
915 SLIST_FOREACH(sce, &d->channels, link) {
916 c = sce->channel;
917 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
918 cnt++;
919 if (c->flags & CHN_F_BUSY)
920 busy++;
921 }
922 }
923
924 newcnt = cnt;
925
926 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
927
928 if (err == 0 && req->newptr != NULL) {
929
930 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
931 pcm_inprog(d, -1);
932 return E2BIG;
933 }
934
935 if (newcnt > cnt) {
936 /* add new vchans - find a parent channel first */
937 SLIST_FOREACH(sce, &d->channels, link) {
938 c = sce->channel;
939 /* not a candidate if not a play channel */
940 if (c->direction != PCMDIR_PLAY)
941 continue;
942 /* not a candidate if a virtual channel */
943 if (c->flags & CHN_F_VIRTUAL)
944 continue;
945 /* not a candidate if it's in use */
946 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
947 continue;
948 /*
949 * if we get here we're a nonvirtual play channel, and either
950 * 1) not busy
951 * 2) busy with children, not directly open
952 *
953 * thus we can add children
954 */
955 goto addok;
956 }
957 pcm_inprog(d, -1);
958 return EBUSY;
959addok:
960 c->flags |= CHN_F_BUSY;
961 while (err == 0 && newcnt > cnt) {
962 err = vchan_create(c);
963 if (err == 0)
964 cnt++;
965 }
966 if (SLIST_EMPTY(&c->children))
967 c->flags &= ~CHN_F_BUSY;
968 } else if (newcnt < cnt) {
969 if (busy > newcnt) {
970 printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
971 pcm_inprog(d, -1);
972 return EBUSY;
973 }
974
975 snd_mtxlock(d->lock);
976 while (err == 0 && newcnt < cnt) {
977 SLIST_FOREACH(sce, &d->channels, link) {
978 c = sce->channel;
979 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
980 goto remok;
981 }
982 snd_mtxunlock(d->lock);
983 pcm_inprog(d, -1);
984 return EINVAL;
985remok:
986 err = vchan_destroy(c);
987 if (err == 0)
988 cnt--;
989 }
990 snd_mtxunlock(d->lock);
991 }
992 }
993 pcm_inprog(d, -1);
994 return err;
995}
996#endif
997
998/************************************************************************/
999
1000static moduledata_t sndpcm_mod = {
1001 "snd_pcm",
1002 NULL,
1003 NULL
1004};
1005DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
1006MODULE_VERSION(snd_pcm, PCM_MODVER);
746/************************************************************************/
747
748static int
749sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
750{
751 struct snddev_info *d;
752 struct snddev_channel *sce;
753 struct pcm_channel *c;
754 struct pcm_feeder *f;
755 int pc, rc, vc;
756
757 if (verbose < 1)
758 return 0;
759
760 d = device_get_softc(dev);
761 if (!d)
762 return ENXIO;
763
764 snd_mtxlock(d->lock);
765 if (!SLIST_EMPTY(&d->channels)) {
766 pc = rc = vc = 0;
767 SLIST_FOREACH(sce, &d->channels, link) {
768 c = sce->channel;
769 if (c->direction == PCMDIR_PLAY) {
770 if (c->flags & CHN_F_VIRTUAL)
771 vc++;
772 else
773 pc++;
774 } else
775 rc++;
776 }
777 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
778 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
779#ifdef USING_DEVFS
780 (device_get_unit(dev) == snd_unit)? " default" : ""
781#else
782 ""
783#endif
784 );
785
786 if (verbose <= 1) {
787 snd_mtxunlock(d->lock);
788 return 0;
789 }
790
791 SLIST_FOREACH(sce, &d->channels, link) {
792 c = sce->channel;
793 sbuf_printf(s, "\n\t");
794
795 /* it would be better to indent child channels */
796 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
797 sbuf_printf(s, "spd %d", c->speed);
798 if (c->speed != sndbuf_getspd(c->bufhard))
799 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
800 sbuf_printf(s, ", fmt 0x%08x", c->format);
801 if (c->format != sndbuf_getfmt(c->bufhard))
802 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
803 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
804 if (c->pid != -1)
805 sbuf_printf(s, ", pid %d", c->pid);
806 sbuf_printf(s, "\n\t");
807
808 if (c->bufhard != NULL && c->bufsoft != NULL) {
809 sbuf_printf(s, "interrupts %d, ", c->interrupts);
810 if (c->direction == PCMDIR_REC)
811 sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
812 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
813 else
814 sbuf_printf(s, "underruns %d, ready %d",
815 c->xruns, sndbuf_getready(c->bufsoft));
816 sbuf_printf(s, "\n\t");
817 }
818
819 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
820 sbuf_printf(s, " -> ");
821 f = c->feeder;
822 while (f->source != NULL)
823 f = f->source;
824 while (f != NULL) {
825 sbuf_printf(s, "%s", f->class->name);
826 if (f->desc->type == FEEDER_FMT)
827 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
828 if (f->desc->type == FEEDER_RATE)
829 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
830 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
831 sbuf_printf(s, "(0x%08x)", f->desc->out);
832 sbuf_printf(s, " -> ");
833 f = f->parent;
834 }
835 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
836 }
837 } else
838 sbuf_printf(s, " (mixer only)");
839 snd_mtxunlock(d->lock);
840
841 return 0;
842}
843
844/************************************************************************/
845
846#ifdef SND_DYNSYSCTL
847int
848sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
849{
850 struct snddev_info *d;
851 struct snddev_channel *sce;
852 struct pcm_channel *c;
853 int err, newcnt, cnt, busy;
854 int x;
855
856 d = oidp->oid_arg1;
857
858 x = pcm_inprog(d, 1);
859 if (x != 1) {
860 printf("x: %d\n", x);
861 pcm_inprog(d, -1);
862 return EINPROGRESS;
863 }
864
865 busy = 0;
866 cnt = 0;
867 SLIST_FOREACH(sce, &d->channels, link) {
868 c = sce->channel;
869 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
870 cnt++;
871 if (c->flags & CHN_F_BUSY)
872 busy++;
873 }
874 }
875
876 newcnt = cnt;
877
878 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
879
880 if (err == 0 && req->newptr != NULL) {
881
882 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
883 pcm_inprog(d, -1);
884 return E2BIG;
885 }
886
887 if (newcnt > cnt) {
888 /* add new vchans - find a parent channel first */
889 SLIST_FOREACH(sce, &d->channels, link) {
890 c = sce->channel;
891 /* not a candidate if not a play channel */
892 if (c->direction != PCMDIR_PLAY)
893 continue;
894 /* not a candidate if a virtual channel */
895 if (c->flags & CHN_F_VIRTUAL)
896 continue;
897 /* not a candidate if it's in use */
898 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
899 continue;
900 /*
901 * if we get here we're a nonvirtual play channel, and either
902 * 1) not busy
903 * 2) busy with children, not directly open
904 *
905 * thus we can add children
906 */
907 goto addok;
908 }
909 pcm_inprog(d, -1);
910 return EBUSY;
911addok:
912 c->flags |= CHN_F_BUSY;
913 while (err == 0 && newcnt > cnt) {
914 err = vchan_create(c);
915 if (err == 0)
916 cnt++;
917 }
918 if (SLIST_EMPTY(&c->children))
919 c->flags &= ~CHN_F_BUSY;
920 } else if (newcnt < cnt) {
921 if (busy > newcnt) {
922 printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
923 pcm_inprog(d, -1);
924 return EBUSY;
925 }
926
927 snd_mtxlock(d->lock);
928 while (err == 0 && newcnt < cnt) {
929 SLIST_FOREACH(sce, &d->channels, link) {
930 c = sce->channel;
931 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
932 goto remok;
933 }
934 snd_mtxunlock(d->lock);
935 pcm_inprog(d, -1);
936 return EINVAL;
937remok:
938 err = vchan_destroy(c);
939 if (err == 0)
940 cnt--;
941 }
942 snd_mtxunlock(d->lock);
943 }
944 }
945 pcm_inprog(d, -1);
946 return err;
947}
948#endif
949
950/************************************************************************/
951
952static moduledata_t sndpcm_mod = {
953 "snd_pcm",
954 NULL,
955 NULL
956};
957DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
958MODULE_VERSION(snd_pcm, PCM_MODVER);