Commit | Line | Data |
---|---|---|
558a398b | 1 | /*- |
2a1ad637 FT |
2 | * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> |
3 | * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 | |
4 | * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> | |
984263bc MD |
5 | * All rights reserved. |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * | |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
26 | * SUCH DAMAGE. | |
27 | */ | |
28 | ||
2a1ad637 FT |
29 | #ifdef HAVE_KERNEL_OPTION_HEADERS |
30 | #include "opt_snd.h" | |
31 | #endif | |
984263bc MD |
32 | |
33 | #include <dev/sound/pcm/sound.h> | |
2a1ad637 | 34 | #include <sys/ctype.h> |
39b4af32 | 35 | #include <sys/devfs.h> |
67931cc4 FT |
36 | #include <sys/device.h> |
37 | #include <sys/eventhandler.h> | |
2a1ad637 | 38 | #include <sys/lock.h> |
2a1ad637 FT |
39 | #include <sys/sysent.h> |
40 | ||
41 | #include <vm/vm.h> | |
42 | #include <vm/vm_object.h> | |
43 | #include <vm/vm_page.h> | |
44 | #include <vm/vm_pager.h> | |
45 | ||
46 | SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/dsp.c 274035 2014-11-03 11:11:45Z bapt $"); | |
47 | ||
48 | static int dsp_mmap_allow_prot_exec = 0; | |
49 | SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW, | |
50 | &dsp_mmap_allow_prot_exec, 0, | |
51 | "linux mmap compatibility (-1=force disable 0=auto 1=force enable)"); | |
52 | ||
53 | struct dsp_cdevinfo { | |
54 | struct pcm_channel *rdch, *wrch; | |
55 | struct pcm_channel *volch; | |
56 | int busy, simplex; | |
57 | TAILQ_ENTRY(dsp_cdevinfo) link; | |
58 | }; | |
984263bc | 59 | |
2a1ad637 FT |
60 | #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) |
61 | #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) | |
62 | #define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch) | |
63 | #define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) | |
64 | ||
65 | #define DSP_CDEVINFO_CACHESIZE 8 | |
66 | ||
67 | #define DSP_REGISTERED(x, y) (PCM_REGISTERED(x) && \ | |
68 | (y) != NULL && (y)->si_drv1 != NULL) | |
984263bc MD |
69 | |
70 | #define OLDPCM_IOCTL | |
71 | ||
72 | static d_open_t dsp_open; | |
73 | static d_close_t dsp_close; | |
74 | static d_read_t dsp_read; | |
75 | static d_write_t dsp_write; | |
76 | static d_ioctl_t dsp_ioctl; | |
88a31edf | 77 | static d_kqfilter_t dsp_kqfilter; |
984263bc | 78 | static d_mmap_t dsp_mmap; |
2a1ad637 | 79 | static d_mmap_single_t dsp_mmap_single; |
984263bc | 80 | |
88a31edf FT |
81 | static void dsp_filter_detach(struct knote *); |
82 | static int dsp_filter_read(struct knote *, long); | |
83 | static int dsp_filter_write(struct knote *, long); | |
84 | ||
0cf7fc2c | 85 | DEVFS_DEFINE_CLONE_BITMAP(dsp); |
39b4af32 | 86 | |
88a31edf | 87 | struct dev_ops dsp_ops = { |
5f097292 | 88 | { "sound", 0, D_MPSAFE }, |
fef8985e MD |
89 | .d_open = dsp_open, |
90 | .d_close = dsp_close, | |
91 | .d_read = dsp_read, | |
92 | .d_write = dsp_write, | |
93 | .d_ioctl = dsp_ioctl, | |
88a31edf | 94 | .d_kqfilter = dsp_kqfilter, |
fef8985e | 95 | .d_mmap = dsp_mmap, |
2a1ad637 | 96 | .d_mmap_single = dsp_mmap_single, |
984263bc MD |
97 | }; |
98 | ||
2a1ad637 FT |
99 | static eventhandler_tag dsp_ehtag = NULL; |
100 | static int dsp_umax = -1; | |
101 | static int dsp_cmax = -1; | |
102 | ||
103 | static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group); | |
104 | static int dsp_oss_syncstart(int sg_id); | |
105 | static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy); | |
106 | static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled); | |
107 | static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); | |
108 | static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); | |
109 | static int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask); | |
110 | #ifdef OSSV4_EXPERIMENT | |
111 | static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); | |
112 | static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); | |
113 | static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); | |
114 | static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); | |
115 | static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name); | |
116 | #endif | |
117 | ||
118 | static struct snddev_info * | |
558a398b | 119 | dsp_get_info(struct cdev *dev) |
984263bc | 120 | { |
2a1ad637 | 121 | return (devclass_get_softc(pcm_devclass, PCMUNIT(dev))); |
984263bc MD |
122 | } |
123 | ||
2a1ad637 | 124 | static uint32_t |
558a398b | 125 | dsp_get_flags(struct cdev *dev) |
984263bc MD |
126 | { |
127 | device_t bdev; | |
984263bc | 128 | |
2a1ad637 | 129 | bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); |
984263bc | 130 | |
2a1ad637 | 131 | return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff); |
984263bc MD |
132 | } |
133 | ||
134 | static void | |
2a1ad637 | 135 | dsp_set_flags(struct cdev *dev, uint32_t flags) |
984263bc MD |
136 | { |
137 | device_t bdev; | |
984263bc | 138 | |
2a1ad637 | 139 | bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); |
984263bc | 140 | |
2a1ad637 FT |
141 | if (bdev != NULL) |
142 | pcm_setflags(bdev, flags); | |
984263bc MD |
143 | } |
144 | ||
145 | /* | |
558a398b | 146 | * return the channels associated with an open device instance. |
984263bc MD |
147 | * lock channels specified. |
148 | */ | |
149 | static int | |
2a1ad637 FT |
150 | getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, |
151 | uint32_t prio) | |
984263bc MD |
152 | { |
153 | struct snddev_info *d; | |
2a1ad637 FT |
154 | struct pcm_channel *ch; |
155 | uint32_t flags; | |
156 | ||
157 | if (PCM_SIMPLEX(dev) != 0) { | |
158 | d = dsp_get_info(dev); | |
159 | if (!PCM_REGISTERED(d)) | |
160 | return (ENXIO); | |
161 | PCM_LOCK(d); | |
162 | PCM_WAIT(d); | |
163 | PCM_ACQUIRE(d); | |
164 | /* | |
165 | * Note: order is important - | |
166 | * pcm flags -> prio query flags -> wild guess | |
167 | */ | |
168 | ch = NULL; | |
169 | flags = dsp_get_flags(dev); | |
170 | if (flags & SD_F_PRIO_WR) { | |
171 | ch = PCM_RDCH(dev); | |
172 | PCM_RDCH(dev) = NULL; | |
173 | } else if (flags & SD_F_PRIO_RD) { | |
174 | ch = PCM_WRCH(dev); | |
175 | PCM_WRCH(dev) = NULL; | |
176 | } else if (prio & SD_F_PRIO_WR) { | |
177 | ch = PCM_RDCH(dev); | |
178 | PCM_RDCH(dev) = NULL; | |
179 | flags |= SD_F_PRIO_WR; | |
180 | } else if (prio & SD_F_PRIO_RD) { | |
181 | ch = PCM_WRCH(dev); | |
182 | PCM_WRCH(dev) = NULL; | |
183 | flags |= SD_F_PRIO_RD; | |
184 | } else if (PCM_WRCH(dev) != NULL) { | |
185 | ch = PCM_RDCH(dev); | |
186 | PCM_RDCH(dev) = NULL; | |
187 | flags |= SD_F_PRIO_WR; | |
188 | } else if (PCM_RDCH(dev) != NULL) { | |
189 | ch = PCM_WRCH(dev); | |
190 | PCM_WRCH(dev) = NULL; | |
191 | flags |= SD_F_PRIO_RD; | |
192 | } | |
193 | PCM_SIMPLEX(dev) = 0; | |
984263bc | 194 | dsp_set_flags(dev, flags); |
2a1ad637 FT |
195 | if (ch != NULL) { |
196 | CHN_LOCK(ch); | |
197 | pcm_chnref(ch, -1); | |
198 | pcm_chnrelease(ch); | |
984263bc | 199 | } |
2a1ad637 FT |
200 | PCM_RELEASE(d); |
201 | PCM_UNLOCK(d); | |
984263bc | 202 | } |
984263bc | 203 | |
2a1ad637 FT |
204 | *rdch = PCM_RDCH(dev); |
205 | *wrch = PCM_WRCH(dev); | |
206 | ||
207 | if (*rdch != NULL && (prio & SD_F_PRIO_RD)) | |
984263bc | 208 | CHN_LOCK(*rdch); |
2a1ad637 | 209 | if (*wrch != NULL && (prio & SD_F_PRIO_WR)) |
984263bc MD |
210 | CHN_LOCK(*wrch); |
211 | ||
2a1ad637 | 212 | return (0); |
984263bc MD |
213 | } |
214 | ||
215 | /* unlock specified channels */ | |
216 | static void | |
2a1ad637 FT |
217 | relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, |
218 | uint32_t prio) | |
984263bc | 219 | { |
2a1ad637 | 220 | if (wrch != NULL && (prio & SD_F_PRIO_WR)) |
984263bc | 221 | CHN_UNLOCK(wrch); |
2a1ad637 | 222 | if (rdch != NULL && (prio & SD_F_PRIO_RD)) |
984263bc | 223 | CHN_UNLOCK(rdch); |
984263bc MD |
224 | } |
225 | ||
2a1ad637 FT |
226 | static void |
227 | dsp_cdevinfo_alloc(struct cdev *dev, | |
228 | struct pcm_channel *rdch, struct pcm_channel *wrch, | |
229 | struct pcm_channel *volch) | |
984263bc | 230 | { |
2a1ad637 FT |
231 | struct snddev_info *d; |
232 | struct dsp_cdevinfo *cdi; | |
233 | int simplex; | |
558a398b | 234 | |
2a1ad637 FT |
235 | d = dsp_get_info(dev); |
236 | ||
237 | KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL && | |
238 | ((rdch == NULL && wrch == NULL) || rdch != wrch), | |
239 | ("bogus %s(), what are you trying to accomplish here?", __func__)); | |
240 | PCM_BUSYASSERT(d); | |
241 | PCM_LOCKASSERT(d); | |
242 | ||
243 | simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0; | |
244 | ||
245 | /* | |
246 | * Scan for free instance entry and put it into the end of list. | |
247 | * Create new one if necessary. | |
248 | */ | |
249 | TAILQ_FOREACH(cdi, &d->dsp_cdevinfo_pool, link) { | |
250 | if (cdi->busy != 0) | |
af8f7a68 | 251 | break; |
2a1ad637 FT |
252 | cdi->rdch = rdch; |
253 | cdi->wrch = wrch; | |
254 | cdi->volch = volch; | |
255 | cdi->simplex = simplex; | |
256 | cdi->busy = 1; | |
257 | TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); | |
258 | TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); | |
259 | dev->si_drv1 = cdi; | |
260 | return; | |
af8f7a68 | 261 | } |
2a1ad637 | 262 | PCM_UNLOCK(d); |
67931cc4 | 263 | cdi = kmalloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO); |
2a1ad637 FT |
264 | PCM_LOCK(d); |
265 | cdi->rdch = rdch; | |
266 | cdi->wrch = wrch; | |
267 | cdi->volch = volch; | |
268 | cdi->simplex = simplex; | |
269 | cdi->busy = 1; | |
270 | TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); | |
271 | dev->si_drv1 = cdi; | |
272 | } | |
984263bc | 273 | |
2a1ad637 FT |
274 | static void |
275 | dsp_cdevinfo_free(struct cdev *dev) | |
276 | { | |
277 | struct snddev_info *d; | |
278 | struct dsp_cdevinfo *cdi, *tmp; | |
279 | uint32_t flags; | |
280 | int i; | |
281 | ||
282 | d = dsp_get_info(dev); | |
283 | ||
284 | KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL && | |
285 | PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL && | |
286 | PCM_VOLCH(dev) == NULL, | |
287 | ("bogus %s(), what are you trying to accomplish here?", __func__)); | |
288 | PCM_BUSYASSERT(d); | |
289 | PCM_LOCKASSERT(d); | |
290 | ||
291 | cdi = dev->si_drv1; | |
292 | dev->si_drv1 = NULL; | |
293 | cdi->rdch = NULL; | |
294 | cdi->wrch = NULL; | |
295 | cdi->volch = NULL; | |
296 | cdi->simplex = 0; | |
297 | cdi->busy = 0; | |
298 | ||
299 | /* | |
300 | * Once it is free, move it back to the beginning of list for | |
301 | * faster new entry allocation. | |
302 | */ | |
303 | TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); | |
304 | TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link); | |
305 | ||
306 | /* | |
307 | * Scan the list, cache free entries up to DSP_CDEVINFO_CACHESIZE. | |
308 | * Reset simplex flags. | |
309 | */ | |
310 | flags = dsp_get_flags(dev) & ~SD_F_PRIO_SET; | |
311 | i = DSP_CDEVINFO_CACHESIZE; | |
67931cc4 | 312 | TAILQ_FOREACH_MUTABLE(cdi, &d->dsp_cdevinfo_pool, link, tmp) { |
2a1ad637 FT |
313 | if (cdi->busy != 0) { |
314 | if (cdi->simplex == 0) { | |
315 | if (cdi->rdch != NULL) | |
316 | flags |= SD_F_PRIO_RD; | |
317 | if (cdi->wrch != NULL) | |
318 | flags |= SD_F_PRIO_WR; | |
319 | } | |
320 | } else { | |
321 | if (i == 0) { | |
322 | TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); | |
67931cc4 | 323 | kfree(cdi, M_DEVBUF); |
2a1ad637 FT |
324 | } else |
325 | i--; | |
326 | } | |
af8f7a68 | 327 | } |
2a1ad637 FT |
328 | dsp_set_flags(dev, flags); |
329 | } | |
984263bc | 330 | |
2a1ad637 FT |
331 | void |
332 | dsp_cdevinfo_init(struct snddev_info *d) | |
333 | { | |
334 | struct dsp_cdevinfo *cdi; | |
335 | int i; | |
336 | ||
337 | KASSERT(d != NULL, ("NULL snddev_info")); | |
338 | PCM_BUSYASSERT(d); | |
339 | PCM_UNLOCKASSERT(d); | |
340 | ||
341 | TAILQ_INIT(&d->dsp_cdevinfo_pool); | |
342 | for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) { | |
67931cc4 | 343 | cdi = kmalloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO); |
2a1ad637 | 344 | TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link); |
af8f7a68 | 345 | } |
2a1ad637 | 346 | } |
984263bc | 347 | |
2a1ad637 FT |
348 | void |
349 | dsp_cdevinfo_flush(struct snddev_info *d) | |
350 | { | |
351 | struct dsp_cdevinfo *cdi, *tmp; | |
352 | ||
353 | KASSERT(d != NULL, ("NULL snddev_info")); | |
354 | PCM_BUSYASSERT(d); | |
355 | PCM_UNLOCKASSERT(d); | |
356 | ||
357 | cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool); | |
358 | while (cdi != NULL) { | |
359 | tmp = TAILQ_NEXT(cdi, link); | |
67931cc4 | 360 | kfree(cdi, M_DEVBUF); |
2a1ad637 | 361 | cdi = tmp; |
984263bc | 362 | } |
2a1ad637 FT |
363 | TAILQ_INIT(&d->dsp_cdevinfo_pool); |
364 | } | |
365 | ||
366 | /* duplex / simplex cdev type */ | |
367 | enum { | |
368 | DSP_CDEV_TYPE_RDONLY, /* simplex read-only (record) */ | |
369 | DSP_CDEV_TYPE_WRONLY, /* simplex write-only (play) */ | |
370 | DSP_CDEV_TYPE_RDWR /* duplex read, write, or both */ | |
371 | }; | |
984263bc | 372 | |
2a1ad637 FT |
373 | enum { |
374 | DSP_CDEV_VOLCTL_NONE, | |
375 | DSP_CDEV_VOLCTL_READ, | |
376 | DSP_CDEV_VOLCTL_WRITE | |
377 | }; | |
af8f7a68 | 378 | |
2a1ad637 FT |
379 | #define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) |
380 | #define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) | |
381 | #define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) | |
382 | #define DSP_F_READ(x) ((x) & FREAD) | |
383 | #define DSP_F_WRITE(x) ((x) & FWRITE) | |
384 | ||
385 | static const struct { | |
386 | int type; | |
387 | char *name; | |
388 | char *sep; | |
389 | char *alias; | |
390 | int use_sep; | |
391 | int hw; | |
392 | int max; | |
393 | int volctl; | |
394 | uint32_t fmt, spd; | |
395 | int query; | |
396 | } dsp_cdevs[] = { | |
397 | { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0, | |
398 | SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, | |
399 | DSP_CDEV_TYPE_RDWR }, | |
400 | { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0, | |
401 | SND_FORMAT(AFMT_MU_LAW, 1, 0), DSP_DEFAULT_SPEED, | |
402 | DSP_CDEV_TYPE_RDWR }, | |
403 | { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0, | |
404 | SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_DEFAULT_SPEED, | |
405 | DSP_CDEV_TYPE_RDWR }, | |
406 | { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1, | |
407 | SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, | |
408 | { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1, | |
409 | SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, | |
410 | { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1, | |
411 | SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, | |
412 | { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1, | |
413 | SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, | |
414 | { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0, | |
415 | SND_FORMAT(AFMT_S16_LE, 2, 0), 44100, DSP_CDEV_TYPE_RDWR }, | |
416 | /* Low priority, OSSv4 aliases. */ | |
417 | { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0, | |
418 | SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, | |
419 | DSP_CDEV_TYPE_RDWR }, | |
420 | { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0, | |
421 | SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, | |
422 | DSP_CDEV_TYPE_RDWR }, | |
423 | { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0, | |
424 | SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, | |
425 | DSP_CDEV_TYPE_RDWR }, | |
426 | { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0, | |
427 | SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, | |
428 | DSP_CDEV_TYPE_RDWR }, | |
429 | { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0, | |
430 | SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, | |
431 | DSP_CDEV_TYPE_RDWR }, | |
432 | }; | |
984263bc | 433 | |
2a1ad637 FT |
434 | #define DSP_FIXUP_ERROR() do { \ |
435 | prio = dsp_get_flags(i_dev); \ | |
5f097292 | 436 | if (!DSP_F_VALID(flags)) \ |
2a1ad637 FT |
437 | error = EINVAL; \ |
438 | if (!DSP_F_DUPLEX(flags) && \ | |
439 | ((DSP_F_READ(flags) && d->reccount == 0) || \ | |
5f097292 | 440 | (DSP_F_WRITE(flags) && d->playcount == 0))) \ |
2a1ad637 FT |
441 | error = ENOTSUP; \ |
442 | else if (!DSP_F_DUPLEX(flags) && (prio & SD_F_SIMPLEX) && \ | |
443 | ((DSP_F_READ(flags) && (prio & SD_F_PRIO_WR)) || \ | |
444 | (DSP_F_WRITE(flags) && (prio & SD_F_PRIO_RD)))) \ | |
445 | error = EBUSY; \ | |
5f097292 | 446 | else if (DSP_REGISTERED(d, i_dev)) \ |
2a1ad637 FT |
447 | error = EBUSY; \ |
448 | } while (0) | |
984263bc | 449 | |
2a1ad637 | 450 | static int |
88a31edf | 451 | dsp_open(struct dev_open_args *ap) |
2a1ad637 | 452 | { |
88a31edf FT |
453 | struct cdev *i_dev = ap->a_head.a_dev; |
454 | int flags = ap->a_oflags; | |
2a1ad637 FT |
455 | struct pcm_channel *rdch, *wrch; |
456 | struct snddev_info *d; | |
457 | uint32_t fmt, spd, prio, volctl; | |
458 | int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; | |
459 | ||
460 | /* Kind of impossible.. */ | |
a9dbef8b | 461 | if (i_dev == NULL) |
2a1ad637 FT |
462 | return (ENODEV); |
463 | ||
464 | d = dsp_get_info(i_dev); | |
465 | if (!PCM_REGISTERED(d)) | |
466 | return (EBADF); | |
467 | ||
468 | PCM_GIANT_ENTER(d); | |
469 | ||
470 | /* Lock snddev so nobody else can monkey with it. */ | |
471 | PCM_LOCK(d); | |
472 | PCM_WAIT(d); | |
473 | ||
474 | /* | |
475 | * Try to acquire cloned device before someone else pick it. | |
476 | * ENODEV means this is not a cloned droids. | |
477 | */ | |
478 | error = snd_clone_acquire(i_dev); | |
479 | if (!(error == 0 || error == ENODEV)) { | |
480 | DSP_FIXUP_ERROR(); | |
481 | PCM_UNLOCK(d); | |
482 | PCM_GIANT_EXIT(d); | |
483 | return (error); | |
984263bc MD |
484 | } |
485 | ||
2a1ad637 FT |
486 | error = 0; |
487 | DSP_FIXUP_ERROR(); | |
488 | ||
489 | if (error != 0) { | |
490 | (void)snd_clone_release(i_dev); | |
491 | PCM_UNLOCK(d); | |
492 | PCM_GIANT_EXIT(d); | |
493 | return (error); | |
494 | } | |
495 | ||
496 | /* | |
497 | * That is just enough. Acquire and unlock pcm lock so | |
498 | * the other will just have to wait until we finish doing | |
499 | * everything. | |
500 | */ | |
501 | PCM_ACQUIRE(d); | |
502 | PCM_UNLOCK(d); | |
503 | ||
504 | devtype = PCMDEV(i_dev); | |
505 | wdevunit = -1; | |
506 | rdevunit = -1; | |
507 | fmt = 0; | |
508 | spd = 0; | |
509 | volctl = DSP_CDEV_VOLCTL_NONE; | |
510 | ||
511 | for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { | |
512 | if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) | |
513 | continue; | |
514 | /* | |
515 | * Volume control only valid for DSPHW devices, | |
516 | * and it must be opened in opposite direction be it | |
517 | * simplex or duplex. Anything else will be handled | |
518 | * as usual. | |
519 | */ | |
520 | if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) { | |
521 | if (dsp_cdevs[i].volctl != 0 && | |
522 | DSP_F_READ(flags)) { | |
523 | volctl = DSP_CDEV_VOLCTL_WRITE; | |
524 | flags &= ~FREAD; | |
525 | flags |= FWRITE; | |
526 | } | |
527 | if (DSP_F_READ(flags)) { | |
528 | (void)snd_clone_release(i_dev); | |
529 | PCM_RELEASE_QUICK(d); | |
530 | PCM_GIANT_EXIT(d); | |
531 | return (ENOTSUP); | |
532 | } | |
533 | wdevunit = dev2unit(i_dev); | |
534 | } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) { | |
535 | if (dsp_cdevs[i].volctl != 0 && | |
536 | DSP_F_WRITE(flags)) { | |
537 | volctl = DSP_CDEV_VOLCTL_READ; | |
538 | flags &= ~FWRITE; | |
539 | flags |= FREAD; | |
540 | } | |
541 | if (DSP_F_WRITE(flags)) { | |
542 | (void)snd_clone_release(i_dev); | |
543 | PCM_RELEASE_QUICK(d); | |
544 | PCM_GIANT_EXIT(d); | |
545 | return (ENOTSUP); | |
546 | } | |
547 | rdevunit = dev2unit(i_dev); | |
548 | } | |
549 | fmt = dsp_cdevs[i].fmt; | |
550 | spd = dsp_cdevs[i].spd; | |
551 | break; | |
552 | } | |
553 | ||
2a1ad637 FT |
554 | rdch = NULL; |
555 | wrch = NULL; | |
556 | rderror = 0; | |
557 | wrerror = 0; | |
558 | ||
558a398b SS |
559 | /* |
560 | * if we get here, the open request is valid- either: | |
561 | * * we were previously not open | |
562 | * * we were open for play xor record and the opener wants | |
563 | * the non-open direction | |
564 | */ | |
2a1ad637 | 565 | if (DSP_F_READ(flags)) { |
558a398b | 566 | /* open for read */ |
2a1ad637 | 567 | rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, |
a9dbef8b | 568 | curproc->p_pid, curproc->p_comm, rdevunit); |
984263bc | 569 | |
2a1ad637 FT |
570 | if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0) |
571 | rderror = ENXIO; | |
984263bc | 572 | |
2a1ad637 FT |
573 | if (volctl == DSP_CDEV_VOLCTL_READ) |
574 | rderror = 0; | |
575 | ||
576 | if (rderror != 0) { | |
577 | if (rdch != NULL) | |
984263bc | 578 | pcm_chnrelease(rdch); |
2a1ad637 FT |
579 | if (!DSP_F_DUPLEX(flags)) { |
580 | (void)snd_clone_release(i_dev); | |
581 | PCM_RELEASE_QUICK(d); | |
582 | PCM_GIANT_EXIT(d); | |
583 | return (rderror); | |
584 | } | |
585 | rdch = NULL; | |
586 | } else if (volctl == DSP_CDEV_VOLCTL_READ) { | |
587 | if (rdch != NULL) { | |
588 | pcm_chnref(rdch, 1); | |
589 | pcm_chnrelease(rdch); | |
590 | } | |
591 | } else { | |
2a1ad637 FT |
592 | if (flags & O_EXCL) |
593 | rdch->flags |= CHN_F_EXCLUSIVE; | |
594 | pcm_chnref(rdch, 1); | |
595 | if (volctl == DSP_CDEV_VOLCTL_NONE) | |
596 | chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); | |
597 | CHN_UNLOCK(rdch); | |
984263bc | 598 | } |
2a1ad637 | 599 | } |
984263bc | 600 | |
2a1ad637 FT |
601 | if (DSP_F_WRITE(flags)) { |
602 | /* open for write */ | |
603 | wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, | |
a9dbef8b | 604 | curproc->p_pid, curproc->p_comm, wdevunit); |
2a1ad637 FT |
605 | |
606 | if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0) | |
607 | wrerror = ENXIO; | |
608 | ||
609 | if (volctl == DSP_CDEV_VOLCTL_WRITE) | |
610 | wrerror = 0; | |
611 | ||
612 | if (wrerror != 0) { | |
613 | if (wrch != NULL) | |
614 | pcm_chnrelease(wrch); | |
615 | if (!DSP_F_DUPLEX(flags)) { | |
616 | if (rdch != NULL) { | |
617 | /* | |
618 | * Lock, deref and release previously | |
619 | * created record channel | |
620 | */ | |
621 | CHN_LOCK(rdch); | |
622 | pcm_chnref(rdch, -1); | |
623 | pcm_chnrelease(rdch); | |
624 | } | |
625 | (void)snd_clone_release(i_dev); | |
626 | PCM_RELEASE_QUICK(d); | |
627 | PCM_GIANT_EXIT(d); | |
628 | return (wrerror); | |
629 | } | |
630 | wrch = NULL; | |
631 | } else if (volctl == DSP_CDEV_VOLCTL_WRITE) { | |
632 | if (wrch != NULL) { | |
633 | pcm_chnref(wrch, 1); | |
634 | pcm_chnrelease(wrch); | |
635 | } | |
636 | } else { | |
2a1ad637 FT |
637 | if (flags & O_EXCL) |
638 | wrch->flags |= CHN_F_EXCLUSIVE; | |
639 | pcm_chnref(wrch, 1); | |
640 | if (volctl == DSP_CDEV_VOLCTL_NONE) | |
641 | chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); | |
642 | CHN_UNLOCK(wrch); | |
643 | } | |
984263bc | 644 | } |
558a398b | 645 | |
558a398b | 646 | |
2a1ad637 | 647 | PCM_LOCK(d); |
558a398b | 648 | |
2a1ad637 FT |
649 | /* |
650 | * We're done. Allocate channels information for this cdev. | |
651 | */ | |
652 | switch (volctl) { | |
653 | case DSP_CDEV_VOLCTL_READ: | |
654 | KASSERT(wrch == NULL, ("wrch=%p not null!", wrch)); | |
655 | dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch); | |
656 | break; | |
657 | case DSP_CDEV_VOLCTL_WRITE: | |
658 | KASSERT(rdch == NULL, ("rdch=%p not null!", rdch)); | |
659 | dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch); | |
660 | break; | |
661 | case DSP_CDEV_VOLCTL_NONE: | |
662 | default: | |
663 | if (wrch == NULL && rdch == NULL) { | |
664 | (void)snd_clone_release(i_dev); | |
665 | PCM_RELEASE(d); | |
666 | PCM_UNLOCK(d); | |
667 | PCM_GIANT_EXIT(d); | |
668 | if (wrerror != 0) | |
669 | return (wrerror); | |
670 | if (rderror != 0) | |
671 | return (rderror); | |
672 | return (EINVAL); | |
984263bc | 673 | } |
2a1ad637 FT |
674 | dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL); |
675 | if (rdch != NULL) | |
676 | CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); | |
677 | if (wrch != NULL) | |
678 | CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); | |
679 | break; | |
984263bc | 680 | } |
558a398b | 681 | |
2a1ad637 FT |
682 | /* |
683 | * Increase clone refcount for its automatic garbage collector. | |
684 | */ | |
685 | (void)snd_clone_ref(i_dev); | |
558a398b | 686 | |
2a1ad637 FT |
687 | PCM_RELEASE(d); |
688 | PCM_UNLOCK(d); | |
af8f7a68 | 689 | |
2a1ad637 | 690 | PCM_GIANT_LEAVE(d); |
af8f7a68 | 691 | |
2a1ad637 | 692 | return (0); |
984263bc MD |
693 | } |
694 | ||
695 | static int | |
88a31edf | 696 | dsp_close(struct dev_close_args *ap) |
984263bc | 697 | { |
88a31edf | 698 | struct cdev *i_dev = ap->a_head.a_dev; |
2a1ad637 | 699 | struct pcm_channel *rdch, *wrch, *volch; |
984263bc | 700 | struct snddev_info *d; |
2a1ad637 | 701 | int sg_ids, rdref, wdref; |
5f097292 MD |
702 | int subunit; |
703 | ||
704 | /* | |
705 | * subunit is a unique number for the cloned device, it | |
706 | * does NOT represent the virtual device channel. | |
707 | */ | |
708 | subunit = PCMSUBUNIT(i_dev); | |
984263bc | 709 | |
558a398b | 710 | d = dsp_get_info(i_dev); |
2a1ad637 FT |
711 | if (!DSP_REGISTERED(d, i_dev)) |
712 | return (EBADF); | |
713 | ||
714 | PCM_GIANT_ENTER(d); | |
715 | ||
716 | PCM_LOCK(d); | |
717 | PCM_WAIT(d); | |
718 | PCM_ACQUIRE(d); | |
719 | ||
720 | rdch = PCM_RDCH(i_dev); | |
721 | wrch = PCM_WRCH(i_dev); | |
722 | volch = PCM_VOLCH(i_dev); | |
723 | ||
724 | PCM_RDCH(i_dev) = NULL; | |
725 | PCM_WRCH(i_dev) = NULL; | |
726 | PCM_VOLCH(i_dev) = NULL; | |
727 | ||
728 | rdref = -1; | |
729 | wdref = -1; | |
730 | ||
731 | if (volch != NULL) { | |
732 | if (volch == rdch) | |
733 | rdref--; | |
734 | else if (volch == wrch) | |
735 | wdref--; | |
736 | else { | |
737 | CHN_LOCK(volch); | |
738 | pcm_chnref(volch, -1); | |
739 | CHN_UNLOCK(volch); | |
740 | } | |
af8f7a68 | 741 | } |
af8f7a68 | 742 | |
2a1ad637 FT |
743 | if (rdch != NULL) |
744 | CHN_REMOVE(d, rdch, channels.pcm.opened); | |
745 | if (wrch != NULL) | |
746 | CHN_REMOVE(d, wrch, channels.pcm.opened); | |
747 | ||
748 | if (rdch != NULL || wrch != NULL) { | |
749 | PCM_UNLOCK(d); | |
750 | if (rdch != NULL) { | |
751 | /* | |
752 | * The channel itself need not be locked because: | |
753 | * a) Adding a channel to a syncgroup happens only | |
754 | * in dsp_ioctl(), which cannot run concurrently | |
755 | * to dsp_close(). | |
756 | * b) The syncmember pointer (sm) is protected by | |
757 | * the global syncgroup list lock. | |
758 | * c) A channel can't just disappear, invalidating | |
759 | * pointers, unless it's closed/dereferenced | |
760 | * first. | |
761 | */ | |
762 | PCM_SG_LOCK(); | |
763 | sg_ids = chn_syncdestroy(rdch); | |
764 | PCM_SG_UNLOCK(); | |
765 | if (sg_ids != 0) | |
766 | free_unr(pcmsg_unrhdr, sg_ids); | |
984263bc | 767 | |
558a398b | 768 | CHN_LOCK(rdch); |
2a1ad637 | 769 | pcm_chnref(rdch, rdref); |
558a398b | 770 | chn_abort(rdch); /* won't sleep */ |
2a1ad637 FT |
771 | rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | |
772 | CHN_F_DEAD | CHN_F_EXCLUSIVE); | |
773 | chn_reset(rdch, 0, 0); | |
558a398b | 774 | pcm_chnrelease(rdch); |
984263bc | 775 | } |
2a1ad637 | 776 | if (wrch != NULL) { |
558a398b | 777 | /* |
2a1ad637 | 778 | * Please see block above. |
558a398b | 779 | */ |
2a1ad637 FT |
780 | PCM_SG_LOCK(); |
781 | sg_ids = chn_syncdestroy(wrch); | |
782 | PCM_SG_UNLOCK(); | |
783 | if (sg_ids != 0) | |
784 | free_unr(pcmsg_unrhdr, sg_ids); | |
785 | ||
786 | CHN_LOCK(wrch); | |
787 | pcm_chnref(wrch, wdref); | |
558a398b | 788 | chn_flush(wrch); /* may sleep */ |
2a1ad637 FT |
789 | wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | |
790 | CHN_F_DEAD | CHN_F_EXCLUSIVE); | |
791 | chn_reset(wrch, 0, 0); | |
558a398b | 792 | pcm_chnrelease(wrch); |
984263bc | 793 | } |
2a1ad637 FT |
794 | PCM_LOCK(d); |
795 | } | |
984263bc | 796 | |
2a1ad637 FT |
797 | dsp_cdevinfo_free(i_dev); |
798 | /* | |
799 | * Release clone busy state and unref it so the automatic | |
800 | * garbage collector will get the hint and do the remaining | |
801 | * cleanup process. | |
802 | */ | |
803 | (void)snd_clone_release(i_dev); | |
804 | ||
805 | /* | |
806 | * destroy_dev() might sleep, so release pcm lock | |
807 | * here and rely on pcm cv serialization. | |
808 | */ | |
809 | PCM_UNLOCK(d); | |
810 | (void)snd_clone_unref(i_dev); | |
811 | PCM_LOCK(d); | |
812 | ||
813 | PCM_RELEASE(d); | |
814 | PCM_UNLOCK(d); | |
815 | ||
816 | PCM_GIANT_LEAVE(d); | |
817 | ||
5f097292 MD |
818 | /* |
819 | * PCMCHAN(i_dev) is not the actual channel but instead is | |
820 | * simply the unique subunit we assigned to the clone device. | |
821 | * | |
822 | * We do not do this here but instead do it when the clone is | |
823 | * GCd. | |
824 | */ | |
825 | /* | |
826 | devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(dsp), subunit); | |
827 | */ | |
39b4af32 | 828 | |
2a1ad637 FT |
829 | return (0); |
830 | } | |
831 | ||
832 | static __inline int | |
bb3a8342 | 833 | dsp_io_ops(struct cdev *i_dev, struct uio *buf, int ioflags) |
2a1ad637 FT |
834 | { |
835 | struct snddev_info *d; | |
836 | struct pcm_channel **ch, *rdch, *wrch; | |
bb3a8342 | 837 | int (*chn_io)(struct pcm_channel *, struct uio *, int); |
2a1ad637 FT |
838 | int prio, ret; |
839 | pid_t runpid; | |
840 | ||
841 | KASSERT(i_dev != NULL && buf != NULL && | |
842 | (buf->uio_rw == UIO_READ || buf->uio_rw == UIO_WRITE), | |
843 | ("%s(): io train wreck!", __func__)); | |
844 | ||
845 | d = dsp_get_info(i_dev); | |
846 | if (!DSP_REGISTERED(d, i_dev)) | |
847 | return (EBADF); | |
848 | ||
849 | PCM_GIANT_ENTER(d); | |
850 | ||
851 | switch (buf->uio_rw) { | |
852 | case UIO_READ: | |
853 | prio = SD_F_PRIO_RD; | |
854 | ch = &rdch; | |
855 | chn_io = chn_read; | |
856 | break; | |
857 | case UIO_WRITE: | |
858 | prio = SD_F_PRIO_WR; | |
859 | ch = &wrch; | |
860 | chn_io = chn_write; | |
861 | break; | |
862 | default: | |
863 | panic("invalid/corrupted uio direction: %d", buf->uio_rw); | |
864 | break; | |
865 | } | |
866 | ||
867 | rdch = NULL; | |
868 | wrch = NULL; | |
869 | runpid = buf->uio_td->td_proc->p_pid; | |
870 | ||
871 | getchns(i_dev, &rdch, &wrch, prio); | |
872 | ||
873 | if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) { | |
874 | PCM_GIANT_EXIT(d); | |
875 | return (EBADF); | |
876 | } | |
877 | ||
878 | if (((*ch)->flags & (CHN_F_MMAP | CHN_F_DEAD)) || | |
879 | (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) { | |
880 | relchns(i_dev, rdch, wrch, prio); | |
881 | PCM_GIANT_EXIT(d); | |
882 | return (EINVAL); | |
883 | } else if (!((*ch)->flags & CHN_F_RUNNING)) { | |
884 | (*ch)->flags |= CHN_F_RUNNING; | |
885 | (*ch)->pid = runpid; | |
984263bc | 886 | } |
2a1ad637 FT |
887 | |
888 | /* | |
889 | * chn_read/write must give up channel lock in order to copy bytes | |
890 | * from/to userland, so up the "in progress" counter to make sure | |
891 | * someone else doesn't come along and muss up the buffer. | |
892 | */ | |
893 | ++(*ch)->inprog; | |
bb3a8342 | 894 | ret = chn_io(*ch, buf, ioflags); |
2a1ad637 FT |
895 | --(*ch)->inprog; |
896 | ||
897 | CHN_BROADCAST(&(*ch)->cv); | |
898 | ||
899 | relchns(i_dev, rdch, wrch, prio); | |
900 | ||
901 | PCM_GIANT_LEAVE(d); | |
902 | ||
903 | return (ret); | |
984263bc MD |
904 | } |
905 | ||
906 | static int | |
88a31edf | 907 | dsp_read(struct dev_read_args *ap) |
984263bc | 908 | { |
88a31edf FT |
909 | struct cdev *i_dev = ap->a_head.a_dev; |
910 | struct uio *buf = ap->a_uio; | |
911 | ||
bb3a8342 | 912 | return (dsp_io_ops(i_dev, buf, ap->a_ioflag)); |
2a1ad637 FT |
913 | } |
914 | ||
915 | static int | |
88a31edf | 916 | dsp_write(struct dev_write_args *ap) |
2a1ad637 | 917 | { |
88a31edf FT |
918 | struct cdev *i_dev = ap->a_head.a_dev; |
919 | struct uio *buf = ap->a_uio; | |
920 | ||
bb3a8342 | 921 | return (dsp_io_ops(i_dev, buf, ap->a_ioflag)); |
2a1ad637 FT |
922 | } |
923 | ||
924 | static int | |
925 | dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch) | |
926 | { | |
927 | struct snddev_info *d; | |
928 | struct pcm_channel *c; | |
929 | int unit; | |
984263bc | 930 | |
2a1ad637 FT |
931 | KASSERT(dev != NULL && volch != NULL, |
932 | ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch)); | |
984263bc | 933 | |
2a1ad637 FT |
934 | d = dsp_get_info(dev); |
935 | if (!PCM_REGISTERED(d)) { | |
936 | *volch = NULL; | |
937 | return (EINVAL); | |
938 | } | |
939 | ||
940 | PCM_UNLOCKASSERT(d); | |
941 | ||
942 | *volch = NULL; | |
943 | ||
944 | c = PCM_VOLCH(dev); | |
945 | if (c != NULL) { | |
946 | if (!(c->feederflags & (1 << FEEDER_VOLUME))) | |
947 | return (-1); | |
948 | *volch = c; | |
949 | return (0); | |
950 | } | |
951 | ||
952 | PCM_LOCK(d); | |
953 | PCM_WAIT(d); | |
954 | PCM_ACQUIRE(d); | |
955 | ||
956 | unit = dev2unit(dev); | |
984263bc | 957 | |
2a1ad637 FT |
958 | CHN_FOREACH(c, d, channels.pcm) { |
959 | CHN_LOCK(c); | |
960 | if (c->unit != unit) { | |
961 | CHN_UNLOCK(c); | |
962 | continue; | |
963 | } | |
964 | *volch = c; | |
965 | pcm_chnref(c, 1); | |
966 | PCM_VOLCH(dev) = c; | |
967 | CHN_UNLOCK(c); | |
968 | PCM_RELEASE(d); | |
969 | PCM_UNLOCK(d); | |
970 | return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1); | |
984263bc | 971 | } |
984263bc | 972 | |
2a1ad637 FT |
973 | PCM_RELEASE(d); |
974 | PCM_UNLOCK(d); | |
975 | ||
976 | return (EINVAL); | |
984263bc MD |
977 | } |
978 | ||
979 | static int | |
2a1ad637 FT |
980 | dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd, |
981 | caddr_t arg) | |
984263bc | 982 | { |
2a1ad637 | 983 | struct snddev_info *d; |
984263bc | 984 | struct pcm_channel *rdch, *wrch; |
2a1ad637 FT |
985 | int j, devtype, ret; |
986 | ||
987 | d = dsp_get_info(dev); | |
988 | if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC)) | |
989 | return (-1); | |
990 | ||
991 | PCM_UNLOCKASSERT(d); | |
992 | ||
993 | j = cmd & 0xff; | |
994 | ||
995 | rdch = PCM_RDCH(dev); | |
996 | wrch = PCM_WRCH(dev); | |
984263bc | 997 | |
2a1ad637 FT |
998 | /* No specific channel, look into cache */ |
999 | if (volch == NULL) | |
1000 | volch = PCM_VOLCH(dev); | |
1001 | ||
1002 | /* Look harder */ | |
1003 | if (volch == NULL) { | |
1004 | if (j == SOUND_MIXER_RECLEV && rdch != NULL) | |
1005 | volch = rdch; | |
1006 | else if (j == SOUND_MIXER_PCM && wrch != NULL) | |
1007 | volch = wrch; | |
1008 | } | |
1009 | ||
1010 | devtype = PCMDEV(dev); | |
1011 | ||
1012 | /* Look super harder */ | |
1013 | if (volch == NULL && | |
1014 | (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY || | |
1015 | devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) { | |
1016 | ret = dsp_get_volume_channel(dev, &volch); | |
1017 | if (ret != 0) | |
1018 | return (ret); | |
1019 | if (volch == NULL) | |
1020 | return (EINVAL); | |
1021 | } | |
984263bc | 1022 | |
2a1ad637 FT |
1023 | /* Final validation */ |
1024 | if (volch != NULL) { | |
1025 | CHN_LOCK(volch); | |
1026 | if (!(volch->feederflags & (1 << FEEDER_VOLUME))) { | |
1027 | CHN_UNLOCK(volch); | |
1028 | return (-1); | |
1029 | } | |
1030 | if (volch->direction == PCMDIR_PLAY) | |
1031 | wrch = volch; | |
1032 | else | |
1033 | rdch = volch; | |
1034 | } | |
984263bc | 1035 | |
2a1ad637 FT |
1036 | ret = EINVAL; |
1037 | ||
1038 | if (volch != NULL && | |
1039 | ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) || | |
1040 | (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) { | |
1041 | if ((cmd & ~0xff) == MIXER_WRITE(0)) { | |
1042 | int left, right, center; | |
1043 | ||
1044 | left = *(int *)arg & 0x7f; | |
1045 | right = ((*(int *)arg) >> 8) & 0x7f; | |
1046 | center = (left + right) >> 1; | |
1047 | chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right, | |
1048 | center); | |
1049 | } else if ((cmd & ~0xff) == MIXER_READ(0)) { | |
1050 | *(int *)arg = CHN_GETVOLUME(volch, | |
1051 | SND_VOL_C_PCM, SND_CHN_T_FL); | |
1052 | *(int *)arg |= CHN_GETVOLUME(volch, | |
1053 | SND_VOL_C_PCM, SND_CHN_T_FR) << 8; | |
1054 | } | |
1055 | ret = 0; | |
1056 | } else if (rdch != NULL || wrch != NULL) { | |
1057 | switch (j) { | |
1058 | case SOUND_MIXER_DEVMASK: | |
1059 | case SOUND_MIXER_CAPS: | |
1060 | case SOUND_MIXER_STEREODEVS: | |
1061 | if ((cmd & ~0xff) == MIXER_READ(0)) { | |
1062 | *(int *)arg = 0; | |
1063 | if (rdch != NULL) | |
1064 | *(int *)arg |= SOUND_MASK_RECLEV; | |
1065 | if (wrch != NULL) | |
1066 | *(int *)arg |= SOUND_MASK_PCM; | |
1067 | } | |
1068 | ret = 0; | |
1069 | break; | |
1070 | case SOUND_MIXER_RECMASK: | |
1071 | case SOUND_MIXER_RECSRC: | |
1072 | if ((cmd & ~0xff) == MIXER_READ(0)) | |
1073 | *(int *)arg = 0; | |
1074 | ret = 0; | |
1075 | break; | |
1076 | default: | |
1077 | break; | |
1078 | } | |
984263bc | 1079 | } |
984263bc | 1080 | |
2a1ad637 FT |
1081 | if (volch != NULL) |
1082 | CHN_UNLOCK(volch); | |
1083 | ||
1084 | return (ret); | |
984263bc MD |
1085 | } |
1086 | ||
1087 | static int | |
88a31edf | 1088 | dsp_ioctl(struct dev_ioctl_args *ap) |
984263bc | 1089 | { |
88a31edf FT |
1090 | struct cdev *i_dev = ap->a_head.a_dev; |
1091 | u_long cmd = ap->a_cmd; | |
1092 | caddr_t arg = ap->a_data; | |
558a398b | 1093 | struct pcm_channel *chn, *rdch, *wrch; |
984263bc | 1094 | struct snddev_info *d; |
2a1ad637 FT |
1095 | u_long xcmd; |
1096 | int *arg_i, ret, tmp; | |
984263bc | 1097 | |
558a398b | 1098 | d = dsp_get_info(i_dev); |
2a1ad637 FT |
1099 | if (!DSP_REGISTERED(d, i_dev)) |
1100 | return (EBADF); | |
984263bc | 1101 | |
2a1ad637 FT |
1102 | PCM_GIANT_ENTER(d); |
1103 | ||
1104 | arg_i = (int *)arg; | |
1105 | ret = 0; | |
1106 | xcmd = 0; | |
1107 | chn = NULL; | |
34e3e97f | 1108 | |
34e3e97f | 1109 | if (IOCGROUP(cmd) == 'M') { |
2a1ad637 FT |
1110 | if (cmd == OSS_GETVERSION) { |
1111 | *arg_i = SOUND_VERSION; | |
1112 | PCM_GIANT_EXIT(d); | |
1113 | return (0); | |
1114 | } | |
1115 | ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg); | |
1116 | if (ret != -1) { | |
1117 | PCM_GIANT_EXIT(d); | |
1118 | return (ret); | |
34e3e97f SS |
1119 | } |
1120 | ||
2a1ad637 FT |
1121 | if (d->mixer_dev != NULL) { |
1122 | PCM_ACQUIRE_QUICK(d); | |
a9dbef8b | 1123 | ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, |
2a1ad637 FT |
1124 | MIXER_CMD_DIRECT); |
1125 | PCM_RELEASE_QUICK(d); | |
1126 | } else | |
1127 | ret = EBADF; | |
1128 | ||
1129 | PCM_GIANT_EXIT(d); | |
1130 | ||
1131 | return (ret); | |
1132 | } | |
1133 | ||
1134 | /* | |
1135 | * Certain ioctls may be made on any type of device (audio, mixer, | |
1136 | * and MIDI). Handle those special cases here. | |
1137 | */ | |
1138 | if (IOCGROUP(cmd) == 'X') { | |
1139 | PCM_ACQUIRE_QUICK(d); | |
1140 | switch(cmd) { | |
1141 | case SNDCTL_SYSINFO: | |
1142 | sound_oss_sysinfo((oss_sysinfo *)arg); | |
1143 | break; | |
1144 | case SNDCTL_CARDINFO: | |
1145 | ret = sound_oss_card_info((oss_card_info *)arg); | |
1146 | break; | |
1147 | case SNDCTL_AUDIOINFO: | |
1148 | case SNDCTL_AUDIOINFO_EX: | |
1149 | case SNDCTL_ENGINEINFO: | |
1150 | ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg); | |
1151 | break; | |
1152 | case SNDCTL_MIXERINFO: | |
1153 | ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg); | |
1154 | break; | |
1155 | default: | |
1156 | ret = EINVAL; | |
1157 | } | |
1158 | PCM_RELEASE_QUICK(d); | |
1159 | PCM_GIANT_EXIT(d); | |
1160 | return (ret); | |
1161 | } | |
1162 | ||
1163 | getchns(i_dev, &rdch, &wrch, 0); | |
1164 | ||
1165 | if (wrch != NULL && (wrch->flags & CHN_F_DEAD)) | |
1166 | wrch = NULL; | |
1167 | if (rdch != NULL && (rdch->flags & CHN_F_DEAD)) | |
1168 | rdch = NULL; | |
1169 | ||
1170 | if (wrch == NULL && rdch == NULL) { | |
1171 | PCM_GIANT_EXIT(d); | |
1172 | return (EINVAL); | |
1173 | } | |
1174 | ||
984263bc MD |
1175 | switch(cmd) { |
1176 | #ifdef OLDPCM_IOCTL | |
1177 | /* | |
1178 | * we start with the new ioctl interface. | |
1179 | */ | |
1180 | case AIONWRITE: /* how many bytes can write ? */ | |
558a398b SS |
1181 | if (wrch) { |
1182 | CHN_LOCK(wrch); | |
984263bc MD |
1183 | /* |
1184 | if (wrch && wrch->bufhard.dl) | |
1185 | while (chn_wrfeed(wrch) == 0); | |
1186 | */ | |
558a398b SS |
1187 | *arg_i = sndbuf_getfree(wrch->bufsoft); |
1188 | CHN_UNLOCK(wrch); | |
1189 | } else { | |
1190 | *arg_i = 0; | |
1191 | ret = EINVAL; | |
1192 | } | |
984263bc MD |
1193 | break; |
1194 | ||
1195 | case AIOSSIZE: /* set the current blocksize */ | |
1196 | { | |
1197 | struct snd_size *p = (struct snd_size *)arg; | |
1198 | ||
1199 | p->play_size = 0; | |
1200 | p->rec_size = 0; | |
2a1ad637 | 1201 | PCM_ACQUIRE_QUICK(d); |
984263bc MD |
1202 | if (wrch) { |
1203 | CHN_LOCK(wrch); | |
1204 | chn_setblocksize(wrch, 2, p->play_size); | |
1205 | p->play_size = sndbuf_getblksz(wrch->bufsoft); | |
1206 | CHN_UNLOCK(wrch); | |
1207 | } | |
1208 | if (rdch) { | |
1209 | CHN_LOCK(rdch); | |
1210 | chn_setblocksize(rdch, 2, p->rec_size); | |
1211 | p->rec_size = sndbuf_getblksz(rdch->bufsoft); | |
1212 | CHN_UNLOCK(rdch); | |
1213 | } | |
2a1ad637 | 1214 | PCM_RELEASE_QUICK(d); |
984263bc MD |
1215 | } |
1216 | break; | |
1217 | case AIOGSIZE: /* get the current blocksize */ | |
1218 | { | |
1219 | struct snd_size *p = (struct snd_size *)arg; | |
1220 | ||
558a398b SS |
1221 | if (wrch) { |
1222 | CHN_LOCK(wrch); | |
984263bc | 1223 | p->play_size = sndbuf_getblksz(wrch->bufsoft); |
558a398b SS |
1224 | CHN_UNLOCK(wrch); |
1225 | } | |
1226 | if (rdch) { | |
1227 | CHN_LOCK(rdch); | |
984263bc | 1228 | p->rec_size = sndbuf_getblksz(rdch->bufsoft); |
558a398b SS |
1229 | CHN_UNLOCK(rdch); |
1230 | } | |
984263bc MD |
1231 | } |
1232 | break; | |
1233 | ||
1234 | case AIOSFMT: | |
558a398b | 1235 | case AIOGFMT: |
984263bc MD |
1236 | { |
1237 | snd_chan_param *p = (snd_chan_param *)arg; | |
1238 | ||
558a398b SS |
1239 | if (cmd == AIOSFMT && |
1240 | ((p->play_format != 0 && p->play_rate == 0) || | |
1241 | (p->rec_format != 0 && p->rec_rate == 0))) { | |
1242 | ret = EINVAL; | |
1243 | break; | |
1244 | } | |
2a1ad637 | 1245 | PCM_ACQUIRE_QUICK(d); |
984263bc MD |
1246 | if (wrch) { |
1247 | CHN_LOCK(wrch); | |
558a398b | 1248 | if (cmd == AIOSFMT && p->play_format != 0) { |
2a1ad637 FT |
1249 | chn_setformat(wrch, |
1250 | SND_FORMAT(p->play_format, | |
1251 | AFMT_CHANNEL(wrch->format), | |
1252 | AFMT_EXTCHANNEL(wrch->format))); | |
558a398b SS |
1253 | chn_setspeed(wrch, p->play_rate); |
1254 | } | |
1255 | p->play_rate = wrch->speed; | |
2a1ad637 | 1256 | p->play_format = AFMT_ENCODING(wrch->format); |
984263bc | 1257 | CHN_UNLOCK(wrch); |
558a398b SS |
1258 | } else { |
1259 | p->play_rate = 0; | |
1260 | p->play_format = 0; | |
984263bc MD |
1261 | } |
1262 | if (rdch) { | |
1263 | CHN_LOCK(rdch); | |
558a398b | 1264 | if (cmd == AIOSFMT && p->rec_format != 0) { |
2a1ad637 FT |
1265 | chn_setformat(rdch, |
1266 | SND_FORMAT(p->rec_format, | |
1267 | AFMT_CHANNEL(rdch->format), | |
1268 | AFMT_EXTCHANNEL(rdch->format))); | |
558a398b SS |
1269 | chn_setspeed(rdch, p->rec_rate); |
1270 | } | |
1271 | p->rec_rate = rdch->speed; | |
2a1ad637 | 1272 | p->rec_format = AFMT_ENCODING(rdch->format); |
984263bc | 1273 | CHN_UNLOCK(rdch); |
558a398b SS |
1274 | } else { |
1275 | p->rec_rate = 0; | |
1276 | p->rec_format = 0; | |
984263bc | 1277 | } |
2a1ad637 | 1278 | PCM_RELEASE_QUICK(d); |
984263bc | 1279 | } |
984263bc MD |
1280 | break; |
1281 | ||
1282 | case AIOGCAP: /* get capabilities */ | |
1283 | { | |
1284 | snd_capabilities *p = (snd_capabilities *)arg; | |
1285 | struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; | |
558a398b | 1286 | struct cdev *pdev; |
984263bc | 1287 | |
2a1ad637 | 1288 | PCM_LOCK(d); |
984263bc MD |
1289 | if (rdch) { |
1290 | CHN_LOCK(rdch); | |
1291 | rcaps = chn_getcaps(rdch); | |
1292 | } | |
1293 | if (wrch) { | |
1294 | CHN_LOCK(wrch); | |
1295 | pcaps = chn_getcaps(wrch); | |
1296 | } | |
1297 | p->rate_min = max(rcaps? rcaps->minspeed : 0, | |
1298 | pcaps? pcaps->minspeed : 0); | |
1299 | p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, | |
1300 | pcaps? pcaps->maxspeed : 1000000); | |
1301 | p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000, | |
1302 | wrch? sndbuf_getsize(wrch->bufsoft) : 1000000); | |
1303 | /* XXX bad on sb16 */ | |
1304 | p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & | |
1305 | (wrch? chn_getformats(wrch) : 0xffffffff); | |
1306 | if (rdch && wrch) | |
558a398b SS |
1307 | p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; |
1308 | pdev = d->mixer_dev; | |
984263bc MD |
1309 | p->mixers = 1; /* default: one mixer */ |
1310 | p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; | |
1311 | p->left = p->right = 100; | |
558a398b SS |
1312 | if (wrch) |
1313 | CHN_UNLOCK(wrch); | |
2a1ad637 FT |
1314 | if (rdch) |
1315 | CHN_UNLOCK(rdch); | |
1316 | PCM_UNLOCK(d); | |
984263bc MD |
1317 | } |
1318 | break; | |
1319 | ||
1320 | case AIOSTOP: | |
558a398b SS |
1321 | if (*arg_i == AIOSYNC_PLAY && wrch) { |
1322 | CHN_LOCK(wrch); | |
984263bc | 1323 | *arg_i = chn_abort(wrch); |
558a398b SS |
1324 | CHN_UNLOCK(wrch); |
1325 | } else if (*arg_i == AIOSYNC_CAPTURE && rdch) { | |
1326 | CHN_LOCK(rdch); | |
984263bc | 1327 | *arg_i = chn_abort(rdch); |
558a398b SS |
1328 | CHN_UNLOCK(rdch); |
1329 | } else { | |
67931cc4 | 1330 | kprintf("AIOSTOP: bad channel 0x%x\n", *arg_i); |
984263bc MD |
1331 | *arg_i = 0; |
1332 | } | |
1333 | break; | |
1334 | ||
1335 | case AIOSYNC: | |
67931cc4 | 1336 | kprintf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n", |
984263bc MD |
1337 | ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); |
1338 | break; | |
1339 | #endif | |
1340 | /* | |
1341 | * here follow the standard ioctls (filio.h etc.) | |
1342 | */ | |
1343 | case FIONREAD: /* get # bytes to read */ | |
558a398b SS |
1344 | if (rdch) { |
1345 | CHN_LOCK(rdch); | |
1346 | /* if (rdch && rdch->bufhard.dl) | |
1347 | while (chn_rdfeed(rdch) == 0); | |
1348 | */ | |
1349 | *arg_i = sndbuf_getready(rdch->bufsoft); | |
1350 | CHN_UNLOCK(rdch); | |
1351 | } else { | |
1352 | *arg_i = 0; | |
1353 | ret = EINVAL; | |
1354 | } | |
984263bc MD |
1355 | break; |
1356 | ||
1357 | case FIOASYNC: /*set/clear async i/o */ | |
67931cc4 | 1358 | DEB( kprintf("FIOASYNC\n") ; ) |
984263bc MD |
1359 | break; |
1360 | ||
558a398b | 1361 | case FIONBIO: /* set/clear non-blocking i/o */ |
bb3a8342 SZ |
1362 | DEB( kprintf("FIONBIO\n") ; ) |
1363 | break; | |
1364 | ||
1365 | case SNDCTL_DSP_NONBLOCK: /* set non-blocking i/o */ | |
1366 | atomic_set_int(&ap->a_fp->f_flag, FNONBLOCK); | |
984263bc MD |
1367 | break; |
1368 | ||
1369 | /* | |
1370 | * Finally, here is the linux-compatible ioctl interface | |
1371 | */ | |
1372 | #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) | |
1373 | case THE_REAL_SNDCTL_DSP_GETBLKSIZE: | |
1374 | case SNDCTL_DSP_GETBLKSIZE: | |
558a398b SS |
1375 | chn = wrch ? wrch : rdch; |
1376 | if (chn) { | |
1377 | CHN_LOCK(chn); | |
1378 | *arg_i = sndbuf_getblksz(chn->bufsoft); | |
1379 | CHN_UNLOCK(chn); | |
1380 | } else { | |
984263bc | 1381 | *arg_i = 0; |
558a398b SS |
1382 | ret = EINVAL; |
1383 | } | |
2a1ad637 | 1384 | break; |
984263bc MD |
1385 | |
1386 | case SNDCTL_DSP_SETBLKSIZE: | |
1387 | RANGE(*arg_i, 16, 65536); | |
2a1ad637 | 1388 | PCM_ACQUIRE_QUICK(d); |
984263bc MD |
1389 | if (wrch) { |
1390 | CHN_LOCK(wrch); | |
1391 | chn_setblocksize(wrch, 2, *arg_i); | |
1392 | CHN_UNLOCK(wrch); | |
1393 | } | |
1394 | if (rdch) { | |
1395 | CHN_LOCK(rdch); | |
1396 | chn_setblocksize(rdch, 2, *arg_i); | |
1397 | CHN_UNLOCK(rdch); | |
1398 | } | |
2a1ad637 | 1399 | PCM_RELEASE_QUICK(d); |
984263bc MD |
1400 | break; |
1401 | ||
1402 | case SNDCTL_DSP_RESET: | |
67931cc4 | 1403 | DEB(kprintf("dsp reset\n")); |
558a398b SS |
1404 | if (wrch) { |
1405 | CHN_LOCK(wrch); | |
984263bc | 1406 | chn_abort(wrch); |
558a398b SS |
1407 | chn_resetbuf(wrch); |
1408 | CHN_UNLOCK(wrch); | |
1409 | } | |
1410 | if (rdch) { | |
1411 | CHN_LOCK(rdch); | |
984263bc | 1412 | chn_abort(rdch); |
558a398b SS |
1413 | chn_resetbuf(rdch); |
1414 | CHN_UNLOCK(rdch); | |
1415 | } | |
984263bc MD |
1416 | break; |
1417 | ||
1418 | case SNDCTL_DSP_SYNC: | |
67931cc4 | 1419 | DEB(kprintf("dsp sync\n")); |
984263bc MD |
1420 | /* chn_sync may sleep */ |
1421 | if (wrch) { | |
1422 | CHN_LOCK(wrch); | |
2a1ad637 | 1423 | chn_sync(wrch, 0); |
984263bc MD |
1424 | CHN_UNLOCK(wrch); |
1425 | } | |
1426 | break; | |
1427 | ||
1428 | case SNDCTL_DSP_SPEED: | |
1429 | /* chn_setspeed may sleep */ | |
1430 | tmp = 0; | |
2a1ad637 | 1431 | PCM_ACQUIRE_QUICK(d); |
984263bc MD |
1432 | if (wrch) { |
1433 | CHN_LOCK(wrch); | |
1434 | ret = chn_setspeed(wrch, *arg_i); | |
1435 | tmp = wrch->speed; | |
1436 | CHN_UNLOCK(wrch); | |
1437 | } | |
1438 | if (rdch && ret == 0) { | |
1439 | CHN_LOCK(rdch); | |
1440 | ret = chn_setspeed(rdch, *arg_i); | |
1441 | if (tmp == 0) | |
1442 | tmp = rdch->speed; | |
1443 | CHN_UNLOCK(rdch); | |
1444 | } | |
2a1ad637 | 1445 | PCM_RELEASE_QUICK(d); |
984263bc MD |
1446 | *arg_i = tmp; |
1447 | break; | |
1448 | ||
1449 | case SOUND_PCM_READ_RATE: | |
558a398b SS |
1450 | chn = wrch ? wrch : rdch; |
1451 | if (chn) { | |
1452 | CHN_LOCK(chn); | |
1453 | *arg_i = chn->speed; | |
1454 | CHN_UNLOCK(chn); | |
1455 | } else { | |
1456 | *arg_i = 0; | |
1457 | ret = EINVAL; | |
1458 | } | |
984263bc MD |
1459 | break; |
1460 | ||
1461 | case SNDCTL_DSP_STEREO: | |
1462 | tmp = -1; | |
2a1ad637 FT |
1463 | *arg_i = (*arg_i)? 2 : 1; |
1464 | PCM_ACQUIRE_QUICK(d); | |
984263bc MD |
1465 | if (wrch) { |
1466 | CHN_LOCK(wrch); | |
2a1ad637 FT |
1467 | ret = chn_setformat(wrch, |
1468 | SND_FORMAT(wrch->format, *arg_i, 0)); | |
1469 | tmp = (AFMT_CHANNEL(wrch->format) > 1)? 1 : 0; | |
984263bc MD |
1470 | CHN_UNLOCK(wrch); |
1471 | } | |
1472 | if (rdch && ret == 0) { | |
1473 | CHN_LOCK(rdch); | |
2a1ad637 FT |
1474 | ret = chn_setformat(rdch, |
1475 | SND_FORMAT(rdch->format, *arg_i, 0)); | |
984263bc | 1476 | if (tmp == -1) |
2a1ad637 | 1477 | tmp = (AFMT_CHANNEL(rdch->format) > 1)? 1 : 0; |
984263bc MD |
1478 | CHN_UNLOCK(rdch); |
1479 | } | |
2a1ad637 | 1480 | PCM_RELEASE_QUICK(d); |
984263bc MD |
1481 | *arg_i = tmp; |
1482 | break; | |
1483 | ||
1484 | case SOUND_PCM_WRITE_CHANNELS: | |
1485 | /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ | |
2a1ad637 FT |
1486 | if (*arg_i < 0) { |
1487 | *arg_i = 0; | |
1488 | ret = EINVAL; | |
1489 | break; | |
1490 | } | |
984263bc | 1491 | if (*arg_i != 0) { |
2a1ad637 FT |
1492 | struct pcmchan_matrix *m; |
1493 | uint32_t ext; | |
1494 | ||
984263bc | 1495 | tmp = 0; |
2a1ad637 FT |
1496 | if (*arg_i > SND_CHN_MAX) |
1497 | *arg_i = SND_CHN_MAX; | |
1498 | ||
1499 | m = feeder_matrix_default_channel_map(*arg_i); | |
1500 | if (m != NULL) | |
1501 | ext = m->ext; | |
1502 | else | |
1503 | ext = 0; | |
1504 | ||
1505 | PCM_ACQUIRE_QUICK(d); | |
984263bc MD |
1506 | if (wrch) { |
1507 | CHN_LOCK(wrch); | |
2a1ad637 FT |
1508 | ret = chn_setformat(wrch, |
1509 | SND_FORMAT(wrch->format, *arg_i, ext)); | |
1510 | tmp = AFMT_CHANNEL(wrch->format); | |
984263bc MD |
1511 | CHN_UNLOCK(wrch); |
1512 | } | |
1513 | if (rdch && ret == 0) { | |
1514 | CHN_LOCK(rdch); | |
2a1ad637 FT |
1515 | ret = chn_setformat(rdch, |
1516 | SND_FORMAT(rdch->format, *arg_i, ext)); | |
984263bc | 1517 | if (tmp == 0) |
2a1ad637 | 1518 | tmp = AFMT_CHANNEL(rdch->format); |
984263bc MD |
1519 | CHN_UNLOCK(rdch); |
1520 | } | |
2a1ad637 | 1521 | PCM_RELEASE_QUICK(d); |
984263bc MD |
1522 | *arg_i = tmp; |
1523 | } else { | |
558a398b SS |
1524 | chn = wrch ? wrch : rdch; |
1525 | CHN_LOCK(chn); | |
2a1ad637 | 1526 | *arg_i = AFMT_CHANNEL(chn->format); |
558a398b | 1527 | CHN_UNLOCK(chn); |
984263bc MD |
1528 | } |
1529 | break; | |
1530 | ||
1531 | case SOUND_PCM_READ_CHANNELS: | |
558a398b SS |
1532 | chn = wrch ? wrch : rdch; |
1533 | if (chn) { | |
1534 | CHN_LOCK(chn); | |
2a1ad637 | 1535 | *arg_i = AFMT_CHANNEL(chn->format); |
558a398b SS |
1536 | CHN_UNLOCK(chn); |
1537 | } else { | |
1538 | *arg_i = 0; | |
1539 | ret = EINVAL; | |
1540 | } | |
984263bc MD |
1541 | break; |
1542 | ||
1543 | case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ | |
558a398b SS |
1544 | chn = wrch ? wrch : rdch; |
1545 | if (chn) { | |
1546 | CHN_LOCK(chn); | |
1547 | *arg_i = chn_getformats(chn); | |
1548 | CHN_UNLOCK(chn); | |
1549 | } else { | |
1550 | *arg_i = 0; | |
1551 | ret = EINVAL; | |
1552 | } | |
2a1ad637 | 1553 | break; |
984263bc MD |
1554 | |
1555 | case SNDCTL_DSP_SETFMT: /* sets _one_ format */ | |
2a1ad637 | 1556 | if (*arg_i != AFMT_QUERY) { |
984263bc | 1557 | tmp = 0; |
2a1ad637 | 1558 | PCM_ACQUIRE_QUICK(d); |
984263bc MD |
1559 | if (wrch) { |
1560 | CHN_LOCK(wrch); | |
2a1ad637 FT |
1561 | ret = chn_setformat(wrch, SND_FORMAT(*arg_i, |
1562 | AFMT_CHANNEL(wrch->format), | |
1563 | AFMT_EXTCHANNEL(wrch->format))); | |
1564 | tmp = wrch->format; | |
984263bc MD |
1565 | CHN_UNLOCK(wrch); |
1566 | } | |
1567 | if (rdch && ret == 0) { | |
1568 | CHN_LOCK(rdch); | |
2a1ad637 FT |
1569 | ret = chn_setformat(rdch, SND_FORMAT(*arg_i, |
1570 | AFMT_CHANNEL(rdch->format), | |
1571 | AFMT_EXTCHANNEL(rdch->format))); | |
984263bc | 1572 | if (tmp == 0) |
2a1ad637 | 1573 | tmp = rdch->format; |
984263bc MD |
1574 | CHN_UNLOCK(rdch); |
1575 | } | |
2a1ad637 FT |
1576 | PCM_RELEASE_QUICK(d); |
1577 | *arg_i = AFMT_ENCODING(tmp); | |
558a398b SS |
1578 | } else { |
1579 | chn = wrch ? wrch : rdch; | |
1580 | CHN_LOCK(chn); | |
2a1ad637 | 1581 | *arg_i = AFMT_ENCODING(chn->format); |
558a398b SS |
1582 | CHN_UNLOCK(chn); |
1583 | } | |
984263bc MD |
1584 | break; |
1585 | ||
1586 | case SNDCTL_DSP_SETFRAGMENT: | |
67931cc4 | 1587 | DEB(kprintf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); |
984263bc | 1588 | { |
2a1ad637 FT |
1589 | uint32_t fragln = (*arg_i) & 0x0000ffff; |
1590 | uint32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16; | |
1591 | uint32_t fragsz; | |
1592 | uint32_t r_maxfrags, r_fragsz; | |
984263bc MD |
1593 | |
1594 | RANGE(fragln, 4, 16); | |
1595 | fragsz = 1 << fragln; | |
1596 | ||
1597 | if (maxfrags == 0) | |
1598 | maxfrags = CHN_2NDBUFMAXSIZE / fragsz; | |
558a398b SS |
1599 | if (maxfrags < 2) |
1600 | maxfrags = 2; | |
984263bc MD |
1601 | if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE) |
1602 | maxfrags = CHN_2NDBUFMAXSIZE / fragsz; | |
1603 | ||
67931cc4 | 1604 | DEB(kprintf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz)); |
2a1ad637 | 1605 | PCM_ACQUIRE_QUICK(d); |
984263bc MD |
1606 | if (rdch) { |
1607 | CHN_LOCK(rdch); | |
1608 | ret = chn_setblocksize(rdch, maxfrags, fragsz); | |
558a398b SS |
1609 | r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft); |
1610 | r_fragsz = sndbuf_getblksz(rdch->bufsoft); | |
984263bc | 1611 | CHN_UNLOCK(rdch); |
558a398b SS |
1612 | } else { |
1613 | r_maxfrags = maxfrags; | |
1614 | r_fragsz = fragsz; | |
984263bc MD |
1615 | } |
1616 | if (wrch && ret == 0) { | |
1617 | CHN_LOCK(wrch); | |
1618 | ret = chn_setblocksize(wrch, maxfrags, fragsz); | |
1619 | maxfrags = sndbuf_getblkcnt(wrch->bufsoft); | |
1620 | fragsz = sndbuf_getblksz(wrch->bufsoft); | |
1621 | CHN_UNLOCK(wrch); | |
558a398b SS |
1622 | } else { /* use whatever came from the read channel */ |
1623 | maxfrags = r_maxfrags; | |
1624 | fragsz = r_fragsz; | |
984263bc | 1625 | } |
2a1ad637 | 1626 | PCM_RELEASE_QUICK(d); |
984263bc MD |
1627 | |
1628 | fragln = 0; | |
1629 | while (fragsz > 1) { | |
1630 | fragln++; | |
1631 | fragsz >>= 1; | |
1632 | } | |
1633 | *arg_i = (maxfrags << 16) | fragln; | |
1634 | } | |
1635 | break; | |
1636 | ||
1637 | case SNDCTL_DSP_GETISPACE: | |
1638 | /* return the size of data available in the input queue */ | |
1639 | { | |
1640 | audio_buf_info *a = (audio_buf_info *)arg; | |
1641 | if (rdch) { | |
1642 | struct snd_dbuf *bs = rdch->bufsoft; | |
1643 | ||
1644 | CHN_LOCK(rdch); | |
1645 | a->bytes = sndbuf_getready(bs); | |
1646 | a->fragments = a->bytes / sndbuf_getblksz(bs); | |
1647 | a->fragstotal = sndbuf_getblkcnt(bs); | |
1648 | a->fragsize = sndbuf_getblksz(bs); | |
1649 | CHN_UNLOCK(rdch); | |
2a1ad637 FT |
1650 | } else |
1651 | ret = EINVAL; | |
984263bc MD |
1652 | } |
1653 | break; | |
1654 | ||
1655 | case SNDCTL_DSP_GETOSPACE: | |
1656 | /* return space available in the output queue */ | |
1657 | { | |
1658 | audio_buf_info *a = (audio_buf_info *)arg; | |
1659 | if (wrch) { | |
1660 | struct snd_dbuf *bs = wrch->bufsoft; | |
1661 | ||
1662 | CHN_LOCK(wrch); | |
558a398b | 1663 | /* XXX abusive DMA update: chn_wrupdate(wrch); */ |
984263bc MD |
1664 | a->bytes = sndbuf_getfree(bs); |
1665 | a->fragments = a->bytes / sndbuf_getblksz(bs); | |
1666 | a->fragstotal = sndbuf_getblkcnt(bs); | |
1667 | a->fragsize = sndbuf_getblksz(bs); | |
1668 | CHN_UNLOCK(wrch); | |
2a1ad637 FT |
1669 | } else |
1670 | ret = EINVAL; | |
984263bc MD |
1671 | } |
1672 | break; | |
1673 | ||
1674 | case SNDCTL_DSP_GETIPTR: | |
1675 | { | |
1676 | count_info *a = (count_info *)arg; | |
1677 | if (rdch) { | |
1678 | struct snd_dbuf *bs = rdch->bufsoft; | |
1679 | ||
1680 | CHN_LOCK(rdch); | |
558a398b | 1681 | /* XXX abusive DMA update: chn_rdupdate(rdch); */ |
984263bc MD |
1682 | a->bytes = sndbuf_gettotal(bs); |
1683 | a->blocks = sndbuf_getblocks(bs) - rdch->blocks; | |
2a1ad637 | 1684 | a->ptr = sndbuf_getfreeptr(bs); |
984263bc MD |
1685 | rdch->blocks = sndbuf_getblocks(bs); |
1686 | CHN_UNLOCK(rdch); | |
1687 | } else | |
1688 | ret = EINVAL; | |
1689 | } | |
1690 | break; | |
1691 | ||
1692 | case SNDCTL_DSP_GETOPTR: | |
1693 | { | |
1694 | count_info *a = (count_info *)arg; | |
1695 | if (wrch) { | |
1696 | struct snd_dbuf *bs = wrch->bufsoft; | |
1697 | ||
1698 | CHN_LOCK(wrch); | |
558a398b | 1699 | /* XXX abusive DMA update: chn_wrupdate(wrch); */ |
984263bc MD |
1700 | a->bytes = sndbuf_gettotal(bs); |
1701 | a->blocks = sndbuf_getblocks(bs) - wrch->blocks; | |
1702 | a->ptr = sndbuf_getreadyptr(bs); | |
1703 | wrch->blocks = sndbuf_getblocks(bs); | |
1704 | CHN_UNLOCK(wrch); | |
1705 | } else | |
1706 | ret = EINVAL; | |
1707 | } | |
1708 | break; | |
1709 | ||
1710 | case SNDCTL_DSP_GETCAPS: | |
2a1ad637 FT |
1711 | PCM_LOCK(d); |
1712 | *arg_i = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER; | |
558a398b | 1713 | if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) |
2a1ad637 FT |
1714 | *arg_i |= PCM_CAP_DUPLEX; |
1715 | PCM_UNLOCK(d); | |
984263bc MD |
1716 | break; |
1717 | ||
1718 | case SOUND_PCM_READ_BITS: | |
558a398b SS |
1719 | chn = wrch ? wrch : rdch; |
1720 | if (chn) { | |
1721 | CHN_LOCK(chn); | |
1722 | if (chn->format & AFMT_8BIT) | |
1723 | *arg_i = 8; | |
1724 | else if (chn->format & AFMT_16BIT) | |
1725 | *arg_i = 16; | |
1726 | else if (chn->format & AFMT_24BIT) | |
1727 | *arg_i = 24; | |
1728 | else if (chn->format & AFMT_32BIT) | |
1729 | *arg_i = 32; | |
1730 | else | |
1731 | ret = EINVAL; | |
1732 | CHN_UNLOCK(chn); | |
1733 | } else { | |
1734 | *arg_i = 0; | |
1735 | ret = EINVAL; | |
1736 | } | |
984263bc MD |
1737 | break; |
1738 | ||
1739 | case SNDCTL_DSP_SETTRIGGER: | |
1740 | if (rdch) { | |
1741 | CHN_LOCK(rdch); | |
2a1ad637 | 1742 | rdch->flags &= ~CHN_F_NOTRIGGER; |
984263bc MD |
1743 | if (*arg_i & PCM_ENABLE_INPUT) |
1744 | chn_start(rdch, 1); | |
2a1ad637 FT |
1745 | else { |
1746 | chn_abort(rdch); | |
1747 | chn_resetbuf(rdch); | |
984263bc | 1748 | rdch->flags |= CHN_F_NOTRIGGER; |
2a1ad637 | 1749 | } |
984263bc MD |
1750 | CHN_UNLOCK(rdch); |
1751 | } | |
1752 | if (wrch) { | |
1753 | CHN_LOCK(wrch); | |
2a1ad637 | 1754 | wrch->flags &= ~CHN_F_NOTRIGGER; |
984263bc MD |
1755 | if (*arg_i & PCM_ENABLE_OUTPUT) |
1756 | chn_start(wrch, 1); | |
2a1ad637 FT |
1757 | else { |
1758 | chn_abort(wrch); | |
1759 | chn_resetbuf(wrch); | |
984263bc | 1760 | wrch->flags |= CHN_F_NOTRIGGER; |
2a1ad637 | 1761 | } |
558a398b | 1762 | CHN_UNLOCK(wrch); |
984263bc MD |
1763 | } |
1764 | break; | |
1765 | ||
1766 | case SNDCTL_DSP_GETTRIGGER: | |
1767 | *arg_i = 0; | |
558a398b SS |
1768 | if (wrch) { |
1769 | CHN_LOCK(wrch); | |
1770 | if (wrch->flags & CHN_F_TRIGGERED) | |
1771 | *arg_i |= PCM_ENABLE_OUTPUT; | |
1772 | CHN_UNLOCK(wrch); | |
1773 | } | |
1774 | if (rdch) { | |
1775 | CHN_LOCK(rdch); | |
1776 | if (rdch->flags & CHN_F_TRIGGERED) | |
1777 | *arg_i |= PCM_ENABLE_INPUT; | |
1778 | CHN_UNLOCK(rdch); | |
1779 | } | |
984263bc MD |
1780 | break; |
1781 | ||
1782 | case SNDCTL_DSP_GETODELAY: | |
1783 | if (wrch) { | |
984263bc MD |
1784 | struct snd_dbuf *bs = wrch->bufsoft; |
1785 | ||
1786 | CHN_LOCK(wrch); | |
558a398b | 1787 | /* XXX abusive DMA update: chn_wrupdate(wrch); */ |
2a1ad637 | 1788 | *arg_i = sndbuf_getready(bs); |
984263bc MD |
1789 | CHN_UNLOCK(wrch); |
1790 | } else | |
1791 | ret = EINVAL; | |
1792 | break; | |
1793 | ||
1794 | case SNDCTL_DSP_POST: | |
1795 | if (wrch) { | |
1796 | CHN_LOCK(wrch); | |
1797 | wrch->flags &= ~CHN_F_NOTRIGGER; | |
1798 | chn_start(wrch, 1); | |
1799 | CHN_UNLOCK(wrch); | |
1800 | } | |
1801 | break; | |
1802 | ||
558a398b SS |
1803 | case SNDCTL_DSP_SETDUPLEX: |
1804 | /* | |
1805 | * switch to full-duplex mode if card is in half-duplex | |
1806 | * mode and is able to work in full-duplex mode | |
1807 | */ | |
2a1ad637 | 1808 | PCM_LOCK(d); |
558a398b SS |
1809 | if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) |
1810 | dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); | |
2a1ad637 FT |
1811 | PCM_UNLOCK(d); |
1812 | break; | |
1813 | ||
1814 | /* | |
1815 | * The following four ioctls are simple wrappers around mixer_ioctl | |
1816 | * with no further processing. xcmd is short for "translated | |
1817 | * command". | |
1818 | */ | |
1819 | case SNDCTL_DSP_GETRECVOL: | |
1820 | if (xcmd == 0) { | |
1821 | xcmd = SOUND_MIXER_READ_RECLEV; | |
1822 | chn = rdch; | |
1823 | } | |
1824 | /* FALLTHROUGH */ | |
1825 | case SNDCTL_DSP_SETRECVOL: | |
1826 | if (xcmd == 0) { | |
1827 | xcmd = SOUND_MIXER_WRITE_RECLEV; | |
1828 | chn = rdch; | |
1829 | } | |
1830 | /* FALLTHROUGH */ | |
1831 | case SNDCTL_DSP_GETPLAYVOL: | |
1832 | if (xcmd == 0) { | |
1833 | xcmd = SOUND_MIXER_READ_PCM; | |
1834 | chn = wrch; | |
1835 | } | |
1836 | /* FALLTHROUGH */ | |
1837 | case SNDCTL_DSP_SETPLAYVOL: | |
1838 | if (xcmd == 0) { | |
1839 | xcmd = SOUND_MIXER_WRITE_PCM; | |
1840 | chn = wrch; | |
1841 | } | |
1842 | ||
1843 | ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg); | |
1844 | if (ret != -1) { | |
1845 | PCM_GIANT_EXIT(d); | |
1846 | return (ret); | |
1847 | } | |
1848 | ||
1849 | if (d->mixer_dev != NULL) { | |
1850 | PCM_ACQUIRE_QUICK(d); | |
a9dbef8b | 1851 | ret = mixer_ioctl_cmd(d->mixer_dev, xcmd, arg, -1, |
2a1ad637 FT |
1852 | MIXER_CMD_DIRECT); |
1853 | PCM_RELEASE_QUICK(d); | |
1854 | } else | |
1855 | ret = ENOTSUP; | |
1856 | ||
1857 | break; | |
1858 | ||
1859 | case SNDCTL_DSP_GET_RECSRC_NAMES: | |
1860 | case SNDCTL_DSP_GET_RECSRC: | |
1861 | case SNDCTL_DSP_SET_RECSRC: | |
1862 | if (d->mixer_dev != NULL) { | |
1863 | PCM_ACQUIRE_QUICK(d); | |
a9dbef8b | 1864 | ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, |
2a1ad637 FT |
1865 | MIXER_CMD_DIRECT); |
1866 | PCM_RELEASE_QUICK(d); | |
1867 | } else | |
1868 | ret = ENOTSUP; | |
1869 | break; | |
1870 | ||
1871 | /* | |
1872 | * The following 3 ioctls aren't very useful at the moment. For | |
1873 | * now, only a single channel is associated with a cdev (/dev/dspN | |
1874 | * instance), so there's only a single output routing to use (i.e., | |
1875 | * the wrch bound to this cdev). | |
1876 | */ | |
1877 | case SNDCTL_DSP_GET_PLAYTGT_NAMES: | |
1878 | { | |
1879 | oss_mixer_enuminfo *ei; | |
1880 | ei = (oss_mixer_enuminfo *)arg; | |
1881 | ei->dev = 0; | |
1882 | ei->ctrl = 0; | |
1883 | ei->version = 0; /* static for now */ | |
1884 | ei->strindex[0] = 0; | |
1885 | ||
1886 | if (wrch != NULL) { | |
1887 | ei->nvalues = 1; | |
1888 | strlcpy(ei->strings, wrch->name, | |
1889 | sizeof(ei->strings)); | |
1890 | } else { | |
1891 | ei->nvalues = 0; | |
1892 | ei->strings[0] = '\0'; | |
1893 | } | |
1894 | } | |
1895 | break; | |
1896 | case SNDCTL_DSP_GET_PLAYTGT: | |
1897 | case SNDCTL_DSP_SET_PLAYTGT: /* yes, they are the same for now */ | |
1898 | /* | |
1899 | * Re: SET_PLAYTGT | |
1900 | * OSSv4: "The value that was accepted by the device will | |
1901 | * be returned back in the variable pointed by the | |
1902 | * argument." | |
1903 | */ | |
1904 | if (wrch != NULL) | |
1905 | *arg_i = 0; | |
1906 | else | |
1907 | ret = EINVAL; | |
1908 | break; | |
1909 | ||
1910 | case SNDCTL_DSP_SILENCE: | |
1911 | /* | |
1912 | * Flush the software (pre-feed) buffer, but try to minimize playback | |
1913 | * interruption. (I.e., record unplayed samples with intent to | |
1914 | * restore by SNDCTL_DSP_SKIP.) Intended for application "pause" | |
1915 | * functionality. | |
1916 | */ | |
1917 | if (wrch == NULL) | |
1918 | ret = EINVAL; | |
1919 | else { | |
1920 | struct snd_dbuf *bs; | |
1921 | CHN_LOCK(wrch); | |
1922 | while (wrch->inprog != 0) | |
1923 | cv_wait(&wrch->cv, wrch->lock); | |
1924 | bs = wrch->bufsoft; | |
1925 | if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) { | |
1926 | bs->sl = sndbuf_getready(bs); | |
1927 | sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs)); | |
1928 | sndbuf_fillsilence(bs); | |
1929 | chn_start(wrch, 0); | |
1930 | } | |
1931 | CHN_UNLOCK(wrch); | |
1932 | } | |
1933 | break; | |
1934 | ||
1935 | case SNDCTL_DSP_SKIP: | |
1936 | /* | |
1937 | * OSSv4 docs: "This ioctl call discards all unplayed samples in the | |
1938 | * playback buffer by moving the current write position immediately | |
1939 | * before the point where the device is currently reading the samples." | |
1940 | */ | |
1941 | if (wrch == NULL) | |
1942 | ret = EINVAL; | |
1943 | else { | |
1944 | struct snd_dbuf *bs; | |
1945 | CHN_LOCK(wrch); | |
1946 | while (wrch->inprog != 0) | |
1947 | cv_wait(&wrch->cv, wrch->lock); | |
1948 | bs = wrch->bufsoft; | |
1949 | if ((bs->shadbuf != NULL) && (bs->sl > 0)) { | |
1950 | sndbuf_softreset(bs); | |
1951 | sndbuf_acquire(bs, bs->shadbuf, bs->sl); | |
1952 | bs->sl = 0; | |
1953 | chn_start(wrch, 0); | |
1954 | } | |
1955 | CHN_UNLOCK(wrch); | |
1956 | } | |
1957 | break; | |
1958 | ||
1959 | case SNDCTL_DSP_CURRENT_OPTR: | |
1960 | case SNDCTL_DSP_CURRENT_IPTR: | |
1961 | /** | |
1962 | * @note Changing formats resets the buffer counters, which differs | |
1963 | * from the 4Front drivers. However, I don't expect this to be | |
1964 | * much of a problem. | |
1965 | * | |
1966 | * @note In a test where @c CURRENT_OPTR is called immediately after write | |
1967 | * returns, this driver is about 32K samples behind whereas | |
1968 | * 4Front's is about 8K samples behind. Should determine source | |
1969 | * of discrepancy, even if only out of curiosity. | |
1970 | * | |
1971 | * @todo Actually test SNDCTL_DSP_CURRENT_IPTR. | |
1972 | */ | |
1973 | chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch; | |
1974 | if (chn == NULL) | |
1975 | ret = EINVAL; | |
1976 | else { | |
1977 | struct snd_dbuf *bs; | |
1978 | /* int tmp; */ | |
1979 | ||
1980 | oss_count_t *oc = (oss_count_t *)arg; | |
1981 | ||
1982 | CHN_LOCK(chn); | |
1983 | bs = chn->bufsoft; | |
1984 | #if 0 | |
1985 | tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b); | |
1986 | oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getalign(b); | |
1987 | oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getalign(b); | |
1988 | #else | |
1989 | oc->samples = sndbuf_gettotal(bs) / sndbuf_getalign(bs); | |
1990 | oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getalign(bs); | |
1991 | #endif | |
1992 | CHN_UNLOCK(chn); | |
1993 | } | |
1994 | break; | |
1995 | ||
1996 | case SNDCTL_DSP_HALT_OUTPUT: | |
1997 | case SNDCTL_DSP_HALT_INPUT: | |
1998 | chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch; | |
1999 | if (chn == NULL) | |
2000 | ret = EINVAL; | |
2001 | else { | |
2002 | CHN_LOCK(chn); | |
2003 | chn_abort(chn); | |
2004 | CHN_UNLOCK(chn); | |
2005 | } | |
2006 | break; | |
2007 | ||
2008 | case SNDCTL_DSP_LOW_WATER: | |
2009 | /* | |
2010 | * Set the number of bytes required to attract attention by | |
2011 | * select/poll. | |
2012 | */ | |
2013 | if (wrch != NULL) { | |
2014 | CHN_LOCK(wrch); | |
2015 | wrch->lw = (*arg_i > 1) ? *arg_i : 1; | |
2016 | CHN_UNLOCK(wrch); | |
2017 | } | |
2018 | if (rdch != NULL) { | |
2019 | CHN_LOCK(rdch); | |
2020 | rdch->lw = (*arg_i > 1) ? *arg_i : 1; | |
2021 | CHN_UNLOCK(rdch); | |
2022 | } | |
2023 | break; | |
2024 | ||
2025 | case SNDCTL_DSP_GETERROR: | |
2026 | /* | |
2027 | * OSSv4 docs: "All errors and counters will automatically be | |
2028 | * cleared to zeroes after the call so each call will return only | |
2029 | * the errors that occurred after the previous invocation. ... The | |
2030 | * play_underruns and rec_overrun fields are the only useful fields | |
2031 | * returned by OSS 4.0." | |
2032 | */ | |
2033 | { | |
2034 | audio_errinfo *ei = (audio_errinfo *)arg; | |
2035 | ||
2036 | bzero((void *)ei, sizeof(*ei)); | |
2037 | ||
2038 | if (wrch != NULL) { | |
2039 | CHN_LOCK(wrch); | |
2040 | ei->play_underruns = wrch->xruns; | |
2041 | wrch->xruns = 0; | |
2042 | CHN_UNLOCK(wrch); | |
2043 | } | |
2044 | if (rdch != NULL) { | |
2045 | CHN_LOCK(rdch); | |
2046 | ei->rec_overruns = rdch->xruns; | |
2047 | rdch->xruns = 0; | |
2048 | CHN_UNLOCK(rdch); | |
2049 | } | |
2050 | } | |
2051 | break; | |
2052 | ||
2053 | case SNDCTL_DSP_SYNCGROUP: | |
2054 | PCM_ACQUIRE_QUICK(d); | |
2055 | ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg); | |
2056 | PCM_RELEASE_QUICK(d); | |
2057 | break; | |
2058 | ||
2059 | case SNDCTL_DSP_SYNCSTART: | |
2060 | PCM_ACQUIRE_QUICK(d); | |
2061 | ret = dsp_oss_syncstart(*arg_i); | |
2062 | PCM_RELEASE_QUICK(d); | |
2063 | break; | |
2064 | ||
2065 | case SNDCTL_DSP_POLICY: | |
2066 | PCM_ACQUIRE_QUICK(d); | |
2067 | ret = dsp_oss_policy(wrch, rdch, *arg_i); | |
2068 | PCM_RELEASE_QUICK(d); | |
2069 | break; | |
2070 | ||
2071 | case SNDCTL_DSP_COOKEDMODE: | |
2072 | PCM_ACQUIRE_QUICK(d); | |
2073 | if (!(dsp_get_flags(i_dev) & SD_F_BITPERFECT)) | |
2074 | ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); | |
2075 | PCM_RELEASE_QUICK(d); | |
2076 | break; | |
2077 | case SNDCTL_DSP_GET_CHNORDER: | |
2078 | PCM_ACQUIRE_QUICK(d); | |
2079 | ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); | |
2080 | PCM_RELEASE_QUICK(d); | |
2081 | break; | |
2082 | case SNDCTL_DSP_SET_CHNORDER: | |
2083 | PCM_ACQUIRE_QUICK(d); | |
2084 | ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); | |
2085 | PCM_RELEASE_QUICK(d); | |
2086 | break; | |
2087 | case SNDCTL_DSP_GETCHANNELMASK: /* XXX vlc */ | |
2088 | PCM_ACQUIRE_QUICK(d); | |
2089 | ret = dsp_oss_getchannelmask(wrch, rdch, (int *)arg); | |
2090 | PCM_RELEASE_QUICK(d); | |
2091 | break; | |
2092 | case SNDCTL_DSP_BIND_CHANNEL: /* XXX what?!? */ | |
2093 | ret = EINVAL; | |
2094 | break; | |
2095 | #ifdef OSSV4_EXPERIMENT | |
2096 | /* | |
2097 | * XXX The following ioctls are not yet supported and just return | |
2098 | * EINVAL. | |
2099 | */ | |
2100 | case SNDCTL_DSP_GETOPEAKS: | |
2101 | case SNDCTL_DSP_GETIPEAKS: | |
2102 | chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch; | |
2103 | if (chn == NULL) | |
2104 | ret = EINVAL; | |
2105 | else { | |
2106 | oss_peaks_t *op = (oss_peaks_t *)arg; | |
2107 | int lpeak, rpeak; | |
2108 | ||
2109 | CHN_LOCK(chn); | |
2110 | ret = chn_getpeaks(chn, &lpeak, &rpeak); | |
2111 | if (ret == -1) | |
2112 | ret = EINVAL; | |
2113 | else { | |
2114 | (*op)[0] = lpeak; | |
2115 | (*op)[1] = rpeak; | |
2116 | } | |
2117 | CHN_UNLOCK(chn); | |
2118 | } | |
2119 | break; | |
2120 | ||
2121 | /* | |
2122 | * XXX Once implemented, revisit this for proper cv protection | |
2123 | * (if necessary). | |
2124 | */ | |
2125 | case SNDCTL_GETLABEL: | |
2126 | ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg); | |
2127 | break; | |
2128 | case SNDCTL_SETLABEL: | |
2129 | ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg); | |
558a398b | 2130 | break; |
2a1ad637 FT |
2131 | case SNDCTL_GETSONG: |
2132 | ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg); | |
2133 | break; | |
2134 | case SNDCTL_SETSONG: | |
2135 | ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg); | |
2136 | break; | |
2137 | case SNDCTL_SETNAME: | |
2138 | ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg); | |
2139 | break; | |
2140 | #if 0 | |
2141 | /** | |
2142 | * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and | |
2143 | * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of | |
2144 | * 4Front Technologies. | |
2145 | */ | |
2146 | case SNDCTL_DSP_READCTL: | |
2147 | case SNDCTL_DSP_WRITECTL: | |
2148 | ret = EINVAL; | |
2149 | break; | |
2150 | #endif /* !0 (explicitly omitted ioctls) */ | |
558a398b | 2151 | |
2a1ad637 | 2152 | #endif /* !OSSV4_EXPERIMENT */ |
984263bc MD |
2153 | case SNDCTL_DSP_MAPINBUF: |
2154 | case SNDCTL_DSP_MAPOUTBUF: | |
2155 | case SNDCTL_DSP_SETSYNCRO: | |
2156 | /* undocumented */ | |
2157 | ||
2158 | case SNDCTL_DSP_SUBDIVIDE: | |
2159 | case SOUND_PCM_WRITE_FILTER: | |
2160 | case SOUND_PCM_READ_FILTER: | |
2161 | /* dunno what these do, don't sound important */ | |
558a398b | 2162 | |
984263bc | 2163 | default: |
67931cc4 | 2164 | DEB(kprintf("default ioctl fn 0x%08lx fail\n", cmd)); |
984263bc MD |
2165 | ret = EINVAL; |
2166 | break; | |
2167 | } | |
984263bc | 2168 | |
2a1ad637 FT |
2169 | PCM_GIANT_LEAVE(d); |
2170 | ||
2171 | return (ret); | |
2172 | } | |
64ab07b8 | 2173 | |
9bade76f FT |
2174 | static struct filterops dsp_read_filtops = |
2175 | { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_read }; | |
2176 | static struct filterops dsp_write_filtops = | |
2177 | { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_write }; | |
2178 | ||
64ab07b8 | 2179 | static int |
9bade76f FT |
2180 | /*dsp_poll(struct cdev *i_dev, int events, struct thread *td)*/ |
2181 | dsp_kqfilter(struct dev_kqfilter_args *ap) | |
64ab07b8 | 2182 | { |
9bade76f FT |
2183 | struct knote *kn = ap->a_kn; |
2184 | struct klist *klist; | |
2185 | struct cdev *i_dev = ap->a_head.a_dev; | |
2a1ad637 FT |
2186 | struct snddev_info *d; |
2187 | struct pcm_channel *wrch, *rdch; | |
9bade76f FT |
2188 | struct snd_dbuf *bs = NULL; |
2189 | int ret; | |
2a1ad637 FT |
2190 | |
2191 | d = dsp_get_info(i_dev); | |
2192 | if (!DSP_REGISTERED(d, i_dev)) | |
2193 | return (EBADF); | |
2194 | ||
2195 | PCM_GIANT_ENTER(d); | |
2196 | ||
2197 | wrch = NULL; | |
2198 | rdch = NULL; | |
2199 | ret = 0; | |
64ab07b8 SG |
2200 | |
2201 | getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); | |
b287d649 | 2202 | |
9bade76f FT |
2203 | switch (kn->kn_filter) { |
2204 | case EVFILT_READ: | |
2205 | if (rdch) { | |
2206 | kn->kn_fop = &dsp_read_filtops; | |
2207 | kn->kn_hook = (caddr_t)rdch; | |
2208 | bs = rdch->bufsoft; | |
2209 | ap->a_result = 0; | |
2210 | } | |
2211 | break; | |
2212 | case EVFILT_WRITE: | |
2213 | if (wrch) { | |
2214 | kn->kn_fop = &dsp_write_filtops; | |
2215 | kn->kn_hook = (caddr_t)wrch; | |
2216 | bs = wrch->bufsoft; | |
2217 | ap->a_result = 0; | |
2218 | } | |
2219 | break; | |
2220 | default: | |
2221 | ap->a_result = EOPNOTSUPP; | |
2222 | break; | |
64ab07b8 SG |
2223 | } |
2224 | ||
9bade76f FT |
2225 | if (ap->a_result == 0) { |
2226 | klist = &sndbuf_getkq(bs)->ki_note; | |
2227 | knote_insert(klist, kn); | |
64ab07b8 SG |
2228 | } |
2229 | ||
2230 | relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); | |
2231 | ||
2a1ad637 | 2232 | PCM_GIANT_LEAVE(d); |
64ab07b8 | 2233 | |
2a1ad637 | 2234 | return (ret); |
64ab07b8 SG |
2235 | } |
2236 | ||
9bade76f FT |
2237 | static void |
2238 | dsp_filter_detach(struct knote *kn) | |
2239 | { | |
2240 | struct pcm_channel *ch = (struct pcm_channel *)kn->kn_hook; | |
2241 | struct snd_dbuf *bs = ch->bufsoft; | |
2242 | struct klist *klist; | |
2243 | ||
2244 | CHN_LOCK(ch); | |
2245 | klist = &sndbuf_getkq(bs)->ki_note; | |
2246 | knote_remove(klist, kn); | |
2247 | CHN_UNLOCK(ch); | |
2248 | } | |
2249 | ||
2250 | static int | |
2251 | dsp_filter_read(struct knote *kn, long hint) | |
2252 | { | |
2253 | struct pcm_channel *rdch = (struct pcm_channel *)kn->kn_hook; | |
2254 | struct thread *td = curthread; | |
2255 | int ready; | |
2256 | ||
2257 | CHN_LOCK(rdch); | |
2258 | ready = chn_poll(rdch, 1, td); | |
2259 | CHN_UNLOCK(rdch); | |
2260 | ||
2261 | return (ready); | |
2262 | } | |
2263 | ||
2264 | static int | |
2265 | dsp_filter_write(struct knote *kn, long hint) | |
2266 | { | |
2267 | struct pcm_channel *wrch = (struct pcm_channel *)kn->kn_hook; | |
2268 | struct thread *td = curthread; | |
2269 | int ready; | |
2270 | ||
2271 | CHN_LOCK(wrch); | |
2272 | ready = chn_poll(wrch, 1, td); | |
2273 | CHN_UNLOCK(wrch); | |
2274 | ||
2275 | return (ready); | |
2276 | } | |
2277 | ||
64ab07b8 | 2278 | static int |
88a31edf | 2279 | dsp_mmap(struct dev_mmap_args *ap) |
64ab07b8 | 2280 | { |
88a31edf | 2281 | vm_offset_t offset = ap->a_offset; |
64ab07b8 | 2282 | |
2a1ad637 | 2283 | /* XXX memattr is not honored */ |
88a31edf | 2284 | ap->a_result = vtophys(offset); |
2a1ad637 | 2285 | return (0); |
64ab07b8 | 2286 | } |
984263bc MD |
2287 | |
2288 | static int | |
88a31edf | 2289 | dsp_mmap_single(struct dev_mmap_single_args *ap) |
984263bc | 2290 | { |
88a31edf FT |
2291 | struct cdev *i_dev = ap->a_head.a_dev; |
2292 | vm_ooffset_t *offset = ap->a_offset; | |
2293 | vm_size_t size = ap->a_size; | |
2294 | struct vm_object **object = ap->a_object; | |
2295 | int nprot = ap->a_nprot; | |
2a1ad637 FT |
2296 | struct snddev_info *d; |
2297 | struct pcm_channel *wrch, *rdch, *c; | |
984263bc | 2298 | |
984263bc | 2299 | /* |
2a1ad637 FT |
2300 | * Reject PROT_EXEC by default. It just doesn't makes sense. |
2301 | * Unfortunately, we have to give up this one due to linux_mmap | |
2302 | * changes. | |
2303 | * | |
2304 | * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html | |
2305 | * | |
984263bc | 2306 | */ |
2a1ad637 FT |
2307 | #ifdef SV_ABI_LINUX |
2308 | if ((nprot & PROT_EXEC) && (dsp_mmap_allow_prot_exec < 0 || | |
2309 | (dsp_mmap_allow_prot_exec == 0 && | |
2310 | SV_CURPROC_ABI() != SV_ABI_LINUX))) | |
984263bc | 2311 | #else |
2a1ad637 | 2312 | if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1) |
984263bc | 2313 | #endif |
2a1ad637 | 2314 | return (EINVAL); |
984263bc | 2315 | |
2a1ad637 FT |
2316 | /* |
2317 | * PROT_READ (alone) selects the input buffer. | |
2318 | * PROT_WRITE (alone) selects the output buffer. | |
2319 | * PROT_WRITE|PROT_READ together select the output buffer. | |
2320 | */ | |
2321 | if ((nprot & (PROT_READ | PROT_WRITE)) == 0) | |
2322 | return (EINVAL); | |
2323 | ||
2324 | d = dsp_get_info(i_dev); | |
2325 | if (!DSP_REGISTERED(d, i_dev)) | |
2326 | return (EINVAL); | |
2327 | ||
2328 | PCM_GIANT_ENTER(d); | |
2329 | ||
2330 | getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); | |
984263bc | 2331 | |
2a1ad637 FT |
2332 | c = ((nprot & PROT_WRITE) != 0) ? wrch : rdch; |
2333 | if (c == NULL || (c->flags & CHN_F_MMAP_INVALID) || | |
2334 | (*offset + size) > sndbuf_getsize(c->bufsoft) || | |
2335 | (wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) || | |
2336 | (rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) { | |
558a398b | 2337 | relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); |
2a1ad637 FT |
2338 | PCM_GIANT_EXIT(d); |
2339 | return (EINVAL); | |
984263bc MD |
2340 | } |
2341 | ||
2a1ad637 FT |
2342 | if (wrch != NULL) |
2343 | wrch->flags |= CHN_F_MMAP; | |
2344 | if (rdch != NULL) | |
2345 | rdch->flags |= CHN_F_MMAP; | |
984263bc | 2346 | |
2a1ad637 | 2347 | *offset = (uintptr_t)sndbuf_getbufofs(c->bufsoft, *offset); |
558a398b | 2348 | relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); |
67931cc4 | 2349 | *object = dev_pager_alloc(i_dev, size, nprot, *offset); |
984263bc | 2350 | |
2a1ad637 FT |
2351 | PCM_GIANT_LEAVE(d); |
2352 | ||
2353 | if (*object == NULL) | |
2354 | return (EINVAL); | |
558a398b | 2355 | return (0); |
984263bc MD |
2356 | } |
2357 | ||
59b2ebda | 2358 | /* |
5f097292 MD |
2359 | * for i = 0 to channels of device N |
2360 | * if dspN.i isn't busy and in the right dir, create a dev_t and return it | |
59b2ebda | 2361 | */ |
18099ae9 FT |
2362 | int |
2363 | dsp_clone(struct dev_clone_args *ap) | |
2a1ad637 | 2364 | { |
18099ae9 | 2365 | struct cdev *i_dev = ap->a_head.a_dev; |
2a1ad637 FT |
2366 | struct snddev_info *d; |
2367 | struct snd_clone_entry *ce; | |
2368 | struct pcm_channel *c; | |
2369 | int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; | |
5f097292 MD |
2370 | size_t len; |
2371 | char sname[64]; | |
18099ae9 | 2372 | int err = EBUSY; |
5f097292 | 2373 | static struct cdev *dev = NULL; |
2a1ad637 FT |
2374 | |
2375 | KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); | |
2376 | ||
5f097292 | 2377 | /* |
8bd5748c MD |
2378 | * The default dsp device has a special unit which must be adjusted, |
2379 | * otherwise the unit number is already correct. | |
5f097292 MD |
2380 | */ |
2381 | unit = PCMUNIT(i_dev); | |
8bd5748c MD |
2382 | if (unit == PCMUNIT_DEFAULT) |
2383 | unit = snd_unit; | |
5f097292 MD |
2384 | |
2385 | ksnprintf(sname, sizeof(sname), "%s", ap->a_name); | |
2386 | len = strlen(sname); | |
2387 | while (len && sname[len-1] >= '0' && sname[len-1] <= '9') | |
2388 | --len; | |
2389 | sname[len] = 0; | |
2a1ad637 | 2390 | |
2a1ad637 FT |
2391 | cunit = -1; |
2392 | devtype = -1; | |
2393 | devhw = 0; | |
2394 | devcmax = -1; | |
2395 | tumax = -1; | |
2a1ad637 | 2396 | |
5f097292 MD |
2397 | for (i = 0; i < sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0]); ++i) { |
2398 | if (strcmp(dsp_cdevs[i].name, sname) != 0) | |
2399 | continue; | |
2a1ad637 | 2400 | devtype = dsp_cdevs[i].type; |
2a1ad637 FT |
2401 | devhw = dsp_cdevs[i].hw; |
2402 | devcmax = dsp_cdevs[i].max - 1; | |
5f097292 | 2403 | break; |
2a1ad637 | 2404 | } |
5f097292 | 2405 | |
2a1ad637 | 2406 | d = devclass_get_softc(pcm_devclass, unit); |
18099ae9 FT |
2407 | if (!PCM_REGISTERED(d) || d->clones == NULL) { |
2408 | return (ENODEV); | |
2409 | } | |
2a1ad637 FT |
2410 | |
2411 | /* XXX Need Giant magic entry ??? */ | |
2412 | ||
2413 | PCM_LOCK(d); | |
2414 | if (snd_clone_disabled(d->clones)) { | |
2415 | PCM_UNLOCK(d); | |
18099ae9 | 2416 | return (ENODEV); |
2a1ad637 FT |
2417 | } |
2418 | ||
2419 | PCM_WAIT(d); | |
2420 | PCM_ACQUIRE(d); | |
2421 | PCM_UNLOCK(d); | |
2422 | ||
2423 | udcmask = snd_u2unit(unit) | snd_d2unit(devtype); | |
2424 | ||
2425 | if (devhw != 0) { | |
2426 | KASSERT(devcmax <= dsp_cmax, | |
2427 | ("overflow: devcmax=%d, dsp_cmax=%d", devcmax, dsp_cmax)); | |
2428 | if (cunit > devcmax) { | |
2429 | PCM_RELEASE_QUICK(d); | |
18099ae9 | 2430 | return (ENODEV); |
2a1ad637 FT |
2431 | } |
2432 | udcmask |= snd_c2unit(cunit); | |
2433 | CHN_FOREACH(c, d, channels.pcm) { | |
2434 | CHN_LOCK(c); | |
2435 | if (c->unit != udcmask) { | |
2436 | CHN_UNLOCK(c); | |
2437 | continue; | |
2438 | } | |
2439 | CHN_UNLOCK(c); | |
2440 | udcmask &= ~snd_c2unit(cunit); | |
2441 | /* | |
2442 | * Temporarily increase clone maxunit to overcome | |
2443 | * vchan flexibility. | |
2444 | * | |
2445 | * # sysctl dev.pcm.0.play.vchans=256 | |
2446 | * dev.pcm.0.play.vchans: 1 -> 256 | |
2447 | * # cat /dev/zero > /dev/dsp0.vp255 & | |
2448 | * [1] 17296 | |
2449 | * # sysctl dev.pcm.0.play.vchans=0 | |
2450 | * dev.pcm.0.play.vchans: 256 -> 1 | |
2451 | * # fg | |
2452 | * [1] + running cat /dev/zero > /dev/dsp0.vp255 | |
2453 | * ^C | |
2454 | * # cat /dev/zero > /dev/dsp0.vp255 | |
2455 | * zsh: operation not supported: /dev/dsp0.vp255 | |
2456 | */ | |
2457 | tumax = snd_clone_getmaxunit(d->clones); | |
2458 | if (cunit > tumax) | |
2459 | snd_clone_setmaxunit(d->clones, cunit); | |
2460 | else | |
2461 | tumax = -1; | |
2462 | goto dsp_clone_alloc; | |
2463 | } | |
2464 | /* | |
2465 | * Ok, so we're requesting unallocated vchan, but still | |
2466 | * within maximum vchan limit. | |
2467 | */ | |
2468 | if (((devtype == SND_DEV_DSPHW_VPLAY && d->pvchancount > 0) || | |
2469 | (devtype == SND_DEV_DSPHW_VREC && d->rvchancount > 0)) && | |
2470 | cunit < snd_maxautovchans) { | |
2471 | udcmask &= ~snd_c2unit(cunit); | |
2472 | tumax = snd_clone_getmaxunit(d->clones); | |
2473 | if (cunit > tumax) | |
2474 | snd_clone_setmaxunit(d->clones, cunit); | |
2475 | else | |
2476 | tumax = -1; | |
2477 | goto dsp_clone_alloc; | |
2478 | } | |
2479 | PCM_RELEASE_QUICK(d); | |
18099ae9 | 2480 | return (err); |
2a1ad637 FT |
2481 | } |
2482 | ||
5f097292 MD |
2483 | /* |
2484 | * Allocate channel, create device if necessary. | |
2485 | */ | |
2a1ad637 | 2486 | dsp_clone_alloc: |
18099ae9 | 2487 | ce = snd_clone_alloc(d->clones, &dev, &cunit, udcmask); |
2a1ad637 FT |
2488 | if (tumax != -1) |
2489 | snd_clone_setmaxunit(d->clones, tumax); | |
2490 | if (ce != NULL) { | |
5f097292 | 2491 | /* |
86ffaa1e MD |
2492 | * NOTE: Subunit is a unique number unrelated to the |
2493 | * channel id. | |
5f097292 | 2494 | */ |
2a1ad637 | 2495 | udcmask |= snd_c2unit(cunit); |
39b4af32 FT |
2496 | int subunit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(dsp), 0); |
2497 | ||
39b4af32 | 2498 | dev = make_only_dev(&dsp_ops, |
5f097292 | 2499 | PCMMKMINOR(unit, devtype, subunit), |
39b4af32 | 2500 | UID_ROOT, GID_WHEEL, 0666, "%s%d.%d", |
5f097292 | 2501 | sname, unit, subunit); |
18099ae9 FT |
2502 | snd_clone_register(ce, dev); |
2503 | err = 0; | |
5f097292 MD |
2504 | } else { |
2505 | /* | |
86ffaa1e MD |
2506 | * Use device already registered, we must add a ref to the |
2507 | * device. | |
5f097292 | 2508 | */ |
5f097292 | 2509 | err = 0; |
2a1ad637 FT |
2510 | } |
2511 | ||
39b4af32 FT |
2512 | ap->a_dev = dev; |
2513 | ||
2a1ad637 | 2514 | PCM_RELEASE_QUICK(d); |
5f097292 | 2515 | |
18099ae9 | 2516 | return (err); |
2a1ad637 FT |
2517 | } |
2518 | ||
2519 | static void | |
2520 | dsp_sysinit(void *p) | |
2521 | { | |
2522 | if (dsp_ehtag != NULL) | |
2523 | return; | |
2524 | /* initialize unit numbering */ | |
2525 | snd_unit_init(); | |
2526 | dsp_umax = PCMMAXUNIT; | |
2527 | dsp_cmax = PCMMAXCHAN; | |
2528 | dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000); | |
5f097292 MD |
2529 | |
2530 | devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(dsp)); | |
2a1ad637 FT |
2531 | } |
2532 | ||
2533 | static void | |
2534 | dsp_sysuninit(void *p) | |
2535 | { | |
2536 | if (dsp_ehtag == NULL) | |
2537 | return; | |
2538 | EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag); | |
2539 | dsp_ehtag = NULL; | |
2540 | } | |
2541 | ||
2542 | SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL); | |
2543 | SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL); | |
2544 | ||
2545 | char * | |
2546 | dsp_unit2name(char *buf, size_t len, int unit) | |
2547 | { | |
2548 | int i, dtype; | |
2549 | ||
2550 | KASSERT(buf != NULL && len != 0, | |
2551 | ("bogus buf=%p len=%ju", buf, (uintmax_t)len)); | |
2552 | ||
2553 | dtype = snd_unit2d(unit); | |
2554 | ||
2555 | for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { | |
2556 | if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) | |
2557 | continue; | |
67931cc4 | 2558 | ksnprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, |
2a1ad637 FT |
2559 | snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); |
2560 | return (buf); | |
2561 | } | |
2562 | ||
2563 | return (NULL); | |
2564 | } | |
2565 | ||
2566 | /** | |
2567 | * @brief Handler for SNDCTL_AUDIOINFO. | |
2568 | * | |
2569 | * Gathers information about the audio device specified in ai->dev. If | |
2570 | * ai->dev == -1, then this function gathers information about the current | |
2571 | * device. If the call comes in on a non-audio device and ai->dev == -1, | |
2572 | * return EINVAL. | |
2573 | * | |
2574 | * This routine is supposed to go practically straight to the hardware, | |
2575 | * getting capabilities directly from the sound card driver, side-stepping | |
2576 | * the intermediate channel interface. | |
2577 | * | |
2578 | * Note, however, that the usefulness of this command is significantly | |
2579 | * decreased when requesting info about any device other than the one serving | |
2580 | * the request. While each snddev_channel refers to a specific device node, | |
2581 | * the converse is *not* true. Currently, when a sound device node is opened, | |
2582 | * the sound subsystem scans for an available audio channel (or channels, if | |
2583 | * opened in read+write) and then assigns them to the si_drv[12] private | |
2584 | * data fields. As a result, any information returned linking a channel to | |
2585 | * a specific character device isn't necessarily accurate. | |
2586 | * | |
2587 | * @note | |
2588 | * Calling threads must not hold any snddev_info or pcm_channel locks. | |
2589 | * | |
2590 | * @param dev device on which the ioctl was issued | |
2591 | * @param ai ioctl request data container | |
2592 | * | |
2593 | * @retval 0 success | |
2594 | * @retval EINVAL ai->dev specifies an invalid device | |
2595 | * | |
2596 | * @todo Verify correctness of Doxygen tags. ;) | |
2597 | */ | |
2598 | int | |
2599 | dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) | |
2600 | { | |
2601 | struct pcmchan_caps *caps; | |
2602 | struct pcm_channel *ch; | |
2603 | struct snddev_info *d; | |
2604 | uint32_t fmts; | |
2605 | int i, nchan, *rates, minch, maxch; | |
2606 | char *devname, buf[CHN_NAMELEN]; | |
2607 | ||
2608 | /* | |
2609 | * If probing the device that received the ioctl, make sure it's a | |
2610 | * DSP device. (Users may use this ioctl with /dev/mixer and | |
2611 | * /dev/midi.) | |
2612 | */ | |
88a31edf | 2613 | if (ai->dev == -1 && i_dev->si_ops != &dsp_ops) |
2a1ad637 FT |
2614 | return (EINVAL); |
2615 | ||
2616 | ch = NULL; | |
2617 | devname = NULL; | |
2618 | nchan = 0; | |
2619 | bzero(buf, sizeof(buf)); | |
2620 | ||
2621 | /* | |
2622 | * Search for the requested audio device (channel). Start by | |
2623 | * iterating over pcm devices. | |
2624 | */ | |
2625 | for (i = 0; pcm_devclass != NULL && | |
2626 | i < devclass_get_maxunit(pcm_devclass); i++) { | |
2627 | d = devclass_get_softc(pcm_devclass, i); | |
2628 | if (!PCM_REGISTERED(d)) | |
2629 | continue; | |
2630 | ||
2631 | /* XXX Need Giant magic entry ??? */ | |
2632 | ||
2633 | /* See the note in function docblock */ | |
2634 | PCM_UNLOCKASSERT(d); | |
2635 | PCM_LOCK(d); | |
2636 | ||
2637 | CHN_FOREACH(ch, d, channels.pcm) { | |
2638 | CHN_UNLOCKASSERT(ch); | |
2639 | CHN_LOCK(ch); | |
2640 | if (ai->dev == -1) { | |
2641 | if (DSP_REGISTERED(d, i_dev) && | |
2642 | (ch == PCM_RDCH(i_dev) || /* record ch */ | |
2643 | ch == PCM_WRCH(i_dev))) { /* playback ch */ | |
2644 | devname = dsp_unit2name(buf, | |
2645 | sizeof(buf), ch->unit); | |
2646 | } | |
2647 | } else if (ai->dev == nchan) { | |
2648 | devname = dsp_unit2name(buf, sizeof(buf), | |
2649 | ch->unit); | |
2650 | } | |
2651 | if (devname != NULL) | |
2652 | break; | |
2653 | CHN_UNLOCK(ch); | |
2654 | ++nchan; | |
2655 | } | |
2656 | ||
2657 | if (devname != NULL) { | |
2658 | /* | |
2659 | * At this point, the following synchronization stuff | |
2660 | * has happened: | |
2661 | * - a specific PCM device is locked. | |
2662 | * - a specific audio channel has been locked, so be | |
2663 | * sure to unlock when exiting; | |
2664 | */ | |
2665 | ||
2666 | caps = chn_getcaps(ch); | |
2667 | ||
2668 | /* | |
2669 | * With all handles collected, zero out the user's | |
2670 | * container and begin filling in its fields. | |
2671 | */ | |
2672 | bzero((void *)ai, sizeof(oss_audioinfo)); | |
2673 | ||
2674 | ai->dev = nchan; | |
2675 | strlcpy(ai->name, ch->name, sizeof(ai->name)); | |
2676 | ||
2677 | if ((ch->flags & CHN_F_BUSY) == 0) | |
2678 | ai->busy = 0; | |
2679 | else | |
2680 | ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ; | |
2681 | ||
2682 | /** | |
2683 | * @note | |
2684 | * @c cmd - OSSv4 docs: "Only supported under Linux at | |
2685 | * this moment." Cop-out, I know, but I'll save | |
2686 | * running around in the process table for later. | |
2687 | * Is there a risk of leaking information? | |
2688 | */ | |
2689 | ai->pid = ch->pid; | |
2690 | ||
2691 | /* | |
2692 | * These flags stolen from SNDCTL_DSP_GETCAPS handler. | |
2693 | * Note, however, that a single channel operates in | |
2694 | * only one direction, so PCM_CAP_DUPLEX is out. | |
2695 | */ | |
2696 | /** | |
2697 | * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep | |
2698 | * these in pcmchan::caps? | |
2699 | */ | |
2700 | ai->caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER | | |
2701 | ((ch->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT : PCM_CAP_INPUT); | |
2702 | ||
2703 | /* | |
2704 | * Collect formats supported @b natively by the | |
2705 | * device. Also determine min/max channels. (I.e., | |
2706 | * mono, stereo, or both?) | |
2707 | * | |
2708 | * If any channel is stereo, maxch = 2; | |
2709 | * if all channels are stereo, minch = 2, too; | |
2710 | * if any channel is mono, minch = 1; | |
2711 | * and if all channels are mono, maxch = 1. | |
2712 | */ | |
2713 | minch = 0; | |
2714 | maxch = 0; | |
2715 | fmts = 0; | |
2716 | for (i = 0; caps->fmtlist[i]; i++) { | |
2717 | fmts |= caps->fmtlist[i]; | |
2718 | if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) { | |
2719 | minch = (minch == 0) ? 2 : minch; | |
2720 | maxch = 2; | |
2721 | } else { | |
2722 | minch = 1; | |
2723 | maxch = (maxch == 0) ? 1 : maxch; | |
2724 | } | |
2725 | } | |
2726 | ||
2727 | if (ch->direction == PCMDIR_PLAY) | |
2728 | ai->oformats = fmts; | |
2729 | else | |
2730 | ai->iformats = fmts; | |
2731 | ||
2732 | /** | |
2733 | * @note | |
2734 | * @c magic - OSSv4 docs: "Reserved for internal use | |
2735 | * by OSS." | |
2736 | * | |
2737 | * @par | |
2738 | * @c card_number - OSSv4 docs: "Number of the sound | |
2739 | * card where this device belongs or -1 if this | |
2740 | * information is not available. Applications | |
2741 | * should normally not use this field for any | |
2742 | * purpose." | |
2743 | */ | |
2744 | ai->card_number = -1; | |
2745 | /** | |
2746 | * @todo @c song_name - depends first on | |
2747 | * SNDCTL_[GS]ETSONG @todo @c label - depends | |
2748 | * on SNDCTL_[GS]ETLABEL | |
2749 | * @todo @c port_number - routing information? | |
2750 | */ | |
2751 | ai->port_number = -1; | |
2752 | ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1; | |
2753 | /** | |
2754 | * @note | |
2755 | * @c real_device - OSSv4 docs: "Obsolete." | |
2756 | */ | |
2757 | ai->real_device = -1; | |
2758 | strlcpy(ai->devnode, "/dev/", sizeof(ai->devnode)); | |
2759 | strlcat(ai->devnode, devname, sizeof(ai->devnode)); | |
2760 | ai->enabled = device_is_attached(d->dev) ? 1 : 0; | |
2761 | /** | |
2762 | * @note | |
2763 | * @c flags - OSSv4 docs: "Reserved for future use." | |
2764 | * | |
2765 | * @note | |
2766 | * @c binding - OSSv4 docs: "Reserved for future use." | |
2767 | * | |
2768 | * @todo @c handle - haven't decided how to generate | |
2769 | * this yet; bus, vendor, device IDs? | |
2770 | */ | |
2771 | ai->min_rate = caps->minspeed; | |
2772 | ai->max_rate = caps->maxspeed; | |
2773 | ||
2774 | ai->min_channels = minch; | |
2775 | ai->max_channels = maxch; | |
2776 | ||
2777 | ai->nrates = chn_getrates(ch, &rates); | |
2778 | if (ai->nrates > OSS_MAX_SAMPLE_RATES) | |
2779 | ai->nrates = OSS_MAX_SAMPLE_RATES; | |
2780 | ||
2781 | for (i = 0; i < ai->nrates; i++) | |
2782 | ai->rates[i] = rates[i]; | |
2783 | ||
2784 | ai->next_play_engine = 0; | |
2785 | ai->next_rec_engine = 0; | |
2786 | ||
2787 | CHN_UNLOCK(ch); | |
2788 | } | |
2789 | ||
2790 | PCM_UNLOCK(d); | |
2791 | ||
2792 | if (devname != NULL) | |
2793 | return (0); | |
2794 | } | |
2795 | ||
2796 | /* Exhausted the search -- nothing is locked, so return. */ | |
2797 | return (EINVAL); | |
2798 | } | |
2799 | ||
2800 | /** | |
2801 | * @brief Assigns a PCM channel to a sync group. | |
2802 | * | |
2803 | * Sync groups are used to enable audio operations on multiple devices | |
2804 | * simultaneously. They may be used with any number of devices and may | |
2805 | * span across applications. Devices are added to groups with | |
2806 | * the SNDCTL_DSP_SYNCGROUP ioctl, and operations are triggered with the | |
2807 | * SNDCTL_DSP_SYNCSTART ioctl. | |
2808 | * | |
2809 | * If the @c id field of the @c group parameter is set to zero, then a new | |
2810 | * sync group is created. Otherwise, wrch and rdch (if set) are added to | |
2811 | * the group specified. | |
2812 | * | |
2813 | * @todo As far as memory allocation, should we assume that things are | |
2814 | * okay and allocate with M_WAITOK before acquiring channel locks, | |
2815 | * freeing later if not? | |
2816 | * | |
2817 | * @param wrch output channel associated w/ device (if any) | |
2818 | * @param rdch input channel associated w/ device (if any) | |
2819 | * @param group Sync group parameters | |
2820 | * | |
2821 | * @retval 0 success | |
2822 | * @retval non-zero error to be propagated upstream | |
2823 | */ | |
2824 | static int | |
2825 | dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group) | |
2826 | { | |
2827 | struct pcmchan_syncmember *smrd, *smwr; | |
2828 | struct pcmchan_syncgroup *sg; | |
2829 | int ret, sg_ids[3]; | |
2830 | ||
2831 | smrd = NULL; | |
2832 | smwr = NULL; | |
2833 | sg = NULL; | |
2834 | ret = 0; | |
2835 | ||
2836 | /* | |
2837 | * Free_unr() may sleep, so store released syncgroup IDs until after | |
2838 | * all locks are released. | |
2839 | */ | |
2840 | sg_ids[0] = sg_ids[1] = sg_ids[2] = 0; | |
2841 | ||
2842 | PCM_SG_LOCK(); | |
2843 | ||
2844 | /* | |
2845 | * - Insert channel(s) into group's member list. | |
2846 | * - Set CHN_F_NOTRIGGER on channel(s). | |
2847 | * - Stop channel(s). | |
2848 | */ | |
2849 | ||
2850 | /* | |
2851 | * If device's channels are already mapped to a group, unmap them. | |
2852 | */ | |
2853 | if (wrch) { | |
2854 | CHN_LOCK(wrch); | |
2855 | sg_ids[0] = chn_syncdestroy(wrch); | |
2856 | } | |
2857 | ||
2858 | if (rdch) { | |
2859 | CHN_LOCK(rdch); | |
2860 | sg_ids[1] = chn_syncdestroy(rdch); | |
2861 | } | |
2862 | ||
2863 | /* | |
2864 | * Verify that mode matches character device properites. | |
2865 | * - Bail if PCM_ENABLE_OUTPUT && wrch == NULL. | |
2866 | * - Bail if PCM_ENABLE_INPUT && rdch == NULL. | |
2867 | */ | |
2868 | if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) || | |
2869 | ((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) { | |
2870 | ret = EINVAL; | |
2871 | goto out; | |
2872 | } | |
2873 | ||
2874 | /* | |
2875 | * An id of zero indicates the user wants to create a new | |
2876 | * syncgroup. | |
2877 | */ | |
2878 | if (group->id == 0) { | |
4e8e900c | 2879 | sg = (struct pcmchan_syncgroup *)kmalloc(sizeof(*sg), M_DEVBUF, M_WAITOK | M_ZERO); |
2a1ad637 FT |
2880 | if (sg != NULL) { |
2881 | SLIST_INIT(&sg->members); | |
2882 | sg->id = alloc_unr(pcmsg_unrhdr); | |
2883 | ||
2884 | group->id = sg->id; | |
2885 | SLIST_INSERT_HEAD(&snd_pcm_syncgroups, sg, link); | |
2886 | } else | |
2887 | ret = ENOMEM; | |
2888 | } else { | |
2889 | SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) { | |
2890 | if (sg->id == group->id) | |
2891 | break; | |
2892 | } | |
2893 | if (sg == NULL) | |
2894 | ret = EINVAL; | |
2895 | } | |
2896 | ||
2897 | /* Couldn't create or find a syncgroup. Fail. */ | |
2898 | if (sg == NULL) | |
2899 | goto out; | |
2900 | ||
2901 | /* | |
2902 | * Allocate a syncmember, assign it and a channel together, and | |
2903 | * insert into syncgroup. | |
2904 | */ | |
2905 | if (group->mode & PCM_ENABLE_INPUT) { | |
4e8e900c | 2906 | smrd = (struct pcmchan_syncmember *)kmalloc(sizeof(*smrd), M_DEVBUF, M_WAITOK | M_ZERO); |
2a1ad637 FT |
2907 | if (smrd == NULL) { |
2908 | ret = ENOMEM; | |
2909 | goto out; | |
2910 | } | |
2911 | ||
2912 | SLIST_INSERT_HEAD(&sg->members, smrd, link); | |
2913 | smrd->parent = sg; | |
2914 | smrd->ch = rdch; | |
2915 | ||
2916 | chn_abort(rdch); | |
2917 | rdch->flags |= CHN_F_NOTRIGGER; | |
2918 | rdch->sm = smrd; | |
2919 | } | |
2920 | ||
2921 | if (group->mode & PCM_ENABLE_OUTPUT) { | |
4e8e900c | 2922 | smwr = (struct pcmchan_syncmember *)kmalloc(sizeof(*smwr), M_DEVBUF, M_WAITOK | M_ZERO); |
2a1ad637 FT |
2923 | if (smwr == NULL) { |
2924 | ret = ENOMEM; | |
2925 | goto out; | |
2926 | } | |
2927 | ||
2928 | SLIST_INSERT_HEAD(&sg->members, smwr, link); | |
2929 | smwr->parent = sg; | |
2930 | smwr->ch = wrch; | |
2931 | ||
2932 | chn_abort(wrch); | |
2933 | wrch->flags |= CHN_F_NOTRIGGER; | |
2934 | wrch->sm = smwr; | |
2935 | } | |
2936 | ||
2937 | ||
2938 | out: | |
2939 | if (ret != 0) { | |
2940 | if (smrd != NULL) | |
67931cc4 | 2941 | kfree(smrd, M_DEVBUF); |
2a1ad637 FT |
2942 | if ((sg != NULL) && SLIST_EMPTY(&sg->members)) { |
2943 | sg_ids[2] = sg->id; | |
2944 | SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); | |
67931cc4 | 2945 | kfree(sg, M_DEVBUF); |
2a1ad637 FT |
2946 | } |
2947 | ||
2948 | if (wrch) | |
2949 | wrch->sm = NULL; | |
2950 | if (rdch) | |
2951 | rdch->sm = NULL; | |
2952 | } | |
2953 | ||
2954 | if (wrch) | |
2955 | CHN_UNLOCK(wrch); | |
2956 | if (rdch) | |
2957 | CHN_UNLOCK(rdch); | |
2958 | ||
2959 | PCM_SG_UNLOCK(); | |
2960 | ||
2961 | if (sg_ids[0]) | |
2962 | free_unr(pcmsg_unrhdr, sg_ids[0]); | |
2963 | if (sg_ids[1]) | |
2964 | free_unr(pcmsg_unrhdr, sg_ids[1]); | |
2965 | if (sg_ids[2]) | |
2966 | free_unr(pcmsg_unrhdr, sg_ids[2]); | |
2967 | ||
2968 | return (ret); | |
2969 | } | |
2970 | ||
2971 | /** | |
2972 | * @brief Launch a sync group into action | |
2973 | * | |
2974 | * Sync groups are established via SNDCTL_DSP_SYNCGROUP. This function | |
2975 | * iterates over all members, triggering them along the way. | |
2976 | * | |
2977 | * @note Caller must not hold any channel locks. | |
2978 | * | |
2979 | * @param sg_id sync group identifier | |
2980 | * | |
2981 | * @retval 0 success | |
2982 | * @retval non-zero error worthy of propagating upstream to user | |
2983 | */ | |
2984 | static int | |
2985 | dsp_oss_syncstart(int sg_id) | |
2986 | { | |
2987 | struct pcmchan_syncmember *sm, *sm_tmp; | |
2988 | struct pcmchan_syncgroup *sg; | |
2989 | struct pcm_channel *c; | |
2990 | int ret, needlocks; | |
2991 | ||
2992 | /* Get the synclists lock */ | |
2993 | PCM_SG_LOCK(); | |
2994 | ||
2995 | do { | |
2996 | ret = 0; | |
2997 | needlocks = 0; | |
2998 | ||
2999 | /* Search for syncgroup by ID */ | |
3000 | SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) { | |
3001 | if (sg->id == sg_id) | |
3002 | break; | |
3003 | } | |
3004 | ||
3005 | /* Return EINVAL if not found */ | |
3006 | if (sg == NULL) { | |
3007 | ret = EINVAL; | |
3008 | break; | |
3009 | } | |
3010 | ||
3011 | /* Any removals resulting in an empty group should've handled this */ | |
3012 | KASSERT(!SLIST_EMPTY(&sg->members), ("found empty syncgroup")); | |
3013 | ||
3014 | /* | |
3015 | * Attempt to lock all member channels - if any are already | |
3016 | * locked, unlock those acquired, sleep for a bit, and try | |
3017 | * again. | |
3018 | */ | |
3019 | SLIST_FOREACH(sm, &sg->members, link) { | |
3020 | if (CHN_TRYLOCK(sm->ch) == 0) { | |
3021 | int timo = hz * 5/1000; | |
3022 | if (timo < 1) | |
3023 | timo = 1; | |
3024 | ||
3025 | /* Release all locked channels so far, retry */ | |
3026 | SLIST_FOREACH(sm_tmp, &sg->members, link) { | |
3027 | /* sm is the member already locked */ | |
3028 | if (sm == sm_tmp) | |
3029 | break; | |
3030 | CHN_UNLOCK(sm_tmp->ch); | |
3031 | } | |
3032 | ||
3033 | /** @todo Is PRIBIO correct/ */ | |
67931cc4 FT |
3034 | ret = lksleep(sm, &snd_pcm_syncgroups_mtx, |
3035 | PCATCH, "pcmsg", timo); | |
2a1ad637 FT |
3036 | if (ret == EINTR || ret == ERESTART) |
3037 | break; | |
3038 | ||
3039 | needlocks = 1; | |
3040 | ret = 0; /* Assumes ret == EAGAIN... */ | |
3041 | } | |
3042 | } | |
3043 | } while (needlocks && ret == 0); | |
3044 | ||
3045 | /* Proceed only if no errors encountered. */ | |
3046 | if (ret == 0) { | |
3047 | /* Launch channels */ | |
3048 | while ((sm = SLIST_FIRST(&sg->members)) != NULL) { | |
3049 | SLIST_REMOVE_HEAD(&sg->members, link); | |
3050 | ||
3051 | c = sm->ch; | |
3052 | c->sm = NULL; | |
3053 | chn_start(c, 1); | |
3054 | c->flags &= ~CHN_F_NOTRIGGER; | |
3055 | CHN_UNLOCK(c); | |
3056 | ||
67931cc4 | 3057 | kfree(sm, M_DEVBUF); |
2a1ad637 FT |
3058 | } |
3059 | ||
3060 | SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); | |
67931cc4 | 3061 | kfree(sg, M_DEVBUF); |
2a1ad637 FT |
3062 | } |
3063 | ||
3064 | PCM_SG_UNLOCK(); | |
3065 | ||
3066 | /* | |
3067 | * Free_unr() may sleep, so be sure to give up the syncgroup lock | |
3068 | * first. | |
3069 | */ | |
3070 | if (ret == 0) | |
3071 | free_unr(pcmsg_unrhdr, sg_id); | |
3072 | ||
3073 | return (ret); | |
3074 | } | |
3075 | ||
3076 | /** | |
3077 | * @brief Handler for SNDCTL_DSP_POLICY | |
3078 | * | |
3079 | * The SNDCTL_DSP_POLICY ioctl is a simpler interface to control fragment | |
3080 | * size and count like with SNDCTL_DSP_SETFRAGMENT. Instead of the user | |
3081 | * specifying those two parameters, s/he simply selects a number from 0..10 | |
3082 | * which corresponds to a buffer size. Smaller numbers request smaller | |
3083 | * buffers with lower latencies (at greater overhead from more frequent | |
3084 | * interrupts), while greater numbers behave in the opposite manner. | |
3085 | * | |
3086 | * The 4Front spec states that a value of 5 should be the default. However, | |
3087 | * this implementation deviates slightly by using a linear scale without | |
3088 | * consulting drivers. I.e., even though drivers may have different default | |
3089 | * buffer sizes, a policy argument of 5 will have the same result across | |
3090 | * all drivers. | |
3091 | * | |
3092 | * See http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html for | |
3093 | * more information. | |
3094 | * | |
3095 | * @todo When SNDCTL_DSP_COOKEDMODE is supported, it'll be necessary to | |
3096 | * work with hardware drivers directly. | |
3097 | * | |
3098 | * @note PCM channel arguments must not be locked by caller. | |
3099 | * | |
3100 | * @param wrch Pointer to opened playback channel (optional; may be NULL) | |
3101 | * @param rdch " recording channel (optional; may be NULL) | |
3102 | * @param policy Integer from [0:10] | |
3103 | * | |
3104 | * @retval 0 constant (for now) | |
3105 | */ | |
3106 | static int | |
3107 | dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) | |
3108 | { | |
3109 | int ret; | |
3110 | ||
3111 | if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX) | |
3112 | return (EIO); | |
3113 | ||
3114 | /* Default: success */ | |
3115 | ret = 0; | |
3116 | ||
3117 | if (rdch) { | |
3118 | CHN_LOCK(rdch); | |
3119 | ret = chn_setlatency(rdch, policy); | |
3120 | CHN_UNLOCK(rdch); | |
3121 | } | |
3122 | ||
3123 | if (wrch && ret == 0) { | |
3124 | CHN_LOCK(wrch); | |
3125 | ret = chn_setlatency(wrch, policy); | |
3126 | CHN_UNLOCK(wrch); | |
3127 | } | |
3128 | ||
3129 | if (ret) | |
3130 | ret = EIO; | |
3131 | ||
3132 | return (ret); | |
3133 | } | |
3134 | ||
3135 | /** | |
3136 | * @brief Enable or disable "cooked" mode | |
3137 | * | |
3138 | * This is a handler for @c SNDCTL_DSP_COOKEDMODE. When in cooked mode, which | |
3139 | * is the default, the sound system handles rate and format conversions | |
3140 | * automatically (ex: user writing 11025Hz/8 bit/unsigned but card only | |
3141 | * operates with 44100Hz/16bit/signed samples). | |
3142 | * | |
3143 | * Disabling cooked mode is intended for applications wanting to mmap() | |
3144 | * a sound card's buffer space directly, bypassing the FreeBSD 2-stage | |
3145 | * feeder architecture, presumably to gain as much control over audio | |
3146 | * hardware as possible. | |
3147 | * | |
3148 | * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html | |
3149 | * for more details. | |
3150 | * | |
3151 | * @param wrch playback channel (optional; may be NULL) | |
3152 | * @param rdch recording channel (optional; may be NULL) | |
3153 | * @param enabled 0 = raw mode, 1 = cooked mode | |
3154 | * | |
3155 | * @retval EINVAL Operation not yet supported. | |
3156 | */ | |
3157 | static int | |
3158 | dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled) | |
3159 | { | |
3160 | ||
3161 | /* | |
3162 | * XXX I just don't get it. Why don't they call it | |
3163 | * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?. | |
3164 | * This is just plain so confusing, incoherent, | |
3165 | * <insert any non-printable characters here>. | |
3166 | */ | |
3167 | if (!(enabled == 1 || enabled == 0)) | |
3168 | return (EINVAL); | |
3169 | ||
3170 | /* | |
3171 | * I won't give in. I'm inverting its logic here and now. | |
3172 | * Brag all you want, but "BITPERFECT" should be the better | |
3173 | * term here. | |
3174 | */ | |
3175 | enabled ^= 0x00000001; | |
3176 | ||
3177 | if (wrch != NULL) { | |
3178 | CHN_LOCK(wrch); | |
3179 | wrch->flags &= ~CHN_F_BITPERFECT; | |
3180 | wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; | |
3181 | CHN_UNLOCK(wrch); | |
3182 | } | |
3183 | ||
3184 | if (rdch != NULL) { | |
3185 | CHN_LOCK(rdch); | |
3186 | rdch->flags &= ~CHN_F_BITPERFECT; | |
3187 | rdch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; | |
3188 | CHN_UNLOCK(rdch); | |
3189 | } | |
3190 | ||
3191 | return (0); | |
3192 | } | |
3193 | ||
3194 | /** | |
3195 | * @brief Retrieve channel interleaving order | |
3196 | * | |
3197 | * This is the handler for @c SNDCTL_DSP_GET_CHNORDER. | |
3198 | * | |
3199 | * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_GET_CHNORDER.html | |
3200 | * for more details. | |
3201 | * | |
3202 | * @note As the ioctl definition is still under construction, FreeBSD | |
3203 | * does not currently support SNDCTL_DSP_GET_CHNORDER. | |
3204 | * | |
3205 | * @param wrch playback channel (optional; may be NULL) | |
3206 | * @param rdch recording channel (optional; may be NULL) | |
3207 | * @param map channel map (result will be stored there) | |
3208 | * | |
3209 | * @retval EINVAL Operation not yet supported. | |
3210 | */ | |
3211 | static int | |
3212 | dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) | |
3213 | { | |
3214 | struct pcm_channel *ch; | |
3215 | int ret; | |
3216 | ||
3217 | ch = (wrch != NULL) ? wrch : rdch; | |
3218 | if (ch != NULL) { | |
3219 | CHN_LOCK(ch); | |
3220 | ret = chn_oss_getorder(ch, map); | |
3221 | CHN_UNLOCK(ch); | |
3222 | } else | |
3223 | ret = EINVAL; | |
3224 | ||
3225 | return (ret); | |
3226 | } | |
3227 | ||
3228 | /** | |
3229 | * @brief Specify channel interleaving order | |
3230 | * | |
3231 | * This is the handler for @c SNDCTL_DSP_SET_CHNORDER. | |
3232 | * | |
3233 | * @note As the ioctl definition is still under construction, FreeBSD | |
3234 | * does not currently support @c SNDCTL_DSP_SET_CHNORDER. | |
3235 | * | |
3236 | * @param wrch playback channel (optional; may be NULL) | |
3237 | * @param rdch recording channel (optional; may be NULL) | |
3238 | * @param map channel map | |
3239 | * | |
3240 | * @retval EINVAL Operation not yet supported. | |
3241 | */ | |
3242 | static int | |
3243 | dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) | |
3244 | { | |
3245 | int ret; | |
3246 | ||
3247 | ret = 0; | |
3248 | ||
3249 | if (wrch != NULL) { | |
3250 | CHN_LOCK(wrch); | |
3251 | ret = chn_oss_setorder(wrch, map); | |
3252 | CHN_UNLOCK(wrch); | |
3253 | } | |
3254 | ||
3255 | if (ret == 0 && rdch != NULL) { | |
3256 | CHN_LOCK(rdch); | |
3257 | ret = chn_oss_setorder(rdch, map); | |
3258 | CHN_UNLOCK(rdch); | |
3259 | } | |
3260 | ||
3261 | return (ret); | |
3262 | } | |
3263 | ||
3264 | static int | |
3265 | dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, | |
3266 | int *mask) | |
3267 | { | |
3268 | struct pcm_channel *ch; | |
3269 | uint32_t chnmask; | |
3270 | int ret; | |
3271 | ||
3272 | chnmask = 0; | |
3273 | ch = (wrch != NULL) ? wrch : rdch; | |
3274 | ||
3275 | if (ch != NULL) { | |
3276 | CHN_LOCK(ch); | |
3277 | ret = chn_oss_getmask(ch, &chnmask); | |
3278 | CHN_UNLOCK(ch); | |
3279 | } else | |
3280 | ret = EINVAL; | |
3281 | ||
3282 | if (ret == 0) | |
3283 | *mask = chnmask; | |
3284 | ||
3285 | return (ret); | |
3286 | } | |
3287 | ||
3288 | #ifdef OSSV4_EXPERIMENT | |
3289 | /** | |
3290 | * @brief Retrieve an audio device's label | |
3291 | * | |
3292 | * This is a handler for the @c SNDCTL_GETLABEL ioctl. | |
3293 | * | |
3294 | * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html | |
3295 | * for more details. | |
3296 | * | |
3297 | * From Hannu@4Front: "For example ossxmix (just like some HW mixer | |
3298 | * consoles) can show variable "labels" for certain controls. By default | |
3299 | * the application name (say quake) is shown as the label but | |
3300 | * applications may change the labels themselves." | |
3301 | * | |
3302 | * @note As the ioctl definition is still under construction, FreeBSD | |
3303 | * does not currently support @c SNDCTL_GETLABEL. | |
3304 | * | |
3305 | * @param wrch playback channel (optional; may be NULL) | |
3306 | * @param rdch recording channel (optional; may be NULL) | |
3307 | * @param label label gets copied here | |
3308 | * | |
3309 | * @retval EINVAL Operation not yet supported. | |
3310 | */ | |
3311 | static int | |
3312 | dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label) | |
3313 | { | |
3314 | return (EINVAL); | |
3315 | } | |
3316 | ||
3317 | /** | |
3318 | * @brief Specify an audio device's label | |
3319 | * | |
3320 | * This is a handler for the @c SNDCTL_SETLABEL ioctl. Please see the | |
3321 | * comments for @c dsp_oss_getlabel immediately above. | |
3322 | * | |
3323 | * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html | |
3324 | * for more details. | |
3325 | * | |
3326 | * @note As the ioctl definition is still under construction, FreeBSD | |
3327 | * does not currently support SNDCTL_SETLABEL. | |
3328 | * | |
3329 | * @param wrch playback channel (optional; may be NULL) | |
3330 | * @param rdch recording channel (optional; may be NULL) | |
3331 | * @param label label gets copied from here | |
3332 | * | |
3333 | * @retval EINVAL Operation not yet supported. | |
3334 | */ | |
3335 | static int | |
3336 | dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label) | |
3337 | { | |
3338 | return (EINVAL); | |
3339 | } | |
3340 | ||
3341 | /** | |
3342 | * @brief Retrieve name of currently played song | |
3343 | * | |
3344 | * This is a handler for the @c SNDCTL_GETSONG ioctl. Audio players could | |
3345 | * tell the system the name of the currently playing song, which would be | |
3346 | * visible in @c /dev/sndstat. | |
3347 | * | |
3348 | * See @c http://manuals.opensound.com/developer/SNDCTL_GETSONG.html | |
3349 | * for more details. | |
3350 | * | |
3351 | * @note As the ioctl definition is still under construction, FreeBSD | |
3352 | * does not currently support SNDCTL_GETSONG. | |
3353 | * | |
3354 | * @param wrch playback channel (optional; may be NULL) | |
3355 | * @param rdch recording channel (optional; may be NULL) | |
3356 | * @param song song name gets copied here | |
3357 | * | |
3358 | * @retval EINVAL Operation not yet supported. | |
3359 | */ | |
3360 | static int | |
3361 | dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song) | |
3362 | { | |
3363 | return (EINVAL); | |
3364 | } | |
3365 | ||
3366 | /** | |
3367 | * @brief Retrieve name of currently played song | |
3368 | * | |
3369 | * This is a handler for the @c SNDCTL_SETSONG ioctl. Audio players could | |
3370 | * tell the system the name of the currently playing song, which would be | |
3371 | * visible in @c /dev/sndstat. | |
3372 | * | |
3373 | * See @c http://manuals.opensound.com/developer/SNDCTL_SETSONG.html | |
3374 | * for more details. | |
3375 | * | |
3376 | * @note As the ioctl definition is still under construction, FreeBSD | |
3377 | * does not currently support SNDCTL_SETSONG. | |
3378 | * | |
3379 | * @param wrch playback channel (optional; may be NULL) | |
3380 | * @param rdch recording channel (optional; may be NULL) | |
3381 | * @param song song name gets copied from here | |
3382 | * | |
3383 | * @retval EINVAL Operation not yet supported. | |
3384 | */ | |
3385 | static int | |
3386 | dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song) | |
3387 | { | |
3388 | return (EINVAL); | |
3389 | } | |
3390 | ||
3391 | /** | |
3392 | * @brief Rename a device | |
3393 | * | |
3394 | * This is a handler for the @c SNDCTL_SETNAME ioctl. | |
3395 | * | |
3396 | * See @c http://manuals.opensound.com/developer/SNDCTL_SETNAME.html for | |
3397 | * more details. | |
3398 | * | |
3399 | * From Hannu@4Front: "This call is used to change the device name | |
3400 | * reported in /dev/sndstat and ossinfo. So instead of using some generic | |
3401 | * 'OSS loopback audio (MIDI) driver' the device may be given a meaningfull | |
3402 | * name depending on the current context (for example 'OSS virtual wave table | |
3403 | * synth' or 'VoIP link to London')." | |
3404 | * | |
3405 | * @note As the ioctl definition is still under construction, FreeBSD | |
3406 | * does not currently support SNDCTL_SETNAME. | |
3407 | * | |
3408 | * @param wrch playback channel (optional; may be NULL) | |
3409 | * @param rdch recording channel (optional; may be NULL) | |
3410 | * @param name new device name gets copied from here | |
3411 | * | |
3412 | * @retval EINVAL Operation not yet supported. | |
3413 | */ | |
3414 | static int | |
3415 | dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name) | |
3416 | { | |
3417 | return (EINVAL); | |
984263bc | 3418 | } |
2a1ad637 | 3419 | #endif /* !OSSV4_EXPERIMENT */ |