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>
33 #include "pcm_local.h"
34 #include "../control/control_local.h"
41 #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
46 int card, device, subdevice;
47 volatile snd_pcm_mmap_status_t *mmap_status;
48 snd_pcm_mmap_control_t *mmap_control;
52 #define SND_FILE_PCM_STREAM_PLAYBACK "/dev/snd/pcmC%iD%ip"
53 #define SND_FILE_PCM_STREAM_CAPTURE "/dev/snd/pcmC%iD%ic"
54 #define SND_PCM_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0)
56 static int snd_pcm_hw_nonblock(snd_pcm_t *pcm, int nonblock)
59 snd_pcm_hw_t *hw = pcm->private;
62 if ((flags = fcntl(fd, F_GETFL)) < 0) {
63 SYSERR("F_GETFL failed");
70 if (fcntl(fd, F_SETFL, flags) < 0) {
71 SYSERR("F_SETFL for O_NONBLOCK failed");
77 static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid)
80 snd_pcm_hw_t *hw = pcm->private;
83 if ((flags = fcntl(fd, F_GETFL)) < 0) {
84 SYSERR("F_GETFL failed");
91 if (fcntl(fd, F_SETFL, flags) < 0) {
92 SYSERR("F_SETFL for O_ASYNC failed");
99 if (fcntl(fd, F_SETSIG, sig) < 0) {
100 SYSERR("F_SETSIG failed");
105 if (fcntl(fd, F_SETOWN, pid) < 0) {
106 SYSERR("F_SETOWN failed");
112 static int _snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
114 snd_pcm_hw_t *hw = pcm->private;
116 if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0) {
117 SYSERR("SND_PCM_IOCTL_INFO failed");
123 static int snd_pcm_hw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
125 snd_pcm_hw_t *hw = pcm->private;
127 if (ioctl(fd, SND_PCM_IOCTL_HW_INFO, info) < 0) {
128 // SYSERR("SND_PCM_IOCTL_HW_INFO failed");
134 static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
136 snd_pcm_hw_t *hw = pcm->private;
138 if (ioctl(fd, SND_PCM_IOCTL_HW_PARAMS, params) < 0) {
139 SYSERR("SND_PCM_IOCTL_HW_PARAMS failed");
145 static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
147 snd_pcm_hw_t *hw = pcm->private;
149 if (ioctl(fd, SND_PCM_IOCTL_SW_PARAMS, params) < 0) {
150 SYSERR("SND_PCM_IOCTL_SW_PARAMS failed");
156 static int snd_pcm_hw_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info)
158 snd_pcm_hw_t *hw = pcm->private;
160 if (ioctl(fd, SND_PCM_IOCTL_DIG_INFO, info) < 0) {
161 SYSERR("SND_PCM_IOCTL_DIG_INFO failed");
167 static int snd_pcm_hw_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params)
169 snd_pcm_hw_t *hw = pcm->private;
171 if (ioctl(fd, SND_PCM_IOCTL_DIG_PARAMS, params) < 0) {
172 SYSERR("SND_PCM_IOCTL_DIG_PARAMS failed");
178 static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
180 snd_pcm_hw_t *hw = pcm->private;
181 snd_pcm_hw_channel_info_t hw_info;
183 hw_info.channel = info->channel;
184 if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &hw_info) < 0) {
185 SYSERR("SND_PCM_IOCTL_CHANNEL_INFO failed");
188 info->channel = hw_info.channel;
189 if (pcm->info & SND_PCM_INFO_MMAP) {
191 info->first = hw_info.first;
192 info->step = hw_info.step;
193 info->type = SND_PCM_AREA_MMAP;
194 info->u.mmap.fd = fd;
195 info->u.mmap.offset = hw_info.offset;
198 return snd_pcm_channel_info_shm(pcm, info, hw->shmid);
201 static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
203 snd_pcm_hw_t *hw = pcm->private;
205 if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0) {
206 SYSERR("SND_PCM_IOCTL_STATUS failed");
212 static int snd_pcm_hw_state(snd_pcm_t *pcm)
214 snd_pcm_hw_t *hw = pcm->private;
215 return hw->mmap_status->state;
218 static int snd_pcm_hw_delay(snd_pcm_t *pcm, ssize_t *delayp)
220 snd_pcm_hw_t *hw = pcm->private;
222 if (ioctl(fd, SND_PCM_IOCTL_DELAY, delayp) < 0) {
223 SYSERR("SND_PCM_IOCTL_DELAY failed");
229 static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
231 snd_pcm_hw_t *hw = pcm->private;
233 if (ioctl(fd, SND_PCM_IOCTL_PREPARE) < 0) {
234 SYSERR("SND_PCM_IOCTL_PREPARE failed");
240 static int snd_pcm_hw_reset(snd_pcm_t *pcm)
242 snd_pcm_hw_t *hw = pcm->private;
244 if (ioctl(fd, SND_PCM_IOCTL_RESET) < 0) {
245 SYSERR("SND_PCM_IOCTL_RESET failed");
251 static int snd_pcm_hw_start(snd_pcm_t *pcm)
253 snd_pcm_hw_t *hw = pcm->private;
256 assert(pcm->stream != SND_PCM_STREAM_PLAYBACK ||
257 snd_pcm_mmap_playback_hw_avail(pcm) > 0);
259 if (ioctl(fd, SND_PCM_IOCTL_START) < 0) {
260 SYSERR("SND_PCM_IOCTL_START failed");
266 static int snd_pcm_hw_drop(snd_pcm_t *pcm)
268 snd_pcm_hw_t *hw = pcm->private;
270 if (ioctl(fd, SND_PCM_IOCTL_DROP) < 0) {
271 SYSERR("SND_PCM_IOCTL_DROP failed");
277 static int snd_pcm_hw_drain(snd_pcm_t *pcm)
279 snd_pcm_hw_t *hw = pcm->private;
281 if (ioctl(fd, SND_PCM_IOCTL_DRAIN) < 0) {
282 SYSERR("SND_PCM_IOCTL_DRAIN failed");
288 static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
290 snd_pcm_hw_t *hw = pcm->private;
292 if (ioctl(fd, SND_PCM_IOCTL_PAUSE, enable) < 0) {
293 SYSERR("SND_PCM_IOCTL_PAUSE failed");
299 static ssize_t snd_pcm_hw_rewind(snd_pcm_t *pcm, size_t frames)
302 if (pcm->xrun_mode == SND_PCM_XRUN_ASAP) {
304 int err = snd_pcm_hw_delay(pcm, &d);
308 hw_avail = snd_pcm_mmap_hw_avail(pcm);
311 if (frames > (size_t)hw_avail)
313 snd_pcm_mmap_appl_backward(pcm, frames);
317 static ssize_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
320 snd_pcm_hw_t *hw = pcm->private;
323 xferi.buf = (char*) buffer;
325 result = ioctl(fd, SND_PCM_IOCTL_WRITEI_FRAMES, &xferi);
331 static ssize_t snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, size_t size)
334 snd_pcm_hw_t *hw = pcm->private;
339 result = ioctl(fd, SND_PCM_IOCTL_WRITEN_FRAMES, &xfern);
345 static ssize_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, size_t size)
348 snd_pcm_hw_t *hw = pcm->private;
353 result = ioctl(fd, SND_PCM_IOCTL_READI_FRAMES, &xferi);
359 ssize_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, size_t size)
362 snd_pcm_hw_t *hw = pcm->private;
367 result = ioctl(fd, SND_PCM_IOCTL_READN_FRAMES, &xfern);
373 static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm)
375 snd_pcm_hw_t *hw = pcm->private;
377 ptr = mmap(NULL, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)), PROT_READ, MAP_FILE|MAP_SHARED,
378 hw->fd, SND_PCM_MMAP_OFFSET_STATUS);
379 if (ptr == MAP_FAILED || ptr == NULL) {
380 SYSERR("status mmap failed");
383 hw->mmap_status = ptr;
384 pcm->hw_ptr = &hw->mmap_status->hw_ptr;
388 static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
390 snd_pcm_hw_t *hw = pcm->private;
392 ptr = mmap(NULL, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
393 hw->fd, SND_PCM_MMAP_OFFSET_CONTROL);
394 if (ptr == MAP_FAILED || ptr == NULL) {
395 SYSERR("control mmap failed");
398 hw->mmap_control = ptr;
399 pcm->appl_ptr = &hw->mmap_control->appl_ptr;
403 static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm)
405 snd_pcm_hw_t *hw = pcm->private;
406 if (munmap((void*)hw->mmap_status, PAGE_ALIGN(sizeof(*hw->mmap_status))) < 0) {
407 SYSERR("status munmap failed");
413 static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm)
415 snd_pcm_hw_t *hw = pcm->private;
416 if (munmap(hw->mmap_control, PAGE_ALIGN(sizeof(*hw->mmap_control))) < 0) {
417 SYSERR("control munmap failed");
423 static int snd_pcm_hw_mmap(snd_pcm_t *pcm)
425 snd_pcm_hw_t *hw = pcm->private;
426 if (!(pcm->info & SND_PCM_INFO_MMAP)) {
427 size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size);
428 int id = shmget(IPC_PRIVATE, size, 0666);
430 SYSERR("shmget failed");
438 static int snd_pcm_hw_munmap(snd_pcm_t *pcm)
440 snd_pcm_hw_t *hw = pcm->private;
441 if (!(pcm->info & SND_PCM_INFO_MMAP)) {
442 if (shmctl(hw->shmid, IPC_RMID, 0) < 0) {
443 SYSERR("shmctl IPC_RMID failed");
450 static int snd_pcm_hw_close(snd_pcm_t *pcm)
452 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);
463 static ssize_t snd_pcm_hw_mmap_forward(snd_pcm_t *pcm, size_t size)
465 if (!(pcm->info & SND_PCM_INFO_MMAP) &&
466 pcm->stream == SND_PCM_STREAM_PLAYBACK)
467 return snd_pcm_write_mmap(pcm, size);
468 snd_pcm_mmap_appl_forward(pcm, size);
472 static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
476 if (pcm->ready_mode == SND_PCM_READY_ASAP ||
477 pcm->xrun_mode == SND_PCM_XRUN_ASAP) {
479 int err = snd_pcm_hw_delay(pcm, &d);
483 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
484 avail = snd_pcm_mmap_playback_avail(pcm);
486 avail = snd_pcm_mmap_capture_avail(pcm);
488 !(pcm->info & SND_PCM_INFO_MMAP)) {
489 err = snd_pcm_read_mmap(pcm, avail);
492 assert((size_t)err == avail);
496 if (avail > pcm->buffer_size)
501 static int snd_pcm_hw_set_avail_min(snd_pcm_t *pcm, size_t frames)
503 snd_pcm_hw_t *hw = pcm->private;
504 hw->mmap_control->avail_min = frames;
508 static void snd_pcm_hw_dump(snd_pcm_t *pcm, FILE *fp)
510 snd_pcm_hw_t *hw = pcm->private;
511 char *name = "Unknown";
512 snd_card_get_name(hw->card, &name);
513 fprintf(fp, "Hardware PCM card %d '%s' device %d subdevice %d\n",
514 hw->card, name, hw->device, hw->subdevice);
517 fprintf(fp, "\nIts setup is:\n");
518 snd_pcm_dump_setup(pcm, fp);
522 snd_pcm_ops_t snd_pcm_hw_ops = {
523 close: snd_pcm_hw_close,
524 info: _snd_pcm_hw_info,
525 hw_info: snd_pcm_hw_hw_info,
526 hw_params: snd_pcm_hw_hw_params,
527 sw_params: snd_pcm_hw_sw_params,
528 dig_info: snd_pcm_hw_dig_info,
529 dig_params: snd_pcm_hw_dig_params,
530 channel_info: snd_pcm_hw_channel_info,
531 dump: snd_pcm_hw_dump,
532 nonblock: snd_pcm_hw_nonblock,
533 async: snd_pcm_hw_async,
534 mmap: snd_pcm_hw_mmap,
535 munmap: snd_pcm_hw_munmap,
538 snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
539 status: snd_pcm_hw_status,
540 state: snd_pcm_hw_state,
541 delay: snd_pcm_hw_delay,
542 prepare: snd_pcm_hw_prepare,
543 reset: snd_pcm_hw_reset,
544 start: snd_pcm_hw_start,
545 drop: snd_pcm_hw_drop,
546 drain: snd_pcm_hw_drain,
547 pause: snd_pcm_hw_pause,
548 rewind: snd_pcm_hw_rewind,
549 writei: snd_pcm_hw_writei,
550 writen: snd_pcm_hw_writen,
551 readi: snd_pcm_hw_readi,
552 readn: snd_pcm_hw_readn,
553 avail_update: snd_pcm_hw_avail_update,
554 mmap_forward: snd_pcm_hw_mmap_forward,
555 set_avail_min: snd_pcm_hw_set_avail_min,
558 int snd_pcm_hw_open_subdevice(snd_pcm_t **pcmp, int card, int device, int subdevice, int stream, int mode)
563 int ret = 0, fd = -1;
568 snd_pcm_t *pcm = NULL;
569 snd_pcm_hw_t *hw = NULL;
573 if ((ret = snd_ctl_hw_open(&ctl, NULL, card)) < 0)
577 case SND_PCM_STREAM_PLAYBACK:
578 filefmt = SND_FILE_PCM_STREAM_PLAYBACK;
580 case SND_PCM_STREAM_CAPTURE:
581 filefmt = SND_FILE_PCM_STREAM_CAPTURE;
586 sprintf(filename, filefmt, card, device);
593 ret = snd_ctl_pcm_prefer_subdevice(ctl, subdevice);
597 if (mode & SND_PCM_NONBLOCK)
599 if (mode & SND_PCM_ASYNC)
601 if ((fd = open(filename, fmode)) < 0) {
602 SYSERR("open %s failed", filename);
606 if (ioctl(fd, SND_PCM_IOCTL_PVERSION, &ver) < 0) {
607 SYSERR("SND_PCM_IOCTL_PVERSION failed");
611 if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_PCM_VERSION_MAX)) {
612 ret = -SND_ERROR_INCOMPATIBLE_VERSION;
615 if (subdevice >= 0) {
616 memset(&info, 0, sizeof(info));
617 if (ioctl(fd, SND_PCM_IOCTL_INFO, &info) < 0) {
618 SYSERR("SND_PCM_IOCTL_INFO failed");
622 if (info.subdevice != subdevice) {
627 hw = calloc(1, sizeof(snd_pcm_hw_t));
634 hw->subdevice = subdevice;
637 pcm = calloc(1, sizeof(snd_pcm_t));
643 pcm->type = SND_PCM_TYPE_HW;
644 pcm->stream = stream;
646 pcm->ops = &snd_pcm_hw_ops;
648 pcm->fast_ops = &snd_pcm_hw_fast_ops;
649 pcm->fast_op_arg = pcm;
653 ret = snd_pcm_hw_mmap_status(pcm);
658 ret = snd_pcm_hw_mmap_control(pcm);
676 int snd_pcm_hw_open_device(snd_pcm_t **pcmp, int card, int device, int stream, int mode)
678 return snd_pcm_hw_open_subdevice(pcmp, card, device, -1, stream, mode);
681 int snd_pcm_hw_open(snd_pcm_t **pcmp, char *name, int card, int device, int subdevice, int stream, int mode)
683 int err = snd_pcm_hw_open_subdevice(pcmp, card, device, subdevice, stream, mode);
687 (*pcmp)->name = strdup(name);
691 int _snd_pcm_hw_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
692 int stream, int mode)
694 snd_config_iterator_t i;
695 long card = -1, device = 0, subdevice = -1;
698 snd_config_foreach(i, conf) {
699 snd_config_t *n = snd_config_entry(i);
700 if (strcmp(n->id, "comment") == 0)
702 if (strcmp(n->id, "type") == 0)
704 if (strcmp(n->id, "stream") == 0)
706 if (strcmp(n->id, "card") == 0) {
707 err = snd_config_integer_get(n, &card);
709 err = snd_config_string_get(n, &str);
712 card = snd_card_get_index(str);
718 if (strcmp(n->id, "device") == 0) {
719 err = snd_config_integer_get(n, &device);
724 if (strcmp(n->id, "subdevice") == 0) {
725 err = snd_config_integer_get(n, &subdevice);
734 return snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, mode);