⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 caiaq-audio.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *   Copyright (c) 2006,2007 Daniel Mack, Karsten Wiese * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU 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 General Public License for more details. * *   You should have received a copy of the GNU General Public License *   along with this program; if not, write to the Free Software *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA*/#include <sound/driver.h>#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/interrupt.h>#include <linux/usb.h>#include <linux/spinlock.h>#include <sound/core.h>#include <sound/initval.h>#include <sound/pcm.h>#include <sound/rawmidi.h>#ifdef CONFIG_SND_USB_CAIAQ_INPUT#include <linux/input.h>#endif#include "caiaq-device.h"#include "caiaq-audio.h"#define N_URBS			32#define CLOCK_DRIFT_TOLERANCE	5#define FRAMES_PER_URB		8#define BYTES_PER_FRAME		512#define CHANNELS_PER_STREAM	2#define BYTES_PER_SAMPLE	3#define BYTES_PER_SAMPLE_USB	4#define MAX_BUFFER_SIZE		(128*1024)				 #define ENDPOINT_CAPTURE	2#define ENDPOINT_PLAYBACK	6#define MAKE_CHECKBYTE(dev,stream,i) \	(stream << 1) | (~(i / (dev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {	.info 		= (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 			   SNDRV_PCM_INFO_BLOCK_TRANSFER),	.formats 	= SNDRV_PCM_FMTBIT_S24_3BE,	.rates 		= (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 			   SNDRV_PCM_RATE_96000),	.rate_min	= 44100,	.rate_max	= 0, /* will overwrite later */	.channels_min	= CHANNELS_PER_STREAM,	.channels_max	= CHANNELS_PER_STREAM,	.buffer_bytes_max = MAX_BUFFER_SIZE,	.period_bytes_min = 4096,	.period_bytes_max = MAX_BUFFER_SIZE,	.periods_min	= 1,	.periods_max	= 1024,};static voidactivate_substream(struct snd_usb_caiaqdev *dev,	           struct snd_pcm_substream *sub){	if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)		dev->sub_playback[sub->number] = sub;	else		dev->sub_capture[sub->number] = sub;}static void deactivate_substream(struct snd_usb_caiaqdev *dev,		     struct snd_pcm_substream *sub){	if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)		dev->sub_playback[sub->number] = NULL;	else		dev->sub_capture[sub->number] = NULL;}static intall_substreams_zero(struct snd_pcm_substream **subs){	int i;	for (i = 0; i < MAX_STREAMS; i++)		if (subs[i] != NULL)			return 0;	return 1;}static int stream_start(struct snd_usb_caiaqdev *dev){	int i, ret;	debug("stream_start(%p)\n", dev);	spin_lock_irq(&dev->spinlock);	if (dev->streaming) {		spin_unlock_irq(&dev->spinlock);		return -EINVAL;	}	dev->input_panic = 0;	dev->output_panic = 0;	dev->first_packet = 1;	dev->streaming = 1;	for (i = 0; i < N_URBS; i++) {		ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC);		if (ret) {			log("unable to trigger initial read #%d! (ret = %d)\n",				i, ret);			dev->streaming = 0;			spin_unlock_irq(&dev->spinlock);			return -EPIPE;		}	}		spin_unlock_irq(&dev->spinlock);	return 0;}static void stream_stop(struct snd_usb_caiaqdev *dev){	int i;		debug("stream_stop(%p)\n", dev);	if (!dev->streaming)		return;		dev->streaming = 0;	for (i = 0; i < N_URBS; i++) {		usb_unlink_urb(dev->data_urbs_in[i]);		usb_unlink_urb(dev->data_urbs_out[i]);	}}static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream){	struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);	debug("snd_usb_caiaq_substream_open(%p)\n", substream);	substream->runtime->hw = dev->pcm_info;	snd_pcm_limit_hw_rates(substream->runtime);	return 0;}static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream){	struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);	debug("snd_usb_caiaq_substream_close(%p)\n", substream);	if (all_substreams_zero(dev->sub_playback) &&	    all_substreams_zero(dev->sub_capture)) {		/* when the last client has stopped streaming, 		 * all sample rates are allowed again */		stream_stop(dev);		dev->pcm_info.rates = dev->samplerates;	}		return 0;}static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub,			     		struct snd_pcm_hw_params *hw_params){	debug("snd_usb_caiaq_pcm_hw_params(%p)\n", sub);	return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));}static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub){	struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);	debug("snd_usb_caiaq_pcm_hw_free(%p)\n", sub);	spin_lock_irq(&dev->spinlock);	deactivate_substream(dev, sub);	spin_unlock_irq(&dev->spinlock);	return snd_pcm_lib_free_pages(sub);}/* this should probably go upstream */#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12#error "Change this table"#endifstatic unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,                                 48000, 64000, 88200, 96000, 176400, 192000 };static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream){	int bytes_per_sample, bpp, ret, i;	int index = substream->number;	struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);	struct snd_pcm_runtime *runtime = substream->runtime;	debug("snd_usb_caiaq_pcm_prepare(%p)\n", substream);		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)		dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;	else		dev->audio_in_buf_pos[index] = 0;		if (dev->streaming)		return 0;		/* the first client that opens a stream defines the sample rate	 * setting for all subsequent calls, until the last client closed. */	for (i=0; i < ARRAY_SIZE(rates); i++)		if (runtime->rate == rates[i])			dev->pcm_info.rates = 1 << i;		snd_pcm_limit_hw_rates(runtime);	bytes_per_sample = BYTES_PER_SAMPLE;	if (dev->spec.data_alignment == 2)		bytes_per_sample++;		bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)		* bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams;		ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate,					     runtime->sample_bits, bpp);	if (ret)		return ret;	ret = stream_start(dev);	if (ret)		return ret;		dev->output_running = 0;	wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ);	if (!dev->output_running) {		stream_stop(dev);		return -EPIPE;	}	return 0;}static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd){	struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		spin_lock(&dev->spinlock);		activate_substream(dev, sub);		spin_unlock(&dev->spinlock);		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		spin_lock(&dev->spinlock);		deactivate_substream(dev, sub);		spin_unlock(&dev->spinlock);		break;	default:		return -EINVAL;	}	return 0;}static snd_pcm_uframes_tsnd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub){	int index = sub->number;	struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);	if (dev->input_panic || dev->output_panic)		return SNDRV_PCM_POS_XRUN;	if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)		return bytes_to_frames(sub->runtime, 					dev->audio_out_buf_pos[index]);	else		return bytes_to_frames(sub->runtime,					dev->audio_in_buf_pos[index]);}/* operators for both playback and capture */static struct snd_pcm_ops snd_usb_caiaq_ops = {	.open =		snd_usb_caiaq_substream_open,	.close =	snd_usb_caiaq_substream_close,	.ioctl =	snd_pcm_lib_ioctl,	.hw_params =	snd_usb_caiaq_pcm_hw_params,	.hw_free =	snd_usb_caiaq_pcm_hw_free,	.prepare =	snd_usb_caiaq_pcm_prepare,	.trigger =	snd_usb_caiaq_pcm_trigger,	.pointer =	snd_usb_caiaq_pcm_pointer};	static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev,				      struct snd_pcm_substream **subs){	int stream, pb, *cnt;	struct snd_pcm_substream *sub;	for (stream = 0; stream < dev->n_streams; stream++) {		sub = subs[stream];		if (!sub)			continue;		pb = frames_to_bytes(sub->runtime, 				     sub->runtime->period_size);		cnt = (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) ?					&dev->period_out_count[stream] :					&dev->period_in_count[stream];		if (*cnt >= pb) {			snd_pcm_period_elapsed(sub);			*cnt %= pb;		}	}}static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev,			      const struct urb *urb,			      const struct usb_iso_packet_descriptor *iso){	unsigned char *usb_buf = urb->transfer_buffer + iso->offset;	struct snd_pcm_substream *sub;	int stream, i;	if (all_substreams_zero(dev->sub_capture))		return;	spin_lock(&dev->spinlock);		for (i = 0; i < iso->actual_length;) {		for (stream = 0; stream < dev->n_streams; stream++, i++) {			sub = dev->sub_capture[stream];			if (sub) {				struct snd_pcm_runtime *rt = sub->runtime;				char *audio_buf = rt->dma_area;				int sz = frames_to_bytes(rt, rt->buffer_size);				audio_buf[dev->audio_in_buf_pos[stream]++] 					= usb_buf[i];				dev->period_in_count[stream]++;				if (dev->audio_in_buf_pos[stream] == sz)					dev->audio_in_buf_pos[stream] = 0;			}		}	}		spin_unlock(&dev->spinlock);}static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -