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 void
  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                rec->sw_ready += (int)frames_to_bytes(runtime, diff);
  60                rec->appl_ptr = appl_ptr;
  61        }
  62        qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  63        while (rec->hw_ready < qsize && rec->sw_ready > 0) {
  64                unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
  65                unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
  66                unsigned int bytes = qsize - rec->hw_ready;
  67                if (rec->sw_ready < (int)bytes)
  68                        bytes = rec->sw_ready;
  69                if (hw_to_end < bytes)
  70                        bytes = hw_to_end;
  71                if (sw_to_end < bytes)
  72                        bytes = sw_to_end;
  73                if (! bytes)
  74                        break;
  75                copy(substream, rec, bytes);
  76                rec->hw_data += bytes;
  77                if (rec->hw_data == rec->hw_buffer_size)
  78                        rec->hw_data = 0;
  79                rec->sw_data += bytes;
  80                if (rec->sw_data == rec->sw_buffer_size)
  81                        rec->sw_data = 0;
  82                rec->hw_ready += bytes;
  83                rec->sw_ready -= bytes;
  84        }
  85}
  86
  87/*
  88 * helper function for playback pointer callback
  89 * ptr = current byte pointer
  90 */
  91static inline snd_pcm_uframes_t
  92snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
  93                                  struct snd_pcm_indirect *rec, unsigned int ptr)
  94{
  95        int bytes = ptr - rec->hw_io;
  96        if (bytes < 0)
  97                bytes += rec->hw_buffer_size;
  98        rec->hw_io = ptr;
  99        rec->hw_ready -= bytes;
 100        rec->sw_io += bytes;
 101        if (rec->sw_io >= rec->sw_buffer_size)
 102                rec->sw_io -= rec->sw_buffer_size;
 103        if (substream->ops->ack)
 104                substream->ops->ack(substream);
 105        return bytes_to_frames(substream->runtime, rec->sw_io);
 106}
 107
 108
 109/*
 110 * helper function for capture ack callback
 111 */
 112static inline void
 113snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
 114                                  struct snd_pcm_indirect *rec,
 115                                  snd_pcm_indirect_copy_t copy)
 116{
 117        struct snd_pcm_runtime *runtime = substream->runtime;
 118        snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
 119        snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
 120
 121        if (diff) {
 122                if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
 123                        diff += runtime->boundary;
 124                rec->sw_ready -= frames_to_bytes(runtime, diff);
 125                rec->appl_ptr = appl_ptr;
 126        }
 127        while (rec->hw_ready > 0 && 
 128               rec->sw_ready < (int)rec->sw_buffer_size) {
 129                size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
 130                size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
 131                size_t bytes = rec->sw_buffer_size - rec->sw_ready;
 132                if (rec->hw_ready < (int)bytes)
 133                        bytes = rec->hw_ready;
 134                if (hw_to_end < bytes)
 135                        bytes = hw_to_end;
 136                if (sw_to_end < bytes)
 137                        bytes = sw_to_end;
 138                if (! bytes)
 139                        break;
 140                copy(substream, rec, bytes);
 141                rec->hw_data += bytes;
 142                if ((int)rec->hw_data == rec->hw_buffer_size)
 143                        rec->hw_data = 0;
 144                rec->sw_data += bytes;
 145                if (rec->sw_data == rec->sw_buffer_size)
 146                        rec->sw_data = 0;
 147                rec->hw_ready -= bytes;
 148                rec->sw_ready += bytes;
 149        }
 150}
 151
 152/*
 153 * helper function for capture pointer callback,
 154 * ptr = current byte pointer
 155 */
 156static inline snd_pcm_uframes_t
 157snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
 158                                 struct snd_pcm_indirect *rec, unsigned int ptr)
 159{
 160        int qsize;
 161        int bytes = ptr - rec->hw_io;
 162        if (bytes < 0)
 163                bytes += rec->hw_buffer_size;
 164        rec->hw_io = ptr;
 165        rec->hw_ready += bytes;
 166        qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
 167        if (rec->hw_ready > qsize)
 168                return SNDRV_PCM_POS_XRUN;
 169        rec->sw_io += bytes;
 170        if (rec->sw_io >= rec->sw_buffer_size)
 171                rec->sw_io -= rec->sw_buffer_size;
 172        if (substream->ops->ack)
 173                substream->ops->ack(substream);
 174        return bytes_to_frames(substream->runtime, rec->sw_io);
 175}
 176
 177#endif /* __SOUND_PCM_INDIRECT_H */
 178