cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pcm_plugin.c (22286B)


      1/*
      2 *  PCM Plug-In shared (kernel/library) code
      3 *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
      4 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
      5 *
      6 *
      7 *   This library is free software; you can redistribute it and/or modify
      8 *   it under the terms of the GNU Library General Public License as
      9 *   published by the Free Software Foundation; either version 2 of
     10 *   the License, or (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 Library General Public License for more details.
     16 *
     17 *   You should have received a copy of the GNU Library General Public
     18 *   License along with this library; if not, write to the Free Software
     19 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     20 *
     21 */
     22  
     23#if 0
     24#define PLUGIN_DEBUG
     25#endif
     26
     27#include <linux/slab.h>
     28#include <linux/time.h>
     29#include <linux/vmalloc.h>
     30#include <sound/core.h>
     31#include <sound/pcm.h>
     32#include <sound/pcm_params.h>
     33#include "pcm_plugin.h"
     34
     35#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
     36#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
     37
     38/*
     39 *  because some cards might have rates "very close", we ignore
     40 *  all "resampling" requests within +-5%
     41 */
     42static int rate_match(unsigned int src_rate, unsigned int dst_rate)
     43{
     44	unsigned int low = (src_rate * 95) / 100;
     45	unsigned int high = (src_rate * 105) / 100;
     46	return dst_rate >= low && dst_rate <= high;
     47}
     48
     49static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames)
     50{
     51	struct snd_pcm_plugin_format *format;
     52	ssize_t width;
     53	size_t size;
     54	unsigned int channel;
     55	struct snd_pcm_plugin_channel *c;
     56
     57	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
     58		format = &plugin->src_format;
     59	} else {
     60		format = &plugin->dst_format;
     61	}
     62	width = snd_pcm_format_physical_width(format->format);
     63	if (width < 0)
     64		return width;
     65	size = array3_size(frames, format->channels, width);
     66	/* check for too large period size once again */
     67	if (size > 1024 * 1024)
     68		return -ENOMEM;
     69	if (snd_BUG_ON(size % 8))
     70		return -ENXIO;
     71	size /= 8;
     72	if (plugin->buf_frames < frames) {
     73		kvfree(plugin->buf);
     74		plugin->buf = kvzalloc(size, GFP_KERNEL);
     75		plugin->buf_frames = frames;
     76	}
     77	if (!plugin->buf) {
     78		plugin->buf_frames = 0;
     79		return -ENOMEM;
     80	}
     81	c = plugin->buf_channels;
     82	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
     83		for (channel = 0; channel < format->channels; channel++, c++) {
     84			c->frames = frames;
     85			c->enabled = 1;
     86			c->wanted = 0;
     87			c->area.addr = plugin->buf;
     88			c->area.first = channel * width;
     89			c->area.step = format->channels * width;
     90		}
     91	} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
     92		if (snd_BUG_ON(size % format->channels))
     93			return -EINVAL;
     94		size /= format->channels;
     95		for (channel = 0; channel < format->channels; channel++, c++) {
     96			c->frames = frames;
     97			c->enabled = 1;
     98			c->wanted = 0;
     99			c->area.addr = plugin->buf + (channel * size);
    100			c->area.first = 0;
    101			c->area.step = width;
    102		}
    103	} else
    104		return -EINVAL;
    105	return 0;
    106}
    107
    108int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
    109{
    110	int err;
    111	if (snd_BUG_ON(!snd_pcm_plug_first(plug)))
    112		return -ENXIO;
    113	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
    114		struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);
    115		while (plugin->next) {
    116			if (plugin->dst_frames)
    117				frames = plugin->dst_frames(plugin, frames);
    118			if ((snd_pcm_sframes_t)frames <= 0)
    119				return -ENXIO;
    120			plugin = plugin->next;
    121			err = snd_pcm_plugin_alloc(plugin, frames);
    122			if (err < 0)
    123				return err;
    124		}
    125	} else {
    126		struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug);
    127		while (plugin->prev) {
    128			if (plugin->src_frames)
    129				frames = plugin->src_frames(plugin, frames);
    130			if ((snd_pcm_sframes_t)frames <= 0)
    131				return -ENXIO;
    132			plugin = plugin->prev;
    133			err = snd_pcm_plugin_alloc(plugin, frames);
    134			if (err < 0)
    135				return err;
    136		}
    137	}
    138	return 0;
    139}
    140
    141
    142snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,
    143				       snd_pcm_uframes_t frames,
    144				       struct snd_pcm_plugin_channel **channels)
    145{
    146	*channels = plugin->buf_channels;
    147	return frames;
    148}
    149
    150int snd_pcm_plugin_build(struct snd_pcm_substream *plug,
    151			 const char *name,
    152			 struct snd_pcm_plugin_format *src_format,
    153			 struct snd_pcm_plugin_format *dst_format,
    154			 size_t extra,
    155			 struct snd_pcm_plugin **ret)
    156{
    157	struct snd_pcm_plugin *plugin;
    158	unsigned int channels;
    159	
    160	if (snd_BUG_ON(!plug))
    161		return -ENXIO;
    162	if (snd_BUG_ON(!src_format || !dst_format))
    163		return -ENXIO;
    164	plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);
    165	if (plugin == NULL)
    166		return -ENOMEM;
    167	plugin->name = name;
    168	plugin->plug = plug;
    169	plugin->stream = snd_pcm_plug_stream(plug);
    170	plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
    171	plugin->src_format = *src_format;
    172	plugin->src_width = snd_pcm_format_physical_width(src_format->format);
    173	snd_BUG_ON(plugin->src_width <= 0);
    174	plugin->dst_format = *dst_format;
    175	plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
    176	snd_BUG_ON(plugin->dst_width <= 0);
    177	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
    178		channels = src_format->channels;
    179	else
    180		channels = dst_format->channels;
    181	plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);
    182	if (plugin->buf_channels == NULL) {
    183		snd_pcm_plugin_free(plugin);
    184		return -ENOMEM;
    185	}
    186	plugin->client_channels = snd_pcm_plugin_client_channels;
    187	*ret = plugin;
    188	return 0;
    189}
    190
    191int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
    192{
    193	if (! plugin)
    194		return 0;
    195	if (plugin->private_free)
    196		plugin->private_free(plugin);
    197	kfree(plugin->buf_channels);
    198	kvfree(plugin->buf);
    199	kfree(plugin);
    200	return 0;
    201}
    202
    203static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
    204					 snd_pcm_sframes_t frames,
    205					 bool check_size)
    206{
    207	struct snd_pcm_plugin *plugin, *plugin_next;
    208
    209	plugin = snd_pcm_plug_first(plug);
    210	while (plugin && frames > 0) {
    211		plugin_next = plugin->next;
    212		if (check_size && plugin->buf_frames &&
    213		    frames > plugin->buf_frames)
    214			frames = plugin->buf_frames;
    215		if (plugin->dst_frames) {
    216			frames = plugin->dst_frames(plugin, frames);
    217			if (frames < 0)
    218				return frames;
    219		}
    220		plugin = plugin_next;
    221	}
    222	return frames;
    223}
    224
    225static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
    226					 snd_pcm_sframes_t frames,
    227					 bool check_size)
    228{
    229	struct snd_pcm_plugin *plugin, *plugin_prev;
    230
    231	plugin = snd_pcm_plug_last(plug);
    232	while (plugin && frames > 0) {
    233		plugin_prev = plugin->prev;
    234		if (plugin->src_frames) {
    235			frames = plugin->src_frames(plugin, frames);
    236			if (frames < 0)
    237				return frames;
    238		}
    239		if (check_size && plugin->buf_frames &&
    240		    frames > plugin->buf_frames)
    241			frames = plugin->buf_frames;
    242		plugin = plugin_prev;
    243	}
    244	return frames;
    245}
    246
    247snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
    248{
    249	if (snd_BUG_ON(!plug))
    250		return -ENXIO;
    251	switch (snd_pcm_plug_stream(plug)) {
    252	case SNDRV_PCM_STREAM_PLAYBACK:
    253		return calc_src_frames(plug, drv_frames, false);
    254	case SNDRV_PCM_STREAM_CAPTURE:
    255		return calc_dst_frames(plug, drv_frames, false);
    256	default:
    257		snd_BUG();
    258		return -EINVAL;
    259	}
    260}
    261
    262snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)
    263{
    264	if (snd_BUG_ON(!plug))
    265		return -ENXIO;
    266	switch (snd_pcm_plug_stream(plug)) {
    267	case SNDRV_PCM_STREAM_PLAYBACK:
    268		return calc_dst_frames(plug, clt_frames, false);
    269	case SNDRV_PCM_STREAM_CAPTURE:
    270		return calc_src_frames(plug, clt_frames, false);
    271	default:
    272		snd_BUG();
    273		return -EINVAL;
    274	}
    275}
    276
    277static int snd_pcm_plug_formats(const struct snd_mask *mask,
    278				snd_pcm_format_t format)
    279{
    280	struct snd_mask formats = *mask;
    281	u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
    282		       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
    283		       SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
    284		       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
    285		       SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
    286		       SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
    287		       SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
    288		       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
    289		       SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
    290	snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW);
    291	
    292	if (formats.bits[0] & lower_32_bits(linfmts))
    293		formats.bits[0] |= lower_32_bits(linfmts);
    294	if (formats.bits[1] & upper_32_bits(linfmts))
    295		formats.bits[1] |= upper_32_bits(linfmts);
    296	return snd_mask_test(&formats, (__force int)format);
    297}
    298
    299static const snd_pcm_format_t preferred_formats[] = {
    300	SNDRV_PCM_FORMAT_S16_LE,
    301	SNDRV_PCM_FORMAT_S16_BE,
    302	SNDRV_PCM_FORMAT_U16_LE,
    303	SNDRV_PCM_FORMAT_U16_BE,
    304	SNDRV_PCM_FORMAT_S24_3LE,
    305	SNDRV_PCM_FORMAT_S24_3BE,
    306	SNDRV_PCM_FORMAT_U24_3LE,
    307	SNDRV_PCM_FORMAT_U24_3BE,
    308	SNDRV_PCM_FORMAT_S24_LE,
    309	SNDRV_PCM_FORMAT_S24_BE,
    310	SNDRV_PCM_FORMAT_U24_LE,
    311	SNDRV_PCM_FORMAT_U24_BE,
    312	SNDRV_PCM_FORMAT_S32_LE,
    313	SNDRV_PCM_FORMAT_S32_BE,
    314	SNDRV_PCM_FORMAT_U32_LE,
    315	SNDRV_PCM_FORMAT_U32_BE,
    316	SNDRV_PCM_FORMAT_S8,
    317	SNDRV_PCM_FORMAT_U8
    318};
    319
    320snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
    321					   const struct snd_mask *format_mask)
    322{
    323	int i;
    324
    325	if (snd_mask_test(format_mask, (__force int)format))
    326		return format;
    327	if (!snd_pcm_plug_formats(format_mask, format))
    328		return (__force snd_pcm_format_t)-EINVAL;
    329	if (snd_pcm_format_linear(format)) {
    330		unsigned int width = snd_pcm_format_width(format);
    331		int unsignd = snd_pcm_format_unsigned(format) > 0;
    332		int big = snd_pcm_format_big_endian(format) > 0;
    333		unsigned int badness, best = -1;
    334		snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;
    335		for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
    336			snd_pcm_format_t f = preferred_formats[i];
    337			unsigned int w;
    338			if (!snd_mask_test(format_mask, (__force int)f))
    339				continue;
    340			w = snd_pcm_format_width(f);
    341			if (w >= width)
    342				badness = w - width;
    343			else
    344				badness = width - w + 32;
    345			badness += snd_pcm_format_unsigned(f) != unsignd;
    346			badness += snd_pcm_format_big_endian(f) != big;
    347			if (badness < best) {
    348				best_format = f;
    349				best = badness;
    350			}
    351		}
    352		if ((__force int)best_format >= 0)
    353			return best_format;
    354		else
    355			return (__force snd_pcm_format_t)-EINVAL;
    356	} else {
    357		switch (format) {
    358		case SNDRV_PCM_FORMAT_MU_LAW:
    359			for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
    360				snd_pcm_format_t format1 = preferred_formats[i];
    361				if (snd_mask_test(format_mask, (__force int)format1))
    362					return format1;
    363			}
    364			fallthrough;
    365		default:
    366			return (__force snd_pcm_format_t)-EINVAL;
    367		}
    368	}
    369}
    370
    371int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug,
    372				struct snd_pcm_hw_params *params,
    373				struct snd_pcm_hw_params *slave_params)
    374{
    375	struct snd_pcm_plugin_format tmpformat;
    376	struct snd_pcm_plugin_format dstformat;
    377	struct snd_pcm_plugin_format srcformat;
    378	snd_pcm_access_t src_access, dst_access;
    379	struct snd_pcm_plugin *plugin = NULL;
    380	int err;
    381	int stream = snd_pcm_plug_stream(plug);
    382	int slave_interleaved = (params_channels(slave_params) == 1 ||
    383				 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
    384
    385	switch (stream) {
    386	case SNDRV_PCM_STREAM_PLAYBACK:
    387		dstformat.format = params_format(slave_params);
    388		dstformat.rate = params_rate(slave_params);
    389		dstformat.channels = params_channels(slave_params);
    390		srcformat.format = params_format(params);
    391		srcformat.rate = params_rate(params);
    392		srcformat.channels = params_channels(params);
    393		src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
    394		dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
    395						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
    396		break;
    397	case SNDRV_PCM_STREAM_CAPTURE:
    398		dstformat.format = params_format(params);
    399		dstformat.rate = params_rate(params);
    400		dstformat.channels = params_channels(params);
    401		srcformat.format = params_format(slave_params);
    402		srcformat.rate = params_rate(slave_params);
    403		srcformat.channels = params_channels(slave_params);
    404		src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
    405						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
    406		dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
    407		break;
    408	default:
    409		snd_BUG();
    410		return -EINVAL;
    411	}
    412	tmpformat = srcformat;
    413		
    414	pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", 
    415		 srcformat.format,
    416		 srcformat.rate,
    417		 srcformat.channels);
    418	pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", 
    419		 dstformat.format,
    420		 dstformat.rate,
    421		 dstformat.channels);
    422
    423	/* Format change (linearization) */
    424	if (! rate_match(srcformat.rate, dstformat.rate) &&
    425	    ! snd_pcm_format_linear(srcformat.format)) {
    426		if (srcformat.format != SNDRV_PCM_FORMAT_MU_LAW)
    427			return -EINVAL;
    428		tmpformat.format = SNDRV_PCM_FORMAT_S16;
    429		err = snd_pcm_plugin_build_mulaw(plug,
    430						 &srcformat, &tmpformat,
    431						 &plugin);
    432		if (err < 0)
    433			return err;
    434		err = snd_pcm_plugin_append(plugin);
    435		if (err < 0) {
    436			snd_pcm_plugin_free(plugin);
    437			return err;
    438		}
    439		srcformat = tmpformat;
    440		src_access = dst_access;
    441	}
    442
    443	/* channels reduction */
    444	if (srcformat.channels > dstformat.channels) {
    445		tmpformat.channels = dstformat.channels;
    446		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);
    447		pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
    448		if (err < 0)
    449			return err;
    450		err = snd_pcm_plugin_append(plugin);
    451		if (err < 0) {
    452			snd_pcm_plugin_free(plugin);
    453			return err;
    454		}
    455		srcformat = tmpformat;
    456		src_access = dst_access;
    457	}
    458
    459	/* rate resampling */
    460	if (!rate_match(srcformat.rate, dstformat.rate)) {
    461		if (srcformat.format != SNDRV_PCM_FORMAT_S16) {
    462			/* convert to S16 for resampling */
    463			tmpformat.format = SNDRV_PCM_FORMAT_S16;
    464			err = snd_pcm_plugin_build_linear(plug,
    465							  &srcformat, &tmpformat,
    466							  &plugin);
    467			if (err < 0)
    468				return err;
    469			err = snd_pcm_plugin_append(plugin);
    470			if (err < 0) {
    471				snd_pcm_plugin_free(plugin);
    472				return err;
    473			}
    474			srcformat = tmpformat;
    475			src_access = dst_access;
    476		}
    477		tmpformat.rate = dstformat.rate;
    478        	err = snd_pcm_plugin_build_rate(plug,
    479        					&srcformat, &tmpformat,
    480						&plugin);
    481		pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
    482		if (err < 0)
    483			return err;
    484		err = snd_pcm_plugin_append(plugin);
    485		if (err < 0) {
    486			snd_pcm_plugin_free(plugin);
    487			return err;
    488		}
    489		srcformat = tmpformat;
    490		src_access = dst_access;
    491        }
    492
    493	/* format change */
    494	if (srcformat.format != dstformat.format) {
    495		tmpformat.format = dstformat.format;
    496		if (srcformat.format == SNDRV_PCM_FORMAT_MU_LAW ||
    497		    tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
    498			err = snd_pcm_plugin_build_mulaw(plug,
    499							 &srcformat, &tmpformat,
    500							 &plugin);
    501		}
    502		else if (snd_pcm_format_linear(srcformat.format) &&
    503			 snd_pcm_format_linear(tmpformat.format)) {
    504			err = snd_pcm_plugin_build_linear(plug,
    505							  &srcformat, &tmpformat,
    506							  &plugin);
    507		}
    508		else
    509			return -EINVAL;
    510		pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
    511		if (err < 0)
    512			return err;
    513		err = snd_pcm_plugin_append(plugin);
    514		if (err < 0) {
    515			snd_pcm_plugin_free(plugin);
    516			return err;
    517		}
    518		srcformat = tmpformat;
    519		src_access = dst_access;
    520	}
    521
    522	/* channels extension */
    523	if (srcformat.channels < dstformat.channels) {
    524		tmpformat.channels = dstformat.channels;
    525		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);
    526		pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
    527		if (err < 0)
    528			return err;
    529		err = snd_pcm_plugin_append(plugin);
    530		if (err < 0) {
    531			snd_pcm_plugin_free(plugin);
    532			return err;
    533		}
    534		srcformat = tmpformat;
    535		src_access = dst_access;
    536	}
    537
    538	/* de-interleave */
    539	if (src_access != dst_access) {
    540		err = snd_pcm_plugin_build_copy(plug,
    541						&srcformat,
    542						&tmpformat,
    543						&plugin);
    544		pdprintf("interleave change (copy: returns %i)\n", err);
    545		if (err < 0)
    546			return err;
    547		err = snd_pcm_plugin_append(plugin);
    548		if (err < 0) {
    549			snd_pcm_plugin_free(plugin);
    550			return err;
    551		}
    552	}
    553
    554	return 0;
    555}
    556
    557snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug,
    558					 char *buf,
    559					 snd_pcm_uframes_t count,
    560					 struct snd_pcm_plugin_channel **channels)
    561{
    562	struct snd_pcm_plugin *plugin;
    563	struct snd_pcm_plugin_channel *v;
    564	struct snd_pcm_plugin_format *format;
    565	int width, nchannels, channel;
    566	int stream = snd_pcm_plug_stream(plug);
    567
    568	if (snd_BUG_ON(!buf))
    569		return -ENXIO;
    570	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
    571		plugin = snd_pcm_plug_first(plug);
    572		format = &plugin->src_format;
    573	} else {
    574		plugin = snd_pcm_plug_last(plug);
    575		format = &plugin->dst_format;
    576	}
    577	v = plugin->buf_channels;
    578	*channels = v;
    579	width = snd_pcm_format_physical_width(format->format);
    580	if (width < 0)
    581		return width;
    582	nchannels = format->channels;
    583	if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
    584		       format->channels > 1))
    585		return -ENXIO;
    586	for (channel = 0; channel < nchannels; channel++, v++) {
    587		v->frames = count;
    588		v->enabled = 1;
    589		v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
    590		v->area.addr = buf;
    591		v->area.first = channel * width;
    592		v->area.step = nchannels * width;
    593	}
    594	return count;
    595}
    596
    597snd_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)
    598{
    599	struct snd_pcm_plugin *plugin, *next;
    600	struct snd_pcm_plugin_channel *dst_channels;
    601	int err;
    602	snd_pcm_sframes_t frames = size;
    603
    604	plugin = snd_pcm_plug_first(plug);
    605	while (plugin) {
    606		if (frames <= 0)
    607			return frames;
    608		next = plugin->next;
    609		if (next) {
    610			snd_pcm_sframes_t frames1 = frames;
    611			if (plugin->dst_frames) {
    612				frames1 = plugin->dst_frames(plugin, frames);
    613				if (frames1 <= 0)
    614					return frames1;
    615			}
    616			err = next->client_channels(next, frames1, &dst_channels);
    617			if (err < 0)
    618				return err;
    619			if (err != frames1) {
    620				frames = err;
    621				if (plugin->src_frames) {
    622					frames = plugin->src_frames(plugin, frames1);
    623					if (frames <= 0)
    624						return frames;
    625				}
    626			}
    627		} else
    628			dst_channels = NULL;
    629		pdprintf("write plugin: %s, %li\n", plugin->name, frames);
    630		frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
    631		if (frames < 0)
    632			return frames;
    633		src_channels = dst_channels;
    634		plugin = next;
    635	}
    636	return calc_src_frames(plug, frames, true);
    637}
    638
    639snd_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)
    640{
    641	struct snd_pcm_plugin *plugin, *next;
    642	struct snd_pcm_plugin_channel *src_channels, *dst_channels;
    643	snd_pcm_sframes_t frames = size;
    644	int err;
    645
    646	frames = calc_src_frames(plug, frames, true);
    647	if (frames < 0)
    648		return frames;
    649
    650	src_channels = NULL;
    651	plugin = snd_pcm_plug_first(plug);
    652	while (plugin && frames > 0) {
    653		next = plugin->next;
    654		if (next) {
    655			err = plugin->client_channels(plugin, frames, &dst_channels);
    656			if (err < 0)
    657				return err;
    658			frames = err;
    659		} else {
    660			dst_channels = dst_channels_final;
    661		}
    662		pdprintf("read plugin: %s, %li\n", plugin->name, frames);
    663		frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
    664		if (frames < 0)
    665			return frames;
    666		plugin = next;
    667		src_channels = dst_channels;
    668	}
    669	return frames;
    670}
    671
    672int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
    673			 size_t samples, snd_pcm_format_t format)
    674{
    675	/* FIXME: sub byte resolution and odd dst_offset */
    676	unsigned char *dst;
    677	unsigned int dst_step;
    678	int width;
    679	const unsigned char *silence;
    680	if (!dst_area->addr)
    681		return 0;
    682	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
    683	width = snd_pcm_format_physical_width(format);
    684	if (width <= 0)
    685		return -EINVAL;
    686	if (dst_area->step == (unsigned int) width && width >= 8)
    687		return snd_pcm_format_set_silence(format, dst, samples);
    688	silence = snd_pcm_format_silence_64(format);
    689	if (! silence)
    690		return -EINVAL;
    691	dst_step = dst_area->step / 8;
    692	if (width == 4) {
    693		/* Ima ADPCM */
    694		int dstbit = dst_area->first % 8;
    695		int dstbit_step = dst_area->step % 8;
    696		while (samples-- > 0) {
    697			if (dstbit)
    698				*dst &= 0xf0;
    699			else
    700				*dst &= 0x0f;
    701			dst += dst_step;
    702			dstbit += dstbit_step;
    703			if (dstbit == 8) {
    704				dst++;
    705				dstbit = 0;
    706			}
    707		}
    708	} else {
    709		width /= 8;
    710		while (samples-- > 0) {
    711			memcpy(dst, silence, width);
    712			dst += dst_step;
    713		}
    714	}
    715	return 0;
    716}
    717
    718int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset,
    719		      const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
    720		      size_t samples, snd_pcm_format_t format)
    721{
    722	/* FIXME: sub byte resolution and odd dst_offset */
    723	char *src, *dst;
    724	int width;
    725	int src_step, dst_step;
    726	src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
    727	if (!src_area->addr)
    728		return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
    729	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
    730	if (!dst_area->addr)
    731		return 0;
    732	width = snd_pcm_format_physical_width(format);
    733	if (width <= 0)
    734		return -EINVAL;
    735	if (src_area->step == (unsigned int) width &&
    736	    dst_area->step == (unsigned int) width && width >= 8) {
    737		size_t bytes = samples * width / 8;
    738		memcpy(dst, src, bytes);
    739		return 0;
    740	}
    741	src_step = src_area->step / 8;
    742	dst_step = dst_area->step / 8;
    743	if (width == 4) {
    744		/* Ima ADPCM */
    745		int srcbit = src_area->first % 8;
    746		int srcbit_step = src_area->step % 8;
    747		int dstbit = dst_area->first % 8;
    748		int dstbit_step = dst_area->step % 8;
    749		while (samples-- > 0) {
    750			unsigned char srcval;
    751			if (srcbit)
    752				srcval = *src & 0x0f;
    753			else
    754				srcval = (*src & 0xf0) >> 4;
    755			if (dstbit)
    756				*dst = (*dst & 0xf0) | srcval;
    757			else
    758				*dst = (*dst & 0x0f) | (srcval << 4);
    759			src += src_step;
    760			srcbit += srcbit_step;
    761			if (srcbit == 8) {
    762				src++;
    763				srcbit = 0;
    764			}
    765			dst += dst_step;
    766			dstbit += dstbit_step;
    767			if (dstbit == 8) {
    768				dst++;
    769				dstbit = 0;
    770			}
    771		}
    772	} else {
    773		width /= 8;
    774		while (samples-- > 0) {
    775			memcpy(dst, src, width);
    776			src += src_step;
    777			dst += dst_step;
    778		}
    779	}
    780	return 0;
    781}