| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  PCM Plug-In shared (kernel/library) code | 
					
						
							| 
									
										
										
										
											2007-10-15 09:50:19 +02:00
										 |  |  |  *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This library is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  *   it under the terms of the GNU Library General Public License as | 
					
						
							|  |  |  |  *   published by the Free Software Foundation; either version 2 of | 
					
						
							|  |  |  |  *   the License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  *   GNU Library General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   You should have received a copy of the GNU Library General Public | 
					
						
							|  |  |  |  *   License along with this library; if not, write to the Free Software | 
					
						
							|  |  |  |  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | #define PLUGIN_DEBUG
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/time.h>
 | 
					
						
							|  |  |  | #include <linux/vmalloc.h>
 | 
					
						
							|  |  |  | #include <sound/core.h>
 | 
					
						
							|  |  |  | #include <sound/pcm.h>
 | 
					
						
							|  |  |  | #include <sound/pcm_params.h>
 | 
					
						
							|  |  |  | #include "pcm_plugin.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
 | 
					
						
							|  |  |  | #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  because some cards might have rates "very close", we ignore | 
					
						
							|  |  |  |  *  all "resampling" requests within +-5% | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int rate_match(unsigned int src_rate, unsigned int dst_rate) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int low = (src_rate * 95) / 100; | 
					
						
							|  |  |  | 	unsigned int high = (src_rate * 105) / 100; | 
					
						
							|  |  |  | 	return dst_rate >= low && dst_rate <= high; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin_format *format; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	ssize_t width; | 
					
						
							|  |  |  | 	size_t size; | 
					
						
							|  |  |  | 	unsigned int channel; | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin_channel *c; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
					
						
							|  |  |  | 		format = &plugin->src_format; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		format = &plugin->dst_format; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if ((width = snd_pcm_format_physical_width(format->format)) < 0) | 
					
						
							|  |  |  | 		return width; | 
					
						
							|  |  |  | 	size = frames * format->channels * width; | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(size % 8)) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	size /= 8; | 
					
						
							|  |  |  | 	if (plugin->buf_frames < frames) { | 
					
						
							|  |  |  | 		vfree(plugin->buf); | 
					
						
							|  |  |  | 		plugin->buf = vmalloc(size); | 
					
						
							|  |  |  | 		plugin->buf_frames = frames; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!plugin->buf) { | 
					
						
							|  |  |  | 		plugin->buf_frames = 0; | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	c = plugin->buf_channels; | 
					
						
							|  |  |  | 	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | 
					
						
							|  |  |  | 		for (channel = 0; channel < format->channels; channel++, c++) { | 
					
						
							|  |  |  | 			c->frames = frames; | 
					
						
							|  |  |  | 			c->enabled = 1; | 
					
						
							|  |  |  | 			c->wanted = 0; | 
					
						
							|  |  |  | 			c->area.addr = plugin->buf; | 
					
						
							|  |  |  | 			c->area.first = channel * width; | 
					
						
							|  |  |  | 			c->area.step = format->channels * width; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 		if (snd_BUG_ON(size % format->channels)) | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		size /= format->channels; | 
					
						
							|  |  |  | 		for (channel = 0; channel < format->channels; channel++, c++) { | 
					
						
							|  |  |  | 			c->frames = frames; | 
					
						
							|  |  |  | 			c->enabled = 1; | 
					
						
							|  |  |  | 			c->wanted = 0; | 
					
						
							|  |  |  | 			c->area.addr = plugin->buf + (channel * size); | 
					
						
							|  |  |  | 			c->area.first = 0; | 
					
						
							|  |  |  | 			c->area.step = width; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(!snd_pcm_plug_first(plug))) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 		struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		while (plugin->next) { | 
					
						
							|  |  |  | 			if (plugin->dst_frames) | 
					
						
							|  |  |  | 				frames = plugin->dst_frames(plugin, frames); | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 			if (snd_BUG_ON(frames <= 0)) | 
					
						
							|  |  |  | 				return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			plugin = plugin->next; | 
					
						
							|  |  |  | 			err = snd_pcm_plugin_alloc(plugin, frames); | 
					
						
							|  |  |  | 			if (err < 0) | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 		struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		while (plugin->prev) { | 
					
						
							|  |  |  | 			if (plugin->src_frames) | 
					
						
							|  |  |  | 				frames = plugin->src_frames(plugin, frames); | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 			if (snd_BUG_ON(frames <= 0)) | 
					
						
							|  |  |  | 				return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			plugin = plugin->prev; | 
					
						
							|  |  |  | 			err = snd_pcm_plugin_alloc(plugin, frames); | 
					
						
							|  |  |  | 			if (err < 0) | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				       snd_pcm_uframes_t frames, | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 				       struct snd_pcm_plugin_channel **channels) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	*channels = plugin->buf_channels; | 
					
						
							|  |  |  | 	return frames; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | int snd_pcm_plugin_build(struct snd_pcm_substream *plug, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			 const char *name, | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 			 struct snd_pcm_plugin_format *src_format, | 
					
						
							|  |  |  | 			 struct snd_pcm_plugin_format *dst_format, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			 size_t extra, | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 			 struct snd_pcm_plugin **ret) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin *plugin; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	unsigned int channels; | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(!plug)) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							|  |  |  | 	if (snd_BUG_ON(!src_format || !dst_format)) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-09-09 14:20:23 +02:00
										 |  |  | 	plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (plugin == NULL) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	plugin->name = name; | 
					
						
							|  |  |  | 	plugin->plug = plug; | 
					
						
							|  |  |  | 	plugin->stream = snd_pcm_plug_stream(plug); | 
					
						
							|  |  |  | 	plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | 
					
						
							|  |  |  | 	plugin->src_format = *src_format; | 
					
						
							|  |  |  | 	plugin->src_width = snd_pcm_format_physical_width(src_format->format); | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	snd_BUG_ON(plugin->src_width <= 0); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	plugin->dst_format = *dst_format; | 
					
						
							|  |  |  | 	plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	snd_BUG_ON(plugin->dst_width <= 0); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
					
						
							|  |  |  | 		channels = src_format->channels; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		channels = dst_format->channels; | 
					
						
							|  |  |  | 	plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (plugin->buf_channels == NULL) { | 
					
						
							|  |  |  | 		snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	plugin->client_channels = snd_pcm_plugin_client_channels; | 
					
						
							|  |  |  | 	*ret = plugin; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (! plugin) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if (plugin->private_free) | 
					
						
							|  |  |  | 		plugin->private_free(plugin); | 
					
						
							|  |  |  | 	kfree(plugin->buf_channels); | 
					
						
							|  |  |  | 	vfree(plugin->buf); | 
					
						
							|  |  |  | 	kfree(plugin); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int stream = snd_pcm_plug_stream(plug); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(!plug)) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (drv_frames == 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
					
						
							|  |  |  | 		plugin = snd_pcm_plug_last(plug); | 
					
						
							|  |  |  | 		while (plugin && drv_frames > 0) { | 
					
						
							|  |  |  | 			plugin_prev = plugin->prev; | 
					
						
							|  |  |  | 			if (plugin->src_frames) | 
					
						
							|  |  |  | 				drv_frames = plugin->src_frames(plugin, drv_frames); | 
					
						
							|  |  |  | 			plugin = plugin_prev; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | 
					
						
							|  |  |  | 		plugin = snd_pcm_plug_first(plug); | 
					
						
							|  |  |  | 		while (plugin && drv_frames > 0) { | 
					
						
							|  |  |  | 			plugin_next = plugin->next; | 
					
						
							|  |  |  | 			if (plugin->dst_frames) | 
					
						
							|  |  |  | 				drv_frames = plugin->dst_frames(plugin, drv_frames); | 
					
						
							|  |  |  | 			plugin = plugin_next; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		snd_BUG(); | 
					
						
							|  |  |  | 	return drv_frames; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	snd_pcm_sframes_t frames; | 
					
						
							|  |  |  | 	int stream = snd_pcm_plug_stream(plug); | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(!plug)) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (clt_frames == 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	frames = clt_frames; | 
					
						
							|  |  |  | 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
					
						
							|  |  |  | 		plugin = snd_pcm_plug_first(plug); | 
					
						
							|  |  |  | 		while (plugin && frames > 0) { | 
					
						
							|  |  |  | 			plugin_next = plugin->next; | 
					
						
							|  |  |  | 			if (plugin->dst_frames) { | 
					
						
							|  |  |  | 				frames = plugin->dst_frames(plugin, frames); | 
					
						
							|  |  |  | 				if (frames < 0) | 
					
						
							|  |  |  | 					return frames; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			plugin = plugin_next; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | 
					
						
							|  |  |  | 		plugin = snd_pcm_plug_last(plug); | 
					
						
							|  |  |  | 		while (plugin) { | 
					
						
							|  |  |  | 			plugin_prev = plugin->prev; | 
					
						
							|  |  |  | 			if (plugin->src_frames) { | 
					
						
							|  |  |  | 				frames = plugin->src_frames(plugin, frames); | 
					
						
							|  |  |  | 				if (frames < 0) | 
					
						
							|  |  |  | 					return frames; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			plugin = plugin_prev; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		snd_BUG(); | 
					
						
							|  |  |  | 	return frames; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_mask formats = *mask; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | | 
					
						
							|  |  |  | 		       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | | 
					
						
							|  |  |  | 		       SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | | 
					
						
							|  |  |  | 		       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | | 
					
						
							|  |  |  | 		       SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | | 
					
						
							| 
									
										
										
										
											2007-08-08 16:49:08 +02:00
										 |  |  | 		       SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE | | 
					
						
							|  |  |  | 		       SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | | 
					
						
							|  |  |  | 		       SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 	snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	if (formats.bits[0] & (u32)linfmts) | 
					
						
							|  |  |  | 		formats.bits[0] |= (u32)linfmts; | 
					
						
							|  |  |  | 	if (formats.bits[1] & (u32)(linfmts >> 32)) | 
					
						
							|  |  |  | 		formats.bits[1] |= (u32)(linfmts >> 32); | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 	return snd_mask_test(&formats, (__force int)format); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | static snd_pcm_format_t preferred_formats[] = { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	SNDRV_PCM_FORMAT_S16_LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_S16_BE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U16_LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U16_BE, | 
					
						
							| 
									
										
										
										
											2007-08-08 16:49:08 +02:00
										 |  |  | 	SNDRV_PCM_FORMAT_S24_3LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_S24_3BE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U24_3LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U24_3BE, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	SNDRV_PCM_FORMAT_S24_LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_S24_BE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U24_LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U24_BE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_S32_LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_S32_BE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U32_LE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U32_BE, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_S8, | 
					
						
							|  |  |  | 	SNDRV_PCM_FORMAT_U8 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, | 
					
						
							|  |  |  | 					   struct snd_mask *format_mask) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-08-08 16:49:08 +02:00
										 |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 	if (snd_mask_test(format_mask, (__force int)format)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return format; | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 	if (!snd_pcm_plug_formats(format_mask, format)) | 
					
						
							|  |  |  | 		return (__force snd_pcm_format_t)-EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (snd_pcm_format_linear(format)) { | 
					
						
							| 
									
										
										
										
											2007-08-08 16:49:08 +02:00
										 |  |  | 		unsigned int width = snd_pcm_format_width(format); | 
					
						
							|  |  |  | 		int unsignd = snd_pcm_format_unsigned(format) > 0; | 
					
						
							|  |  |  | 		int big = snd_pcm_format_big_endian(format) > 0; | 
					
						
							|  |  |  | 		unsigned int badness, best = -1; | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 		snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1; | 
					
						
							| 
									
										
										
										
											2007-08-08 16:49:08 +02:00
										 |  |  | 		for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 			snd_pcm_format_t f = preferred_formats[i]; | 
					
						
							| 
									
										
										
										
											2007-08-08 16:49:08 +02:00
										 |  |  | 			unsigned int w; | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 			if (!snd_mask_test(format_mask, (__force int)f)) | 
					
						
							| 
									
										
										
										
											2007-08-08 16:49:08 +02:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			w = snd_pcm_format_width(f); | 
					
						
							|  |  |  | 			if (w >= width) | 
					
						
							|  |  |  | 				badness = w - width; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				badness = width - w + 32; | 
					
						
							|  |  |  | 			badness += snd_pcm_format_unsigned(f) != unsignd; | 
					
						
							|  |  |  | 			badness += snd_pcm_format_big_endian(f) != big; | 
					
						
							|  |  |  | 			if (badness < best) { | 
					
						
							|  |  |  | 				best_format = f; | 
					
						
							|  |  |  | 				best = badness; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 		if ((__force int)best_format >= 0) | 
					
						
							|  |  |  | 			return best_format; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			return (__force snd_pcm_format_t)-EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		switch (format) { | 
					
						
							|  |  |  | 		case SNDRV_PCM_FORMAT_MU_LAW: | 
					
						
							|  |  |  | 			for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 				snd_pcm_format_t format1 = preferred_formats[i]; | 
					
						
							|  |  |  | 				if (snd_mask_test(format_mask, (__force int)format1)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					return format1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 			return (__force snd_pcm_format_t)-EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, | 
					
						
							|  |  |  | 				struct snd_pcm_hw_params *params, | 
					
						
							|  |  |  | 				struct snd_pcm_hw_params *slave_params) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin_format tmpformat; | 
					
						
							|  |  |  | 	struct snd_pcm_plugin_format dstformat; | 
					
						
							|  |  |  | 	struct snd_pcm_plugin_format srcformat; | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 	snd_pcm_access_t src_access, dst_access; | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin *plugin = NULL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 	int stream = snd_pcm_plug_stream(plug); | 
					
						
							|  |  |  | 	int slave_interleaved = (params_channels(slave_params) == 1 || | 
					
						
							|  |  |  | 				 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (stream) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_STREAM_PLAYBACK: | 
					
						
							|  |  |  | 		dstformat.format = params_format(slave_params); | 
					
						
							|  |  |  | 		dstformat.rate = params_rate(slave_params); | 
					
						
							|  |  |  | 		dstformat.channels = params_channels(slave_params); | 
					
						
							|  |  |  | 		srcformat.format = params_format(params); | 
					
						
							|  |  |  | 		srcformat.rate = params_rate(params); | 
					
						
							|  |  |  | 		srcformat.channels = params_channels(params); | 
					
						
							|  |  |  | 		src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | 
					
						
							|  |  |  | 		dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | 
					
						
							|  |  |  | 						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_STREAM_CAPTURE: | 
					
						
							|  |  |  | 		dstformat.format = params_format(params); | 
					
						
							|  |  |  | 		dstformat.rate = params_rate(params); | 
					
						
							|  |  |  | 		dstformat.channels = params_channels(params); | 
					
						
							|  |  |  | 		srcformat.format = params_format(slave_params); | 
					
						
							|  |  |  | 		srcformat.rate = params_rate(slave_params); | 
					
						
							|  |  |  | 		srcformat.channels = params_channels(slave_params); | 
					
						
							|  |  |  | 		src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | 
					
						
							|  |  |  | 						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | 
					
						
							|  |  |  | 		dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		snd_BUG(); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tmpformat = srcformat; | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 	pdprintf("srcformat: format=%i, rate=%i, channels=%i\n",  | 
					
						
							|  |  |  | 		 srcformat.format, | 
					
						
							|  |  |  | 		 srcformat.rate, | 
					
						
							|  |  |  | 		 srcformat.channels); | 
					
						
							|  |  |  | 	pdprintf("dstformat: format=%i, rate=%i, channels=%i\n",  | 
					
						
							|  |  |  | 		 dstformat.format, | 
					
						
							|  |  |  | 		 dstformat.rate, | 
					
						
							|  |  |  | 		 dstformat.channels); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Format change (linearization) */ | 
					
						
							| 
									
										
										
										
											2006-01-13 12:09:12 +01:00
										 |  |  | 	if (! rate_match(srcformat.rate, dstformat.rate) && | 
					
						
							|  |  |  | 	    ! snd_pcm_format_linear(srcformat.format)) { | 
					
						
							|  |  |  | 		if (srcformat.format != SNDRV_PCM_FORMAT_MU_LAW) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return -EINVAL; | 
					
						
							| 
									
										
										
										
											2006-01-13 12:09:12 +01:00
										 |  |  | 		tmpformat.format = SNDRV_PCM_FORMAT_S16; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_build_mulaw(plug, | 
					
						
							|  |  |  | 						 &srcformat, &tmpformat, | 
					
						
							|  |  |  | 						 &plugin); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_append(plugin); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		srcformat = tmpformat; | 
					
						
							|  |  |  | 		src_access = dst_access; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* channels reduction */ | 
					
						
							|  |  |  | 	if (srcformat.channels > dstformat.channels) { | 
					
						
							|  |  |  | 		tmpformat.channels = dstformat.channels; | 
					
						
							| 
									
										
										
										
											2006-01-13 12:09:12 +01:00
										 |  |  | 		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | 
					
						
							| 
									
										
										
										
											2006-01-13 12:09:12 +01:00
										 |  |  | 		if (err < 0) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return err; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_append(plugin); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		srcformat = tmpformat; | 
					
						
							|  |  |  | 		src_access = dst_access; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* rate resampling */ | 
					
						
							|  |  |  | 	if (!rate_match(srcformat.rate, dstformat.rate)) { | 
					
						
							| 
									
										
										
										
											2006-01-13 12:09:12 +01:00
										 |  |  | 		if (srcformat.format != SNDRV_PCM_FORMAT_S16) { | 
					
						
							|  |  |  | 			/* convert to S16 for resampling */ | 
					
						
							|  |  |  | 			tmpformat.format = SNDRV_PCM_FORMAT_S16; | 
					
						
							|  |  |  | 			err = snd_pcm_plugin_build_linear(plug, | 
					
						
							|  |  |  | 							  &srcformat, &tmpformat, | 
					
						
							|  |  |  | 							  &plugin); | 
					
						
							|  |  |  | 			if (err < 0) | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			err = snd_pcm_plugin_append(plugin); | 
					
						
							|  |  |  | 			if (err < 0) { | 
					
						
							|  |  |  | 				snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			srcformat = tmpformat; | 
					
						
							|  |  |  | 			src_access = dst_access; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		tmpformat.rate = dstformat.rate; | 
					
						
							|  |  |  |         	err = snd_pcm_plugin_build_rate(plug, | 
					
						
							|  |  |  |         					&srcformat, &tmpformat, | 
					
						
							|  |  |  | 						&plugin); | 
					
						
							|  |  |  | 		pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); | 
					
						
							| 
									
										
										
										
											2006-01-13 12:09:12 +01:00
										 |  |  | 		if (err < 0) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return err; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_append(plugin); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		srcformat = tmpformat; | 
					
						
							|  |  |  | 		src_access = dst_access; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* format change */ | 
					
						
							|  |  |  | 	if (srcformat.format != dstformat.format) { | 
					
						
							|  |  |  | 		tmpformat.format = dstformat.format; | 
					
						
							| 
									
										
										
										
											2006-01-20 17:13:45 +01:00
										 |  |  | 		if (srcformat.format == SNDRV_PCM_FORMAT_MU_LAW || | 
					
						
							|  |  |  | 		    tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			err = snd_pcm_plugin_build_mulaw(plug, | 
					
						
							|  |  |  | 							 &srcformat, &tmpformat, | 
					
						
							|  |  |  | 							 &plugin); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else if (snd_pcm_format_linear(srcformat.format) && | 
					
						
							|  |  |  | 			 snd_pcm_format_linear(tmpformat.format)) { | 
					
						
							|  |  |  | 			err = snd_pcm_plugin_build_linear(plug, | 
					
						
							|  |  |  | 							  &srcformat, &tmpformat, | 
					
						
							|  |  |  | 							  &plugin); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_append(plugin); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		srcformat = tmpformat; | 
					
						
							|  |  |  | 		src_access = dst_access; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-13 12:09:12 +01:00
										 |  |  | 	/* channels extension */ | 
					
						
							|  |  |  | 	if (srcformat.channels < dstformat.channels) { | 
					
						
							|  |  |  | 		tmpformat.channels = dstformat.channels; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin); | 
					
						
							|  |  |  | 		pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_append(plugin); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		srcformat = tmpformat; | 
					
						
							|  |  |  | 		src_access = dst_access; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/* de-interleave */ | 
					
						
							|  |  |  | 	if (src_access != dst_access) { | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_build_copy(plug, | 
					
						
							|  |  |  | 						&srcformat, | 
					
						
							|  |  |  | 						&tmpformat, | 
					
						
							|  |  |  | 						&plugin); | 
					
						
							|  |  |  | 		pdprintf("interleave change (copy: returns %i)\n", err); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		err = snd_pcm_plugin_append(plugin); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			snd_pcm_plugin_free(plugin); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					 char *buf, | 
					
						
							|  |  |  | 					 snd_pcm_uframes_t count, | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 					 struct snd_pcm_plugin_channel **channels) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin *plugin; | 
					
						
							|  |  |  | 	struct snd_pcm_plugin_channel *v; | 
					
						
							|  |  |  | 	struct snd_pcm_plugin_format *format; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int width, nchannels, channel; | 
					
						
							|  |  |  | 	int stream = snd_pcm_plug_stream(plug); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(!buf)) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
					
						
							|  |  |  | 		plugin = snd_pcm_plug_first(plug); | 
					
						
							|  |  |  | 		format = &plugin->src_format; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		plugin = snd_pcm_plug_last(plug); | 
					
						
							|  |  |  | 		format = &plugin->dst_format; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	v = plugin->buf_channels; | 
					
						
							|  |  |  | 	*channels = v; | 
					
						
							|  |  |  | 	if ((width = snd_pcm_format_physical_width(format->format)) < 0) | 
					
						
							|  |  |  | 		return width; | 
					
						
							|  |  |  | 	nchannels = format->channels; | 
					
						
							| 
									
										
										
										
											2008-08-08 17:09:09 +02:00
										 |  |  | 	if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && | 
					
						
							|  |  |  | 		       format->channels > 1)) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	for (channel = 0; channel < nchannels; channel++, v++) { | 
					
						
							|  |  |  | 		v->frames = count; | 
					
						
							|  |  |  | 		v->enabled = 1; | 
					
						
							|  |  |  | 		v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); | 
					
						
							|  |  |  | 		v->area.addr = buf; | 
					
						
							|  |  |  | 		v->area.first = channel * width; | 
					
						
							|  |  |  | 		v->area.step = nchannels * width; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin *plugin, *next; | 
					
						
							|  |  |  | 	struct snd_pcm_plugin_channel *dst_channels; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 	snd_pcm_sframes_t frames = size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	plugin = snd_pcm_plug_first(plug); | 
					
						
							|  |  |  | 	while (plugin && frames > 0) { | 
					
						
							|  |  |  | 		if ((next = plugin->next) != NULL) { | 
					
						
							|  |  |  | 			snd_pcm_sframes_t frames1 = frames; | 
					
						
							|  |  |  | 			if (plugin->dst_frames) | 
					
						
							|  |  |  | 				frames1 = plugin->dst_frames(plugin, frames); | 
					
						
							|  |  |  | 			if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (err != frames1) { | 
					
						
							|  |  |  | 				frames = err; | 
					
						
							|  |  |  | 				if (plugin->src_frames) | 
					
						
							|  |  |  | 					frames = plugin->src_frames(plugin, frames1); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			dst_channels = NULL; | 
					
						
							|  |  |  | 		pdprintf("write plugin: %s, %li\n", plugin->name, frames); | 
					
						
							|  |  |  | 		if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | 
					
						
							|  |  |  | 			return frames; | 
					
						
							|  |  |  | 		src_channels = dst_channels; | 
					
						
							|  |  |  | 		plugin = next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return snd_pcm_plug_client_size(plug, frames); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | 	struct snd_pcm_plugin *plugin, *next; | 
					
						
							|  |  |  | 	struct snd_pcm_plugin_channel *src_channels, *dst_channels; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	snd_pcm_sframes_t frames = size; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	frames = snd_pcm_plug_slave_size(plug, frames); | 
					
						
							|  |  |  | 	if (frames < 0) | 
					
						
							|  |  |  | 		return frames; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	src_channels = NULL; | 
					
						
							|  |  |  | 	plugin = snd_pcm_plug_first(plug); | 
					
						
							|  |  |  | 	while (plugin && frames > 0) { | 
					
						
							|  |  |  | 		if ((next = plugin->next) != NULL) { | 
					
						
							|  |  |  | 			if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			frames = err; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			dst_channels = dst_channels_final; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		pdprintf("read plugin: %s, %li\n", plugin->name, frames); | 
					
						
							|  |  |  | 		if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | 
					
						
							|  |  |  | 			return frames; | 
					
						
							|  |  |  | 		plugin = next; | 
					
						
							|  |  |  | 		src_channels = dst_channels; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return frames; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 			 size_t samples, snd_pcm_format_t format) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* FIXME: sub byte resolution and odd dst_offset */ | 
					
						
							|  |  |  | 	unsigned char *dst; | 
					
						
							|  |  |  | 	unsigned int dst_step; | 
					
						
							|  |  |  | 	int width; | 
					
						
							|  |  |  | 	const unsigned char *silence; | 
					
						
							|  |  |  | 	if (!dst_area->addr) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | 
					
						
							|  |  |  | 	width = snd_pcm_format_physical_width(format); | 
					
						
							|  |  |  | 	if (width <= 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	if (dst_area->step == (unsigned int) width && width >= 8) | 
					
						
							|  |  |  | 		return snd_pcm_format_set_silence(format, dst, samples); | 
					
						
							|  |  |  | 	silence = snd_pcm_format_silence_64(format); | 
					
						
							|  |  |  | 	if (! silence) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	dst_step = dst_area->step / 8; | 
					
						
							|  |  |  | 	if (width == 4) { | 
					
						
							|  |  |  | 		/* Ima ADPCM */ | 
					
						
							|  |  |  | 		int dstbit = dst_area->first % 8; | 
					
						
							|  |  |  | 		int dstbit_step = dst_area->step % 8; | 
					
						
							|  |  |  | 		while (samples-- > 0) { | 
					
						
							|  |  |  | 			if (dstbit) | 
					
						
							|  |  |  | 				*dst &= 0xf0; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				*dst &= 0x0f; | 
					
						
							|  |  |  | 			dst += dst_step; | 
					
						
							|  |  |  | 			dstbit += dstbit_step; | 
					
						
							|  |  |  | 			if (dstbit == 8) { | 
					
						
							|  |  |  | 				dst++; | 
					
						
							|  |  |  | 				dstbit = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		width /= 8; | 
					
						
							|  |  |  | 		while (samples-- > 0) { | 
					
						
							|  |  |  | 			memcpy(dst, silence, width); | 
					
						
							|  |  |  | 			dst += dst_step; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-17 14:01:49 +01:00
										 |  |  | int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, | 
					
						
							|  |  |  | 		      const struct snd_pcm_channel_area *dst_area, size_t dst_offset, | 
					
						
							| 
									
										
										
										
											2011-02-14 11:00:47 +01:00
										 |  |  | 		      size_t samples, snd_pcm_format_t format) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* FIXME: sub byte resolution and odd dst_offset */ | 
					
						
							|  |  |  | 	char *src, *dst; | 
					
						
							|  |  |  | 	int width; | 
					
						
							|  |  |  | 	int src_step, dst_step; | 
					
						
							|  |  |  | 	src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; | 
					
						
							|  |  |  | 	if (!src_area->addr) | 
					
						
							|  |  |  | 		return snd_pcm_area_silence(dst_area, dst_offset, samples, format); | 
					
						
							|  |  |  | 	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | 
					
						
							|  |  |  | 	if (!dst_area->addr) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	width = snd_pcm_format_physical_width(format); | 
					
						
							|  |  |  | 	if (width <= 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	if (src_area->step == (unsigned int) width && | 
					
						
							|  |  |  | 	    dst_area->step == (unsigned int) width && width >= 8) { | 
					
						
							|  |  |  | 		size_t bytes = samples * width / 8; | 
					
						
							|  |  |  | 		memcpy(dst, src, bytes); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	src_step = src_area->step / 8; | 
					
						
							|  |  |  | 	dst_step = dst_area->step / 8; | 
					
						
							|  |  |  | 	if (width == 4) { | 
					
						
							|  |  |  | 		/* Ima ADPCM */ | 
					
						
							|  |  |  | 		int srcbit = src_area->first % 8; | 
					
						
							|  |  |  | 		int srcbit_step = src_area->step % 8; | 
					
						
							|  |  |  | 		int dstbit = dst_area->first % 8; | 
					
						
							|  |  |  | 		int dstbit_step = dst_area->step % 8; | 
					
						
							|  |  |  | 		while (samples-- > 0) { | 
					
						
							|  |  |  | 			unsigned char srcval; | 
					
						
							|  |  |  | 			if (srcbit) | 
					
						
							|  |  |  | 				srcval = *src & 0x0f; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				srcval = (*src & 0xf0) >> 4; | 
					
						
							|  |  |  | 			if (dstbit) | 
					
						
							|  |  |  | 				*dst = (*dst & 0xf0) | srcval; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				*dst = (*dst & 0x0f) | (srcval << 4); | 
					
						
							|  |  |  | 			src += src_step; | 
					
						
							|  |  |  | 			srcbit += srcbit_step; | 
					
						
							|  |  |  | 			if (srcbit == 8) { | 
					
						
							|  |  |  | 				src++; | 
					
						
							|  |  |  | 				srcbit = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			dst += dst_step; | 
					
						
							|  |  |  | 			dstbit += dstbit_step; | 
					
						
							|  |  |  | 			if (dstbit == 8) { | 
					
						
							|  |  |  | 				dst++; | 
					
						
							|  |  |  | 				dstbit = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		width /= 8; | 
					
						
							|  |  |  | 		while (samples-- > 0) { | 
					
						
							|  |  |  | 			memcpy(dst, src, width); | 
					
						
							|  |  |  | 			src += src_step; | 
					
						
							|  |  |  | 			dst += dst_step; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } |