usbaudio.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,293 行 · 第 1/5 页
C
2,293 行
/* * (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/moduleparam.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 = 4; /* max. number of packets per urb */static int async_unlink = 1;static int boot_devs;module_param_array(index, int, boot_devs, 0444);MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");module_param_array(id, charp, boot_devs, 0444);MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");module_param_array(enable, bool, boot_devs, 0444);MODULE_PARM_DESC(enable, "Enable USB audio adapter.");module_param_array(vid, int, boot_devs, 0444);MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");module_param_array(pid, int, boot_devs, 0444);MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");module_param(nrpacks, int, 0444);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.");/* * debug the h/w constraints *//* #define HW_CONST_DEBUG *//* * */#define MAX_PACKS 10#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */#define MAX_URBS 5 /* max. 20ms long packets */#define SYNC_URBS 2 /* always two urbs for sync */#define MIN_PACKS_URB 1 /* minimum 1 packet per urb */typedef struct snd_usb_substream snd_usb_substream_t;typedef struct snd_usb_stream snd_usb_stream_t;typedef struct snd_urb_ctx snd_urb_ctx_t;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_urb_ctx { struct urb *urb; snd_usb_substream_t *subs; int index; /* index for urb array */ int packets; /* number of packets per urb */ int transfer; /* transferred size */ char *buf; /* buffer for capture */};struct snd_urb_ops { int (*prepare)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u); int (*retire)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u); int (*prepare_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u); int (*retire_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);};struct snd_usb_substream { snd_usb_stream_t *stream; struct usb_device *dev; snd_pcm_substream_t *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 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 running: 1; /* running status */ unsigned int hwptr; /* free frame position in the buffer (only for playback) */ unsigned int hwptr_done; /* processed frame position in the buffer */ unsigned int transfer_sched; /* scheduled frames since last period (for playback) */ 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 */ snd_urb_ctx_t dataurb[MAX_URBS]; /* data urb table */ snd_urb_ctx_t syncurb[SYNC_URBS]; /* sync urb table */ char syncbuf[SYNC_URBS * MAX_PACKS * 4]; /* sync buffer; it's so small - let's get static */ char *tmpbuf; /* temporary buffer for playback */ u64 formats; /* format bitmasks (all or'ed) */ unsigned int num_formats; /* number of supported audio formats (list) */ struct list_head fmt_list; /* format list */ spinlock_t lock; struct snd_urb_ops ops; /* callbacks (must be filled at init) */};struct snd_usb_stream { snd_usb_audio_t *chip; snd_pcm_t *pcm; int pcm_index; unsigned int fmt_type; /* USB audio format type (1-3) */ snd_usb_substream_t 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 DECLARE_MUTEX(register_mutex);static snd_usb_audio_t *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 */inline static 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 */inline static 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 */inline static 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 */inline static 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(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb){ unsigned char *cp = urb->transfer_buffer; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; int i, offs; urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) { urb->iso_frame_desc[i].length = 3; urb->iso_frame_desc[i].offset = offs; 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(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb){ unsigned char *cp = urb->transfer_buffer; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; int i, offs; urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) { urb->iso_frame_desc[i].length = 4; urb->iso_frame_desc[i].offset = offs; 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(snd_usb_substream_t *subs, snd_pcm_runtime_t *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(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb){ int i, offs; unsigned long flags; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; offs = 0; urb->dev = ctx->subs->dev; /* we need to set this at each time */ urb->number_of_packets = 0; spin_lock_irqsave(&subs->lock, flags); 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->number_of_packets++; subs->transfer_sched += subs->curframesize; if (subs->transfer_sched >= runtime->period_size) { subs->transfer_sched -= runtime->period_size; break; } } spin_unlock_irqrestore(&subs->lock, flags); urb->transfer_buffer = ctx->buf; urb->transfer_buffer_length = offs;#if 0 // for check if (! urb->bandwidth) { int bustime; bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) return bustime; printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets); usb_claim_bandwidth(urb->dev, urb, bustime, 1); }#endif // for check 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(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb){ unsigned long flags; unsigned char *cp; int i; unsigned int stride, len, oldptr; 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; 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); } /* update the pointer, call callback if necessary */ spin_lock_irqsave(&subs->lock, flags); if (subs->transfer_done >= runtime->period_size) { subs->transfer_done -= runtime->period_size; spin_unlock_irqrestore(&subs->lock, flags); snd_pcm_period_elapsed(subs->pcm_substream); } else spin_unlock_irqrestore(&subs->lock, flags); } 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(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb){ int i, offs; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) { urb->iso_frame_desc[i].length = 3; urb->iso_frame_desc[i].offset = offs; } 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(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb){ int i, offs; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; urb->number_of_packets = ctx->packets; urb->dev = ctx->subs->dev; /* we need to set this at each time */ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) { urb->iso_frame_desc[i].length = 4; urb->iso_frame_desc[i].offset = offs; } return 0;}/* * process after full speed playback sync complete * * retrieve the current 10.14 frequency from pipe, and set it.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?