From 51ef9c22560fa2806448797c3a6a49e5d3f61964 Mon Sep 17 00:00:00 2001 From: Sean McNeil Date: Thu, 16 Apr 2009 07:48:56 +0700 Subject: Sync with main branch. Cupcake has same shared memory support removal. --- src/pcm/pcm_hw.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 202 insertions(+), 8 deletions(-) (limited to 'src/pcm/pcm_hw.c') diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 8495f71..95bb2ac 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -36,9 +36,9 @@ #include #include #include -#include #include "pcm_local.h" #include "../control/control_local.h" +#include "../timer/timer_local.h" //#define DEBUG_RW /* use to debug readi/writei/readn/writen */ //#define DEBUG_MMAP /* debug mmap_commit */ @@ -79,6 +79,8 @@ struct sndrv_pcm_hw_params_old { static int use_old_hw_params_ioctl(int fd, unsigned int cmd, snd_pcm_hw_params_t *params); static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm); +static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops; +static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops_timer; /* * @@ -94,6 +96,10 @@ typedef struct { struct sndrv_pcm_sync_ptr *sync_ptr; snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t appl_ptr; + int period_event; + snd_timer_t *period_timer; + struct pollfd period_timer_pfd; + int period_timer_need_poll; /* restricted parameters */ snd_pcm_format_t format; int rate; @@ -139,6 +145,54 @@ static inline int sync_ptr(snd_pcm_hw_t *hw, unsigned int flags) return hw->sync_ptr ? sync_ptr1(hw, flags) : 0; } +static int snd_pcm_hw_clear_timer_queue(snd_pcm_hw_t *hw) +{ + if (hw->period_timer_need_poll) { + while (poll(&hw->period_timer_pfd, 1, 0) > 0) { + snd_timer_tread_t rbuf[4]; + snd_timer_read(hw->period_timer, rbuf, sizeof(rbuf)); + } + } else { + snd_timer_tread_t rbuf[4]; + snd_timer_read(hw->period_timer, rbuf, sizeof(rbuf)); + } + return 0; +} + +static int snd_pcm_hw_poll_descriptors_count(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 2; +} + +static int snd_pcm_hw_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space) +{ + snd_pcm_hw_t *hw = pcm->private_data; + + if (space < 2) + return -ENOMEM; + pfds[0].fd = hw->fd; + pfds[0].events = pcm->poll_events | POLLERR | POLLNVAL; + pfds[1].fd = hw->period_timer_pfd.fd; + pfds[1].events = POLLIN | POLLERR | POLLNVAL; + return 2; +} + +static int snd_pcm_hw_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned nfds, unsigned short *revents) +{ + snd_pcm_hw_t *hw = pcm->private_data; + unsigned int events; + + if (nfds != 2 || pfds[0].fd != hw->fd || pfds[1].fd != hw->period_timer_pfd.fd) + return -EINVAL; + events = pfds[0].revents; + if (pfds[1].revents & POLLIN) { + snd_pcm_hw_clear_timer_queue(hw); + events |= pcm->poll_events & ~(POLLERR|POLLNVAL); + } + *revents = events; + return 0; +} + static int snd_pcm_hw_nonblock(snd_pcm_t *pcm, int nonblock) { long flags; @@ -245,6 +299,11 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) // SYSMSG("SNDRV_PCM_IOCTL_HW_REFINE failed"); return err; } + + if (params->info != ~0U) { + params->info &= ~0xf0000000; + params->info |= (pcm->monotonic ? SND_PCM_INFO_MONOTONIC : 0); + } return 0; } @@ -288,16 +347,94 @@ static int snd_pcm_hw_hw_free(snd_pcm_t *pcm) return 0; } +static void snd_pcm_hw_close_timer(snd_pcm_hw_t *hw) +{ + if (hw->period_timer) { + snd_timer_close(hw->period_timer); + hw->period_timer = NULL; + } +} + +static int snd_pcm_hw_change_timer(snd_pcm_t *pcm, int enable) +{ + snd_pcm_hw_t *hw = pcm->private_data; + snd_timer_params_t *params; + unsigned int suspend, resume; + int err; + + if (enable) { + snd_timer_params_alloca(¶ms); + err = snd_timer_hw_open(&hw->period_timer, "hw-pcm-period-event", SND_TIMER_CLASS_PCM, SND_TIMER_SCLASS_NONE, hw->card, hw->device, hw->subdevice, SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD); + if (err < 0) { + err = snd_timer_hw_open(&hw->period_timer, "hw-pcm-period-event", SND_TIMER_CLASS_PCM, SND_TIMER_SCLASS_NONE, hw->card, hw->device, hw->subdevice, SND_TIMER_OPEN_NONBLOCK); + return err; + } + if (snd_timer_poll_descriptors_count(hw->period_timer) != 1) { + snd_pcm_hw_close_timer(hw); + return -EINVAL; + } + hw->period_timer_pfd.events = POLLIN; + hw->period_timer_pfd.revents = 0; + snd_timer_poll_descriptors(hw->period_timer, &hw->period_timer_pfd, 1); + hw->period_timer_need_poll = 0; + suspend = 1<period_timer_pfd.fd, SNDRV_TIMER_IOCTL_PVERSION, &ver); + /* In older versions, check via poll before read() is needed + * because of the confliction between TIMER_START and + * FIONBIO ioctls. + */ + if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 4)) + hw->period_timer_need_poll = 1; + /* + * In older versions, timer uses pause events instead + * suspend/resume events. + */ + if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 5)) { + suspend = 1<period_timer, params); + if (err < 0) { + snd_pcm_hw_close_timer(hw); + return err; + } + err = snd_timer_start(hw->period_timer); + if (err < 0) { + snd_pcm_hw_close_timer(hw); + return err; + } + pcm->fast_ops = &snd_pcm_hw_fast_ops_timer; + } else { + snd_pcm_hw_close_timer(hw); + pcm->fast_ops = &snd_pcm_hw_fast_ops; + } + return 0; +} + static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) { snd_pcm_hw_t *hw = pcm->private_data; int fd = hw->fd, err; + int old_period_event = params->period_event; + params->period_event = 0; if ((snd_pcm_tstamp_t) params->tstamp_mode == pcm->tstamp_mode && params->period_step == pcm->period_step && params->start_threshold == pcm->start_threshold && params->stop_threshold == pcm->stop_threshold && params->silence_threshold == pcm->silence_threshold && - params->silence_size == pcm->silence_size) { + params->silence_size == pcm->silence_size && + old_period_event == hw->period_event) { hw->mmap_control->avail_min = params->avail_min; return sync_ptr(hw, 0); } @@ -306,7 +443,14 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed"); return err; } + params->period_event = old_period_event; hw->mmap_control->avail_min = params->avail_min; + if (hw->period_event != old_period_event) { + err = snd_pcm_hw_change_timer(pcm, old_period_event); + if (err < 0) + return err; + hw->period_event = old_period_event; + } return 0; } @@ -475,6 +619,7 @@ static int snd_pcm_hw_drop(snd_pcm_t *pcm) err = -errno; SYSMSG("SNDRV_PCM_IOCTL_DROP failed"); return err; + } else { } return 0; } @@ -503,6 +648,11 @@ static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable) return 0; } +static snd_pcm_sframes_t snd_pcm_hw_rewindable(snd_pcm_t *pcm) +{ + return snd_pcm_mmap_hw_avail(pcm); +} + static snd_pcm_sframes_t snd_pcm_hw_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { snd_pcm_hw_t *hw = pcm->private_data; @@ -518,6 +668,11 @@ static snd_pcm_sframes_t snd_pcm_hw_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t fra return frames; } +static snd_pcm_sframes_t snd_pcm_hw_forwardable(snd_pcm_t *pcm) +{ + return snd_pcm_mmap_avail(pcm); +} + static snd_pcm_sframes_t snd_pcm_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { snd_pcm_hw_t *hw = pcm->private_data; @@ -884,7 +1039,7 @@ static void snd_pcm_hw_dump(snd_pcm_t *pcm, snd_output_t *out) } } -static snd_pcm_ops_t snd_pcm_hw_ops = { +static const snd_pcm_ops_t snd_pcm_hw_ops = { .close = snd_pcm_hw_close, .info = snd_pcm_hw_info, .hw_refine = snd_pcm_hw_hw_refine, @@ -899,7 +1054,7 @@ static snd_pcm_ops_t snd_pcm_hw_ops = { .munmap = snd_pcm_hw_munmap, }; -static snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = { +static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = { .status = snd_pcm_hw_status, .state = snd_pcm_hw_state, .hwsync = snd_pcm_hw_hwsync, @@ -910,7 +1065,9 @@ static snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = { .drop = snd_pcm_hw_drop, .drain = snd_pcm_hw_drain, .pause = snd_pcm_hw_pause, + .rewindable = snd_pcm_hw_rewindable, .rewind = snd_pcm_hw_rewind, + .forwardable = snd_pcm_hw_forwardable, .forward = snd_pcm_hw_forward, .resume = snd_pcm_hw_resume, .link = snd_pcm_hw_link, @@ -928,6 +1085,37 @@ static snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = { .poll_revents = NULL, }; +static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops_timer = { + .status = snd_pcm_hw_status, + .state = snd_pcm_hw_state, + .hwsync = snd_pcm_hw_hwsync, + .delay = snd_pcm_hw_delay, + .prepare = snd_pcm_hw_prepare, + .reset = snd_pcm_hw_reset, + .start = snd_pcm_hw_start, + .drop = snd_pcm_hw_drop, + .drain = snd_pcm_hw_drain, + .pause = snd_pcm_hw_pause, + .rewindable = snd_pcm_hw_rewindable, + .rewind = snd_pcm_hw_rewind, + .forwardable = snd_pcm_hw_forwardable, + .forward = snd_pcm_hw_forward, + .resume = snd_pcm_hw_resume, + .link = snd_pcm_hw_link, + .link_slaves = snd_pcm_hw_link_slaves, + .unlink = snd_pcm_hw_unlink, + .writei = snd_pcm_hw_writei, + .writen = snd_pcm_hw_writen, + .readi = snd_pcm_hw_readi, + .readn = snd_pcm_hw_readn, + .avail_update = snd_pcm_hw_avail_update, + .mmap_commit = snd_pcm_hw_mmap_commit, + .htimestamp = snd_pcm_hw_htimestamp, + .poll_descriptors = snd_pcm_hw_poll_descriptors, + .poll_descriptors_count = snd_pcm_hw_poll_descriptors_count, + .poll_revents = snd_pcm_hw_poll_revents, +}; + /** * \brief Creates a new hw PCM * \param pcmp Returns created PCM handle @@ -994,7 +1182,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_PCM_VERSION_MAX)) return -SND_ERROR_INCOMPATIBLE_VERSION; -#ifdef HAVE_CLOCK_GETTIME +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) { struct timespec timespec; if (clock_gettime(CLOCK_MONOTONIC, ×pec) == 0) { @@ -1006,9 +1194,9 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, } monotonic = 1; } - } + } else #endif - else if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) { + if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) { int on = 1; if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { ret = -errno; @@ -1318,7 +1506,13 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, /* revert to blocking mode for read/write access */ snd_pcm_hw_nonblock(*pcmp, 0); (*pcmp)->mode = mode; - } + } else + /* make sure the SND_PCM_NO_xxx flags don't get lost on the + * way */ + (*pcmp)->mode |= mode & (SND_PCM_NO_AUTO_RESAMPLE| + SND_PCM_NO_AUTO_CHANNELS| + SND_PCM_NO_AUTO_FORMAT| + SND_PCM_NO_SOFTVOL); hw = (*pcmp)->private_data; if (format != SND_PCM_FORMAT_UNKNOWN) -- cgit v1.2.3