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

📄 usbaudio.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *   (Tentative) USB Audio Driver for ALSA * *   Main and PCM part * *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> * *   Many codes borrowed from audio.c by *	    Alan Cox (alan@lxorguk.ukuu.org.uk) *	    Thomas Sailer (sailer@ife.ee.ethz.ch) * * *   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 * * *  NOTES: * *   - async unlink should be used for avoiding the sleep inside lock. *     2.4.22 usb-uhci seems buggy for async unlinking and results in *     oops.  in such a cse, pass async_unlink=0 option. *   - the linked URBs would be preferred but not used so far because of *     the instability of unlinking. *   - type II is not supported properly.  there is no device which supports *     this type *correctly*.  SB extigy looks as if it supports, but it's *     indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream). */#include <sound/driver.h>#include <linux/bitops.h>#include <linux/init.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/usb.h>#include <linux/vmalloc.h>#include <linux/moduleparam.h>#include <linux/mutex.h>#include <sound/core.h>#include <sound/info.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/initval.h>#include "usbaudio.h"MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");MODULE_DESCRIPTION("USB Audio");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */static int nrpacks = 8;		/* max. number of packets per urb */static int async_unlink = 1;static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable USB audio adapter.");module_param_array(vid, int, NULL, 0444);MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");module_param_array(pid, int, NULL, 0444);MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");module_param(nrpacks, int, 0644);MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");module_param(async_unlink, bool, 0444);MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");module_param_array(device_setup, int, NULL, 0444);MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");/* * debug the h/w constraints *//* #define HW_CONST_DEBUG *//* * */#define MAX_PACKS	20#define MAX_PACKS_HS	(MAX_PACKS * 8)	/* in high speed mode */#define MAX_URBS	8#define SYNC_URBS	4	/* always four urbs for sync */#define MIN_PACKS_URB	1	/* minimum 1 packet per urb */struct audioformat {	struct list_head list;	snd_pcm_format_t format;	/* format type */	unsigned int channels;		/* # channels */	unsigned int fmt_type;		/* USB audio format type (1-3) */	unsigned int frame_size;	/* samples per frame for non-audio */	int iface;			/* interface number */	unsigned char altsetting;	/* corresponding alternate setting */	unsigned char altset_idx;	/* array index of altenate setting */	unsigned char attributes;	/* corresponding attributes of cs endpoint */	unsigned char endpoint;		/* endpoint */	unsigned char ep_attr;		/* endpoint attributes */	unsigned int maxpacksize;	/* max. packet size */	unsigned int rates;		/* rate bitmasks */	unsigned int rate_min, rate_max;	/* min/max rates */	unsigned int nr_rates;		/* number of rate table entries */	unsigned int *rate_table;	/* rate table */};struct snd_usb_substream;struct snd_urb_ctx {	struct urb *urb;	unsigned int buffer_size;	/* size of data buffer, if data URB */	struct snd_usb_substream *subs;	int index;	/* index for urb array */	int packets;	/* number of packets per urb */};struct snd_urb_ops {	int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);	int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);	int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);	int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);};struct snd_usb_substream {	struct snd_usb_stream *stream;	struct usb_device *dev;	struct snd_pcm_substream *pcm_substream;	int direction;	/* playback or capture */	int interface;	/* current interface */	int endpoint;	/* assigned endpoint */	struct audioformat *cur_audiofmt;	/* current audioformat pointer (for hw_params callback) */	unsigned int cur_rate;		/* current rate (for hw_params callback) */	unsigned int period_bytes;	/* current period bytes (for hw_params callback) */	unsigned int format;     /* USB data format */	unsigned int datapipe;   /* the data i/o pipe */	unsigned int syncpipe;   /* 1 - async out or adaptive in */	unsigned int datainterval;	/* log_2 of data packet interval */	unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */	unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */	unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */	unsigned int freqmax;    /* maximum sampling rate, used for buffer management */	unsigned int phase;      /* phase accumulator */	unsigned int maxpacksize;	/* max packet size in bytes */	unsigned int maxframesize;	/* max packet size in frames */	unsigned int curpacksize;	/* current packet size in bytes (for capture) */	unsigned int curframesize;	/* current packet size in frames (for capture) */	unsigned int fill_max: 1;	/* fill max packet size always */	unsigned int fmt_type;		/* USB audio format type (1-3) */	unsigned int packs_per_ms;	/* packets per millisecond (for playback) */	unsigned int running: 1;	/* running status */	unsigned int hwptr_done;			/* processed frame position in the buffer */	unsigned int transfer_done;		/* processed frames since last period update */	unsigned long active_mask;	/* bitmask of active urbs */	unsigned long unlink_mask;	/* bitmask of unlinked urbs */	unsigned int nurbs;			/* # urbs */	struct snd_urb_ctx dataurb[MAX_URBS];	/* data urb table */	struct snd_urb_ctx syncurb[SYNC_URBS];	/* sync urb table */	char *syncbuf;				/* sync buffer for all sync URBs */	dma_addr_t sync_dma;			/* DMA address of syncbuf */	u64 formats;			/* format bitmasks (all or'ed) */	unsigned int num_formats;		/* number of supported audio formats (list) */	struct list_head fmt_list;	/* format list */	struct snd_pcm_hw_constraint_list rate_list;	/* limited rates */	spinlock_t lock;	struct snd_urb_ops ops;		/* callbacks (must be filled at init) */};struct snd_usb_stream {	struct snd_usb_audio *chip;	struct snd_pcm *pcm;	int pcm_index;	unsigned int fmt_type;		/* USB audio format type (1-3) */	struct snd_usb_substream substream[2];	struct list_head list;};/* * we keep the snd_usb_audio_t instances by ourselves for merging * the all interfaces on the same card as one sound device. */static DEFINE_MUTEX(register_mutex);static struct snd_usb_audio *usb_chip[SNDRV_CARDS];/* * convert a sampling rate into our full speed format (fs/1000 in Q16.16) * this will overflow at approx 524 kHz */static inline unsigned get_usb_full_speed_rate(unsigned int rate){	return ((rate << 13) + 62) / 125;}/* * convert a sampling rate into USB high speed format (fs/8000 in Q16.16) * this will overflow at approx 4 MHz */static inline unsigned get_usb_high_speed_rate(unsigned int rate){	return ((rate << 10) + 62) / 125;}/* convert our full speed USB rate into sampling rate in Hz */static inline unsigned get_full_speed_hz(unsigned int usb_rate){	return (usb_rate * 125 + (1 << 12)) >> 13;}/* convert our high speed USB rate into sampling rate in Hz */static inline unsigned get_high_speed_hz(unsigned int usb_rate){	return (usb_rate * 125 + (1 << 9)) >> 10;}/* * prepare urb for full speed capture sync pipe * * fill the length and offset of each urb descriptor. * the fixed 10.14 frequency is passed through the pipe. */static int prepare_capture_sync_urb(struct snd_usb_substream *subs,				    struct snd_pcm_runtime *runtime,				    struct urb *urb){	unsigned char *cp = urb->transfer_buffer;	struct snd_urb_ctx *ctx = urb->context;	urb->dev = ctx->subs->dev; /* we need to set this at each time */	urb->iso_frame_desc[0].length = 3;	urb->iso_frame_desc[0].offset = 0;	cp[0] = subs->freqn >> 2;	cp[1] = subs->freqn >> 10;	cp[2] = subs->freqn >> 18;	return 0;}/* * prepare urb for high speed capture sync pipe * * fill the length and offset of each urb descriptor. * the fixed 12.13 frequency is passed as 16.16 through the pipe. */static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,				       struct snd_pcm_runtime *runtime,				       struct urb *urb){	unsigned char *cp = urb->transfer_buffer;	struct snd_urb_ctx *ctx = urb->context;	urb->dev = ctx->subs->dev; /* we need to set this at each time */	urb->iso_frame_desc[0].length = 4;	urb->iso_frame_desc[0].offset = 0;	cp[0] = subs->freqn;	cp[1] = subs->freqn >> 8;	cp[2] = subs->freqn >> 16;	cp[3] = subs->freqn >> 24;	return 0;}/* * process after capture sync complete * - nothing to do */static int retire_capture_sync_urb(struct snd_usb_substream *subs,				   struct snd_pcm_runtime *runtime,				   struct urb *urb){	return 0;}/* * prepare urb for capture data pipe * * fill the offset and length of each descriptor. * * we use a temporary buffer to write the captured data. * since the length of written data is determined by host, we cannot * write onto the pcm buffer directly...  the data is thus copied * later at complete callback to the global buffer. */static int prepare_capture_urb(struct snd_usb_substream *subs,			       struct snd_pcm_runtime *runtime,			       struct urb *urb){	int i, offs;	struct snd_urb_ctx *ctx = urb->context;	offs = 0;	urb->dev = ctx->subs->dev; /* we need to set this at each time */	for (i = 0; i < ctx->packets; i++) {		urb->iso_frame_desc[i].offset = offs;		urb->iso_frame_desc[i].length = subs->curpacksize;		offs += subs->curpacksize;	}	urb->transfer_buffer_length = offs;	urb->number_of_packets = ctx->packets;	return 0;}/* * process after capture complete * * copy the data from each desctiptor to the pcm buffer, and * update the current position. */static int retire_capture_urb(struct snd_usb_substream *subs,			      struct snd_pcm_runtime *runtime,			      struct urb *urb){	unsigned long flags;	unsigned char *cp;	int i;	unsigned int stride, len, oldptr;	int period_elapsed = 0;	stride = runtime->frame_bits >> 3;	for (i = 0; i < urb->number_of_packets; i++) {		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;		if (urb->iso_frame_desc[i].status) {			snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);			// continue;		}		len = urb->iso_frame_desc[i].actual_length / stride;		if (! len)			continue;		/* update the current pointer */		spin_lock_irqsave(&subs->lock, flags);		oldptr = subs->hwptr_done;		subs->hwptr_done += len;		if (subs->hwptr_done >= runtime->buffer_size)			subs->hwptr_done -= runtime->buffer_size;		subs->transfer_done += len;		if (subs->transfer_done >= runtime->period_size) {			subs->transfer_done -= runtime->period_size;			period_elapsed = 1;		}		spin_unlock_irqrestore(&subs->lock, flags);		/* copy a data chunk */		if (oldptr + len > runtime->buffer_size) {			unsigned int cnt = runtime->buffer_size - oldptr;			unsigned int blen = cnt * stride;			memcpy(runtime->dma_area + oldptr * stride, cp, blen);			memcpy(runtime->dma_area, cp + blen, len * stride - blen);		} else {			memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);		}	}	if (period_elapsed)		snd_pcm_period_elapsed(subs->pcm_substream);	return 0;}/* * Process after capture complete when paused.  Nothing to do. */static int retire_paused_capture_urb(struct snd_usb_substream *subs,				     struct snd_pcm_runtime *runtime,				     struct urb *urb){	return 0;}/* * prepare urb for full speed playback sync pipe * * set up the offset and length to receive the current frequency. */static int prepare_playback_sync_urb(struct snd_usb_substream *subs,				     struct snd_pcm_runtime *runtime,				     struct urb *urb){	struct snd_urb_ctx *ctx = urb->context;	urb->dev = ctx->subs->dev; /* we need to set this at each time */	urb->iso_frame_desc[0].length = 3;	urb->iso_frame_desc[0].offset = 0;	return 0;}/* * prepare urb for high speed playback sync pipe * * set up the offset and length to receive the current frequency. */static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,					struct snd_pcm_runtime *runtime,					struct urb *urb){	struct snd_urb_ctx *ctx = urb->context;	urb->dev = ctx->subs->dev; /* we need to set this at each time */	urb->iso_frame_desc[0].length = 4;	urb->iso_frame_desc[0].offset = 0;	return 0;}/* * process after full speed playback sync complete * * retrieve the current 10.14 frequency from pipe, and set it. * the value is referred in prepare_playback_urb(). */static int retire_playback_sync_urb(struct snd_usb_substream *subs,				    struct snd_pcm_runtime *runtime,				    struct urb *urb){	unsigned int f;	unsigned long flags;	if (urb->iso_frame_desc[0].status == 0 &&	    urb->iso_frame_desc[0].actual_length == 3) {		f = combine_triple((u8*)urb->transfer_buffer) << 2;		if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {			spin_lock_irqsave(&subs->lock, flags);			subs->freqm = f;			spin_unlock_irqrestore(&subs->lock, flags);		}	}	return 0;}/* * process after high speed playback sync complete

⌨️ 快捷键说明

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