📄 usbaudio.c
字号:
list_for_each_entry(fp, &subs->fmt_list, list) { int i; for (i = 0; i < fp->nr_rates; i++) subs->rate_list.list[count++] = fp->rate_table[i]; } err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &subs->rate_list); if (err < 0) return err; return 0;}/* * set up the runtime hardware information. */static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs){ struct list_head *p; int err; runtime->hw.formats = subs->formats; runtime->hw.rate_min = 0x7fffffff; runtime->hw.rate_max = 0; runtime->hw.channels_min = 256; runtime->hw.channels_max = 0; runtime->hw.rates = 0; /* check min/max rates and channels */ list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); runtime->hw.rates |= fp->rates; if (runtime->hw.rate_min > fp->rate_min) runtime->hw.rate_min = fp->rate_min; if (runtime->hw.rate_max < fp->rate_max) runtime->hw.rate_max = fp->rate_max; if (runtime->hw.channels_min > fp->channels) runtime->hw.channels_min = fp->channels; if (runtime->hw.channels_max < fp->channels) runtime->hw.channels_max = fp->channels; if (fp->fmt_type == USB_FORMAT_TYPE_II && fp->frame_size > 0) { /* FIXME: there might be more than one audio formats... */ runtime->hw.period_bytes_min = runtime->hw.period_bytes_max = fp->frame_size; } } /* set the period time minimum 1ms */ /* FIXME: high-speed mode allows 125us minimum period, but many parts * in the current code assume the 1ms period. */ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000 * MIN_PACKS_URB, /*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX); if (check_hw_params_convention(subs)) { hwc_debug("setting extra hw constraints...\n"); if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, subs, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) return err; if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, subs, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_RATE, -1)) < 0) return err; if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, hw_rule_format, subs, SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) return err; if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) return err; } return 0;}static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction){ struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = &as->substream[direction]; subs->interface = -1; subs->format = 0; runtime->hw = snd_usb_hardware; runtime->private_data = subs; subs->pcm_substream = substream; return setup_hw_info(runtime, subs);}static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction){ struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_usb_substream *subs = &as->substream[direction]; if (subs->interface >= 0) { usb_set_interface(subs->dev, subs->interface, 0); subs->interface = -1; } subs->pcm_substream = NULL; return 0;}static int snd_usb_playback_open(struct snd_pcm_substream *substream){ return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);}static int snd_usb_playback_close(struct snd_pcm_substream *substream){ return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK);}static int snd_usb_capture_open(struct snd_pcm_substream *substream){ return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);}static int snd_usb_capture_close(struct snd_pcm_substream *substream){ return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);}static struct snd_pcm_ops snd_usb_playback_ops = { .open = snd_usb_playback_open, .close = snd_usb_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usb_hw_params, .hw_free = snd_usb_hw_free, .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_pcm_playback_trigger, .pointer = snd_usb_pcm_pointer, .page = snd_pcm_get_vmalloc_page,};static struct snd_pcm_ops snd_usb_capture_ops = { .open = snd_usb_capture_open, .close = snd_usb_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usb_hw_params, .hw_free = snd_usb_hw_free, .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_pcm_capture_trigger, .pointer = snd_usb_pcm_pointer, .page = snd_pcm_get_vmalloc_page,};/* * helper functions *//* * combine bytes and get an integer value */unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size){ switch (size) { case 1: return *bytes; case 2: return combine_word(bytes); case 3: return combine_triple(bytes); case 4: return combine_quad(bytes); default: return 0; }}/* * parse descriptor buffer and return the pointer starting the given * descriptor type. */void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype){ u8 *p, *end, *next; p = descstart; end = p + desclen; for (; p < end;) { if (p[0] < 2) return NULL; next = p + p[0]; if (next > end) return NULL; if (p[1] == dtype && (!after || (void *)p > after)) { return p; } p = next; } return NULL;}/* * find a class-specified interface descriptor with the given subtype. */void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype){ unsigned char *p = after; while ((p = snd_usb_find_desc(buffer, buflen, p, USB_DT_CS_INTERFACE)) != NULL) { if (p[0] >= 3 && p[2] == dsubtype) return p; } return NULL;}/* * Wrapper for usb_control_msg(). * Allocates a temp buffer to prevent dmaing from/to the stack. */int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout){ int err; void *buf = NULL; if (size > 0) { buf = kmemdup(data, size, GFP_KERNEL); if (!buf) return -ENOMEM; } err = usb_control_msg(dev, pipe, request, requesttype, value, index, buf, size, timeout); if (size > 0) { memcpy(data, buf, size); kfree(buf); } return err;}/* * entry point for linux usb interface */static int usb_audio_probe(struct usb_interface *intf, const struct usb_device_id *id);static void usb_audio_disconnect(struct usb_interface *intf);static struct usb_device_id usb_audio_ids [] = {#include "usbquirks.h" { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL }, { } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, usb_audio_ids);static struct usb_driver usb_audio_driver = { .name = "snd-usb-audio", .probe = usb_audio_probe, .disconnect = usb_audio_disconnect, .id_table = usb_audio_ids,};#if defined(CONFIG_PROC_FS) && defined(CONFIG_SND_VERBOSE_PROCFS)/* * proc interface for list the supported pcm formats */static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer){ struct list_head *p; static char *sync_types[4] = { "NONE", "ASYNC", "ADAPTIVE", "SYNC" }; list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); snd_iprintf(buffer, " Interface %d\n", fp->iface); snd_iprintf(buffer, " Altset %d\n", fp->altsetting); snd_iprintf(buffer, " Format: 0x%x\n", fp->format); snd_iprintf(buffer, " Channels: %d\n", fp->channels); snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", fp->endpoint & USB_ENDPOINT_NUMBER_MASK, fp->endpoint & USB_DIR_IN ? "IN" : "OUT", sync_types[(fp->ep_attr & EP_ATTR_MASK) >> 2]); if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) { snd_iprintf(buffer, " Rates: %d - %d (continuous)\n", fp->rate_min, fp->rate_max); } else { unsigned int i; snd_iprintf(buffer, " Rates: "); for (i = 0; i < fp->nr_rates; i++) { if (i > 0) snd_iprintf(buffer, ", "); snd_iprintf(buffer, "%d", fp->rate_table[i]); } snd_iprintf(buffer, "\n"); } // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); // snd_iprintf(buffer, " EP Attribute = 0x%x\n", fp->attributes); }}static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer){ if (subs->running) { unsigned int i; snd_iprintf(buffer, " Status: Running\n"); snd_iprintf(buffer, " Interface = %d\n", subs->interface); snd_iprintf(buffer, " Altset = %d\n", subs->format); snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs); for (i = 0; i < subs->nurbs; i++) snd_iprintf(buffer, "%d ", subs->dataurb[i].packets); snd_iprintf(buffer, "]\n"); snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize); snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n", snd_usb_get_speed(subs->dev) == USB_SPEED_FULL ? get_full_speed_hz(subs->freqm) : get_high_speed_hz(subs->freqm), subs->freqm >> 16, subs->freqm & 0xffff); } else { snd_iprintf(buffer, " Status: Stop\n"); }}static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ struct snd_usb_stream *stream = entry->private_data; snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name); if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) { snd_iprintf(buffer, "\nPlayback:\n"); proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); } if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) { snd_iprintf(buffer, "\nCapture:\n"); proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); }}static void proc_pcm_format_add(struct snd_usb_stream *stream){ struct snd_info_entry *entry; char name[32]; struct snd_card *card = stream->chip->card; sprintf(name, "stream%d", stream->pcm_index); if (! snd_card_proc_new(card, name, &entry)) snd_info_set_text_ops(entry, stream, proc_pcm_format_read);}#elsestatic inline void proc_pcm_format_add(struct snd_usb_stream *stream){}#endif/* * initialize the substream instance. */static void init_substream(struct snd_usb_stream *as, int stream, struct audioformat *fp){ struct snd_usb_substream *subs = &as->substream[stream]; INIT_LIST_HEAD(&subs->fmt_list); spin_lock_init(&subs->lock); subs->stream = as; subs->direction = stream; subs->dev = as->chip->dev; if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) subs->ops = audio_urb_ops[stream]; else subs->ops = audio_urb_ops_high_speed[stream]; snd_pcm_set_ops(as->pcm, stream, stream == SNDRV_PCM_STREAM_PLAYBACK ? &snd_usb_playback_ops : &snd_usb_capture_ops); list_add_tail(&fp->list, &subs->fmt_list); subs->formats |= 1ULL << fp->format; subs->endpoint = fp->endpoint; subs->num_formats++; subs->fmt_type = fp->fmt_type;}/* * free a substream */static void free_substream(struct snd_usb_substream *subs){ struct list_head *p, *n; if (! subs->num_formats) return; /* not initialized */ list_for_each_safe(p, n, &subs->fmt_list) { struct audioformat *fp = list_entry(p, struct audioformat, list); kfree(fp->rate_table); kfree(fp); } kfree(subs->rate_list.list);}/* * free a usb stream instance */static void snd_usb_audio_stream_free(struct snd_usb_stream *stream){ free_substream(&stream->substream[0]); free_substream(&stream->substream[1]); list_del(&stream->list); kfree(stream);}static void snd_usb_audio_pcm_free(struct snd_pcm *pcm){ struct snd_usb_stream *stream = pcm->private_data; if (stream) { stream->pcm = NULL; snd_usb_audio_stream_free(stream); }}/* * add this endpoint to the chip instance. * if a stream with the same endpoint already exists, append to it. * if not, create a new pcm stream. */static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp){ struct list_head *p; struct snd_usb_stream *as; struct snd_usb_substream *subs; struct snd_pcm *pcm; int err; list_for_each(p, &chip->pcm_list) { as = list_entry(p, struct snd_usb_stream, list); if (as->fmt_type != fp->fmt_type) continue; subs = &as->substream[stream];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -