📄 usbaudio.c
字号:
} ep &= USB_ENDPOINT_NUMBER_MASK; if (is_playback) subs->syncpipe = usb_rcvisocpipe(dev, ep); else subs->syncpipe = usb_sndisocpipe(dev, ep); if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && get_endpoint(alts, 1)->bRefresh >= 1 && get_endpoint(alts, 1)->bRefresh <= 9) subs->syncinterval = get_endpoint(alts, 1)->bRefresh; else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) subs->syncinterval = 1; else if (get_endpoint(alts, 1)->bInterval >= 1 && get_endpoint(alts, 1)->bInterval <= 16) subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1; else subs->syncinterval = 3; } /* always fill max packet size */ if (fmt->attributes & EP_CS_ATTR_FILL_MAX) subs->fill_max = 1; if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0) return err; subs->cur_audiofmt = fmt;#if 0 printk("setting done: format = %d, rate = %d, channels = %d\n", fmt->format, fmt->rate, fmt->channels); printk(" datapipe = 0x%0x, syncpipe = 0x%0x\n", subs->datapipe, subs->syncpipe);#endif return 0;}/* * hw_params callback * * allocate a buffer and set the given audio format. * * so far we use a physically linear buffer although packetize transfer * doesn't need a continuous area. * if sg buffer is supported on the later version of alsa, we'll follow * that. */static int snd_usb_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct snd_usb_substream *subs = substream->runtime->private_data; struct audioformat *fmt; unsigned int channels, rate, format; int ret, changed; ret = snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); fmt = find_format(subs, format, rate, channels); if (! fmt) { snd_printd(KERN_DEBUG "cannot set format: format = 0x%x, rate = %d, channels = %d\n", format, rate, channels); return -EINVAL; } changed = subs->cur_audiofmt != fmt || subs->period_bytes != params_period_bytes(hw_params) || subs->cur_rate != rate; if ((ret = set_format(subs, fmt)) < 0) return ret; if (subs->cur_rate != rate) { struct usb_host_interface *alts; struct usb_interface *iface; iface = usb_ifnum_to_if(subs->dev, fmt->iface); alts = &iface->altsetting[fmt->altset_idx]; ret = init_usb_sample_rate(subs->dev, subs->interface, alts, fmt, rate); if (ret < 0) return ret; subs->cur_rate = rate; } if (changed) { /* format changed */ release_substream_urbs(subs, 0); /* influenced: period_bytes, channels, rate, format, */ ret = init_substream_urbs(subs, params_period_bytes(hw_params), params_rate(hw_params), snd_pcm_format_physical_width(params_format(hw_params)) * params_channels(hw_params)); } return ret;}/* * hw_free callback * * reset the audio format and release the buffer */static int snd_usb_hw_free(struct snd_pcm_substream *substream){ struct snd_usb_substream *subs = substream->runtime->private_data; subs->cur_audiofmt = NULL; subs->cur_rate = 0; subs->period_bytes = 0; if (!subs->stream->chip->shutdown) release_substream_urbs(subs, 0); return snd_pcm_free_vmalloc_buffer(substream);}/* * prepare callback * * only a few subtle things... */static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = runtime->private_data; if (! subs->cur_audiofmt) { snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); return -ENXIO; } /* some unit conversions in runtime */ subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); /* reset the pointer */ subs->hwptr_done = 0; subs->transfer_done = 0; subs->phase = 0; /* clear urbs (to be sure) */ deactivate_urbs(subs, 0, 1); wait_clear_urbs(subs); /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { subs->ops.prepare = prepare_nodata_playback_urb; return start_urbs(subs, runtime); } else return 0;}static struct snd_pcm_hardware snd_usb_hardware ={ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE, .buffer_bytes_max = 1024 * 1024, .period_bytes_min = 64, .period_bytes_max = 512 * 1024, .periods_min = 2, .periods_max = 1024,};/* * h/w constraints */#ifdef HW_CONST_DEBUG#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)#else#define hwc_debug(fmt, args...) /**/#endifstatic int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audioformat *fp){ struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); /* check the format */ if (! snd_mask_test(fmts, fp->format)) { hwc_debug(" > check: no supported format %d\n", fp->format); return 0; } /* check the channels */ if (fp->channels < ct->min || fp->channels > ct->max) { hwc_debug(" > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max); return 0; } /* check the rate is within the range */ if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) { hwc_debug(" > check: rate_min %d > max %d\n", fp->rate_min, it->max); return 0; } if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) { hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min); return 0; } return 1;}static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_usb_substream *subs = rule->private; struct list_head *p; struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); unsigned int rmin, rmax; int changed; hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); changed = 0; rmin = rmax = 0; list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); if (! hw_check_valid_format(params, fp)) continue; if (changed++) { if (rmin > fp->rate_min) rmin = fp->rate_min; if (rmax < fp->rate_max) rmax = fp->rate_max; } else { rmin = fp->rate_min; rmax = fp->rate_max; } } if (! changed) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; } changed = 0; if (it->min < rmin) { it->min = rmin; it->openmin = 0; changed = 1; } if (it->max > rmax) { it->max = rmax; it->openmax = 0; changed = 1; } if (snd_interval_checkempty(it)) { it->empty = 1; return -EINVAL; } hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); return changed;}static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_usb_substream *subs = rule->private; struct list_head *p; struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); unsigned int rmin, rmax; int changed; hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max); changed = 0; rmin = rmax = 0; list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); if (! hw_check_valid_format(params, fp)) continue; if (changed++) { if (rmin > fp->channels) rmin = fp->channels; if (rmax < fp->channels) rmax = fp->channels; } else { rmin = fp->channels; rmax = fp->channels; } } if (! changed) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; } changed = 0; if (it->min < rmin) { it->min = rmin; it->openmin = 0; changed = 1; } if (it->max > rmax) { it->max = rmax; it->openmax = 0; changed = 1; } if (snd_interval_checkempty(it)) { it->empty = 1; return -EINVAL; } hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); return changed;}static int hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_usb_substream *subs = rule->private; struct list_head *p; struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); u64 fbits; u32 oldbits[2]; int changed; hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]); fbits = 0; list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); if (! hw_check_valid_format(params, fp)) continue; fbits |= (1ULL << fp->format); } oldbits[0] = fmt->bits[0]; oldbits[1] = fmt->bits[1]; fmt->bits[0] &= (u32)fbits; fmt->bits[1] &= (u32)(fbits >> 32); if (! fmt->bits[0] && ! fmt->bits[1]) { hwc_debug(" --> get empty\n"); return -EINVAL; } changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]); hwc_debug(" --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed); return changed;}#define MAX_MASK 64/* * check whether the registered audio formats need special hw-constraints */static int check_hw_params_convention(struct snd_usb_substream *subs){ int i; u32 *channels; u32 *rates; u32 cmaster, rmaster; u32 rate_min = 0, rate_max = 0; struct list_head *p; int err = 1; channels = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); rates = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); list_for_each(p, &subs->fmt_list) { struct audioformat *f; f = list_entry(p, struct audioformat, list); /* unconventional channels? */ if (f->channels > 32) goto __out; /* continuous rate min/max matches? */ if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) { if (rate_min && f->rate_min != rate_min) goto __out; if (rate_max && f->rate_max != rate_max) goto __out; rate_min = f->rate_min; rate_max = f->rate_max; } /* combination of continuous rates and fixed rates? */ if (rates[f->format] & SNDRV_PCM_RATE_CONTINUOUS) { if (f->rates != rates[f->format]) goto __out; } if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) { if (rates[f->format] && rates[f->format] != f->rates) goto __out; } channels[f->format] |= (1 << f->channels); rates[f->format] |= f->rates; /* needs knot? */ if (f->rates & SNDRV_PCM_RATE_KNOT) goto __out; } /* check whether channels and rates match for all formats */ cmaster = rmaster = 0; for (i = 0; i < MAX_MASK; i++) { if (cmaster != channels[i] && cmaster && channels[i]) goto __out; if (rmaster != rates[i] && rmaster && rates[i]) goto __out; if (channels[i]) cmaster = channels[i]; if (rates[i]) rmaster = rates[i]; } /* check whether channels match for all distinct rates */ memset(channels, 0, MAX_MASK * sizeof(u32)); list_for_each(p, &subs->fmt_list) { struct audioformat *f; f = list_entry(p, struct audioformat, list); if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) continue; for (i = 0; i < 32; i++) { if (f->rates & (1 << i)) channels[i] |= (1 << f->channels); } } cmaster = 0; for (i = 0; i < 32; i++) { if (cmaster != channels[i] && cmaster && channels[i]) goto __out; if (channels[i]) cmaster = channels[i]; } err = 0; __out: kfree(channels); kfree(rates); return err;}/* * If the device supports unusual bit rates, does the request meet these? */static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs){ struct audioformat *fp; int count = 0, needs_knot = 0; int err; list_for_each_entry(fp, &subs->fmt_list, list) { if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) return 0; count += fp->nr_rates; if (fp->rates & SNDRV_PCM_RATE_KNOT) needs_knot = 1; } if (!needs_knot) return 0; subs->rate_list.count = count; subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL); subs->rate_list.mask = 0; count = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -