aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mixer.c10
-rw-r--r--src/pcm.c207
2 files changed, 211 insertions, 6 deletions
diff --git a/src/mixer.c b/src/mixer.c
index 6a104fe..fe590e8 100644
--- a/src/mixer.c
+++ b/src/mixer.c
@@ -990,6 +990,11 @@ int mixer_ctl_get_array(const struct mixer_ctl *ctl, void *array, size_t count)
break;
}
+ case SNDRV_CTL_ELEM_TYPE_IEC958:
+ size = sizeof(ev.value.iec958);
+ source = &ev.value.iec958;
+ break;
+
default:
return -EINVAL;
}
@@ -1116,6 +1121,11 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
}
break;
+ case SNDRV_CTL_ELEM_TYPE_IEC958:
+ size = sizeof(ev.value.iec958);
+ dest = &ev.value.iec958;
+ break;
+
default:
return -EINVAL;
}
diff --git a/src/pcm.c b/src/pcm.c
index f0751f7..352ddc6 100644
--- a/src/pcm.c
+++ b/src/pcm.c
@@ -70,6 +70,83 @@
#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
#endif /* SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP */
+/* Logs information into a string; follows snprintf() in that
+ * offset may be greater than size, and though no characters are copied
+ * into string, characters are still counted into offset. */
+#define STRLOG(string, offset, size, ...) \
+ do { int temp, clipoffset = offset > size ? size : offset; \
+ temp = snprintf(string + clipoffset, size - clipoffset, __VA_ARGS__); \
+ if (temp > 0) offset += temp; } while (0)
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+/* refer to SNDRV_PCM_ACCESS_##index in sound/asound.h. */
+static const char * const access_lookup[] = {
+ "MMAP_INTERLEAVED",
+ "MMAP_NONINTERLEAVED",
+ "MMAP_COMPLEX",
+ "RW_INTERLEAVED",
+ "RW_NONINTERLEAVED",
+};
+
+/* refer to SNDRV_PCM_FORMAT_##index in sound/asound.h. */
+static const char * const format_lookup[] = {
+ /*[0] =*/ "S8",
+ "U8",
+ "S16_LE",
+ "S16_BE",
+ "U16_LE",
+ "U16_BE",
+ "S24_LE",
+ "S24_BE",
+ "U24_LE",
+ "U24_BE",
+ "S32_LE",
+ "S32_BE",
+ "U32_LE",
+ "U32_BE",
+ "FLOAT_LE",
+ "FLOAT_BE",
+ "FLOAT64_LE",
+ "FLOAT64_BE",
+ "IEC958_SUBFRAME_LE",
+ "IEC958_SUBFRAME_BE",
+ "MU_LAW",
+ "A_LAW",
+ "IMA_ADPCM",
+ "MPEG",
+ /*[24] =*/ "GSM",
+ /* gap */
+ [31] = "SPECIAL",
+ "S24_3LE",
+ "S24_3BE",
+ "U24_3LE",
+ "U24_3BE",
+ "S20_3LE",
+ "S20_3BE",
+ "U20_3LE",
+ "U20_3BE",
+ "S18_3LE",
+ "S18_3BE",
+ "U18_3LE",
+ /*[43] =*/ "U18_3BE",
+#if 0
+ /* recent additions, may not be present on local asound.h */
+ "G723_24",
+ "G723_24_1B",
+ "G723_40",
+ "G723_40_1B",
+ "DSD_U8",
+ "DSD_U16_LE",
+#endif
+};
+
+/* refer to SNDRV_PCM_SUBFORMAT_##index in sound/asound.h. */
+static const char * const subformat_lookup[] = {
+ "STD",
+};
static inline int param_is_mask(int p)
{
@@ -255,7 +332,7 @@ static int oops(struct pcm *pcm, int e, const char *fmt, ...)
va_end(ap);
sz = strlen(pcm->error);
- if (errno)
+ if (e)
snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
": %s", strerror(e));
return -1;
@@ -361,6 +438,7 @@ int pcm_set_config(struct pcm *pcm, const struct pcm_config *config)
pcm->config.start_threshold = config->period_count * config->period_size;
pcm->config.stop_threshold = config->period_count * config->period_size;
pcm->config.silence_threshold = 0;
+ pcm->config.silence_size = 0;
} else
pcm->config = *config;
@@ -377,7 +455,7 @@ int pcm_set_config(struct pcm *pcm, const struct pcm_config *config)
if (pcm->flags & PCM_NOIRQ) {
if (!(pcm->flags & PCM_MMAP)) {
- oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
+ oops(pcm, EINVAL, "noirq only currently supported with mmap().");
return -EINVAL;
}
@@ -442,7 +520,7 @@ int pcm_set_config(struct pcm *pcm, const struct pcm_config *config)
sparams.stop_threshold = config->stop_threshold;
sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
- sparams.silence_size = 0;
+ sparams.silence_size = config->silence_size;
sparams.silence_threshold = config->silence_threshold;
pcm->boundary = sparams.boundary = pcm->buffer_size;
@@ -798,6 +876,87 @@ unsigned int pcm_params_get_max(const struct pcm_params *pcm_params,
return param_get_max(params, p);
}
+static int pcm_mask_test(const struct pcm_mask *m, unsigned int index)
+{
+ const unsigned int bitshift = 5; /* for 32 bit integer */
+ const unsigned int bitmask = (1 << bitshift) - 1;
+ unsigned int element;
+
+ element = index >> bitshift;
+ if (element >= ARRAY_SIZE(m->bits))
+ return 0; /* for safety, but should never occur */
+ return (m->bits[element] >> (index & bitmask)) & 1;
+}
+
+static int pcm_mask_to_string(const struct pcm_mask *m, char *string, unsigned int size,
+ char *mask_name,
+ const char * const *bit_array_name, size_t bit_array_size)
+{
+ unsigned int i;
+ unsigned int offset = 0;
+
+ if (m == NULL)
+ return 0;
+ if (bit_array_size < 32) {
+ STRLOG(string, offset, size, "%12s:\t%#08x\n", mask_name, m->bits[0]);
+ } else { /* spans two or more bitfields, print with an array index */
+ for (i = 0; i < (bit_array_size + 31) >> 5; ++i) {
+ STRLOG(string, offset, size, "%9s[%d]:\t%#08x\n",
+ mask_name, i, m->bits[i]);
+ }
+ }
+ for (i = 0; i < bit_array_size; ++i) {
+ if (pcm_mask_test(m, i)) {
+ STRLOG(string, offset, size, "%12s \t%s\n", "", bit_array_name[i]);
+ }
+ }
+ return offset;
+}
+
+int pcm_params_to_string(struct pcm_params *params, char *string, unsigned int size)
+{
+ const struct pcm_mask *m;
+ unsigned int min, max;
+ unsigned int clipoffset, offset;
+
+ m = pcm_params_get_mask(params, PCM_PARAM_ACCESS);
+ offset = pcm_mask_to_string(m, string, size,
+ "Access", access_lookup, ARRAY_SIZE(access_lookup));
+ m = pcm_params_get_mask(params, PCM_PARAM_FORMAT);
+ clipoffset = offset > size ? size : offset;
+ offset += pcm_mask_to_string(m, string + clipoffset, size - clipoffset,
+ "Format", format_lookup, ARRAY_SIZE(format_lookup));
+ m = pcm_params_get_mask(params, PCM_PARAM_SUBFORMAT);
+ clipoffset = offset > size ? size : offset;
+ offset += pcm_mask_to_string(m, string + clipoffset, size - clipoffset,
+ "Subformat", subformat_lookup, ARRAY_SIZE(subformat_lookup));
+ min = pcm_params_get_min(params, PCM_PARAM_RATE);
+ max = pcm_params_get_max(params, PCM_PARAM_RATE);
+ STRLOG(string, offset, size, " Rate:\tmin=%uHz\tmax=%uHz\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
+ max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
+ STRLOG(string, offset, size, " Channels:\tmin=%u\t\tmax=%u\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
+ max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
+ STRLOG(string, offset, size, " Sample bits:\tmin=%u\t\tmax=%u\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
+ max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
+ STRLOG(string, offset, size, " Period size:\tmin=%u\t\tmax=%u\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
+ max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
+ STRLOG(string, offset, size, "Period count:\tmin=%u\t\tmax=%u\n", min, max);
+ return offset;
+}
+
+int pcm_params_format_test(struct pcm_params *params, enum pcm_format format)
+{
+ unsigned int alsa_format = pcm_format_to_alsa(format);
+
+ if (alsa_format == SNDRV_PCM_FORMAT_S16_LE && format != PCM_FORMAT_S16_LE)
+ return 0; /* caution: format not recognized is equivalent to S16_LE */
+ return pcm_mask_test(pcm_params_get_mask(params, PCM_PARAM_FORMAT), alsa_format);
+}
+
/** Closes a PCM returned by @ref pcm_open.
* @param pcm A PCM returned by @ref pcm_open.
* May not be NULL.
@@ -923,7 +1082,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
rc = pcm_hw_mmap_status(pcm);
if (rc < 0) {
- oops(pcm, rc, "mmap status failed");
+ oops(pcm, errno, "mmap status failed");
goto fail;
}
@@ -932,7 +1091,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
rc = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
if (rc < 0) {
- oops(pcm, rc, "cannot set timestamp type");
+ oops(pcm, errno, "cannot set timestamp type");
goto fail;
}
}
@@ -1077,7 +1236,7 @@ static inline int pcm_mmap_capture_avail(struct pcm *pcm)
return avail;
}
-static inline int pcm_mmap_avail(struct pcm *pcm)
+int pcm_mmap_avail(struct pcm *pcm)
{
if (pcm->flags & PCM_IN)
return pcm_mmap_capture_avail(pcm);
@@ -1185,6 +1344,11 @@ static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
return count;
}
+int pcm_get_poll_fd(struct pcm *pcm)
+{
+ return pcm->fd;
+}
+
int pcm_avail_update(struct pcm *pcm)
{
pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
@@ -1396,6 +1560,37 @@ int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
return pcm_mmap_transfer(pcm, data, pcm_bytes_to_frames(pcm, count));
}
+/* Returns current read/write position in the mmap buffer with associated time stamp. */
+int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp)
+{
+ int rc;
+
+ if (pcm == NULL || hw_ptr == NULL || tstamp == NULL)
+ return oops(pcm, EINVAL, "pcm %p, hw_ptr %p, tstamp %p", pcm, hw_ptr, tstamp);
+
+ if (!pcm_is_ready(pcm))
+ return oops(pcm, errno, "pcm_is_ready failed");
+
+ rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
+ if (rc < 0)
+ return oops(pcm, errno, "pcm_sync_ptr failed");
+
+ if (pcm->mmap_status == NULL)
+ return oops(pcm, EINVAL, "pcm %p, mmap_status is NULL", pcm);
+
+ if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
+ (pcm->mmap_status->state != PCM_STATE_DRAINING))
+ return oops(pcm, ENOSYS, "invalid stream state %d", pcm->mmap_status->state);
+
+ *tstamp = pcm->mmap_status->tstamp;
+ if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
+ return oops(pcm, errno, "invalid time stamp");
+
+ *hw_ptr = pcm->mmap_status->hw_ptr;
+
+ return 0;
+}
+
static int pcm_rw_transfer(struct pcm *pcm, void *data, unsigned int frames)
{
int is_playback;