linux/include/sound/pcm-indirect.h
<<
>>
Prefs
   1/*
   2 * Helper functions for indirect PCM data transfer
   3 *
   4 *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
   5 *                   Jaroslav Kysela <perex@perex.cz>
   6 *
   7 *   This program is free software; you can redistribute it and/or modify
   8 *   it under the terms of the GNU General Public License as published by
   9 *   the Free Software Foundation; either version 2 of the License, or
  10 *   (at your option) any later version.
  11 *
  12 *   This program is distributed in the hope that it will be useful,
  13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 *   GNU General Public License for more details.
  16 *
  17 *   You should have received a copy of the GNU General Public License
  18 *   along with this program; if not, write to the Free Software
  19 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 */
  21
  22#ifndef __SOUND_PCM_INDIRECT_H
  23#define __SOUND_PCM_INDIRECT_H
  24
  25#include <sound/pcm.h>
  26
  27struct snd_pcm_indirect {
  28        unsigned int hw_buffer_size;    /* Byte size of hardware buffer */
  29        unsigned int hw_queue_size;     /* Max queue size of hw buffer (0 = buffer size) */
  30        unsigned int hw_data;   /* Offset to next dst (or src) in hw ring buffer */
  31        unsigned int hw_io;     /* Ring buffer hw pointer */
  32        int hw_ready;           /* Bytes ready for play (or captured) in hw ring buffer */
  33        unsigned int sw_buffer_size;    /* Byte size of software buffer */
  34        unsigned int sw_data;   /* Offset to next dst (or src) in sw ring buffer */
  35        unsigned int sw_io;     /* Current software pointer in bytes */
  36        int sw_ready;           /* Bytes ready to be transferred to/from hw */
  37        snd_pcm_uframes_t appl_ptr;     /* Last seen appl_ptr */
  38};
  39
  40typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
  41                                        struct snd_pcm_indirect *rec, size_t bytes);
  42
  43/*
  44 * helper function for playback ack callback
  45 */
  46static inline int
  47snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
  48                                   struct snd_pcm_indirect *rec,
  49                                   snd_pcm_indirect_copy_t copy)
  50{
  51        struct snd_pcm_runtime *runtime = substream->runtime;
  52        snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  53        snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  54        int qsize;
  55
  56        if (diff) {
  57                if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  58                        diff += runtime->boundary;
  59                if (diff < 0)
  60                        return -EINVAL;
  61                rec->sw_ready += (int)frames_to_bytes(runtime, diff);
  62                rec->appl_ptr = appl_ptr;
  63        }
  64        qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  65        while (rec->hw_ready < qsize && rec->sw_ready > 0) {
  66                unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
  67                unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
  68                unsigned int bytes = qsize - rec->hw_ready;
  69                if (rec->sw_ready < (int)bytes)
  70                        bytes = rec->sw_ready;
  71                if (hw_to_end < bytes)
  72                        bytes = hw_to_end;
  73                if (sw_to_end < bytes)
  74                        bytes = sw_to_end;
  75                if (! bytes)
  76                        break;
  77                copy(substream, rec, bytes);
  78                rec->hw_data += bytes;
  79                if (rec->hw_data == rec->hw_buffer_size)
  80                        rec->hw_data = 0;
  81                rec->sw_data += bytes;
  82                if (rec->sw_data == rec->sw_buffer_size)
  83                        rec->sw_data = 0;
  84                rec->hw_ready += bytes;
  85                rec->sw_ready -= bytes;
  86        }
  87        return 0;
  88}
  89
  90/*
  91 * helper function for playback pointer callback
  92 * ptr = current byte pointer
  93 */
  94static inline snd_pcm_uframes_t
  95snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
  96                                  struct snd_pcm_indirect *rec, unsigned int ptr)
  97{
  98        int bytes = ptr - rec->hw_io;
  99        if (bytes < 0)
 100                bytes += rec->hw_buffer_size;
 101        rec->hw_io = ptr;
 102        rec->hw_ready -= bytes;
 103        rec->sw_io += bytes;
 104        if (rec->sw_io >= rec->sw_buffer_size)
 105                rec->sw_io -= rec->sw_buffer_size;
 106        if (substream->ops->ack)
 107                substream->ops->ack(substream);
 108        return bytes_to_frames(substream->runtime, rec->sw_io);
 109}
 110
 111
 112/*
 113 * helper function for capture ack callback
 114 */
 115static inline int
 116snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
 117                                  struct snd_pcm_indirect *rec,
 118                                  snd_pcm_indirect_copy_t copy)
 119{
 120        struct snd_pcm_runtime *runtime = substream->runtime;
 121        snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
 122        snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
 123
 124        if (diff) {
 125                if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
 126                        diff += runtime->boundary;
 127                if (diff < 0)
 128                        return -EINVAL;
 129                rec->sw_ready -= frames_to_bytes(runtime, diff);
 130                rec->appl_ptr = appl_ptr;
 131        }
 132        while (rec->hw_ready > 0 && 
 133               rec->sw_ready < (int)rec->sw_buffer_size) {
 134                size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
 135                size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
 136                size_t bytes = rec->sw_buffer_size - rec->sw_ready;
 137                if (rec->hw_ready < (int)bytes)
 138                        bytes = rec->hw_ready;
 139                if (hw_to_end < bytes)
 140                        bytes = hw_to_end;
 141                if (sw_to_end < bytes)
 142                        bytes = sw_to_end;
 143                if (! bytes)
 144                        break;
 145                copy(substream, rec, bytes);
 146                rec->hw_data += bytes;
 147                if ((int)rec->hw_data == rec->hw_buffer_size)
 148                        rec->hw_data = 0;
 149                rec->sw_data += bytes;
 150                if (rec->sw_data == rec->sw_buffer_size)
 151                        rec->sw_data = 0;
 152                rec->hw_ready -= bytes;
 153                rec->sw_ready += bytes;
 154        }
 155        return 0;
 156}
 157
 158/*
 159 * helper function for capture pointer callback,
 160 * ptr = current byte pointer
 161 */
 162static inline snd_pcm_uframes_t
 163snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
 164                                 struct snd_pcm_indirect *rec, unsigned int ptr)
 165{
 166        int qsize;
 167        int bytes = ptr - rec->hw_io;
 168        if (bytes < 0)
 169                bytes += rec->hw_buffer_size;
 170        rec->hw_io = ptr;
 171        rec->hw_ready += bytes;
 172        qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
 173        if (rec->hw_ready > qsize)
 174                return SNDRV_PCM_POS_XRUN;
 175        rec->sw_io += bytes;
 176        if (rec->sw_io >= rec->sw_buffer_size)
 177                rec->sw_io -= rec->sw_buffer_size;
 178        if (substream->ops->ack)
 179                substream->ops->ack(substream);
 180        return bytes_to_frames(substream->runtime, rec->sw_io);
 181}
 182
 183#endif /* __SOUND_PCM_INDIRECT_H */
 184