aboutsummaryrefslogtreecommitdiffstats
path: root/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'pcm.c')
-rw-r--r--pcm.c258
1 files changed, 30 insertions, 228 deletions
diff --git a/pcm.c b/pcm.c
index 509eae7..411db40 100644
--- a/pcm.c
+++ b/pcm.c
@@ -15,10 +15,20 @@
#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args)
#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args)
-#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \
- (((ap)->pcm_ops->callback) ? \
- (ap)->pcm_ops->callback((substream), ##__VA_ARGS__) : \
- 0)
+/**
+ * DO_AUDIOPATH_CB - Used to call into an Audio Path's ALSA PCM callback from
+ * AVIRT, where AVIRT's own callback must be attended to first.
+ */
+#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \
+ ((!substream->stream) ? \
+ (((ap)->pcm_playback_ops->callback) ? \
+ (ap)->pcm_playback_ops->callback((substream), \
+ ##__VA_ARGS__) : \
+ 0) : \
+ (((ap)->pcm_capture_ops->callback) ? \
+ (ap)->pcm_capture_ops->callback((substream), \
+ ##__VA_ARGS__) : \
+ 0))
#define PRIVATE_DATA(substream) \
((struct snd_avirt_private_data *)substream->private_data)
@@ -57,9 +67,14 @@ static int pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_hardware *hw;
unsigned int chans = 0;
+ // Find the Audio Path mapped to this device
stream = snd_avirt_stream_find_by_device(substream->pcm->device);
+ if (IS_ERR_VALUE(stream) || !stream)
+ return PTR_ERR(stream);
audiopath = snd_avirt_audiopath_get(stream->map);
CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", stream->map);
+
+ // Set the private data's Audio Path
avirt_private_data = substream->private_data;
avirt_private_data->audiopath = audiopath;
@@ -67,10 +82,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
hw = &substream->runtime->hw;
memcpy(hw, audiopath->hw, sizeof(struct snd_pcm_hardware));
- stream = snd_avirt_stream_find_by_device(substream->pcm->device);
- if (IS_ERR_VALUE(stream) || !stream)
- return PTR_ERR(stream);
-
// Setup remaining hw properties
chans = stream->channels;
hw->channels_min = chans;
@@ -81,22 +92,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
}
/**
- * pcm_close - Implements 'close' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- *
- * This is called when a PCM substream is closed.
- *
- * Returns 0 on success or error code otherwise.
- */
-static int pcm_close(struct snd_pcm_substream *substream)
-{
- // Do additional Audio Path 'close' callback
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- close, substream);
-}
-
-/**
* pcm_hw_params - Implements 'hw_params' callback for PCM middle layer
* @substream: pointer to ALSA PCM substream
* @hw_params: contains the hardware parameters for the PCM
@@ -111,23 +106,19 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
{
int retval;
size_t bufsz;
- struct snd_avirt_audiopath *audiopath;
- struct snd_avirt_stream *stream;
- stream = snd_avirt_stream_find_by_device(substream->pcm->device);
- if (IS_ERR_VALUE(stream) || !stream)
- return PTR_ERR(stream);
-
- if ((params_channels(hw_params) > stream->channels) ||
- (params_channels(hw_params) < stream->channels)) {
+ /* Check supported channels */
+ if ((params_channels(hw_params) <
+ substream->runtime->hw.channels_min) ||
+ (params_channels(hw_params) >
+ substream->runtime->hw.channels_max)) {
D_ERRORK("Requested number of channels: %d not supported",
params_channels(hw_params));
return -EINVAL;
}
- audiopath =
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath;
- bufsz = params_buffer_bytes(hw_params) * audiopath->hw->periods_max;
+ bufsz = params_buffer_bytes(hw_params) *
+ substream->runtime->hw.periods_max;
retval = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz);
if (retval < 0)
@@ -152,208 +143,19 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
// Do additional Audio Path 'hw_free' callback
err = DO_AUDIOPATH_CB(
(struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- hw_free, substream);
+ hw_free, substream);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
/**
- * pcm_prepare - Implements 'prepare' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- *
- * The format rate, sample rate, etc., can be set here. This callback can be
- * called many times at each setup. This function is also used to handle overrun
- * and underrun conditions when we try and resync the DMA (if we're using DMA).
- *
- * Returns 0 on success or error code otherwise.
+ * Default PCM ops table. Selected PCM ops must pass through AVIRT before
+ * calling back to their respective Audio Paths
*/
-static int pcm_prepare(struct snd_pcm_substream *substream)
-{
-
- // Do additional Audio Path 'prepare' callback
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- prepare, substream);
-}
-
-/**
- * pcm_trigger - Implements 'trigger' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- * @cmd: action to be performed (start or stop)
- *
- * This is called when the PCM is started, stopped or paused. The action
- * indicated action is specified in the second argument, SNDRV_PCM_TRIGGER_XXX
- *
- * Returns 0 on success or error code otherwise.
- */
-static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- break;
- default:
- D_ERRORK("Invalid trigger cmd: %d", cmd);
- return -EINVAL;
- }
-
- // Do additional Audio Path 'trigger' callback
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- trigger, substream, cmd);
-}
-
-/**
- * pcm_pointer - Implements 'pointer' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- *
- * This gets called when the user space needs a DMA buffer index. IO errors will
- * be generated if the index does not increment, or drives beyond the frame
- * threshold of the buffer itself.
- *
- * Returns the current hardware buffer frame index.
- */
-static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
-{
- // Do additional Audio Path 'pointer' callback
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- pointer, substream);
-}
-
-/**
- * pcm_pointer - Implements 'get_time_info' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- * @system_ts
- * @audio_ts
- * @audio_tstamp_config
- * @audio_tstamp_report
- *
- * Generic way to get system timestamp and audio timestamp info
- *
- * Returns 0 on success or error code otherwise
- */
-static int
-pcm_get_time_info(struct snd_pcm_substream *substream,
- struct timespec *system_ts, struct timespec *audio_ts,
- struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
- struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
-{
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- get_time_info, substream, system_ts, audio_ts,
- audio_tstamp_config, audio_tstamp_report);
-}
-
-/**
- * pcm_copy_user - Implements 'copy_user' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- * @channel:
- * @pos: The offset in the DMA
- * @src: Audio PCM data from the user space
- * @count:
- *
- * This is where we need to copy user audio PCM data into the sound driver
- *
- * Returns 0 on success or error code otherwise.
- *
- */
-static int pcm_copy_user(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void __user *src,
- snd_pcm_uframes_t count)
-{
-
- // struct snd_pcm_runtime *runtime;
- // int offset;
-
- // runtime = substream->runtime;
- // offset = frames_to_bytes(runtime, pos);
-
- // Do additional Audio Path 'copy_user' callback
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- copy_user, substream, channel, pos, src, count);
-}
-
-/**
- * pcm_copy_kernel - Implements 'copy_kernel' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- * @channel:
- * @pos: The offset in the DMA
- * @src: Audio PCM data from the user space
- * @count:
- *
- * This is where we need to copy kernel audio PCM data into the sound driver
- *
- * Returns 0 on success or error code otherwise.
- *
- */
-static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel,
- unsigned long pos, void *buf, unsigned long count)
-{
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- copy_kernel, substream, channel, pos, buf, count);
-}
-
-/**
- * pcm_ack - Implements 'ack' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- *
- * This is where we need to ack
- *
- * Returns 0 on success or error code otherwise.
- *
- */
-static int pcm_ack(struct snd_pcm_substream *substream)
-{
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- ack, substream);
-}
-
-static int pcm_silence(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
-{
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- fill_silence, substream, channel, pos, count);
-}
-
-/**
- * pcm_mmap - Implements 'mmap' callback for PCM middle layer
- * @substream: pointer to ALSA PCM substream
- *
- * This is where we need to ack
- *
- * Returns 0 on success or error code otherwise.
- *
- */
-static int pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- return DO_AUDIOPATH_CB(
- (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
- mmap, substream, vma);
-}
-
-struct snd_pcm_ops pcm_ops = {
+struct snd_pcm_ops pcm_ops_avirt = {
.open = pcm_open,
- .close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_hw_params,
.hw_free = pcm_hw_free,
- .prepare = pcm_prepare,
- .trigger = pcm_trigger,
- .pointer = pcm_pointer,
- .get_time_info = pcm_get_time_info,
- .fill_silence = pcm_silence,
- .copy_user = pcm_copy_user,
- .copy_kernel = pcm_copy_kernel,
.page = snd_pcm_lib_get_vmalloc_page,
- .ack = pcm_ack,
- .mmap = pcm_mmap,
};