linux/include/sound/pcm-indirect.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * Helper functions for indirect PCM data transfer
   4 *
   5 *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
   6 *                   Jaroslav Kysela <perex@perex.cz>
   7 */
   8
   9#ifndef __SOUND_PCM_INDIRECT_H
  10#define __SOUND_PCM_INDIRECT_H
  11
  12#include <sound/pcm.h>
  13
  14struct snd_pcm_indirect {
  15        unsigned int hw_buffer_size;    /* Byte size of hardware buffer */
  16        unsigned int hw_queue_size;     /* Max queue size of hw buffer (0 = buffer size) */
  17        unsigned int hw_data;   /* Offset to next dst (or src) in hw ring buffer */
  18        unsigned int hw_io;     /* Ring buffer hw pointer */
  19        int hw_ready;           /* Bytes ready for play (or captured) in hw ring buffer */
  20        unsigned int sw_buffer_size;    /* Byte size of software buffer */
  21        unsigned int sw_data;   /* Offset to next dst (or src) in sw ring buffer */
  22        unsigned int sw_io;     /* Current software pointer in bytes */
  23        int sw_ready;           /* Bytes ready to be transferred to/from hw */
  24        snd_pcm_uframes_t appl_ptr;     /* Last seen appl_ptr */
  25};
  26
  27typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
  28                                        struct snd_pcm_indirect *rec, size_t bytes);
  29
  30/*
  31 * helper function for playback ack callback
  32 */
  33static inline int
  34snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
  35                                   struct snd_pcm_indirect *rec,
  36                                   snd_pcm_indirect_copy_t copy)
  37{
  38        struct snd_pcm_runtime *runtime = substream->runtime;
  39        snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  40        snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  41        int qsize;
  42
  43        if (diff) {
  44                if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  45                        diff += runtime->boundary;
  46                if (diff < 0)
  47                        return -EINVAL;
  48                rec->sw_ready += (int)frames_to_bytes(runtime, diff);
  49                rec->appl_ptr = appl_ptr;
  50        }
  51        qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  52        while (rec->hw_ready < qsize && rec->sw_ready > 0) {
  53                unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
  54                unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
  55                unsigned int bytes = qsize - rec->hw_ready;
  56                if (rec->sw_ready < (int)bytes)
  57                        bytes = rec->sw_ready;
  58                if (hw_to_end < bytes)
  59                        bytes = hw_to_end;
  60                if (sw_to_end < bytes)
  61                        bytes = sw_to_end;
  62                if (! bytes)
  63                        break;
  64                copy(substream, rec, bytes);
  65                rec->hw_data += bytes;
  66                if (rec->hw_data == rec->hw_buffer_size)
  67                        rec->hw_data = 0;
  68                rec->sw_data += bytes;
  69                if (rec->sw_data == rec->sw_buffer_size)
  70                        rec->sw_data = 0;
  71                rec->hw_ready += bytes;
  72                rec->sw_ready -= bytes;
  73        }
  74        return 0;
  75}
  76
  77/*
  78 * helper function for playback pointer callback
  79 * ptr = current byte pointer
  80 */
  81static inline snd_pcm_uframes_t
  82snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
  83                                  struct snd_pcm_indirect *rec, unsigned int ptr)
  84{
  85        int bytes = ptr - rec->hw_io;
  86        if (bytes < 0)
  87                bytes += rec->hw_buffer_size;
  88        rec->hw_io = ptr;
  89        rec->hw_ready -= bytes;
  90        rec->sw_io += bytes;
  91        if (rec->sw_io >= rec->sw_buffer_size)
  92                rec->sw_io -= rec->sw_buffer_size;
  93        if (substream->ops->ack)
  94                substream->ops->ack(substream);
  95        return bytes_to_frames(substream->runtime, rec->sw_io);
  96}
  97
  98
  99/*
 100 * helper function for capture ack callback
 101 */
 102static inline int
 103snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
 104                                  struct snd_pcm_indirect *rec,
 105                                  snd_pcm_indirect_copy_t copy)
 106{
 107        struct snd_pcm_runtime *runtime = substream->runtime;
 108        snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
 109        snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
 110
 111        if (diff) {
 112                if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
 113                        diff += runtime->boundary;
 114                if (diff < 0)
 115                        return -EINVAL;
 116                rec->sw_ready -= frames_to_bytes(runtime, diff);
 117                rec->appl_ptr = appl_ptr;
 118        }
 119        while (rec->hw_ready > 0 && 
 120               rec->sw_ready < (int)rec->sw_buffer_size) {
 121                size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
 122                size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
 123                size_t bytes = rec->sw_buffer_size - rec->sw_ready;
 124                if (rec->hw_ready < (int)bytes)
 125                        bytes = rec->hw_ready;
 126                if (hw_to_end < bytes)
 127                        bytes = hw_to_end;
 128                if (sw_to_end < bytes)
 129                        bytes = sw_to_end;
 130                if (! bytes)
 131                        break;
 132                copy(substream, rec, bytes);
 133                rec->hw_data += bytes;
 134                if ((int)rec->hw_data == rec->hw_buffer_size)
 135                        rec->hw_data = 0;
 136                rec->sw_data += bytes;
 137                if (rec->sw_data == rec->sw_buffer_size)
 138                        rec->sw_data = 0;
 139                rec->hw_ready -= bytes;
 140                rec->sw_ready += bytes;
 141        }
 142        return 0;
 143}
 144
 145/*
 146 * helper function for capture pointer callback,
 147 * ptr = current byte pointer
 148 */
 149static inline snd_pcm_uframes_t
 150snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
 151                                 struct snd_pcm_indirect *rec, unsigned int ptr)
 152{
 153        int qsize;
 154        int bytes = ptr - rec->hw_io;
 155        if (bytes < 0)
 156                bytes += rec->hw_buffer_size;
 157        rec->hw_io = ptr;
 158        rec->hw_ready += bytes;
 159        qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
 160        if (rec->hw_ready > qsize)
 161                return SNDRV_PCM_POS_XRUN;
 162        rec->sw_io += bytes;
 163        if (rec->sw_io >= rec->sw_buffer_size)
 164                rec->sw_io -= rec->sw_buffer_size;
 165        if (substream->ops->ack)
 166                substream->ops->ack(substream);
 167        return bytes_to_frames(substream->runtime, rec->sw_io);
 168}
 169
 170#endif /* __SOUND_PCM_INDIRECT_H */
 171