2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/soundcard.h>
28 #include <sys/ioctl.h>
33 #include <sys/types.h>
37 #include <pulse/xmalloc.h>
38 #include <pulsecore/core-error.h>
39 #include <pulsecore/core-util.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/macro.h>
45 int pa_oss_open(const char *device
, int *mode
, int* pcaps
) {
52 pa_assert(*mode
== O_RDWR
|| *mode
== O_RDONLY
|| *mode
== O_WRONLY
);
57 if (*mode
== O_RDWR
) {
58 if ((fd
= pa_open_cloexec(device
, O_RDWR
|O_NDELAY
, 0)) >= 0) {
59 ioctl(fd
, SNDCTL_DSP_SETDUPLEX
, 0);
61 if (ioctl(fd
, SNDCTL_DSP_GETCAPS
, pcaps
) < 0) {
62 pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno
));
66 if (*pcaps
& DSP_CAP_DUPLEX
)
69 pa_log_warn("'%s' doesn't support full duplex", device
);
74 if ((fd
= pa_open_cloexec(device
, (*mode
= O_WRONLY
)|O_NDELAY
, 0)) < 0) {
75 if ((fd
= pa_open_cloexec(device
, (*mode
= O_RDONLY
)|O_NDELAY
, 0)) < 0) {
76 pa_log("open('%s'): %s", device
, pa_cstrerror(errno
));
81 if ((fd
= pa_open_cloexec(device
, *mode
|O_NDELAY
, 0)) < 0) {
82 pa_log("open('%s'): %s", device
, pa_cstrerror(errno
));
89 if (ioctl(fd
, SNDCTL_DSP_GETCAPS
, pcaps
) < 0) {
90 pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno
));
96 t
= pa_sprintf_malloc(
97 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
98 *pcaps
& DSP_CAP_BATCH
? " BATCH" : "",
100 *pcaps
& DSP_CAP_BIND
? " BIND" : "",
104 *pcaps
& DSP_CAP_COPROC
? " COPROC" : "",
105 *pcaps
& DSP_CAP_DUPLEX
? " DUPLEX" : "",
106 #ifdef DSP_CAP_FREERATE
107 *pcaps
& DSP_CAP_FREERATE
? " FREERATE" : "",
112 *pcaps
& DSP_CAP_INPUT
? " INPUT" : "",
116 *pcaps
& DSP_CAP_MMAP
? " MMAP" : "",
118 *pcaps
& DSP_CAP_MODEM
? " MODEM" : "",
123 *pcaps
& DSP_CAP_MULTI
? " MULTI" : "",
127 #ifdef DSP_CAP_OUTPUT
128 *pcaps
& DSP_CAP_OUTPUT
? " OUTPUT" : "",
132 *pcaps
& DSP_CAP_REALTIME
? " REALTIME" : "",
133 #ifdef DSP_CAP_SHADOW
134 *pcaps
& DSP_CAP_SHADOW
? " SHADOW" : "",
138 #ifdef DSP_CAP_VIRTUAL
139 *pcaps
& DSP_CAP_VIRTUAL
? " VIRTUAL" : "",
143 *pcaps
& DSP_CAP_TRIGGER
? " TRIGGER" : "");
145 pa_log_debug("capabilities:%s", t
);
156 int pa_oss_auto_format(int fd
, pa_sample_spec
*ss
) {
157 int format
, channels
, speed
, reqformat
;
158 pa_sample_format_t orig_format
;
160 static const int format_trans
[PA_SAMPLE_MAX
] = {
161 [PA_SAMPLE_U8
] = AFMT_U8
,
162 [PA_SAMPLE_ALAW
] = AFMT_A_LAW
,
163 [PA_SAMPLE_ULAW
] = AFMT_MU_LAW
,
164 [PA_SAMPLE_S16LE
] = AFMT_S16_LE
,
165 [PA_SAMPLE_S16BE
] = AFMT_S16_BE
,
166 [PA_SAMPLE_FLOAT32LE
] = AFMT_QUERY
, /* not supported */
167 [PA_SAMPLE_FLOAT32BE
] = AFMT_QUERY
, /* not supported */
168 [PA_SAMPLE_S32LE
] = AFMT_QUERY
, /* not supported */
169 [PA_SAMPLE_S32BE
] = AFMT_QUERY
, /* not supported */
170 [PA_SAMPLE_S24LE
] = AFMT_QUERY
, /* not supported */
171 [PA_SAMPLE_S24BE
] = AFMT_QUERY
, /* not supported */
172 [PA_SAMPLE_S24_32LE
] = AFMT_QUERY
, /* not supported */
173 [PA_SAMPLE_S24_32BE
] = AFMT_QUERY
, /* not supported */
179 orig_format
= ss
->format
;
181 reqformat
= format
= format_trans
[ss
->format
];
182 if (reqformat
== AFMT_QUERY
|| ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= reqformat
) {
183 format
= AFMT_S16_NE
;
184 if (ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= AFMT_S16_NE
) {
185 int f
= AFMT_S16_NE
== AFMT_S16_LE
? AFMT_S16_BE
: AFMT_S16_LE
;
187 if (ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= f
) {
189 if (ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= AFMT_U8
) {
190 pa_log("SNDCTL_DSP_SETFMT: %s", format
!= AFMT_U8
? "No supported sample format" : pa_cstrerror(errno
));
193 ss
->format
= PA_SAMPLE_U8
;
195 ss
->format
= f
== AFMT_S16_LE
? PA_SAMPLE_S16LE
: PA_SAMPLE_S16BE
;
197 ss
->format
= PA_SAMPLE_S16NE
;
200 if (orig_format
!= ss
->format
)
201 pa_log_warn("device doesn't support sample format %s, changed to %s.",
202 pa_sample_format_to_string(orig_format
),
203 pa_sample_format_to_string(ss
->format
));
205 channels
= ss
->channels
;
206 if (ioctl(fd
, SNDCTL_DSP_CHANNELS
, &channels
) < 0) {
207 pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno
));
210 pa_assert(channels
> 0);
212 if (ss
->channels
!= channels
) {
213 pa_log_warn("device doesn't support %i channels, using %i channels.", ss
->channels
, channels
);
214 ss
->channels
= (uint8_t) channels
;
217 speed
= (int) ss
->rate
;
218 if (ioctl(fd
, SNDCTL_DSP_SPEED
, &speed
) < 0) {
219 pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno
));
222 pa_assert(speed
> 0);
224 if (ss
->rate
!= (unsigned) speed
) {
225 pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss
->rate
, speed
);
227 /* If the sample rate deviates too much, we need to resample */
228 if (speed
< ss
->rate
*.95 || speed
> ss
->rate
*1.05)
229 ss
->rate
= (uint32_t) speed
;
235 static int simple_log2(int v
) {
247 int pa_oss_set_fragments(int fd
, int nfrags
, int frag_size
) {
249 arg
= ((int) nfrags
<< 16) | simple_log2(frag_size
);
251 pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags
, 1 << simple_log2(frag_size
), frag_size
);
253 if (ioctl(fd
, SNDCTL_DSP_SETFRAGMENT
, &arg
) < 0) {
254 pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno
));
261 int pa_oss_get_volume(int fd
, unsigned long mixer
, const pa_sample_spec
*ss
, pa_cvolume
*volume
) {
262 char cv
[PA_CVOLUME_SNPRINT_MAX
];
269 if (ioctl(fd
, mixer
, &vol
) < 0)
272 pa_cvolume_reset(volume
, ss
->channels
);
274 volume
->values
[0] = ((vol
& 0xFF) * PA_VOLUME_NORM
) / 100;
276 if (volume
->channels
>= 2)
277 volume
->values
[1] = (((vol
>> 8) & 0xFF) * PA_VOLUME_NORM
) / 100;
279 pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv
, sizeof(cv
), volume
));
283 int pa_oss_set_volume(int fd
, unsigned long mixer
, const pa_sample_spec
*ss
, const pa_cvolume
*volume
) {
284 char cv
[PA_CVOLUME_SNPRINT_MAX
];
288 l
= volume
->values
[0] > PA_VOLUME_NORM
? PA_VOLUME_NORM
: volume
->values
[0];
290 vol
= (l
*100)/PA_VOLUME_NORM
;
292 if (ss
->channels
>= 2) {
293 r
= volume
->values
[1] > PA_VOLUME_NORM
? PA_VOLUME_NORM
: volume
->values
[1];
294 vol
|= ((r
*100)/PA_VOLUME_NORM
) << 8;
297 if (ioctl(fd
, mixer
, &vol
) < 0)
300 pa_log_debug("Wrote mixer settings: %s", pa_cvolume_snprint(cv
, sizeof(cv
), volume
));
304 static int get_device_number(const char *dev
) {
309 if (!(p
= rp
= pa_readlink(dev
))) {
311 if (errno
!= EINVAL
&& errno
!= ENOLINK
) {
313 if (errno
!= EINVAL
) {
322 if ((e
= strrchr(p
, '/')))
332 if (*p
>= '0' && *p
<= '9') {
344 int pa_oss_get_hw_description(const char *dev
, char *name
, size_t l
) {
349 if ((n
= get_device_number(dev
)) < 0)
352 if (!(f
= pa_fopen_cloexec("/dev/sndstat", "r")) &&
353 !(f
= pa_fopen_cloexec("/proc/sndstat", "r")) &&
354 !(f
= pa_fopen_cloexec("/proc/asound/oss/sndstat", "r"))) {
357 pa_log_warn("failed to open OSS sndstat device: %s", pa_cstrerror(errno
));
366 if (!fgets(line
, sizeof(line
), f
))
369 line
[strcspn(line
, "\r\n")] = 0;
372 b
= strcmp(line
, "Audio devices:") == 0;
379 if (sscanf(line
, "%i: ", &device
) != 1)
383 char *k
= strchr(line
, ':');
388 if (pa_endswith(k
, " (DUPLEX)"))
391 pa_strlcpy(name
, k
, l
);
401 static int open_mixer(const char *mixer
) {
404 if ((fd
= pa_open_cloexec(mixer
, O_RDWR
|O_NDELAY
, 0)) >= 0)
410 int pa_oss_open_mixer_for_device(const char *device
) {
415 if ((n
= get_device_number(device
)) < 0)
419 if ((fd
= open_mixer("/dev/mixer")) >= 0)
422 fn
= pa_sprintf_malloc("/dev/mixer%i", n
);
427 pa_log_warn("Failed to open mixer '%s': %s", device
, pa_cstrerror(errno
));