| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: ossaudio.c,v 1.80 2020/04/15 15:25:33 nia Exp $ */ | | 1 | /* $NetBSD: ossaudio.c,v 1.81 2020/04/15 16:39:06 nia Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 1997, 2008 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 1997, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
| @@ -17,27 +17,27 @@ | | | @@ -17,27 +17,27 @@ |
17 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 17 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. | | 26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ | | 27 | */ |
28 | | | 28 | |
29 | #include <sys/cdefs.h> | | 29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.80 2020/04/15 15:25:33 nia Exp $"); | | 30 | __KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.81 2020/04/15 16:39:06 nia Exp $"); |
31 | | | 31 | |
32 | #include <sys/param.h> | | 32 | #include <sys/param.h> |
33 | #include <sys/proc.h> | | 33 | #include <sys/proc.h> |
34 | #include <sys/systm.h> | | 34 | #include <sys/systm.h> |
35 | #include <sys/file.h> | | 35 | #include <sys/file.h> |
36 | #include <sys/vnode.h> | | 36 | #include <sys/vnode.h> |
37 | #include <sys/filedesc.h> | | 37 | #include <sys/filedesc.h> |
38 | #include <sys/ioctl.h> | | 38 | #include <sys/ioctl.h> |
39 | #include <sys/mount.h> | | 39 | #include <sys/mount.h> |
40 | #include <sys/kernel.h> | | 40 | #include <sys/kernel.h> |
41 | #include <sys/audioio.h> | | 41 | #include <sys/audioio.h> |
42 | #include <sys/midiio.h> | | 42 | #include <sys/midiio.h> |
43 | #include <sys/kauth.h> | | 43 | #include <sys/kauth.h> |
| @@ -58,26 +58,27 @@ int ossdebug = 0; | | | @@ -58,26 +58,27 @@ int ossdebug = 0; |
58 | | | 58 | |
59 | #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) | | 59 | #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) |
60 | #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) | | 60 | #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) |
61 | | | 61 | |
62 | #define GETPRINFO(info, name) \ | | 62 | #define GETPRINFO(info, name) \ |
63 | (((info)->mode == AUMODE_RECORD) \ | | 63 | (((info)->mode == AUMODE_RECORD) \ |
64 | ? (info)->record.name : (info)->play.name) | | 64 | ? (info)->record.name : (info)->play.name) |
65 | | | 65 | |
66 | static struct audiodevinfo *getdevinfo(file_t *); | | 66 | static struct audiodevinfo *getdevinfo(file_t *); |
67 | static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq); | | 67 | static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq); |
68 | static int enum_to_ord(struct audiodevinfo *di, int enm); | | 68 | static int enum_to_ord(struct audiodevinfo *di, int enm); |
69 | static int enum_to_mask(struct audiodevinfo *di, int enm); | | 69 | static int enum_to_mask(struct audiodevinfo *di, int enm); |
70 | | | 70 | |
| | | 71 | static void setchannels(file_t *, int, int); |
71 | static void setblocksize(file_t *, struct audio_info *); | | 72 | static void setblocksize(file_t *, struct audio_info *); |
72 | | | 73 | |
73 | #ifdef AUDIO_DEBUG | | 74 | #ifdef AUDIO_DEBUG |
74 | static const char * | | 75 | static const char * |
75 | compat_ossaudio_getcmd(u_long cmd) | | 76 | compat_ossaudio_getcmd(u_long cmd) |
76 | { | | 77 | { |
77 | static char buf[64]; | | 78 | static char buf[64]; |
78 | switch (cmd) { | | 79 | switch (cmd) { |
79 | #define _DO(_a) \ | | 80 | #define _DO(_a) \ |
80 | case _a: \ | | 81 | case _a: \ |
81 | return # _a; | | 82 | return # _a; |
82 | _DO(OSS_SNDCTL_DSP_RESET) | | 83 | _DO(OSS_SNDCTL_DSP_RESET) |
83 | _DO(OSS_SNDCTL_DSP_SYNC) | | 84 | _DO(OSS_SNDCTL_DSP_SYNC) |
| @@ -477,35 +478,33 @@ oss_ioctl_audio(struct lwp *l, const str | | | @@ -477,35 +478,33 @@ oss_ioctl_audio(struct lwp *l, const str |
477 | DPRINTF(("%s: SOUND_PCM_READ_BITS %d = %d\n", | | 478 | DPRINTF(("%s: SOUND_PCM_READ_BITS %d = %d\n", |
478 | __func__, idat, error)); | | 479 | __func__, idat, error)); |
479 | goto out; | | 480 | goto out; |
480 | } | | 481 | } |
481 | break; | | 482 | break; |
482 | case OSS_SNDCTL_DSP_CHANNELS: | | 483 | case OSS_SNDCTL_DSP_CHANNELS: |
483 | AUDIO_INITINFO(&tmpinfo); | | 484 | AUDIO_INITINFO(&tmpinfo); |
484 | error = copyin(SCARG(uap, data), &idat, sizeof idat); | | 485 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
485 | if (error) { | | 486 | if (error) { |
486 | DPRINTF(("%s: SNDCTL_DSP_CHANNELS %d\n", | | 487 | DPRINTF(("%s: SNDCTL_DSP_CHANNELS %d\n", |
487 | __func__, error)); | | 488 | __func__, error)); |
488 | goto out; | | 489 | goto out; |
489 | } | | 490 | } |
490 | tmpinfo.play.channels = | | 491 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
491 | tmpinfo.record.channels = idat; | | | |
492 | DPRINTF(("%s: SNDCTL_DSP_CHANNELS > %d\n", __func__, idat)); | | | |
493 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); | | | |
494 | if (error) { | | 492 | if (error) { |
495 | DPRINTF(("%s: AUDIO_SETINFO %d\n", | | 493 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n", |
496 | __func__, error)); | | 494 | __func__, error)); |
497 | goto out; | | 495 | goto out; |
498 | } | | 496 | } |
| | | 497 | setchannels(fp, tmpinfo.mode, idat); |
499 | /* FALLTHROUGH */ | | 498 | /* FALLTHROUGH */ |
500 | case OSS_SOUND_PCM_READ_CHANNELS: | | 499 | case OSS_SOUND_PCM_READ_CHANNELS: |
501 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); | | 500 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
502 | if (error) { | | 501 | if (error) { |
503 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n", | | 502 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n", |
504 | __func__, error)); | | 503 | __func__, error)); |
505 | goto out; | | 504 | goto out; |
506 | } | | 505 | } |
507 | idat = GETPRINFO(&tmpinfo, channels); | | 506 | idat = GETPRINFO(&tmpinfo, channels); |
508 | DPRINTF(("%s: SOUND_PCM_READ_CHANNELS < %d\n", __func__, idat)); | | 507 | DPRINTF(("%s: SOUND_PCM_READ_CHANNELS < %d\n", __func__, idat)); |
509 | error = copyout(&idat, SCARG(uap, data), sizeof idat); | | 508 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
510 | if (error) { | | 509 | if (error) { |
511 | DPRINTF(("%s: SOUND_PCM_READ_CHANNELS %d = %d\n", | | 510 | DPRINTF(("%s: SOUND_PCM_READ_CHANNELS %d = %d\n", |
| @@ -1522,26 +1521,74 @@ oss_ioctl_sequencer(struct lwp *l, const | | | @@ -1522,26 +1521,74 @@ oss_ioctl_sequencer(struct lwp *l, const |
1522 | DPRINTF(("%s: Unknown sequencer command 0x%lx\n", __func__, | | 1521 | DPRINTF(("%s: Unknown sequencer command 0x%lx\n", __func__, |
1523 | com)); | | 1522 | com)); |
1524 | error = EINVAL; | | 1523 | error = EINVAL; |
1525 | goto out; | | 1524 | goto out; |
1526 | } | | 1525 | } |
1527 | | | 1526 | |
1528 | error = copyout(&idat, SCARG(uap, data), sizeof idat); | | 1527 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
1529 | out: | | 1528 | out: |
1530 | fd_putfile(SCARG(uap, fd)); | | 1529 | fd_putfile(SCARG(uap, fd)); |
1531 | return error; | | 1530 | return error; |
1532 | } | | 1531 | } |
1533 | | | 1532 | |
1534 | /* | | 1533 | /* |
| | | 1534 | * When AUDIO_SETINFO fails to set a channel count, the application's chosen |
| | | 1535 | * number is out of range of what the kernel allows. |
| | | 1536 | * |
| | | 1537 | * When this happens, we use the current hardware settings. This is just in |
| | | 1538 | * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and |
| | | 1539 | * returns a reasonable value, even if it wasn't what the user requested. |
| | | 1540 | * |
| | | 1541 | * XXX: If a device is opened for both playback and recording, and supports |
| | | 1542 | * fewer channels for recording than playback, applications that do both will |
| | | 1543 | * behave very strangely. OSS doesn't allow for reporting separate channel |
| | | 1544 | * counts for recording and playback. This could be worked around by always |
| | | 1545 | * mixing recorded data up to the same number of channels as is being used |
| | | 1546 | * for playback. |
| | | 1547 | */ |
| | | 1548 | static void |
| | | 1549 | setchannels(file_t *fp, int mode, int nchannels) |
| | | 1550 | { |
| | | 1551 | struct audio_info tmpinfo, hwfmt; |
| | | 1552 | int (*ioctlf)(file_t *, u_long, void *); |
| | | 1553 | |
| | | 1554 | ioctlf = fp->f_ops->fo_ioctl; |
| | | 1555 | |
| | | 1556 | if (ioctlf(fp, AUDIO_GETFORMAT, &hwfmt) < 0) { |
| | | 1557 | hwfmt.record.channels = hwfmt.play.channels = 2; |
| | | 1558 | } |
| | | 1559 | |
| | | 1560 | if (mode & AUMODE_PLAY) { |
| | | 1561 | AUDIO_INITINFO(&tmpinfo); |
| | | 1562 | tmpinfo.play.channels = nchannels; |
| | | 1563 | if (ioctlf(fp, AUDIO_SETINFO, &tmpinfo) < 0) { |
| | | 1564 | AUDIO_INITINFO(&tmpinfo); |
| | | 1565 | tmpinfo.play.channels = hwfmt.play.channels; |
| | | 1566 | (void)ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
| | | 1567 | } |
| | | 1568 | } |
| | | 1569 | |
| | | 1570 | if (mode & AUMODE_RECORD) { |
| | | 1571 | AUDIO_INITINFO(&tmpinfo); |
| | | 1572 | tmpinfo.record.channels = nchannels; |
| | | 1573 | if (ioctlf(fp, AUDIO_SETINFO, &tmpinfo) < 0) { |
| | | 1574 | AUDIO_INITINFO(&tmpinfo); |
| | | 1575 | tmpinfo.record.channels = hwfmt.record.channels; |
| | | 1576 | (void)ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
| | | 1577 | } |
| | | 1578 | } |
| | | 1579 | } |
| | | 1580 | |
| | | 1581 | /* |
1535 | * Check that the blocksize is a power of 2 as OSS wants. | | 1582 | * Check that the blocksize is a power of 2 as OSS wants. |
1536 | * If not, set it to be. | | 1583 | * If not, set it to be. |
1537 | */ | | 1584 | */ |
1538 | static void | | 1585 | static void |
1539 | setblocksize(file_t *fp, struct audio_info *info) | | 1586 | setblocksize(file_t *fp, struct audio_info *info) |
1540 | { | | 1587 | { |
1541 | struct audio_info set; | | 1588 | struct audio_info set; |
1542 | u_int s; | | 1589 | u_int s; |
1543 | | | 1590 | |
1544 | if (info->blocksize & (info->blocksize - 1)) { | | 1591 | if (info->blocksize & (info->blocksize - 1)) { |
1545 | for(s = 32; s < info->blocksize; s <<= 1) | | 1592 | for(s = 32; s < info->blocksize; s <<= 1) |
1546 | continue; | | 1593 | continue; |
1547 | AUDIO_INITINFO(&set); | | 1594 | AUDIO_INITINFO(&set); |