"Fossies" - the Fresh Open Source Software Archive 
Member "alsa-oss-1.1.8/alsa/pcm.c" (7 Jan 2019, 43375 Bytes) of package /linux/misc/alsa-oss-1.1.8.tar.bz2:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "pcm.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
1.0.28_vs_1.1.6.
1 /*
2 * OSS -> ALSA compatibility layer
3 * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>,
4 * Jaroslav Kysela <perex@perex.cz>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23 #define _GNU_SOURCE
24
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <sys/stat.h>
28 #include <sys/poll.h>
29 #include <sys/select.h>
30 #include <sys/mman.h>
31 #include <stdarg.h>
32 #include <unistd.h>
33 #include <dlfcn.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <linux/soundcard.h>
40 #include <alsa/asoundlib.h>
41
42 #include "alsa-local.h"
43
44 int alsa_oss_debug = 0;
45 snd_output_t *alsa_oss_debug_out = NULL;
46
47 typedef struct {
48 snd_pcm_t *pcm;
49 snd_pcm_sw_params_t *sw_params;
50 size_t frame_bytes;
51 struct {
52 snd_pcm_uframes_t period_size;
53 snd_pcm_uframes_t buffer_size;
54 snd_pcm_uframes_t boundary;
55 snd_pcm_uframes_t appl_ptr;
56 snd_pcm_uframes_t old_hw_ptr;
57 size_t mmap_buffer_bytes;
58 size_t mmap_period_bytes;
59 } alsa;
60 struct {
61 snd_pcm_uframes_t period_size;
62 unsigned int periods;
63 snd_pcm_uframes_t buffer_size;
64 size_t bytes;
65 size_t hw_bytes;
66 size_t boundary;
67 } oss;
68 unsigned int stopped:1;
69 void *mmap_buffer;
70 size_t mmap_bytes;
71 snd_pcm_channel_area_t *mmap_areas;
72 snd_pcm_uframes_t mmap_advance;
73 } oss_dsp_stream_t;
74
75 typedef struct {
76 int hwset;
77 unsigned int channels;
78 unsigned int rate;
79 unsigned int oss_format;
80 snd_pcm_format_t format;
81 unsigned int fragshift;
82 unsigned int maxfrags;
83 unsigned int subdivision;
84 oss_dsp_stream_t streams[2];
85 } oss_dsp_t;
86
87 typedef struct fd {
88 int fileno;
89 oss_dsp_t *dsp;
90 void *mmap_area;
91 struct fd *next;
92 } fd_t;
93
94 static fd_t *pcm_fds = NULL;
95
96
97 static fd_t *look_for_fd(int fd)
98 {
99 fd_t *result = pcm_fds;
100 while (result) {
101 if (result->fileno == fd)
102 return result;
103 result = result->next;
104 }
105 return NULL;
106 }
107
108 static inline oss_dsp_t *look_for_dsp(int fd)
109 {
110 fd_t *xfd = look_for_fd(fd);
111 return xfd ? xfd->dsp : NULL;
112 }
113
114 static inline oss_dsp_t *look_for_mmap_addr(void * addr)
115 {
116 fd_t *result = pcm_fds;
117 while (result) {
118 if (result->mmap_area == addr)
119 return result->dsp ? result->dsp : NULL;
120 result = result->next;
121 }
122 return NULL;
123 }
124
125 static void insert_fd(fd_t *xfd)
126 {
127 xfd->next = pcm_fds;
128 pcm_fds = xfd;
129 }
130
131 static void remove_fd(fd_t *xfd)
132 {
133 fd_t *result = pcm_fds, *prev = NULL;
134 while (result) {
135 if (result == xfd) {
136 if (prev == NULL)
137 pcm_fds = xfd->next;
138 else
139 prev->next = xfd->next;
140 return;
141 }
142 prev = result;
143 result = result->next;
144 }
145 assert(0);
146 }
147
148 static unsigned int ld2(u_int32_t v)
149 {
150 unsigned r = 0;
151
152 if (v >= 0x10000) {
153 v >>= 16;
154 r += 16;
155 }
156 if (v >= 0x100) {
157 v >>= 8;
158 r += 8;
159 }
160 if (v >= 0x10) {
161 v >>= 4;
162 r += 4;
163 }
164 if (v >= 4) {
165 v >>= 2;
166 r += 2;
167 }
168 if (v >= 2)
169 r++;
170 return r;
171 }
172
173 static snd_pcm_format_t oss_format_to_alsa(int format)
174 {
175 switch (format) {
176 case AFMT_MU_LAW: return SND_PCM_FORMAT_MU_LAW;
177 case AFMT_A_LAW: return SND_PCM_FORMAT_A_LAW;
178 case AFMT_IMA_ADPCM: return SND_PCM_FORMAT_IMA_ADPCM;
179 case AFMT_U8: return SND_PCM_FORMAT_U8;
180 case AFMT_S16_LE: return SND_PCM_FORMAT_S16_LE;
181 case AFMT_S16_BE: return SND_PCM_FORMAT_S16_BE;
182 case AFMT_S8: return SND_PCM_FORMAT_S8;
183 case AFMT_U16_LE: return SND_PCM_FORMAT_U16_LE;
184 case AFMT_U16_BE: return SND_PCM_FORMAT_U16_BE;
185 case AFMT_MPEG: return SND_PCM_FORMAT_MPEG;
186 default: return SND_PCM_FORMAT_U8;
187 }
188 }
189
190 static int alsa_format_to_oss(snd_pcm_format_t format)
191 {
192 switch (format) {
193 case SND_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
194 case SND_PCM_FORMAT_A_LAW: return AFMT_A_LAW;
195 case SND_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;
196 case SND_PCM_FORMAT_U8: return AFMT_U8;
197 case SND_PCM_FORMAT_S16_LE: return AFMT_S16_LE;
198 case SND_PCM_FORMAT_S16_BE: return AFMT_S16_BE;
199 case SND_PCM_FORMAT_S8: return AFMT_S8;
200 case SND_PCM_FORMAT_U16_LE: return AFMT_U16_LE;
201 case SND_PCM_FORMAT_U16_BE: return AFMT_U16_BE;
202 case SND_PCM_FORMAT_MPEG: return AFMT_MPEG;
203 default: return -EINVAL;
204 }
205 }
206
207 static int oss_dsp_hw_params(oss_dsp_t *dsp)
208 {
209 int k;
210 for (k = 1; k >= 0; --k) {
211 oss_dsp_stream_t *str = &dsp->streams[k];
212 snd_pcm_t *pcm = str->pcm;
213 snd_pcm_hw_params_t *hw;
214 int err;
215 unsigned int rate, periods_min;
216 if (!pcm)
217 continue;
218 dsp->format = oss_format_to_alsa(dsp->oss_format);
219 str->frame_bytes = snd_pcm_format_physical_width(dsp->format) * dsp->channels / 8;
220 snd_pcm_hw_params_alloca(&hw);
221 snd_pcm_hw_params_any(pcm, hw);
222
223 err = snd_pcm_hw_params_set_format(pcm, hw, dsp->format);
224 if (err < 0)
225 return err;
226 err = snd_pcm_hw_params_set_channels(pcm, hw, dsp->channels);
227 if (err < 0)
228 return err;
229 rate = dsp->rate;
230 err = snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, 0);
231 if (err < 0)
232 return err;
233 #if 0
234 err = snd_pcm_hw_params_set_periods_integer(pcm, hw);
235 if (err < 0)
236 return err;
237 #endif
238
239 if (str->mmap_buffer) {
240 snd_pcm_uframes_t size;
241 snd_pcm_access_mask_t *mask;
242 snd_pcm_access_mask_alloca(&mask);
243 snd_pcm_access_mask_any(mask);
244 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
245 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
246 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
247 err = snd_pcm_hw_params_set_access_mask(pcm, hw, mask);
248 if (err < 0)
249 return err;
250 size = str->alsa.mmap_period_bytes / str->frame_bytes;
251 err = snd_pcm_hw_params_set_period_size_near(pcm, hw, &size, NULL);
252 if (err < 0)
253 return err;
254 size = str->alsa.mmap_buffer_bytes / str->frame_bytes;
255 err = snd_pcm_hw_params_set_buffer_size_near(pcm, hw, &size);
256 if (err < 0)
257 return err;
258 err = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED);
259 if (err < 0)
260 return err;
261 } else {
262 err = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
263 if (err < 0)
264 return err;
265 periods_min = 2;
266 if (!dsp->maxfrags) {
267 err = snd_pcm_hw_params_set_periods_min(pcm, hw, &periods_min, 0);
268 if (err < 0)
269 return err;
270 } else {
271 unsigned int periods_max = periods_min > dsp->maxfrags
272 ? periods_min : dsp->maxfrags;
273 err = snd_pcm_hw_params_set_periods_max(pcm, hw,
274 &periods_max, 0);
275 if (err < 0)
276 return err;
277 }
278 if (dsp->fragshift > 0) {
279 snd_pcm_uframes_t s = (1 << dsp->fragshift) / str->frame_bytes;
280 s *= 16;
281 while (s >= 1024 && (err = snd_pcm_hw_params_set_buffer_size(pcm, hw, s)) < 0)
282 s /= 2;
283 s = (1 << dsp->fragshift) / str->frame_bytes;
284 while (s >= 256 && (err = snd_pcm_hw_params_set_period_size(pcm, hw, s, 0)) < 0)
285 s /= 2;
286 if (err < 0) {
287 s = (1 << dsp->fragshift) / str->frame_bytes;
288 err = snd_pcm_hw_params_set_period_size_near(pcm, hw, &s, 0);
289 }
290 } else {
291 snd_pcm_uframes_t s = 16, old_s;
292 while (s * 2 < dsp->rate / 2)
293 s *= 2;
294 old_s = s = s / 2;
295 while (s >= 1024 && (err = snd_pcm_hw_params_set_buffer_size(pcm, hw, s)) < 0)
296 s /= 2;
297 s = old_s;
298 while (s >= 256 && (err = snd_pcm_hw_params_set_period_size(pcm, hw, s, 0)) < 0)
299 s /= 2;
300 if (err < 0) {
301 s = old_s;
302 err = snd_pcm_hw_params_set_period_size_near(pcm, hw, &s, 0);
303 }
304 }
305 if (err < 0)
306 return err;
307 }
308 err = snd_pcm_hw_params(pcm, hw);
309 if (err < 0)
310 return err;
311 #if 0
312 if (alsa_oss_debug && alsa_oss_debug_out)
313 snd_pcm_dump_setup(pcm, alsa_oss_debug_out);
314 #endif
315 if (err < 0)
316 return err;
317 dsp->oss_format = alsa_format_to_oss(dsp->format);
318 err = snd_pcm_hw_params_get_period_size(hw, &str->alsa.period_size, 0);
319 if (err < 0)
320 return err;
321 err = snd_pcm_hw_params_get_buffer_size(hw, &str->alsa.buffer_size);
322 if (err < 0)
323 return err;
324 if (str->mmap_buffer == NULL) {
325 str->oss.buffer_size = 1 << ld2(str->alsa.buffer_size);
326 if (str->oss.buffer_size < str->alsa.buffer_size)
327 str->oss.buffer_size *= 2;
328 str->oss.period_size = 1 << ld2(str->alsa.period_size);
329 if (str->oss.period_size < str->alsa.period_size)
330 str->oss.period_size *= 2;
331 } else {
332 str->oss.buffer_size = str->alsa.mmap_period_bytes / str->frame_bytes;
333 str->oss.period_size = str->alsa.mmap_buffer_bytes / str->frame_bytes;
334 }
335 str->oss.periods = str->oss.buffer_size / str->oss.period_size;
336 if (str->mmap_areas)
337 free(str->mmap_areas);
338 str->mmap_areas = NULL;
339 if (str->mmap_buffer) {
340 unsigned int c;
341 snd_pcm_channel_area_t *a;
342 unsigned int bits_per_sample, bits_per_frame;
343 str->mmap_areas = calloc(dsp->channels, sizeof(*str->mmap_areas));
344 if (!str->mmap_areas)
345 return -ENOMEM;
346 bits_per_sample = snd_pcm_format_physical_width(dsp->format);
347 bits_per_frame = bits_per_sample * dsp->channels;
348 a = str->mmap_areas;
349 for (c = 0; c < dsp->channels; c++, a++) {
350 a->addr = str->mmap_buffer;
351 a->first = bits_per_sample * c;
352 a->step = bits_per_frame;
353 }
354 }
355 str->oss.hw_bytes = 0;
356 str->oss.boundary = (0x3fffffff / str->oss.buffer_size) * str->oss.buffer_size;
357 str->alsa.appl_ptr = 0;
358 str->alsa.old_hw_ptr = 0;
359 str->mmap_advance = str->oss.period_size;
360 }
361 return 0;
362 }
363
364 static int oss_dsp_sw_params(oss_dsp_t *dsp)
365 {
366 int k;
367 for (k = 1; k >= 0; --k) {
368 oss_dsp_stream_t *str = &dsp->streams[k];
369 snd_pcm_t *pcm = str->pcm;
370 snd_pcm_sw_params_t *sw;
371 int err;
372 if (!pcm)
373 continue;
374 sw = str->sw_params;
375 snd_pcm_sw_params_current(pcm, sw);
376 snd_pcm_sw_params_set_xfer_align(pcm, sw, 1);
377 snd_pcm_sw_params_set_start_threshold(pcm, sw,
378 str->stopped ? str->alsa.buffer_size + 1 :
379 str->alsa.period_size);
380 #if 1
381 snd_pcm_sw_params_set_stop_threshold(pcm, sw,
382 str->mmap_buffer ? LONG_MAX :
383 str->alsa.buffer_size);
384 #else
385 snd_pcm_sw_params_set_stop_threshold(pcm, sw,
386 LONG_MAX);
387 snd_pcm_sw_params_set_silence_threshold(pcm, sw,
388 str->alsa.period_size);
389 snd_pcm_sw_params_set_silence_size(pcm, sw,
390 str->alsa.period_size);
391 #endif
392 err = snd_pcm_sw_params(pcm, sw);
393 if (err < 0)
394 return err;
395 err = snd_pcm_sw_params_current(pcm, sw);
396 if (err < 0)
397 return err;
398 err = snd_pcm_sw_params_get_boundary(sw, &str->alsa.boundary);
399 if (err < 0)
400 return err;
401 }
402 return 0;
403 }
404
405 static int oss_dsp_params(oss_dsp_t *dsp)
406 {
407 int err;
408 dsp->hwset = 0;
409 err = oss_dsp_hw_params(dsp);
410 if (err < 0)
411 return err;
412 dsp->hwset = 1;
413 err = oss_dsp_sw_params(dsp);
414 if (err < 0)
415 return err;
416 #if 0
417 if (alsa_oss_debug && alsa_oss_debug_out) {
418 int k;
419 for (k = 1; k >= 0; --k) {
420 oss_dsp_stream_t *str = &dsp->streams[k];
421 if (str->pcm)
422 snd_pcm_dump(str->pcm, alsa_oss_debug_out);
423 }
424 }
425 #endif
426 return 0;
427 }
428
429 int lib_oss_pcm_close(int fd)
430 {
431 int result = 0;
432 int k;
433 fd_t *xfd = look_for_fd(fd);
434 oss_dsp_t *dsp;
435
436 if (xfd == NULL) {
437 errno = ENOENT;
438 return -1;
439 }
440 dsp = xfd->dsp;
441 for (k = 0; k < 2; ++k) {
442 oss_dsp_stream_t *str = &dsp->streams[k];
443 if (str->sw_params)
444 snd_pcm_sw_params_free(str->sw_params);
445 }
446 for (k = 0; k < 2; ++k) {
447 int err;
448 oss_dsp_stream_t *str = &dsp->streams[k];
449 if (!str->pcm)
450 continue;
451 if (k == SND_PCM_STREAM_PLAYBACK) {
452 if (snd_pcm_state(str->pcm) != SND_PCM_STATE_OPEN)
453 snd_pcm_drain(str->pcm);
454 }
455 err = snd_pcm_close(str->pcm);
456 if (err < 0)
457 result = err;
458 }
459 remove_fd(xfd);
460 free(dsp);
461 free(xfd);
462 if (result < 0) {
463 errno = -result;
464 result = -1;
465 }
466 close(fd);
467 DEBUG("close(%d) -> %d", fd, result);
468 if (result < 0)
469 DEBUG("(errno=%d)\n", errno);
470 else
471 DEBUG("\n");
472 return 0;
473 }
474
475 static int open_pcm(oss_dsp_t *dsp, const char *name, unsigned int pcm_mode,
476 unsigned int streams)
477 {
478 int k, result;
479
480 result = -ENODEV;
481 for (k = 0; k < 2; ++k) {
482 if (!(streams & (1 << k)))
483 continue;
484 result = snd_pcm_open(&dsp->streams[k].pcm, name, k, SND_PCM_NONBLOCK);
485 DEBUG("Opened PCM %s for stream %d (result = %d)\n", name, k, result);
486 if (result < 0) {
487 if (k == 1 && dsp->streams[0].pcm != NULL) {
488 dsp->streams[1].pcm = NULL;
489 streams &= ~(1 << SND_PCM_STREAM_CAPTURE);
490 result = 0;
491 }
492 break;
493 } else if (! pcm_mode)
494 /* reset the blocking mode */
495 snd_pcm_nonblock(dsp->streams[k].pcm, 0);
496 }
497 return result;
498 }
499
500 static int oss_dsp_open(int card, int device, int oflag, mode_t mode ATTRIBUTE_UNUSED)
501 {
502 oss_dsp_t *dsp;
503 unsigned int pcm_mode = 0;
504 unsigned int streams, k;
505 int format = AFMT_MU_LAW;
506 int fd = -1;
507 fd_t *xfd;
508 int result;
509 char name[64];
510
511 char *s = getenv("ALSA_OSS_DEBUG");
512 if (s) {
513 alsa_oss_debug = 1;
514 if (alsa_oss_debug_out == NULL) {
515 if (snd_output_stdio_attach(&alsa_oss_debug_out, stderr, 0) < 0)
516 alsa_oss_debug_out = NULL;
517 }
518 }
519 switch (device) {
520 case OSS_DEVICE_DSP:
521 format = AFMT_U8;
522 sprintf(name, "dsp%d", card);
523 break;
524 case OSS_DEVICE_DSPW:
525 format = AFMT_S16_LE;
526 sprintf(name, "dspW%d", card);
527 break;
528 case OSS_DEVICE_AUDIO:
529 sprintf(name, "audio%d", card);
530 break;
531 case OSS_DEVICE_ADSP:
532 sprintf(name, "adsp%d", card);
533 break;
534 default:
535 errno = ENOENT;
536 return -1;
537 }
538 if (oflag & O_NONBLOCK)
539 pcm_mode = SND_PCM_NONBLOCK;
540 switch (oflag & O_ACCMODE) {
541 case O_RDONLY:
542 streams = 1 << SND_PCM_STREAM_CAPTURE;
543 break;
544 case O_WRONLY:
545 streams = 1 << SND_PCM_STREAM_PLAYBACK;
546 break;
547 case O_RDWR:
548 streams = ((1 << SND_PCM_STREAM_PLAYBACK) |
549 (1 << SND_PCM_STREAM_CAPTURE));
550 break;
551 default:
552 errno = EINVAL;
553 return -1;
554 }
555 fd = open("/dev/null", oflag & O_ACCMODE);
556 if (fd < 0)
557 return -1;
558 xfd = calloc(1, sizeof(fd_t));
559 if (!xfd) {
560 close(fd);
561 errno = ENOMEM;
562 return -1;
563 }
564 dsp = calloc(1, sizeof(oss_dsp_t));
565 if (!dsp) {
566 result = -ENOMEM;
567 goto _error;
568 }
569 xfd->dsp = dsp;
570 dsp->channels = 1;
571 dsp->rate = 8000;
572 dsp->oss_format = format;
573 result = -EINVAL;
574 for (k = 0; k < 2; ++k) {
575 if (!(streams & (1 << k)))
576 continue;
577 result = snd_pcm_sw_params_malloc(&dsp->streams[k].sw_params);
578 if (result < 0)
579 goto _error;
580 }
581 s = getenv("ALSA_OSS_PCM_DEVICE");
582 result = -ENODEV;
583 if (s && *s)
584 result = open_pcm(dsp, s, pcm_mode, streams);
585 if (result < 0)
586 result = open_pcm(dsp, name, pcm_mode, streams);
587 if (result < 0) {
588 /* try to open the default pcm as fallback */
589 if (card == 0 && (device == OSS_DEVICE_DSP || device == OSS_DEVICE_AUDIO))
590 strcpy(name, "default");
591 else
592 sprintf(name, "default:%d", card);
593 result = open_pcm(dsp, name, pcm_mode, streams);
594 if (result < 0)
595 goto _error;
596 }
597 result = oss_dsp_params(dsp);
598 if (result < 0) {
599 DEBUG("Error setting params\n");
600 goto _error;
601 }
602 xfd->fileno = fd;
603 insert_fd(xfd);
604 return fd;
605
606 _error:
607 for (k = 0; k < 2; ++k) {
608 if (dsp->streams[k].pcm)
609 snd_pcm_close(dsp->streams[k].pcm);
610 if (dsp->streams[k].sw_params)
611 snd_pcm_sw_params_free(dsp->streams[k].sw_params);
612 }
613 close(fd);
614 if (xfd->dsp)
615 free(xfd->dsp);
616 free(xfd);
617 errno = -result;
618 return -1;
619 }
620
621 static int xrun(snd_pcm_t *pcm)
622 {
623 switch (snd_pcm_state(pcm)) {
624 case SND_PCM_STATE_XRUN:
625 return snd_pcm_prepare(pcm);
626 case SND_PCM_STATE_DRAINING:
627 if (snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE)
628 return snd_pcm_prepare(pcm);
629 break;
630 default:
631 break;
632 }
633 return -EIO;
634 }
635
636 static int resume(snd_pcm_t *pcm)
637 {
638 int res;
639 while ((res = snd_pcm_resume(pcm)) == -EAGAIN)
640 sleep(1);
641 if (! res)
642 return 0;
643 return snd_pcm_prepare(pcm);
644 }
645
646 ssize_t lib_oss_pcm_write(int fd, const void *buf, size_t n)
647 {
648 ssize_t result;
649 oss_dsp_t *dsp = look_for_dsp(fd);
650 oss_dsp_stream_t *str;
651 snd_pcm_t *pcm;
652 snd_pcm_uframes_t frames;
653
654 if (dsp == NULL) {
655 errno = EBADFD;
656 result = -1;
657 goto _end;
658 }
659 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
660 pcm = str->pcm;
661 if (!pcm) {
662 errno = EBADFD;
663 result = -1;
664 goto _end;
665 }
666 frames = n / str->frame_bytes;
667 _again:
668 result = snd_pcm_writei(pcm, buf, frames);
669 if (result == -EPIPE) {
670 if (! (result = xrun(pcm)))
671 goto _again;
672 } else if (result == -ESTRPIPE) {
673 if (! (result = resume(pcm)))
674 goto _again;
675 }
676 if (result < 0) {
677 errno = -result;
678 result = -1;
679 goto _end;
680 }
681 str->alsa.appl_ptr += result;
682 str->alsa.appl_ptr %= str->alsa.boundary;
683 result *= str->frame_bytes;
684 str->oss.bytes += result;
685 _end:
686 DEBUG("write(%d, %p, %ld) -> %ld", fd, buf, (long)n, (long)result);
687 if (result < 0)
688 DEBUG("(errno=%d)\n", errno);
689 else
690 DEBUG("\n");
691 return result;
692 }
693
694 ssize_t lib_oss_pcm_read(int fd, void *buf, size_t n)
695 {
696 ssize_t result;
697 oss_dsp_t *dsp = look_for_dsp(fd);
698 oss_dsp_stream_t *str;
699 snd_pcm_t *pcm;
700 snd_pcm_uframes_t frames;
701
702 if (dsp == NULL) {
703 errno = EBADFD;
704 result = -1;
705 goto _end;
706 }
707 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
708 pcm = str->pcm;
709 if (!pcm) {
710 errno = EBADFD;
711 result = -1;
712 goto _end;
713 }
714 frames = n / str->frame_bytes;
715 _again:
716 result = snd_pcm_readi(pcm, buf, frames);
717 if (result == -EPIPE) {
718 if (! (result = xrun(pcm)))
719 goto _again;
720 } else if (result == -ESTRPIPE) {
721 if (! (result = resume(pcm)))
722 goto _again;
723 }
724 if (result < 0) {
725 errno = -result;
726 result = -1;
727 goto _end;
728 }
729 str->alsa.appl_ptr += result;
730 str->alsa.appl_ptr %= str->alsa.boundary;
731 result *= str->frame_bytes;
732 str->oss.bytes += result;
733 _end:
734 DEBUG("read(%d, %p, %ld) -> %ld", fd, buf, (long)n, (long)result);
735 if (result < 0)
736 DEBUG("(errno=%d)\n", errno);
737 else
738 DEBUG("\n");
739 return result;
740 }
741
742 #define USE_REWIND 1
743
744 static void oss_dsp_mmap_update(oss_dsp_t *dsp, snd_pcm_stream_t stream,
745 snd_pcm_sframes_t delay)
746 {
747 oss_dsp_stream_t *str = &dsp->streams[stream];
748 snd_pcm_t *pcm = str->pcm;
749 snd_pcm_sframes_t err;
750 snd_pcm_uframes_t size;
751 const snd_pcm_channel_area_t *areas;
752 switch (stream) {
753 case SND_PCM_STREAM_PLAYBACK:
754 if (delay < 0) {
755 str->mmap_advance -= delay;
756 if (str->mmap_advance > dsp->rate / 10)
757 str->mmap_advance = dsp->rate / 10;
758 //fprintf(stderr, "mmap_advance=%ld\n", str->mmap_advance);
759 err = snd_pcm_forward(pcm, -delay);
760 if (err >= 0) {
761 str->alsa.appl_ptr += err;
762 str->alsa.appl_ptr %= str->alsa.boundary;
763 }
764 }
765 #if USE_REWIND
766 err = snd_pcm_rewind(pcm, str->alsa.buffer_size);
767 if (err < 0) {
768 /* fallback to not very accurate method */
769 size = str->mmap_advance - delay;
770 } else {
771 str->alsa.appl_ptr -= err;
772 str->alsa.appl_ptr %= str->alsa.boundary;
773 size = str->mmap_advance;
774 }
775 //fprintf(stderr, "delay=%ld rewind=%ld forward=%ld\n", delay, err, size);
776 #else
777 size = str->mmap_advance - delay;
778 #endif
779 while (size > 0) {
780 snd_pcm_uframes_t ofs;
781 snd_pcm_uframes_t frames = size;
782 snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
783 if (frames == 0)
784 break;
785 // fprintf(stderr, "copy %ld %ld %d\n", ofs, frames, dsp->format);
786 snd_pcm_areas_copy(areas, ofs, str->mmap_areas,
787 str->alsa.appl_ptr % str->oss.buffer_size,
788 dsp->channels, frames,
789 dsp->format);
790 err = snd_pcm_mmap_commit(pcm, ofs, frames);
791 if (err <= 0)
792 break;
793 size -= err;
794 str->alsa.appl_ptr += err;
795 str->alsa.appl_ptr %= str->alsa.boundary;
796 }
797 break;
798 case SND_PCM_STREAM_CAPTURE:
799 if (delay > (snd_pcm_sframes_t)str->alsa.buffer_size) {
800 err = snd_pcm_forward(pcm, delay - str->alsa.buffer_size);
801 if (err >= 0) {
802 str->alsa.appl_ptr += err;
803 str->alsa.appl_ptr %= str->alsa.boundary;
804 size = str->alsa.buffer_size;
805 } else {
806 size = delay;
807 }
808 } else {
809 size = delay;
810 }
811 while (size > 0) {
812 snd_pcm_uframes_t ofs;
813 snd_pcm_uframes_t frames = size;
814 snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
815 if (frames == 0)
816 break;
817 snd_pcm_areas_copy(str->mmap_areas,
818 str->alsa.appl_ptr % str->oss.buffer_size,
819 areas, ofs,
820 dsp->channels, frames,
821 dsp->format);
822 err = snd_pcm_mmap_commit(pcm, ofs, frames);
823 if (err < 0)
824 break;
825 size -= err;
826 str->alsa.appl_ptr += err;
827 str->alsa.appl_ptr %= str->alsa.boundary;
828 }
829 break;
830 }
831 }
832
833 int lib_oss_pcm_ioctl(int fd, unsigned long cmd, ...)
834 {
835 int result, err = 0;
836 va_list args;
837 void *arg;
838 oss_dsp_t *dsp = look_for_dsp(fd);
839 oss_dsp_stream_t *str;
840 snd_pcm_t *pcm;
841
842 if (dsp == NULL) {
843 errno = EBADFD;
844 return -1;
845 }
846 va_start(args, cmd);
847 arg = va_arg(args, void *);
848 va_end(args);
849 DEBUG("ioctl(%d, ", fd);
850 switch (cmd) {
851 case OSS_GETVERSION:
852 *(int*)arg = SOUND_VERSION;
853 DEBUG("OSS_GETVERSION, %p) -> [%d]\n", arg, *(int*)arg);
854 break;
855 case SNDCTL_DSP_RESET:
856 {
857 int k;
858 DEBUG("SNDCTL_DSP_RESET)\n");
859 if (!dsp->hwset) {
860 errno = -EIO;
861 return -1;
862 }
863 result = 0;
864 for (k = 0; k < 2; ++k) {
865 str = &dsp->streams[k];
866 pcm = str->pcm;
867 if (!pcm)
868 continue;
869 err = snd_pcm_drop(pcm);
870 if (err >= 0)
871 err = snd_pcm_prepare(pcm);
872 if (err < 0)
873 result = err;
874 str->oss.bytes = 0;
875 str->oss.hw_bytes = 0;
876 str->alsa.appl_ptr = 0;
877 str->alsa.old_hw_ptr = 0;
878 }
879 err = result;
880 break;
881 }
882 case SNDCTL_DSP_SYNC:
883 {
884 int k;
885 DEBUG("SNDCTL_DSP_SYNC)\n");
886 if (!dsp->hwset) {
887 errno = -EIO;
888 return -1;
889 }
890 result = 0;
891 for (k = 0; k < 2; ++k) {
892 str = &dsp->streams[k];
893 pcm = str->pcm;
894 if (!pcm)
895 continue;
896 err = snd_pcm_drain(pcm);
897 if (err >= 0)
898 err = snd_pcm_prepare(pcm);
899 if (err < 0)
900 result = err;
901 str->oss.hw_bytes = 0;
902 str->alsa.appl_ptr = 0;
903 str->alsa.old_hw_ptr = 0;
904 }
905 err = result;
906 break;
907 }
908 case SNDCTL_DSP_SPEED:
909 dsp->rate = *(int *)arg;
910 err = oss_dsp_params(dsp);
911 DEBUG("SNDCTL_DSP_SPEED, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->rate);
912 *(int *)arg = dsp->rate;
913 break;
914 case SNDCTL_DSP_STEREO:
915 if (*(int *)arg)
916 dsp->channels = 2;
917 else
918 dsp->channels = 1;
919 err = oss_dsp_params(dsp);
920 DEBUG("SNDCTL_DSP_STEREO, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->channels - 1);
921 *(int *)arg = dsp->channels - 1;
922 break;
923 case SNDCTL_DSP_CHANNELS:
924 dsp->channels = (*(int *)arg);
925 err = oss_dsp_params(dsp);
926 if (err < 0)
927 break;
928 DEBUG("SNDCTL_DSP_CHANNELS, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->channels);
929 *(int *)arg = dsp->channels;
930 break;
931 case SNDCTL_DSP_SETFMT:
932 if (*(int *)arg != AFMT_QUERY) {
933 dsp->oss_format = *(int *)arg;
934 err = oss_dsp_params(dsp);
935 if (err < 0)
936 break;
937 }
938 DEBUG("SNDCTL_DSP_SETFMT, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->oss_format);
939 *(int *) arg = dsp->oss_format;
940 break;
941 case SNDCTL_DSP_GETBLKSIZE:
942 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
943 if (!str->pcm)
944 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
945 pcm = str->pcm;
946 *(int *) arg = str->oss.period_size * str->frame_bytes;
947 DEBUG("SNDCTL_DSP_GETBLKSIZE, %p) -> [%d]\n", arg, *(int *)arg);
948 break;
949 case SNDCTL_DSP_POST:
950 DEBUG("SNDCTL_DSP_POST)\n");
951 break;
952 case SNDCTL_DSP_SUBDIVIDE:
953 DEBUG("SNDCTL_DSP_SUBDIVIDE, %p[%d])\n", arg, *(int *)arg);
954 dsp->subdivision = *(int *)arg;
955 if (dsp->subdivision < 1)
956 dsp->subdivision = 1;
957 err = oss_dsp_params(dsp);
958 break;
959 case SNDCTL_DSP_SETFRAGMENT:
960 {
961 DEBUG("SNDCTL_DSP_SETFRAGMENT, %p[%x])\n", arg, *(int *)arg);
962 dsp->fragshift = *(int *)arg & 0xffff;
963 if (dsp->fragshift < 4)
964 dsp->fragshift = 4;
965 dsp->maxfrags = ((*(int *)arg) >> 16) & 0xffff;
966 if (dsp->maxfrags < 2)
967 dsp->maxfrags = 2;
968 err = oss_dsp_params(dsp);
969 break;
970 }
971 case SNDCTL_DSP_GETFMTS:
972 {
973 *(int *)arg = (AFMT_MU_LAW | AFMT_A_LAW | AFMT_IMA_ADPCM |
974 AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE |
975 AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE);
976 DEBUG("SNDCTL_DSP_GETFMTS, %p) -> [%d]\n", arg, *(int *)arg);
977 break;
978 }
979 case SNDCTL_DSP_NONBLOCK:
980 {
981 DEBUG("SNDCTL_DSP_NONBLOCK)\n");
982 return lib_oss_pcm_nonblock(fd, 1);
983 }
984 case SNDCTL_DSP_GETCAPS:
985 {
986 result = DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP;
987 if (dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm &&
988 dsp->streams[SND_PCM_STREAM_CAPTURE].pcm)
989 result |= DSP_CAP_DUPLEX;
990 *(int*)arg = result;
991 DEBUG("SNDCTL_DSP_GETCAPS, %p) -> [%d]\n", arg, *(int*)arg);
992 break;
993 }
994 case SNDCTL_DSP_GETTRIGGER:
995 {
996 int s = 0;
997 pcm = dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm;
998 if (pcm) {
999 if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING)
1000 s |= PCM_ENABLE_OUTPUT;
1001 }
1002 pcm = dsp->streams[SND_PCM_STREAM_CAPTURE].pcm;
1003 if (pcm) {
1004 if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING)
1005 s |= PCM_ENABLE_INPUT;
1006 }
1007 *(int*)arg = s;
1008 DEBUG("SNDCTL_DSP_GETTRIGGER, %p) -> [%d]\n", arg, *(int*)arg);
1009 break;
1010 }
1011 case SNDCTL_DSP_SETTRIGGER:
1012 {
1013 DEBUG("SNDCTL_DSP_SETTRIGGER, %p[%d])\n", arg, *(int*)arg);
1014 result = *(int*) arg;
1015 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
1016 pcm = str->pcm;
1017 if (pcm) {
1018 if (result & PCM_ENABLE_INPUT) {
1019 if (str->stopped) {
1020 str->stopped = 0;
1021 err = oss_dsp_sw_params(dsp);
1022 if (err < 0)
1023 break;
1024 err = snd_pcm_start(pcm);
1025 if (err < 0)
1026 break;
1027 }
1028 } else {
1029 if (!str->stopped) {
1030 str->stopped = 1;
1031 err = snd_pcm_drop(pcm);
1032 if (err < 0)
1033 break;
1034 err = oss_dsp_sw_params(dsp);
1035 if (err < 0)
1036 break;
1037 err = snd_pcm_prepare(pcm);
1038 if (err < 0)
1039 break;
1040 }
1041 }
1042 }
1043 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
1044 pcm = str->pcm;
1045 if (pcm) {
1046 if (result & PCM_ENABLE_OUTPUT) {
1047 if (str->stopped) {
1048 str->stopped = 0;
1049 err = oss_dsp_sw_params(dsp);
1050 if (err < 0)
1051 break;
1052 if (str->mmap_buffer) {
1053 const snd_pcm_channel_area_t *areas;
1054 snd_pcm_uframes_t offset;
1055 snd_pcm_uframes_t size = str->alsa.buffer_size;
1056 ssize_t cres;
1057 snd_pcm_mmap_begin(pcm, &areas, &offset, &size);
1058 snd_pcm_areas_copy(areas, 0, str->mmap_areas, 0,
1059 dsp->channels, size,
1060 dsp->format);
1061 cres = snd_pcm_mmap_commit(pcm, offset, size);
1062 if (cres > 0) {
1063 str->alsa.appl_ptr += cres;
1064 str->alsa.appl_ptr %= str->alsa.boundary;
1065 }
1066 }
1067 err = snd_pcm_start(pcm);
1068 if (err < 0)
1069 break;
1070 }
1071 } else {
1072 if (!str->stopped) {
1073 str->stopped = 1;
1074 err = snd_pcm_drop(pcm);
1075 if (err < 0)
1076 break;
1077 err = oss_dsp_sw_params(dsp);
1078 if (err < 0)
1079 break;
1080 err = snd_pcm_prepare(pcm);
1081 if (err < 0)
1082 break;
1083 }
1084 }
1085 }
1086 break;
1087 }
1088 case SNDCTL_DSP_GETISPACE:
1089 {
1090 snd_pcm_sframes_t avail, delay;
1091 snd_pcm_state_t state;
1092 audio_buf_info *info = arg;
1093 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
1094 pcm = str->pcm;
1095 if (!pcm) {
1096 err = -EINVAL;
1097 break;
1098 }
1099 state = snd_pcm_state(pcm);
1100 if (state == SND_PCM_STATE_XRUN) {
1101 err = xrun(pcm);
1102 if (err < 0)
1103 break;
1104 state = snd_pcm_state(pcm);
1105 }
1106 if (state == SND_PCM_STATE_SUSPENDED) {
1107 err = resume(pcm);
1108 if (err < 0)
1109 break;
1110 state = snd_pcm_state(pcm);
1111 }
1112 if (state == SND_PCM_STATE_RUNNING) {
1113 snd_pcm_delay(pcm, &delay);
1114 if (str->mmap_buffer)
1115 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_CAPTURE, delay);
1116 }
1117 avail = snd_pcm_avail_update(pcm);
1118 if (avail < 0)
1119 avail = 0;
1120 if ((snd_pcm_uframes_t)avail > str->oss.buffer_size)
1121 avail = str->oss.buffer_size;
1122 info->fragsize = str->oss.period_size * str->frame_bytes;
1123 info->fragstotal = str->oss.periods;
1124 info->bytes = avail * str->frame_bytes;
1125 info->fragments = avail / str->oss.period_size;
1126 DEBUG("SNDCTL_DSP_GETISPACE, %p) -> {%d, %d, %d, %d}\n", arg,
1127 info->fragments,
1128 info->fragstotal,
1129 info->fragsize,
1130 info->bytes);
1131 break;
1132 }
1133 case SNDCTL_DSP_GETOSPACE:
1134 {
1135 snd_pcm_sframes_t avail, delay;
1136 snd_pcm_state_t state;
1137 audio_buf_info *info = arg;
1138 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
1139 pcm = str->pcm;
1140 if (!pcm) {
1141 err = -EINVAL;
1142 break;
1143 }
1144 state = snd_pcm_state(pcm);
1145 if (state == SND_PCM_STATE_XRUN) {
1146 err = xrun(pcm);
1147 if (err < 0)
1148 break;
1149 state = snd_pcm_state(pcm);
1150 }
1151 if (state == SND_PCM_STATE_SUSPENDED) {
1152 err = resume(pcm);
1153 if (err < 0)
1154 break;
1155 state = snd_pcm_state(pcm);
1156 }
1157 if (state == SND_PCM_STATE_RUNNING ||
1158 state == SND_PCM_STATE_DRAINING) {
1159 snd_pcm_delay(pcm, &delay);
1160 if (str->mmap_buffer)
1161 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, delay);
1162 }
1163 avail = snd_pcm_avail_update(pcm);
1164 if (avail < 0 || (snd_pcm_uframes_t)avail > str->oss.buffer_size)
1165 avail = str->oss.buffer_size;
1166 info->fragsize = str->oss.period_size * str->frame_bytes;
1167 info->fragstotal = str->oss.periods;
1168 info->bytes = avail * str->frame_bytes;
1169 info->fragments = avail / str->oss.period_size;
1170 DEBUG("SNDCTL_DSP_GETOSPACE, %p) -> {%d %d %d %d}\n", arg,
1171 info->fragments,
1172 info->fragstotal,
1173 info->fragsize,
1174 info->bytes);
1175 break;
1176 }
1177 case SNDCTL_DSP_GETIPTR:
1178 {
1179 snd_pcm_sframes_t delay = 0, avail, diff;
1180 snd_pcm_uframes_t hw_ptr;
1181 snd_pcm_state_t state;
1182 count_info *info = arg;
1183 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
1184 pcm = str->pcm;
1185 if (!pcm) {
1186 err = -EINVAL;
1187 break;
1188 }
1189 state = snd_pcm_state(pcm);
1190 if (state == SND_PCM_STATE_XRUN) {
1191 err = xrun(pcm);
1192 if (err < 0)
1193 break;
1194 state = snd_pcm_state(pcm);
1195 }
1196 if (state == SND_PCM_STATE_SUSPENDED) {
1197 err = resume(pcm);
1198 if (err < 0)
1199 break;
1200 state = snd_pcm_state(pcm);
1201 }
1202 if (state == SND_PCM_STATE_RUNNING) {
1203 snd_pcm_delay(pcm, &delay);
1204 if (str->mmap_buffer)
1205 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_CAPTURE, delay);
1206 }
1207 avail = snd_pcm_avail_update(pcm);
1208 hw_ptr = (str->alsa.appl_ptr + avail) % str->alsa.boundary;
1209 diff = hw_ptr - str->alsa.old_hw_ptr;
1210 if (diff < 0)
1211 diff += str->alsa.boundary;
1212 str->oss.hw_bytes += diff;
1213 str->oss.hw_bytes %= str->oss.boundary;
1214 info->bytes = (str->oss.hw_bytes * str->frame_bytes) & 0x7fffffff;
1215 info->ptr = (str->oss.hw_bytes % str->oss.buffer_size) * str->frame_bytes;
1216 if (str->mmap_buffer) {
1217 ssize_t n = (hw_ptr / str->oss.period_size) - (str->alsa.old_hw_ptr / str->oss.period_size);
1218 if (n < 0)
1219 n += str->alsa.boundary / str->oss.period_size;
1220 info->blocks = n;
1221 } else {
1222 info->blocks = delay / str->oss.period_size;
1223 }
1224 str->alsa.old_hw_ptr = hw_ptr;
1225 DEBUG("SNDCTL_DSP_GETIPTR, %p) -> {%d %d %d}\n", arg,
1226 info->bytes,
1227 info->blocks,
1228 info->ptr);
1229 break;
1230 }
1231 case SNDCTL_DSP_GETOPTR:
1232 {
1233 snd_pcm_sframes_t delay = 0, avail, diff;
1234 snd_pcm_uframes_t hw_ptr;
1235 snd_pcm_state_t state;
1236 count_info *info = arg;
1237 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
1238 pcm = str->pcm;
1239 if (!pcm) {
1240 err = -EINVAL;
1241 break;
1242 }
1243 if (state == SND_PCM_STATE_XRUN) {
1244 err = xrun(pcm);
1245 if (err < 0)
1246 break;
1247 state = snd_pcm_state(pcm);
1248 }
1249 state = snd_pcm_state(pcm);
1250 if (state == SND_PCM_STATE_SUSPENDED) {
1251 err = resume(pcm);
1252 if (err < 0)
1253 break;
1254 state = snd_pcm_state(pcm);
1255 }
1256 if (state == SND_PCM_STATE_RUNNING ||
1257 state == SND_PCM_STATE_DRAINING) {
1258 snd_pcm_delay(pcm, &delay);
1259 if (str->mmap_buffer)
1260 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, delay);
1261 }
1262 avail = snd_pcm_avail_update(pcm);
1263 hw_ptr = (str->alsa.appl_ptr - (str->alsa.buffer_size - avail)) % str->alsa.boundary;
1264 diff = hw_ptr - str->alsa.old_hw_ptr;
1265 if (diff < 0)
1266 diff += str->alsa.boundary;
1267 str->oss.hw_bytes += diff;
1268 str->oss.hw_bytes %= str->oss.boundary;
1269 info->bytes = (str->oss.hw_bytes * str->frame_bytes) & 0x7fffffff;
1270 info->ptr = (str->oss.hw_bytes % str->oss.buffer_size) * str->frame_bytes;
1271 if (str->mmap_buffer) {
1272 ssize_t n = (hw_ptr / str->oss.period_size) - (str->alsa.old_hw_ptr / str->oss.period_size);
1273 if (n < 0)
1274 n += str->alsa.boundary / str->oss.period_size;
1275 info->blocks = n;
1276 } else {
1277 info->blocks = delay / str->oss.period_size;
1278 }
1279 str->alsa.old_hw_ptr = hw_ptr;
1280 DEBUG("SNDCTL_DSP_GETOPTR, %p) -> {%d %d %d}\n", arg,
1281 info->bytes,
1282 info->blocks,
1283 info->ptr);
1284 break;
1285 }
1286 case SNDCTL_DSP_GETODELAY:
1287 {
1288 snd_pcm_sframes_t delay = 0;
1289 snd_pcm_state_t state;
1290 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
1291 pcm = str->pcm;
1292 if (!pcm) {
1293 err = -EINVAL;
1294 break;
1295 }
1296 state = snd_pcm_state(pcm);
1297 if (state == SND_PCM_STATE_SUSPENDED) {
1298 err = resume(pcm);
1299 if (err < 0)
1300 break;
1301 state = snd_pcm_state(pcm);
1302 }
1303 if (state == SND_PCM_STATE_RUNNING ||
1304 state == SND_PCM_STATE_DRAINING) {
1305 snd_pcm_delay(pcm, &delay);
1306 if (str->mmap_buffer)
1307 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, delay);
1308 }
1309 *(int *)arg = delay * str->frame_bytes;
1310 DEBUG("SNDCTL_DSP_GETODELAY, %p) -> [%d]\n", arg, *(int*)arg);
1311 break;
1312 }
1313 case SNDCTL_DSP_SETDUPLEX:
1314 DEBUG("SNDCTL_DSP_SETDUPLEX)\n");
1315 break;
1316 case SOUND_PCM_READ_RATE:
1317 {
1318 *(int *)arg = dsp->rate;
1319 DEBUG("SOUND_PCM_READ_RATE, %p) -> [%d]\n", arg, *(int*)arg);
1320 break;
1321 }
1322 case SOUND_PCM_READ_CHANNELS:
1323 {
1324 *(int *)arg = dsp->channels;
1325 DEBUG("SOUND_PCM_READ_CHANNELS, %p) -> [%d]\n", arg, *(int*)arg);
1326 break;
1327 }
1328 case SOUND_PCM_READ_BITS:
1329 {
1330 *(int *)arg = snd_pcm_format_width(dsp->format);
1331 DEBUG("SOUND_PCM_READ_BITS, %p) -> [%d]\n", arg, *(int*)arg);
1332 break;
1333 }
1334 case SNDCTL_DSP_MAPINBUF:
1335 DEBUG("SNDCTL_DSP_MAPINBUF)\n");
1336 err = -EINVAL;
1337 break;
1338 case SNDCTL_DSP_MAPOUTBUF:
1339 DEBUG("SNDCTL_DSP_MAPOUTBUF)\n");
1340 err = -EINVAL;
1341 break;
1342 case SNDCTL_DSP_SETSYNCRO:
1343 DEBUG("SNDCTL_DSP_SETSYNCRO)\n");
1344 err = -EINVAL;
1345 break;
1346 case SOUND_PCM_READ_FILTER:
1347 DEBUG("SOUND_PCM_READ_FILTER)\n");
1348 err = -EINVAL;
1349 break;
1350 case SOUND_PCM_WRITE_FILTER:
1351 DEBUG("SOUND_PCM_WRITE_FILTER)\n");
1352 err = -EINVAL;
1353 break;
1354 default:
1355 DEBUG("%lx, %p)\n", cmd, arg);
1356 // return oss_mixer_ioctl(...);
1357 err = -ENXIO;
1358 break;
1359 }
1360 if (err >= 0)
1361 return 0;
1362 DEBUG("dsp ioctl error = %d\n", err);
1363 errno = -err;
1364 return -1;
1365 }
1366
1367 int lib_oss_pcm_nonblock(int fd, int nonblock)
1368 {
1369 oss_dsp_t *dsp = look_for_dsp(fd);
1370 int k;
1371
1372 if (dsp == NULL) {
1373 errno = EBADFD;
1374 return -1;
1375 }
1376 for (k = 0; k < 2; ++k) {
1377 snd_pcm_t *pcm = dsp->streams[k].pcm;
1378 int err;
1379 if (!pcm)
1380 continue;
1381 err = snd_pcm_nonblock(pcm, nonblock);
1382 if (err < 0) {
1383 errno = -err;
1384 return -1;
1385 }
1386 }
1387 return 0;
1388 }
1389
1390 void * lib_oss_pcm_mmap(void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED, int prot, int flags ATTRIBUTE_UNUSED, int fd, off_t offset ATTRIBUTE_UNUSED)
1391 {
1392 int err;
1393 void *result;
1394 oss_dsp_t *dsp = look_for_dsp(fd);
1395 oss_dsp_stream_t *str;
1396
1397 if (dsp == NULL) {
1398 errno = -EBADFD;
1399 return MAP_FAILED;
1400 }
1401 switch (prot & (PROT_READ | PROT_WRITE)) {
1402 case PROT_READ:
1403 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
1404 break;
1405 case PROT_WRITE:
1406 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
1407 break;
1408 case PROT_READ | PROT_WRITE:
1409 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
1410 if (!str->pcm)
1411 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
1412 break;
1413 default:
1414 errno = EINVAL;
1415 result = MAP_FAILED;
1416 goto _end;
1417 }
1418 if (!str->pcm) {
1419 errno = EBADFD;
1420 result = MAP_FAILED;
1421 goto _end;
1422 }
1423 assert(!str->mmap_buffer);
1424 result = malloc(len);
1425 if (!result) {
1426 result = MAP_FAILED;
1427 goto _end;
1428 }
1429 str->mmap_buffer = result;
1430 str->mmap_bytes = len;
1431 str->alsa.mmap_period_bytes = str->oss.period_size * str->frame_bytes;
1432 str->alsa.mmap_buffer_bytes = str->oss.buffer_size * str->frame_bytes;
1433 err = oss_dsp_params(dsp);
1434 if (err < 0) {
1435 free(result);
1436 str->mmap_buffer = NULL;
1437 str->mmap_bytes = 0;
1438 errno = -err;
1439 result = MAP_FAILED;
1440 goto _end;
1441 }
1442 _end:
1443 DEBUG("mmap(%p, %lu, %d, %d, %d, %ld) -> %p\n", addr, (unsigned long)len, prot, flags, fd, offset, result);
1444 return result;
1445 }
1446
1447 int lib_oss_pcm_munmap(void *addr, size_t len)
1448 {
1449 int err;
1450 oss_dsp_t *dsp = look_for_mmap_addr(addr);
1451 oss_dsp_stream_t *str;
1452
1453 if (dsp == NULL) {
1454 errno = EBADFD;
1455 return -1;
1456 }
1457 DEBUG("munmap(%p, %lu)\n", addr, (unsigned long)len);
1458 str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
1459 if (!str->pcm)
1460 str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
1461 assert(str->mmap_buffer);
1462 free(str->mmap_buffer);
1463 str->mmap_buffer = 0;
1464 str->mmap_bytes = 0;
1465 err = oss_dsp_params(dsp);
1466 if (err < 0) {
1467 errno = -err;
1468 return -1;
1469 }
1470 return 0;
1471 }
1472
1473 static void set_oss_mmap_avail_min(oss_dsp_stream_t *str, int stream ATTRIBUTE_UNUSED, snd_pcm_t *pcm)
1474 {
1475 snd_pcm_uframes_t hw_ptr;
1476 snd_pcm_sframes_t diff;
1477
1478 hw_ptr = str->alsa.old_hw_ptr -
1479 (str->alsa.old_hw_ptr % str->oss.period_size) +
1480 str->oss.period_size;
1481 diff = hw_ptr - str->alsa.appl_ptr;
1482 if (diff < 0)
1483 diff += str->alsa.buffer_size;
1484 if (diff < 1)
1485 diff = 1;
1486 //fprintf(stderr, "avail_min (%i): hw_ptr = %lu, appl_ptr = %lu, diff = %lu\n", stream, hw_ptr, str->alsa.appl_ptr, diff);
1487 snd_pcm_sw_params_set_avail_min(pcm, str->sw_params, diff);
1488 snd_pcm_sw_params(pcm, str->sw_params);
1489 }
1490
1491 int lib_oss_pcm_select_prepare(int fd, int fmode, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
1492 {
1493 oss_dsp_t *dsp = look_for_dsp(fd);
1494 int k, maxfd = -1;
1495
1496 if (dsp == NULL) {
1497 errno = EBADFD;
1498 return -1;
1499 }
1500 for (k = 0; k < 2; ++k) {
1501 oss_dsp_stream_t *str = &dsp->streams[k];
1502 snd_pcm_t *pcm = str->pcm;
1503 int err, count;
1504 if (!pcm)
1505 continue;
1506 if ((fmode & O_ACCMODE) == O_RDONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
1507 continue;
1508 if ((fmode & O_ACCMODE) == O_WRONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE)
1509 continue;
1510 if (str->mmap_buffer)
1511 set_oss_mmap_avail_min(str, k, pcm);
1512 count = snd_pcm_poll_descriptors_count(pcm);
1513 if (count < 0) {
1514 errno = -count;
1515 return -1;
1516 }
1517 {
1518 struct pollfd ufds[count];
1519 int j;
1520 err = snd_pcm_poll_descriptors(pcm, ufds, count);
1521 if (err < 0) {
1522 errno = -err;
1523 return -1;
1524 }
1525 for (j = 0; j < count; j++) {
1526 int fd = ufds[j].fd;
1527 unsigned short events = ufds[j].events;
1528 if (maxfd < fd)
1529 maxfd = fd;
1530 if (readfds) {
1531 FD_CLR(fd, readfds);
1532 if (events & POLLIN)
1533 FD_SET(fd, readfds);
1534 }
1535 if (writefds) {
1536 FD_CLR(fd, writefds);
1537 if (events & POLLOUT)
1538 FD_SET(fd, writefds);
1539 }
1540 if (exceptfds) {
1541 FD_CLR(fd, exceptfds);
1542 if (events & (POLLERR|POLLNVAL))
1543 FD_SET(fd, exceptfds);
1544 }
1545 }
1546 }
1547 }
1548 return maxfd;
1549 }
1550
1551 int lib_oss_pcm_select_result(int fd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
1552 {
1553 oss_dsp_t *dsp = look_for_dsp(fd);
1554 int k, result = 0;
1555
1556 if (dsp == NULL) {
1557 errno = EBADFD;
1558 return -1;
1559 }
1560 for (k = 0; k < 2; ++k) {
1561 snd_pcm_t *pcm = dsp->streams[k].pcm;
1562 int err, count;
1563 if (!pcm)
1564 continue;
1565 count = snd_pcm_poll_descriptors_count(pcm);
1566 if (count < 0) {
1567 errno = -count;
1568 return -1;
1569 }
1570 {
1571 struct pollfd ufds[count];
1572 int j;
1573 unsigned short revents;
1574 err = snd_pcm_poll_descriptors(pcm, ufds, count);
1575 if (err < 0) {
1576 errno = -err;
1577 return -1;
1578 }
1579 for (j = 0; j < count; j++) {
1580 int fd = ufds[j].fd;
1581 revents = 0;
1582 if (readfds && FD_ISSET(fd, readfds))
1583 revents |= POLLIN;
1584 if (writefds && FD_ISSET(fd, writefds))
1585 revents |= POLLOUT;
1586 if (exceptfds && FD_ISSET(fd, exceptfds))
1587 revents |= POLLERR;
1588 ufds[j].revents = revents;
1589 }
1590 err = snd_pcm_poll_descriptors_revents(pcm, ufds, count, &revents);
1591 if (err < 0) {
1592 errno = -err;
1593 return -1;
1594 }
1595 if (revents & (POLLNVAL|POLLERR))
1596 result |= OSS_WAIT_EVENT_ERROR;
1597 if (revents & POLLIN)
1598 result |= OSS_WAIT_EVENT_READ;
1599 if (revents & POLLOUT)
1600 result |= OSS_WAIT_EVENT_WRITE;
1601 }
1602 }
1603 return result;
1604 }
1605
1606 extern int lib_oss_pcm_poll_fds(int fd)
1607 {
1608 oss_dsp_t *dsp = look_for_dsp(fd);
1609 int k, result = 0;
1610
1611 if (dsp == NULL) {
1612 errno = EBADFD;
1613 return -1;
1614 }
1615 for (k = 0; k < 2; ++k) {
1616 snd_pcm_t *pcm = dsp->streams[k].pcm;
1617 int err;
1618 if (!pcm)
1619 continue;
1620 err = snd_pcm_poll_descriptors_count(pcm);
1621 if (err < 0) {
1622 errno = -err;
1623 return -1;
1624 }
1625 result += err;
1626 }
1627 return result;
1628 }
1629
1630 int lib_oss_pcm_poll_prepare(int fd, int fmode, struct pollfd *ufds)
1631 {
1632 oss_dsp_t *dsp = look_for_dsp(fd);
1633 int k, result = 0;
1634
1635 if (dsp == NULL) {
1636 errno = EBADFD;
1637 return -1;
1638 }
1639 for (k = 0; k < 2; ++k) {
1640 oss_dsp_stream_t *str = &dsp->streams[k];
1641 snd_pcm_t *pcm = str->pcm;
1642 int err, count;
1643 if (!pcm)
1644 continue;
1645 if ((fmode & O_ACCMODE) == O_RDONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
1646 continue;
1647 if ((fmode & O_ACCMODE) == O_WRONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE)
1648 continue;
1649 if (str->mmap_buffer)
1650 set_oss_mmap_avail_min(str, k, pcm);
1651 count = snd_pcm_poll_descriptors_count(pcm);
1652 if (count < 0) {
1653 errno = -count;
1654 return -1;
1655 }
1656 err = snd_pcm_poll_descriptors(pcm, ufds, count);
1657 if (err < 0) {
1658 errno = -err;
1659 return -1;
1660 }
1661 ufds += count;
1662 result += count;
1663 }
1664 return result;
1665 }
1666
1667 int lib_oss_pcm_poll_result(int fd, struct pollfd *ufds)
1668 {
1669 oss_dsp_t *dsp = look_for_dsp(fd);
1670 int k, result = 0;
1671
1672 if (dsp == NULL) {
1673 errno = EBADFD;
1674 return -1;
1675 }
1676 for (k = 0; k < 2; ++k) {
1677 snd_pcm_t *pcm = dsp->streams[k].pcm;
1678 int err, count;
1679 unsigned short revents;
1680 if (!pcm)
1681 continue;
1682 count = snd_pcm_poll_descriptors_count(pcm);
1683 if (count < 0) {
1684 errno = -count;
1685 return -1;
1686 }
1687 err = snd_pcm_poll_descriptors_revents(pcm, ufds, count, &revents);
1688 if (err < 0) {
1689 errno = -err;
1690 return -1;
1691 }
1692 if (revents & (POLLNVAL|POLLERR))
1693 result |= OSS_WAIT_EVENT_ERROR;
1694 if (revents & POLLIN)
1695 result |= OSS_WAIT_EVENT_READ;
1696 if (revents & POLLOUT)
1697 result |= OSS_WAIT_EVENT_WRITE;
1698 ufds += count;
1699 }
1700 return result;
1701 }
1702
1703
1704 static void error_handler(const char *file ATTRIBUTE_UNUSED,
1705 int line ATTRIBUTE_UNUSED,
1706 const char *func ATTRIBUTE_UNUSED,
1707 int err ATTRIBUTE_UNUSED,
1708 const char *fmt ATTRIBUTE_UNUSED,
1709 ...)
1710 {
1711 /* suppress the error message from alsa-lib */
1712 }
1713
1714 int lib_oss_pcm_open(const char *file, int oflag, ...)
1715 {
1716 int result;
1717 int minor, card, device;
1718 struct stat s;
1719 mode_t mode;
1720 va_list args;
1721 va_start(args, oflag);
1722 mode = va_arg(args, mode_t);
1723 va_end(args);
1724 result = stat(file, &s);
1725 if (result < 0) {
1726 if (!strncmp(file, "/dev/dsp", 8))
1727 minor = (atoi(file + 8) << 4) | OSS_DEVICE_DSP;
1728 else if (!strncmp(file, "/dev/dspW", 9))
1729 minor = (atoi(file + 9) << 4) | OSS_DEVICE_DSPW;
1730 else if (!strncmp(file, "/dev/adsp", 9))
1731 minor = (atoi(file + 9) << 4) | OSS_DEVICE_ADSP;
1732 else if (!strncmp(file, "/dev/audio", 10))
1733 minor = (atoi(file + 10) << 4) | OSS_DEVICE_AUDIO;
1734 else if (!strncmp(file, "/dev/sound/dsp", 14))
1735 minor = (atoi(file + 14) << 4) | OSS_DEVICE_DSP;
1736 else if (!strncmp(file, "/dev/sound/dspW", 15))
1737 minor = (atoi(file + 15) << 4) | OSS_DEVICE_DSPW;
1738 else if (!strncmp(file, "/dev/sound/adsp", 15))
1739 minor = (atoi(file + 15) << 4) | OSS_DEVICE_ADSP;
1740 else if (!strncmp(file, "/dev/sound/audio", 16))
1741 minor = (atoi(file + 16) << 4) | OSS_DEVICE_AUDIO;
1742 else {
1743 errno = ENOENT;
1744 return -1;
1745 }
1746 } else {
1747 if (!S_ISCHR(s.st_mode) || ((s.st_rdev >> 8) & 0xff) != OSS_MAJOR) {
1748 errno = ENOENT;
1749 return -1;
1750 }
1751 minor = s.st_rdev & 0xff;
1752 }
1753 if (! alsa_oss_debug)
1754 snd_lib_error_set_handler(error_handler);
1755 card = minor >> 4;
1756 device = minor & 0x0f;
1757 switch (device) {
1758 case OSS_DEVICE_DSP:
1759 case OSS_DEVICE_DSPW:
1760 case OSS_DEVICE_AUDIO:
1761 case OSS_DEVICE_ADSP:
1762 result = oss_dsp_open(card, device, oflag, mode);
1763 DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
1764 return result;
1765 default:
1766 errno = ENOENT;
1767 return -1;
1768 }
1769 }