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
= open(device
, O_RDWR
|O_NDELAY
|O_NOCTTY
)) >= 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
= open(device
, (*mode
= O_WRONLY
)|O_NDELAY
|O_NOCTTY
)) < 0) {
75 if ((fd
= open(device
, (*mode
= O_RDONLY
)|O_NDELAY
|O_NOCTTY
)) < 0) {
76 pa_log("open('%s'): %s", device
, pa_cstrerror(errno
));
81 if ((fd
= open(device
, *mode
|O_NDELAY
|O_NOCTTY
)) < 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
);
148 pa_make_fd_cloexec(fd
);
158 int pa_oss_auto_format(int fd
, pa_sample_spec
*ss
) {
159 int format
, channels
, speed
, reqformat
;
160 pa_sample_format_t orig_format
;
162 static const int format_trans
[PA_SAMPLE_MAX
] = {
163 [PA_SAMPLE_U8
] = AFMT_U8
,
164 [PA_SAMPLE_ALAW
] = AFMT_A_LAW
,
165 [PA_SAMPLE_ULAW
] = AFMT_MU_LAW
,
166 [PA_SAMPLE_S16LE
] = AFMT_S16_LE
,
167 [PA_SAMPLE_S16BE
] = AFMT_S16_BE
,
168 [PA_SAMPLE_FLOAT32LE
] = AFMT_QUERY
, /* not supported */
169 [PA_SAMPLE_FLOAT32BE
] = AFMT_QUERY
, /* not supported */
170 [PA_SAMPLE_S32LE
] = AFMT_QUERY
, /* not supported */
171 [PA_SAMPLE_S32BE
] = AFMT_QUERY
, /* not supported */
172 [PA_SAMPLE_S24LE
] = AFMT_QUERY
, /* not supported */
173 [PA_SAMPLE_S24BE
] = AFMT_QUERY
, /* not supported */
174 [PA_SAMPLE_S24_32LE
] = AFMT_QUERY
, /* not supported */
175 [PA_SAMPLE_S24_32BE
] = AFMT_QUERY
, /* not supported */
181 orig_format
= ss
->format
;
183 reqformat
= format
= format_trans
[ss
->format
];
184 if (reqformat
== AFMT_QUERY
|| ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= reqformat
) {
185 format
= AFMT_S16_NE
;
186 if (ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= AFMT_S16_NE
) {
187 int f
= AFMT_S16_NE
== AFMT_S16_LE
? AFMT_S16_BE
: AFMT_S16_LE
;
189 if (ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= f
) {
191 if (ioctl(fd
, SNDCTL_DSP_SETFMT
, &format
) < 0 || format
!= AFMT_U8
) {
192 pa_log("SNDCTL_DSP_SETFMT: %s", format
!= AFMT_U8
? "No supported sample format" : pa_cstrerror(errno
));
195 ss
->format
= PA_SAMPLE_U8
;
197 ss
->format
= f
== AFMT_S16_LE
? PA_SAMPLE_S16LE
: PA_SAMPLE_S16BE
;
199 ss
->format
= PA_SAMPLE_S16NE
;
202 if (orig_format
!= ss
->format
)
203 pa_log_warn("device doesn't support sample format %s, changed to %s.",
204 pa_sample_format_to_string(orig_format
),
205 pa_sample_format_to_string(ss
->format
));
207 channels
= ss
->channels
;
208 if (ioctl(fd
, SNDCTL_DSP_CHANNELS
, &channels
) < 0) {
209 pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno
));
212 pa_assert(channels
> 0);
214 if (ss
->channels
!= channels
) {
215 pa_log_warn("device doesn't support %i channels, using %i channels.", ss
->channels
, channels
);
216 ss
->channels
= (uint8_t) channels
;
219 speed
= (int) ss
->rate
;
220 if (ioctl(fd
, SNDCTL_DSP_SPEED
, &speed
) < 0) {
221 pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno
));
224 pa_assert(speed
> 0);
226 if (ss
->rate
!= (unsigned) speed
) {
227 pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss
->rate
, speed
);
229 /* If the sample rate deviates too much, we need to resample */
230 if (speed
< ss
->rate
*.95 || speed
> ss
->rate
*1.05)
231 ss
->rate
= (uint32_t) speed
;
237 static int simple_log2(int v
) {
249 int pa_oss_set_fragments(int fd
, int nfrags
, int frag_size
) {
251 arg
= ((int) nfrags
<< 16) | simple_log2(frag_size
);
253 pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags
, 1 << simple_log2(frag_size
), frag_size
);
255 if (ioctl(fd
, SNDCTL_DSP_SETFRAGMENT
, &arg
) < 0) {
256 pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno
));
263 int pa_oss_get_volume(int fd
, unsigned long mixer
, const pa_sample_spec
*ss
, pa_cvolume
*volume
) {
264 char cv
[PA_CVOLUME_SNPRINT_MAX
];
271 if (ioctl(fd
, mixer
, &vol
) < 0)
274 pa_cvolume_reset(volume
, ss
->channels
);
276 volume
->values
[0] = ((vol
& 0xFF) * PA_VOLUME_NORM
) / 100;
278 if (volume
->channels
>= 2)
279 volume
->values
[1] = (((vol
>> 8) & 0xFF) * PA_VOLUME_NORM
) / 100;
281 pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv
, sizeof(cv
), volume
));
285 int pa_oss_set_volume(int fd
, unsigned long mixer
, const pa_sample_spec
*ss
, const pa_cvolume
*volume
) {
286 char cv
[PA_CVOLUME_SNPRINT_MAX
];
290 l
= volume
->values
[0] > PA_VOLUME_NORM
? PA_VOLUME_NORM
: volume
->values
[0];
292 vol
= (l
*100)/PA_VOLUME_NORM
;
294 if (ss
->channels
>= 2) {
295 r
= volume
->values
[1] > PA_VOLUME_NORM
? PA_VOLUME_NORM
: volume
->values
[1];
296 vol
|= ((r
*100)/PA_VOLUME_NORM
) << 8;
299 if (ioctl(fd
, mixer
, &vol
) < 0)
302 pa_log_debug("Wrote mixer settings: %s", pa_cvolume_snprint(cv
, sizeof(cv
), volume
));
306 static int get_device_number(const char *dev
) {
311 if (!(p
= rp
= pa_readlink(dev
))) {
313 if (errno
!= EINVAL
&& errno
!= ENOLINK
) {
315 if (errno
!= EINVAL
) {
324 if ((e
= strrchr(p
, '/')))
334 if (*p
>= '0' && *p
<= '9') {
346 int pa_oss_get_hw_description(const char *dev
, char *name
, size_t l
) {
351 if ((n
= get_device_number(dev
)) < 0)
354 if (!(f
= fopen("/dev/sndstat", "r")) &&
355 !(f
= fopen("/proc/sndstat", "r")) &&
356 !(f
= fopen("/proc/asound/oss/sndstat", "r"))) {
359 pa_log_warn("failed to open OSS sndstat device: %s", pa_cstrerror(errno
));
368 if (!fgets(line
, sizeof(line
), f
))
371 line
[strcspn(line
, "\r\n")] = 0;
374 b
= strcmp(line
, "Audio devices:") == 0;
381 if (sscanf(line
, "%i: ", &device
) != 1)
385 char *k
= strchr(line
, ':');
390 if (pa_endswith(k
, " (DUPLEX)"))
393 pa_strlcpy(name
, k
, l
);
403 static int open_mixer(const char *mixer
) {
406 if ((fd
= open(mixer
, O_RDWR
|O_NDELAY
|O_NOCTTY
)) >= 0)
412 int pa_oss_open_mixer_for_device(const char *device
) {
417 if ((n
= get_device_number(device
)) < 0)
421 if ((fd
= open_mixer("/dev/mixer")) >= 0)
424 fn
= pa_sprintf_malloc("/dev/mixer%i", n
);
429 pa_log_warn("Failed to open mixer '%s': %s", device
, pa_cstrerror(errno
));