Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  PCM Plug-In shared (kernel/library) code
0003  *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
0004  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
0005  *
0006  *
0007  *   This library is free software; you can redistribute it and/or modify
0008  *   it under the terms of the GNU Library General Public License as
0009  *   published by the Free Software Foundation; either version 2 of
0010  *   the License, or (at your option) any later version.
0011  *
0012  *   This program is distributed in the hope that it will be useful,
0013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  *   GNU Library General Public License for more details.
0016  *
0017  *   You should have received a copy of the GNU Library General Public
0018  *   License along with this library; if not, write to the Free Software
0019  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
0020  *
0021  */
0022   
0023 #if 0
0024 #define PLUGIN_DEBUG
0025 #endif
0026 
0027 #include <linux/slab.h>
0028 #include <linux/time.h>
0029 #include <linux/vmalloc.h>
0030 #include <sound/core.h>
0031 #include <sound/pcm.h>
0032 #include <sound/pcm_params.h>
0033 #include "pcm_plugin.h"
0034 
0035 #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
0036 #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
0037 
0038 /*
0039  *  because some cards might have rates "very close", we ignore
0040  *  all "resampling" requests within +-5%
0041  */
0042 static int rate_match(unsigned int src_rate, unsigned int dst_rate)
0043 {
0044     unsigned int low = (src_rate * 95) / 100;
0045     unsigned int high = (src_rate * 105) / 100;
0046     return dst_rate >= low && dst_rate <= high;
0047 }
0048 
0049 static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames)
0050 {
0051     struct snd_pcm_plugin_format *format;
0052     ssize_t width;
0053     size_t size;
0054     unsigned int channel;
0055     struct snd_pcm_plugin_channel *c;
0056 
0057     if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0058         format = &plugin->src_format;
0059     } else {
0060         format = &plugin->dst_format;
0061     }
0062     width = snd_pcm_format_physical_width(format->format);
0063     if (width < 0)
0064         return width;
0065     size = array3_size(frames, format->channels, width);
0066     /* check for too large period size once again */
0067     if (size > 1024 * 1024)
0068         return -ENOMEM;
0069     if (snd_BUG_ON(size % 8))
0070         return -ENXIO;
0071     size /= 8;
0072     if (plugin->buf_frames < frames) {
0073         kvfree(plugin->buf);
0074         plugin->buf = kvzalloc(size, GFP_KERNEL);
0075         plugin->buf_frames = frames;
0076     }
0077     if (!plugin->buf) {
0078         plugin->buf_frames = 0;
0079         return -ENOMEM;
0080     }
0081     c = plugin->buf_channels;
0082     if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
0083         for (channel = 0; channel < format->channels; channel++, c++) {
0084             c->frames = frames;
0085             c->enabled = 1;
0086             c->wanted = 0;
0087             c->area.addr = plugin->buf;
0088             c->area.first = channel * width;
0089             c->area.step = format->channels * width;
0090         }
0091     } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
0092         if (snd_BUG_ON(size % format->channels))
0093             return -EINVAL;
0094         size /= format->channels;
0095         for (channel = 0; channel < format->channels; channel++, c++) {
0096             c->frames = frames;
0097             c->enabled = 1;
0098             c->wanted = 0;
0099             c->area.addr = plugin->buf + (channel * size);
0100             c->area.first = 0;
0101             c->area.step = width;
0102         }
0103     } else
0104         return -EINVAL;
0105     return 0;
0106 }
0107 
0108 int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
0109 {
0110     int err;
0111     if (snd_BUG_ON(!snd_pcm_plug_first(plug)))
0112         return -ENXIO;
0113     if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
0114         struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);
0115         while (plugin->next) {
0116             if (plugin->dst_frames)
0117                 frames = plugin->dst_frames(plugin, frames);
0118             if ((snd_pcm_sframes_t)frames <= 0)
0119                 return -ENXIO;
0120             plugin = plugin->next;
0121             err = snd_pcm_plugin_alloc(plugin, frames);
0122             if (err < 0)
0123                 return err;
0124         }
0125     } else {
0126         struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug);
0127         while (plugin->prev) {
0128             if (plugin->src_frames)
0129                 frames = plugin->src_frames(plugin, frames);
0130             if ((snd_pcm_sframes_t)frames <= 0)
0131                 return -ENXIO;
0132             plugin = plugin->prev;
0133             err = snd_pcm_plugin_alloc(plugin, frames);
0134             if (err < 0)
0135                 return err;
0136         }
0137     }
0138     return 0;
0139 }
0140 
0141 
0142 snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,
0143                        snd_pcm_uframes_t frames,
0144                        struct snd_pcm_plugin_channel **channels)
0145 {
0146     *channels = plugin->buf_channels;
0147     return frames;
0148 }
0149 
0150 int snd_pcm_plugin_build(struct snd_pcm_substream *plug,
0151              const char *name,
0152              struct snd_pcm_plugin_format *src_format,
0153              struct snd_pcm_plugin_format *dst_format,
0154              size_t extra,
0155              struct snd_pcm_plugin **ret)
0156 {
0157     struct snd_pcm_plugin *plugin;
0158     unsigned int channels;
0159     
0160     if (snd_BUG_ON(!plug))
0161         return -ENXIO;
0162     if (snd_BUG_ON(!src_format || !dst_format))
0163         return -ENXIO;
0164     plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);
0165     if (plugin == NULL)
0166         return -ENOMEM;
0167     plugin->name = name;
0168     plugin->plug = plug;
0169     plugin->stream = snd_pcm_plug_stream(plug);
0170     plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
0171     plugin->src_format = *src_format;
0172     plugin->src_width = snd_pcm_format_physical_width(src_format->format);
0173     snd_BUG_ON(plugin->src_width <= 0);
0174     plugin->dst_format = *dst_format;
0175     plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
0176     snd_BUG_ON(plugin->dst_width <= 0);
0177     if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
0178         channels = src_format->channels;
0179     else
0180         channels = dst_format->channels;
0181     plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);
0182     if (plugin->buf_channels == NULL) {
0183         snd_pcm_plugin_free(plugin);
0184         return -ENOMEM;
0185     }
0186     plugin->client_channels = snd_pcm_plugin_client_channels;
0187     *ret = plugin;
0188     return 0;
0189 }
0190 
0191 int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
0192 {
0193     if (! plugin)
0194         return 0;
0195     if (plugin->private_free)
0196         plugin->private_free(plugin);
0197     kfree(plugin->buf_channels);
0198     kvfree(plugin->buf);
0199     kfree(plugin);
0200     return 0;
0201 }
0202 
0203 static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
0204                      snd_pcm_sframes_t frames,
0205                      bool check_size)
0206 {
0207     struct snd_pcm_plugin *plugin, *plugin_next;
0208 
0209     plugin = snd_pcm_plug_first(plug);
0210     while (plugin && frames > 0) {
0211         plugin_next = plugin->next;
0212         if (check_size && plugin->buf_frames &&
0213             frames > plugin->buf_frames)
0214             frames = plugin->buf_frames;
0215         if (plugin->dst_frames) {
0216             frames = plugin->dst_frames(plugin, frames);
0217             if (frames < 0)
0218                 return frames;
0219         }
0220         plugin = plugin_next;
0221     }
0222     return frames;
0223 }
0224 
0225 static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
0226                      snd_pcm_sframes_t frames,
0227                      bool check_size)
0228 {
0229     struct snd_pcm_plugin *plugin, *plugin_prev;
0230 
0231     plugin = snd_pcm_plug_last(plug);
0232     while (plugin && frames > 0) {
0233         plugin_prev = plugin->prev;
0234         if (plugin->src_frames) {
0235             frames = plugin->src_frames(plugin, frames);
0236             if (frames < 0)
0237                 return frames;
0238         }
0239         if (check_size && plugin->buf_frames &&
0240             frames > plugin->buf_frames)
0241             frames = plugin->buf_frames;
0242         plugin = plugin_prev;
0243     }
0244     return frames;
0245 }
0246 
0247 snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
0248 {
0249     if (snd_BUG_ON(!plug))
0250         return -ENXIO;
0251     switch (snd_pcm_plug_stream(plug)) {
0252     case SNDRV_PCM_STREAM_PLAYBACK:
0253         return calc_src_frames(plug, drv_frames, false);
0254     case SNDRV_PCM_STREAM_CAPTURE:
0255         return calc_dst_frames(plug, drv_frames, false);
0256     default:
0257         snd_BUG();
0258         return -EINVAL;
0259     }
0260 }
0261 
0262 snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)
0263 {
0264     if (snd_BUG_ON(!plug))
0265         return -ENXIO;
0266     switch (snd_pcm_plug_stream(plug)) {
0267     case SNDRV_PCM_STREAM_PLAYBACK:
0268         return calc_dst_frames(plug, clt_frames, false);
0269     case SNDRV_PCM_STREAM_CAPTURE:
0270         return calc_src_frames(plug, clt_frames, false);
0271     default:
0272         snd_BUG();
0273         return -EINVAL;
0274     }
0275 }
0276 
0277 static int snd_pcm_plug_formats(const struct snd_mask *mask,
0278                 snd_pcm_format_t format)
0279 {
0280     struct snd_mask formats = *mask;
0281     u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
0282                SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
0283                SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
0284                SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
0285                SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
0286                SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
0287                SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
0288                SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
0289                SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
0290     snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW);
0291     
0292     if (formats.bits[0] & lower_32_bits(linfmts))
0293         formats.bits[0] |= lower_32_bits(linfmts);
0294     if (formats.bits[1] & upper_32_bits(linfmts))
0295         formats.bits[1] |= upper_32_bits(linfmts);
0296     return snd_mask_test(&formats, (__force int)format);
0297 }
0298 
0299 static const snd_pcm_format_t preferred_formats[] = {
0300     SNDRV_PCM_FORMAT_S16_LE,
0301     SNDRV_PCM_FORMAT_S16_BE,
0302     SNDRV_PCM_FORMAT_U16_LE,
0303     SNDRV_PCM_FORMAT_U16_BE,
0304     SNDRV_PCM_FORMAT_S24_3LE,
0305     SNDRV_PCM_FORMAT_S24_3BE,
0306     SNDRV_PCM_FORMAT_U24_3LE,
0307     SNDRV_PCM_FORMAT_U24_3BE,
0308     SNDRV_PCM_FORMAT_S24_LE,
0309     SNDRV_PCM_FORMAT_S24_BE,
0310     SNDRV_PCM_FORMAT_U24_LE,
0311     SNDRV_PCM_FORMAT_U24_BE,
0312     SNDRV_PCM_FORMAT_S32_LE,
0313     SNDRV_PCM_FORMAT_S32_BE,
0314     SNDRV_PCM_FORMAT_U32_LE,
0315     SNDRV_PCM_FORMAT_U32_BE,
0316     SNDRV_PCM_FORMAT_S8,
0317     SNDRV_PCM_FORMAT_U8
0318 };
0319 
0320 snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
0321                        const struct snd_mask *format_mask)
0322 {
0323     int i;
0324 
0325     if (snd_mask_test(format_mask, (__force int)format))
0326         return format;
0327     if (!snd_pcm_plug_formats(format_mask, format))
0328         return (__force snd_pcm_format_t)-EINVAL;
0329     if (snd_pcm_format_linear(format)) {
0330         unsigned int width = snd_pcm_format_width(format);
0331         int unsignd = snd_pcm_format_unsigned(format) > 0;
0332         int big = snd_pcm_format_big_endian(format) > 0;
0333         unsigned int badness, best = -1;
0334         snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;
0335         for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
0336             snd_pcm_format_t f = preferred_formats[i];
0337             unsigned int w;
0338             if (!snd_mask_test(format_mask, (__force int)f))
0339                 continue;
0340             w = snd_pcm_format_width(f);
0341             if (w >= width)
0342                 badness = w - width;
0343             else
0344                 badness = width - w + 32;
0345             badness += snd_pcm_format_unsigned(f) != unsignd;
0346             badness += snd_pcm_format_big_endian(f) != big;
0347             if (badness < best) {
0348                 best_format = f;
0349                 best = badness;
0350             }
0351         }
0352         if ((__force int)best_format >= 0)
0353             return best_format;
0354         else
0355             return (__force snd_pcm_format_t)-EINVAL;
0356     } else {
0357         switch (format) {
0358         case SNDRV_PCM_FORMAT_MU_LAW:
0359             for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
0360                 snd_pcm_format_t format1 = preferred_formats[i];
0361                 if (snd_mask_test(format_mask, (__force int)format1))
0362                     return format1;
0363             }
0364             fallthrough;
0365         default:
0366             return (__force snd_pcm_format_t)-EINVAL;
0367         }
0368     }
0369 }
0370 
0371 int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug,
0372                 struct snd_pcm_hw_params *params,
0373                 struct snd_pcm_hw_params *slave_params)
0374 {
0375     struct snd_pcm_plugin_format tmpformat;
0376     struct snd_pcm_plugin_format dstformat;
0377     struct snd_pcm_plugin_format srcformat;
0378     snd_pcm_access_t src_access, dst_access;
0379     struct snd_pcm_plugin *plugin = NULL;
0380     int err;
0381     int stream = snd_pcm_plug_stream(plug);
0382     int slave_interleaved = (params_channels(slave_params) == 1 ||
0383                  params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
0384 
0385     switch (stream) {
0386     case SNDRV_PCM_STREAM_PLAYBACK:
0387         dstformat.format = params_format(slave_params);
0388         dstformat.rate = params_rate(slave_params);
0389         dstformat.channels = params_channels(slave_params);
0390         srcformat.format = params_format(params);
0391         srcformat.rate = params_rate(params);
0392         srcformat.channels = params_channels(params);
0393         src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
0394         dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
0395                           SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
0396         break;
0397     case SNDRV_PCM_STREAM_CAPTURE:
0398         dstformat.format = params_format(params);
0399         dstformat.rate = params_rate(params);
0400         dstformat.channels = params_channels(params);
0401         srcformat.format = params_format(slave_params);
0402         srcformat.rate = params_rate(slave_params);
0403         srcformat.channels = params_channels(slave_params);
0404         src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
0405                           SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
0406         dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
0407         break;
0408     default:
0409         snd_BUG();
0410         return -EINVAL;
0411     }
0412     tmpformat = srcformat;
0413         
0414     pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", 
0415          srcformat.format,
0416          srcformat.rate,
0417          srcformat.channels);
0418     pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", 
0419          dstformat.format,
0420          dstformat.rate,
0421          dstformat.channels);
0422 
0423     /* Format change (linearization) */
0424     if (! rate_match(srcformat.rate, dstformat.rate) &&
0425         ! snd_pcm_format_linear(srcformat.format)) {
0426         if (srcformat.format != SNDRV_PCM_FORMAT_MU_LAW)
0427             return -EINVAL;
0428         tmpformat.format = SNDRV_PCM_FORMAT_S16;
0429         err = snd_pcm_plugin_build_mulaw(plug,
0430                          &srcformat, &tmpformat,
0431                          &plugin);
0432         if (err < 0)
0433             return err;
0434         err = snd_pcm_plugin_append(plugin);
0435         if (err < 0) {
0436             snd_pcm_plugin_free(plugin);
0437             return err;
0438         }
0439         srcformat = tmpformat;
0440         src_access = dst_access;
0441     }
0442 
0443     /* channels reduction */
0444     if (srcformat.channels > dstformat.channels) {
0445         tmpformat.channels = dstformat.channels;
0446         err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);
0447         pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
0448         if (err < 0)
0449             return err;
0450         err = snd_pcm_plugin_append(plugin);
0451         if (err < 0) {
0452             snd_pcm_plugin_free(plugin);
0453             return err;
0454         }
0455         srcformat = tmpformat;
0456         src_access = dst_access;
0457     }
0458 
0459     /* rate resampling */
0460     if (!rate_match(srcformat.rate, dstformat.rate)) {
0461         if (srcformat.format != SNDRV_PCM_FORMAT_S16) {
0462             /* convert to S16 for resampling */
0463             tmpformat.format = SNDRV_PCM_FORMAT_S16;
0464             err = snd_pcm_plugin_build_linear(plug,
0465                               &srcformat, &tmpformat,
0466                               &plugin);
0467             if (err < 0)
0468                 return err;
0469             err = snd_pcm_plugin_append(plugin);
0470             if (err < 0) {
0471                 snd_pcm_plugin_free(plugin);
0472                 return err;
0473             }
0474             srcformat = tmpformat;
0475             src_access = dst_access;
0476         }
0477         tmpformat.rate = dstformat.rate;
0478             err = snd_pcm_plugin_build_rate(plug,
0479                             &srcformat, &tmpformat,
0480                         &plugin);
0481         pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
0482         if (err < 0)
0483             return err;
0484         err = snd_pcm_plugin_append(plugin);
0485         if (err < 0) {
0486             snd_pcm_plugin_free(plugin);
0487             return err;
0488         }
0489         srcformat = tmpformat;
0490         src_access = dst_access;
0491         }
0492 
0493     /* format change */
0494     if (srcformat.format != dstformat.format) {
0495         tmpformat.format = dstformat.format;
0496         if (srcformat.format == SNDRV_PCM_FORMAT_MU_LAW ||
0497             tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
0498             err = snd_pcm_plugin_build_mulaw(plug,
0499                              &srcformat, &tmpformat,
0500                              &plugin);
0501         }
0502         else if (snd_pcm_format_linear(srcformat.format) &&
0503              snd_pcm_format_linear(tmpformat.format)) {
0504             err = snd_pcm_plugin_build_linear(plug,
0505                               &srcformat, &tmpformat,
0506                               &plugin);
0507         }
0508         else
0509             return -EINVAL;
0510         pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
0511         if (err < 0)
0512             return err;
0513         err = snd_pcm_plugin_append(plugin);
0514         if (err < 0) {
0515             snd_pcm_plugin_free(plugin);
0516             return err;
0517         }
0518         srcformat = tmpformat;
0519         src_access = dst_access;
0520     }
0521 
0522     /* channels extension */
0523     if (srcformat.channels < dstformat.channels) {
0524         tmpformat.channels = dstformat.channels;
0525         err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);
0526         pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
0527         if (err < 0)
0528             return err;
0529         err = snd_pcm_plugin_append(plugin);
0530         if (err < 0) {
0531             snd_pcm_plugin_free(plugin);
0532             return err;
0533         }
0534         srcformat = tmpformat;
0535         src_access = dst_access;
0536     }
0537 
0538     /* de-interleave */
0539     if (src_access != dst_access) {
0540         err = snd_pcm_plugin_build_copy(plug,
0541                         &srcformat,
0542                         &tmpformat,
0543                         &plugin);
0544         pdprintf("interleave change (copy: returns %i)\n", err);
0545         if (err < 0)
0546             return err;
0547         err = snd_pcm_plugin_append(plugin);
0548         if (err < 0) {
0549             snd_pcm_plugin_free(plugin);
0550             return err;
0551         }
0552     }
0553 
0554     return 0;
0555 }
0556 
0557 snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug,
0558                      char *buf,
0559                      snd_pcm_uframes_t count,
0560                      struct snd_pcm_plugin_channel **channels)
0561 {
0562     struct snd_pcm_plugin *plugin;
0563     struct snd_pcm_plugin_channel *v;
0564     struct snd_pcm_plugin_format *format;
0565     int width, nchannels, channel;
0566     int stream = snd_pcm_plug_stream(plug);
0567 
0568     if (snd_BUG_ON(!buf))
0569         return -ENXIO;
0570     if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
0571         plugin = snd_pcm_plug_first(plug);
0572         format = &plugin->src_format;
0573     } else {
0574         plugin = snd_pcm_plug_last(plug);
0575         format = &plugin->dst_format;
0576     }
0577     v = plugin->buf_channels;
0578     *channels = v;
0579     width = snd_pcm_format_physical_width(format->format);
0580     if (width < 0)
0581         return width;
0582     nchannels = format->channels;
0583     if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
0584                format->channels > 1))
0585         return -ENXIO;
0586     for (channel = 0; channel < nchannels; channel++, v++) {
0587         v->frames = count;
0588         v->enabled = 1;
0589         v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
0590         v->area.addr = buf;
0591         v->area.first = channel * width;
0592         v->area.step = nchannels * width;
0593     }
0594     return count;
0595 }
0596 
0597 snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *src_channels, snd_pcm_uframes_t size)
0598 {
0599     struct snd_pcm_plugin *plugin, *next;
0600     struct snd_pcm_plugin_channel *dst_channels;
0601     int err;
0602     snd_pcm_sframes_t frames = size;
0603 
0604     plugin = snd_pcm_plug_first(plug);
0605     while (plugin) {
0606         if (frames <= 0)
0607             return frames;
0608         next = plugin->next;
0609         if (next) {
0610             snd_pcm_sframes_t frames1 = frames;
0611             if (plugin->dst_frames) {
0612                 frames1 = plugin->dst_frames(plugin, frames);
0613                 if (frames1 <= 0)
0614                     return frames1;
0615             }
0616             err = next->client_channels(next, frames1, &dst_channels);
0617             if (err < 0)
0618                 return err;
0619             if (err != frames1) {
0620                 frames = err;
0621                 if (plugin->src_frames) {
0622                     frames = plugin->src_frames(plugin, frames1);
0623                     if (frames <= 0)
0624                         return frames;
0625                 }
0626             }
0627         } else
0628             dst_channels = NULL;
0629         pdprintf("write plugin: %s, %li\n", plugin->name, frames);
0630         frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
0631         if (frames < 0)
0632             return frames;
0633         src_channels = dst_channels;
0634         plugin = next;
0635     }
0636     return calc_src_frames(plug, frames, true);
0637 }
0638 
0639 snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size)
0640 {
0641     struct snd_pcm_plugin *plugin, *next;
0642     struct snd_pcm_plugin_channel *src_channels, *dst_channels;
0643     snd_pcm_sframes_t frames = size;
0644     int err;
0645 
0646     frames = calc_src_frames(plug, frames, true);
0647     if (frames < 0)
0648         return frames;
0649 
0650     src_channels = NULL;
0651     plugin = snd_pcm_plug_first(plug);
0652     while (plugin && frames > 0) {
0653         next = plugin->next;
0654         if (next) {
0655             err = plugin->client_channels(plugin, frames, &dst_channels);
0656             if (err < 0)
0657                 return err;
0658             frames = err;
0659         } else {
0660             dst_channels = dst_channels_final;
0661         }
0662         pdprintf("read plugin: %s, %li\n", plugin->name, frames);
0663         frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
0664         if (frames < 0)
0665             return frames;
0666         plugin = next;
0667         src_channels = dst_channels;
0668     }
0669     return frames;
0670 }
0671 
0672 int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
0673              size_t samples, snd_pcm_format_t format)
0674 {
0675     /* FIXME: sub byte resolution and odd dst_offset */
0676     unsigned char *dst;
0677     unsigned int dst_step;
0678     int width;
0679     const unsigned char *silence;
0680     if (!dst_area->addr)
0681         return 0;
0682     dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
0683     width = snd_pcm_format_physical_width(format);
0684     if (width <= 0)
0685         return -EINVAL;
0686     if (dst_area->step == (unsigned int) width && width >= 8)
0687         return snd_pcm_format_set_silence(format, dst, samples);
0688     silence = snd_pcm_format_silence_64(format);
0689     if (! silence)
0690         return -EINVAL;
0691     dst_step = dst_area->step / 8;
0692     if (width == 4) {
0693         /* Ima ADPCM */
0694         int dstbit = dst_area->first % 8;
0695         int dstbit_step = dst_area->step % 8;
0696         while (samples-- > 0) {
0697             if (dstbit)
0698                 *dst &= 0xf0;
0699             else
0700                 *dst &= 0x0f;
0701             dst += dst_step;
0702             dstbit += dstbit_step;
0703             if (dstbit == 8) {
0704                 dst++;
0705                 dstbit = 0;
0706             }
0707         }
0708     } else {
0709         width /= 8;
0710         while (samples-- > 0) {
0711             memcpy(dst, silence, width);
0712             dst += dst_step;
0713         }
0714     }
0715     return 0;
0716 }
0717 
0718 int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset,
0719               const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
0720               size_t samples, snd_pcm_format_t format)
0721 {
0722     /* FIXME: sub byte resolution and odd dst_offset */
0723     char *src, *dst;
0724     int width;
0725     int src_step, dst_step;
0726     src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
0727     if (!src_area->addr)
0728         return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
0729     dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
0730     if (!dst_area->addr)
0731         return 0;
0732     width = snd_pcm_format_physical_width(format);
0733     if (width <= 0)
0734         return -EINVAL;
0735     if (src_area->step == (unsigned int) width &&
0736         dst_area->step == (unsigned int) width && width >= 8) {
0737         size_t bytes = samples * width / 8;
0738         memcpy(dst, src, bytes);
0739         return 0;
0740     }
0741     src_step = src_area->step / 8;
0742     dst_step = dst_area->step / 8;
0743     if (width == 4) {
0744         /* Ima ADPCM */
0745         int srcbit = src_area->first % 8;
0746         int srcbit_step = src_area->step % 8;
0747         int dstbit = dst_area->first % 8;
0748         int dstbit_step = dst_area->step % 8;
0749         while (samples-- > 0) {
0750             unsigned char srcval;
0751             if (srcbit)
0752                 srcval = *src & 0x0f;
0753             else
0754                 srcval = (*src & 0xf0) >> 4;
0755             if (dstbit)
0756                 *dst = (*dst & 0xf0) | srcval;
0757             else
0758                 *dst = (*dst & 0x0f) | (srcval << 4);
0759             src += src_step;
0760             srcbit += srcbit_step;
0761             if (srcbit == 8) {
0762                 src++;
0763                 srcbit = 0;
0764             }
0765             dst += dst_step;
0766             dstbit += dstbit_step;
0767             if (dstbit == 8) {
0768                 dst++;
0769                 dstbit = 0;
0770             }
0771         }
0772     } else {
0773         width /= 8;
0774         while (samples-- > 0) {
0775             memcpy(dst, src, width);
0776             src += src_step;
0777             dst += dst_step;
0778         }
0779     }
0780     return 0;
0781 }