3 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sys/ioctl.h>
32 #include "pcm_local.h"
40 int card, device, subdevice;
41 volatile snd_pcm_mmap_status_t *mmap_status;
42 snd_pcm_mmap_control_t *mmap_control;
45 #define SND_FILE_PCM_STREAM_PLAYBACK "/dev/snd/pcmC%iD%ip"
46 #define SND_FILE_PCM_STREAM_CAPTURE "/dev/snd/pcmC%iD%ic"
47 #define SND_PCM_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0)
49 static int snd_pcm_hw_nonblock(snd_pcm_t *pcm, int nonblock)
52 snd_pcm_hw_t *hw = pcm->private;
55 if ((flags = fcntl(fd, F_GETFL)) < 0) {
56 SYSERR("F_GETFL failed");
63 if (fcntl(fd, F_SETFL, flags) < 0) {
64 SYSERR("F_SETFL for O_NONBLOCK failed");
70 static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid)
73 snd_pcm_hw_t *hw = pcm->private;
76 if ((flags = fcntl(fd, F_GETFL)) < 0) {
77 SYSERR("F_GETFL failed");
84 if (fcntl(fd, F_SETFL, flags) < 0) {
85 SYSERR("F_SETFL for O_ASYNC failed");
92 if (fcntl(fd, F_SETSIG, sig) < 0) {
93 SYSERR("F_SETSIG failed");
98 if (fcntl(fd, F_SETOWN, pid) < 0) {
99 SYSERR("F_SETOWN failed");
105 static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
107 snd_pcm_hw_t *hw = pcm->private;
109 if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0) {
110 SYSERR("SND_PCM_IOCTL_INFO failed");
116 static int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
118 snd_pcm_hw_t *hw = pcm->private;
120 if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0) {
121 SYSERR("SND_PCM_IOCTL_PARAMS_INFO failed");
127 static int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
129 snd_pcm_hw_t *hw = pcm->private;
131 if (ioctl(fd, SND_PCM_IOCTL_PARAMS, params) < 0) {
132 SYSERR("SND_PCM_IOCTL_PARAMS failed");
138 static int snd_pcm_hw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
140 snd_pcm_hw_t *hw = pcm->private;
142 if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0) {
143 SYSERR("SND_PCM_IOCTL_SETUP failed");
146 if (setup->mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
147 if (setup->xfer_mode == SND_PCM_XFER_INTERLEAVED)
148 setup->mmap_shape = SND_PCM_MMAP_INTERLEAVED;
150 setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
155 static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
157 snd_pcm_hw_t *hw = pcm->private;
159 if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0) {
160 SYSERR("SND_PCM_IOCTL_CHANNEL_INFO failed");
166 static int snd_pcm_hw_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
168 snd_pcm_hw_t *hw = pcm->private;
170 if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0) {
171 SYSERR("SND_PCM_IOCTL_CHANNEL_PARAMS failed");
177 static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
179 snd_pcm_hw_t *hw = pcm->private;
181 if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0) {
182 SYSERR("SND_PCM_IOCTL_CHANNEL_SETUP failed");
187 if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
188 if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) {
189 setup->running_area.addr = pcm->mmap_info->addr;
190 setup->running_area.first = setup->channel * pcm->bits_per_sample;
191 setup->running_area.step = pcm->bits_per_frame;
193 setup->running_area.addr = pcm->mmap_info->addr + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
194 setup->running_area.first = 0;
195 setup->running_area.step = pcm->bits_per_sample;
197 setup->stopped_area = setup->running_area;
199 setup->running_area.addr = pcm->mmap_info->addr + (long)setup->running_area.addr;
200 setup->stopped_area.addr = setup->running_area.addr;
205 static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
207 snd_pcm_hw_t *hw = pcm->private;
209 if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0) {
210 SYSERR("SND_PCM_IOCTL_STATUS failed");
216 static int snd_pcm_hw_state(snd_pcm_t *pcm)
218 snd_pcm_hw_t *hw = pcm->private;
219 return hw->mmap_status->state;
222 static int snd_pcm_hw_delay(snd_pcm_t *pcm, ssize_t *delayp)
224 snd_pcm_hw_t *hw = pcm->private;
226 if (ioctl(fd, SND_PCM_IOCTL_DELAY, delayp) < 0) {
227 SYSERR("SND_PCM_IOCTL_DELAY failed");
233 static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
235 snd_pcm_hw_t *hw = pcm->private;
237 if (ioctl(fd, SND_PCM_IOCTL_PREPARE) < 0) {
238 SYSERR("SND_PCM_IOCTL_PREPARE failed");
244 static int snd_pcm_hw_start(snd_pcm_t *pcm)
246 snd_pcm_hw_t *hw = pcm->private;
248 if (ioctl(fd, SND_PCM_IOCTL_START) < 0) {
249 SYSERR("SND_PCM_IOCTL_START failed");
255 static int snd_pcm_hw_drop(snd_pcm_t *pcm)
257 snd_pcm_hw_t *hw = pcm->private;
259 if (ioctl(fd, SND_PCM_IOCTL_DROP) < 0) {
260 SYSERR("SND_PCM_IOCTL_DROP failed");
266 static int snd_pcm_hw_drain(snd_pcm_t *pcm)
268 snd_pcm_hw_t *hw = pcm->private;
270 if (ioctl(fd, SND_PCM_IOCTL_DRAIN) < 0) {
271 SYSERR("SND_PCM_IOCTL_DRAIN failed");
277 static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
279 snd_pcm_hw_t *hw = pcm->private;
281 if (ioctl(fd, SND_PCM_IOCTL_PAUSE, enable) < 0) {
282 SYSERR("SND_PCM_IOCTL_PAUSE failed");
288 static ssize_t snd_pcm_hw_rewind(snd_pcm_t *pcm, size_t frames)
291 if (pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) {
293 int err = snd_pcm_hw_delay(pcm, &d);
297 hw_avail = snd_pcm_mmap_hw_avail(pcm);
300 if (frames > (size_t)hw_avail)
302 snd_pcm_mmap_appl_backward(pcm, frames);
306 static ssize_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
309 snd_pcm_hw_t *hw = pcm->private;
312 xferi.buf = (char*) buffer;
314 result = ioctl(fd, SND_PCM_IOCTL_WRITEI_FRAMES, &xferi);
320 static ssize_t snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, size_t size)
323 snd_pcm_hw_t *hw = pcm->private;
328 result = ioctl(fd, SND_PCM_IOCTL_WRITEN_FRAMES, &xfern);
334 static ssize_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, size_t size)
337 snd_pcm_hw_t *hw = pcm->private;
342 result = ioctl(fd, SND_PCM_IOCTL_READI_FRAMES, &xferi);
348 ssize_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, size_t size)
351 snd_pcm_hw_t *hw = pcm->private;
356 result = ioctl(fd, SND_PCM_IOCTL_READN_FRAMES, &xfern);
362 static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm)
364 snd_pcm_hw_t *hw = pcm->private;
366 ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED,
367 hw->fd, SND_PCM_MMAP_OFFSET_STATUS);
368 if (ptr == MAP_FAILED || ptr == NULL) {
369 SYSERR("status mmap failed");
372 hw->mmap_status = ptr;
373 pcm->hw_ptr = &hw->mmap_status->hw_ptr;
377 static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
379 snd_pcm_hw_t *hw = pcm->private;
381 ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
382 hw->fd, SND_PCM_MMAP_OFFSET_CONTROL);
383 if (ptr == MAP_FAILED || ptr == NULL) {
384 SYSERR("control mmap failed");
387 hw->mmap_control = ptr;
388 pcm->appl_ptr = &hw->mmap_control->appl_ptr;
392 static int snd_pcm_hw_mmap(snd_pcm_t *pcm)
394 snd_pcm_hw_t *hw = pcm->private;
395 snd_pcm_mmap_info_t *i = calloc(1, sizeof(*i));
399 if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
400 err = snd_pcm_alloc_user_mmap(pcm, i);
406 err = snd_pcm_alloc_kernel_mmap(pcm, i, hw->fd);
413 pcm->mmap_info_count = 1;
417 static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm)
419 snd_pcm_hw_t *hw = pcm->private;
420 if (munmap((void*)hw->mmap_status, sizeof(*hw->mmap_status)) < 0) {
421 SYSERR("status munmap failed");
427 static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm)
429 snd_pcm_hw_t *hw = pcm->private;
430 if (munmap(hw->mmap_control, sizeof(*hw->mmap_control)) < 0) {
431 SYSERR("control munmap failed");
437 static int snd_pcm_hw_munmap(snd_pcm_t *pcm)
439 int err = snd_pcm_free_mmap(pcm, pcm->mmap_info);
442 pcm->mmap_info_count = 0;
443 free(pcm->mmap_info);
448 static int snd_pcm_hw_close(snd_pcm_t *pcm)
450 snd_pcm_hw_t *hw = pcm->private;
454 SYSERR("close failed\n");
457 snd_pcm_hw_munmap_status(pcm);
458 snd_pcm_hw_munmap_control(pcm);
462 static ssize_t snd_pcm_hw_mmap_forward(snd_pcm_t *pcm, size_t size)
464 if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED && pcm->stream == SND_PCM_STREAM_PLAYBACK)
465 return snd_pcm_write_mmap(pcm, size);
466 snd_pcm_mmap_appl_forward(pcm, size);
470 static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
474 if (pcm->setup.ready_mode == SND_PCM_READY_ASAP ||
475 pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) {
477 int err = snd_pcm_hw_delay(pcm, &d);
481 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
482 avail = snd_pcm_mmap_playback_avail(pcm);
484 avail = snd_pcm_mmap_capture_avail(pcm);
485 if (avail > 0 && pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
486 err = snd_pcm_read_mmap(pcm, avail);
489 assert((size_t)err == avail);
493 if (avail > pcm->setup.buffer_size)
498 static int snd_pcm_hw_set_avail_min(snd_pcm_t *pcm, size_t frames)
500 snd_pcm_hw_t *hw = pcm->private;
501 hw->mmap_control->avail_min = frames;
505 static void snd_pcm_hw_dump(snd_pcm_t *pcm, FILE *fp)
507 snd_pcm_hw_t *hw = pcm->private;
508 char *name = "Unknown";
509 snd_card_get_name(hw->card, &name);
510 fprintf(fp, "Hardware PCM card %d '%s' device %d subdevice %d\n",
511 hw->card, name, hw->device, hw->subdevice);
513 if (pcm->valid_setup) {
514 fprintf(fp, "\nIts setup is:\n");
515 snd_pcm_dump_setup(pcm, fp);
519 snd_pcm_ops_t snd_pcm_hw_ops = {
520 close: snd_pcm_hw_close,
521 info: snd_pcm_hw_info,
522 params_info: snd_pcm_hw_params_info,
523 params: snd_pcm_hw_params,
524 setup: snd_pcm_hw_setup,
525 channel_info: snd_pcm_hw_channel_info,
526 channel_params: snd_pcm_hw_channel_params,
527 channel_setup: snd_pcm_hw_channel_setup,
528 dump: snd_pcm_hw_dump,
529 nonblock: snd_pcm_hw_nonblock,
530 async: snd_pcm_hw_async,
531 mmap: snd_pcm_hw_mmap,
532 munmap: snd_pcm_hw_munmap,
535 snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
536 status: snd_pcm_hw_status,
537 state: snd_pcm_hw_state,
538 delay: snd_pcm_hw_delay,
539 prepare: snd_pcm_hw_prepare,
540 start: snd_pcm_hw_start,
541 drop: snd_pcm_hw_drop,
542 drain: snd_pcm_hw_drain,
543 pause: snd_pcm_hw_pause,
544 rewind: snd_pcm_hw_rewind,
545 writei: snd_pcm_hw_writei,
546 writen: snd_pcm_hw_writen,
547 readi: snd_pcm_hw_readi,
548 readn: snd_pcm_hw_readn,
549 avail_update: snd_pcm_hw_avail_update,
550 mmap_forward: snd_pcm_hw_mmap_forward,
551 set_avail_min: snd_pcm_hw_set_avail_min,
554 int snd_pcm_hw_open_subdevice(snd_pcm_t **pcmp, int card, int device, int subdevice, int stream, int mode)
559 int ret = 0, fd = -1;
564 snd_pcm_t *pcm = NULL;
565 snd_pcm_hw_t *hw = NULL;
569 if ((ret = snd_ctl_hw_open(&ctl, NULL, card)) < 0)
573 case SND_PCM_STREAM_PLAYBACK:
574 filefmt = SND_FILE_PCM_STREAM_PLAYBACK;
576 case SND_PCM_STREAM_CAPTURE:
577 filefmt = SND_FILE_PCM_STREAM_CAPTURE;
582 sprintf(filename, filefmt, card, device);
589 ret = snd_ctl_pcm_prefer_subdevice(ctl, subdevice);
595 if (mode & SND_PCM_NONBLOCK)
597 if (mode & SND_PCM_ASYNC)
599 if ((fd = open(filename, fmode)) < 0) {
600 SYSERR("open %s failed", filename);
604 if (ioctl(fd, SND_PCM_IOCTL_PVERSION, &ver) < 0) {
605 SYSERR("SND_PCM_IOCTL_PVERSION failed");
609 if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_PCM_VERSION_MAX)) {
610 ret = -SND_ERROR_INCOMPATIBLE_VERSION;
613 if (subdevice >= 0) {
614 memset(&info, 0, sizeof(info));
615 if (ioctl(fd, SND_PCM_IOCTL_INFO, &info) < 0) {
616 SYSERR("SND_PCM_IOCTL_INFO failed");
620 if (info.subdevice != subdevice) {
625 hw = calloc(1, sizeof(snd_pcm_hw_t));
632 hw->subdevice = subdevice;
635 pcm = calloc(1, sizeof(snd_pcm_t));
640 pcm->type = SND_PCM_TYPE_HW;
641 pcm->stream = stream;
643 pcm->ops = &snd_pcm_hw_ops;
645 pcm->fast_ops = &snd_pcm_hw_fast_ops;
646 pcm->fast_op_arg = pcm;
650 ret = snd_pcm_hw_mmap_status(pcm);
656 ret = snd_pcm_hw_mmap_control(pcm);
674 int snd_pcm_hw_open_device(snd_pcm_t **pcmp, int card, int device, int stream, int mode)
676 return snd_pcm_hw_open_subdevice(pcmp, card, device, -1, stream, mode);
679 int snd_pcm_hw_open(snd_pcm_t **pcmp, char *name, int card, int device, int subdevice, int stream, int mode)
681 int err = snd_pcm_hw_open_subdevice(pcmp, card, device, subdevice, stream, mode);
685 (*pcmp)->name = strdup(name);
689 int _snd_pcm_hw_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
690 int stream, int mode)
692 snd_config_iterator_t i;
693 long card = -1, device = 0, subdevice = -1;
696 snd_config_foreach(i, conf) {
697 snd_config_t *n = snd_config_entry(i);
698 if (strcmp(n->id, "comment") == 0)
700 if (strcmp(n->id, "type") == 0)
702 if (strcmp(n->id, "stream") == 0)
704 if (strcmp(n->id, "card") == 0) {
705 err = snd_config_integer_get(n, &card);
707 err = snd_config_string_get(n, &str);
710 card = snd_card_get_index(str);
716 if (strcmp(n->id, "device") == 0) {
717 err = snd_config_integer_get(n, &device);
722 if (strcmp(n->id, "subdevice") == 0) {
723 err = snd_config_integer_get(n, &subdevice);
732 return snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, mode);