📄 usbaudio.c
字号:
/* * start/stop playback substream */static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd){ struct snd_usb_substream *subs = substream->runtime->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: subs->ops.prepare = prepare_playback_urb; return 0; case SNDRV_PCM_TRIGGER_STOP: return deactivate_urbs(subs, 0, 0); case SNDRV_PCM_TRIGGER_PAUSE_PUSH: subs->ops.prepare = prepare_nodata_playback_urb; return 0; default: return -EINVAL; }}/* * start/stop capture substream */static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd){ struct snd_usb_substream *subs = substream->runtime->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: subs->ops.retire = retire_capture_urb; return start_urbs(subs, substream->runtime); case SNDRV_PCM_TRIGGER_STOP: return deactivate_urbs(subs, 0, 0); case SNDRV_PCM_TRIGGER_PAUSE_PUSH: subs->ops.retire = retire_paused_capture_urb; return 0; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: subs->ops.retire = retire_capture_urb; return 0; default: return -EINVAL; }}/* * release a urb data */static void release_urb_ctx(struct snd_urb_ctx *u){ if (u->urb) { if (u->buffer_size) usb_buffer_free(u->subs->dev, u->buffer_size, u->urb->transfer_buffer, u->urb->transfer_dma); usb_free_urb(u->urb); u->urb = NULL; }}/* * release a substream */static void release_substream_urbs(struct snd_usb_substream *subs, int force){ int i; /* stop urbs (to be sure) */ deactivate_urbs(subs, force, 1); wait_clear_urbs(subs); for (i = 0; i < MAX_URBS; i++) release_urb_ctx(&subs->dataurb[i]); for (i = 0; i < SYNC_URBS; i++) release_urb_ctx(&subs->syncurb[i]); usb_buffer_free(subs->dev, SYNC_URBS * 4, subs->syncbuf, subs->sync_dma); subs->syncbuf = NULL; subs->nurbs = 0;}/* * initialize a substream for plaback/capture */static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int period_bytes, unsigned int rate, unsigned int frame_bits){ unsigned int maxsize, n, i; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; unsigned int npacks[MAX_URBS], urb_packs, total_packs, packs_per_ms; /* calculate the frequency in 16.16 format */ if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) subs->freqn = get_usb_full_speed_rate(rate); else subs->freqn = get_usb_high_speed_rate(rate); subs->freqm = subs->freqn; /* calculate max. frequency */ if (subs->maxpacksize) { /* whatever fits into a max. size packet */ maxsize = subs->maxpacksize; subs->freqmax = (maxsize / (frame_bits >> 3)) << (16 - subs->datainterval); } else { /* no max. packet size: just take 25% higher than nominal */ subs->freqmax = subs->freqn + (subs->freqn >> 2); maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) >> (16 - subs->datainterval); } subs->phase = 0; if (subs->fill_max) subs->curpacksize = subs->maxpacksize; else subs->curpacksize = maxsize; if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) packs_per_ms = 8 >> subs->datainterval; else packs_per_ms = 1; subs->packs_per_ms = packs_per_ms; if (is_playback) { urb_packs = nrpacks; urb_packs = max(urb_packs, (unsigned int)MIN_PACKS_URB); urb_packs = min(urb_packs, (unsigned int)MAX_PACKS); } else urb_packs = 1; urb_packs *= packs_per_ms; /* decide how many packets to be used */ if (is_playback) { unsigned int minsize; /* determine how small a packet can be */ minsize = (subs->freqn >> (16 - subs->datainterval)) * (frame_bits >> 3); /* with sync from device, assume it can be 12% lower */ if (subs->syncpipe) minsize -= minsize >> 3; minsize = max(minsize, 1u); total_packs = (period_bytes + minsize - 1) / minsize; /* round up to multiple of packs_per_ms */ total_packs = (total_packs + packs_per_ms - 1) & ~(packs_per_ms - 1); /* we need at least two URBs for queueing */ if (total_packs < 2 * MIN_PACKS_URB * packs_per_ms) total_packs = 2 * MIN_PACKS_URB * packs_per_ms; } else { total_packs = MAX_URBS * urb_packs; } subs->nurbs = (total_packs + urb_packs - 1) / urb_packs; if (subs->nurbs > MAX_URBS) { /* too much... */ subs->nurbs = MAX_URBS; total_packs = MAX_URBS * urb_packs; } n = total_packs; for (i = 0; i < subs->nurbs; i++) { npacks[i] = n > urb_packs ? urb_packs : n; n -= urb_packs; } if (subs->nurbs <= 1) { /* too little - we need at least two packets * to ensure contiguous playback/capture */ subs->nurbs = 2; npacks[0] = (total_packs + 1) / 2; npacks[1] = total_packs - npacks[0]; } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB * packs_per_ms) { /* the last packet is too small.. */ if (subs->nurbs > 2) { /* merge to the first one */ npacks[0] += npacks[subs->nurbs - 1]; subs->nurbs--; } else { /* divide to two */ subs->nurbs = 2; npacks[0] = (total_packs + 1) / 2; npacks[1] = total_packs - npacks[0]; } } /* allocate and initialize data urbs */ for (i = 0; i < subs->nurbs; i++) { struct snd_urb_ctx *u = &subs->dataurb[i]; u->index = i; u->subs = subs; u->packets = npacks[i]; u->buffer_size = maxsize * u->packets; if (subs->fmt_type == USB_FORMAT_TYPE_II) u->packets++; /* for transfer delimiter */ u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); if (! u->urb) goto out_of_memory; u->urb->transfer_buffer = usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL, &u->urb->transfer_dma); if (! u->urb->transfer_buffer) goto out_of_memory; u->urb->pipe = subs->datapipe; u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; u->urb->interval = 1 << subs->datainterval; u->urb->context = u; u->urb->complete = snd_complete_urb; } if (subs->syncpipe) { /* allocate and initialize sync urbs */ subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4, GFP_KERNEL, &subs->sync_dma); if (! subs->syncbuf) goto out_of_memory; for (i = 0; i < SYNC_URBS; i++) { struct snd_urb_ctx *u = &subs->syncurb[i]; u->index = i; u->subs = subs; u->packets = 1; u->urb = usb_alloc_urb(1, GFP_KERNEL); if (! u->urb) goto out_of_memory; u->urb->transfer_buffer = subs->syncbuf + i * 4; u->urb->transfer_dma = subs->sync_dma + i * 4; u->urb->transfer_buffer_length = 4; u->urb->pipe = subs->syncpipe; u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; u->urb->number_of_packets = 1; u->urb->interval = 1 << subs->syncinterval; u->urb->context = u; u->urb->complete = snd_complete_sync_urb; } } return 0;out_of_memory: release_substream_urbs(subs, 0); return -ENOMEM;}/* * find a matching audio format */static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format, unsigned int rate, unsigned int channels){ struct list_head *p; struct audioformat *found = NULL; int cur_attr = 0, attr; list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); if (fp->format != format || fp->channels != channels) continue; if (rate < fp->rate_min || rate > fp->rate_max) continue; if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { unsigned int i; for (i = 0; i < fp->nr_rates; i++) if (fp->rate_table[i] == rate) break; if (i >= fp->nr_rates) continue; } attr = fp->ep_attr & EP_ATTR_MASK; if (! found) { found = fp; cur_attr = attr; continue; } /* avoid async out and adaptive in if the other method * supports the same format. * this is a workaround for the case like * M-audio audiophile USB. */ if (attr != cur_attr) { if ((attr == EP_ATTR_ASYNC && subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || (attr == EP_ATTR_ADAPTIVE && subs->direction == SNDRV_PCM_STREAM_CAPTURE)) continue; if ((cur_attr == EP_ATTR_ASYNC && subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || (cur_attr == EP_ATTR_ADAPTIVE && subs->direction == SNDRV_PCM_STREAM_CAPTURE)) { found = fp; cur_attr = attr; continue; } } /* find the format with the largest max. packet size */ if (fp->maxpacksize > found->maxpacksize) { found = fp; cur_attr = attr; } } return found;}/* * initialize the picth control and sample rate */static int init_usb_pitch(struct usb_device *dev, int iface, struct usb_host_interface *alts, struct audioformat *fmt){ unsigned int ep; unsigned char data[1]; int err; ep = get_endpoint(alts, 0)->bEndpointAddress; /* if endpoint has pitch control, enable it */ if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { data[0] = 1; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) { snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", dev->devnum, iface, ep); return err; } } return 0;}static int init_usb_sample_rate(struct usb_device *dev, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate){ unsigned int ep; unsigned char data[3]; int err; ep = get_endpoint(alts, 0)->bEndpointAddress; /* if endpoint has sampling rate control, set it */ if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) { int crate; data[0] = rate; data[1] = rate >> 8; data[2] = rate >> 16; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) { snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n", dev->devnum, iface, fmt->altsetting, rate, ep); return err; } if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) { snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep 0x%x\n", dev->devnum, iface, fmt->altsetting, ep); return 0; /* some devices don't support reading */ } crate = data[0] | (data[1] << 8) | (data[2] << 16); if (crate != rate) { snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); // runtime->rate = crate; } } return 0;}/* * find a matching format and set up the interface */static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt){ struct usb_device *dev = subs->dev; struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; struct usb_interface *iface; unsigned int ep, attr; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; int err; iface = usb_ifnum_to_if(dev, fmt->iface); snd_assert(iface, return -EINVAL); alts = &iface->altsetting[fmt->altset_idx]; altsd = get_iface_desc(alts); snd_assert(altsd->bAlternateSetting == fmt->altsetting, return -EINVAL); if (fmt == subs->cur_audiofmt) return 0; /* close the old interface */ if (subs->interface >= 0 && subs->interface != fmt->iface) { if (usb_set_interface(subs->dev, subs->interface, 0) < 0) { snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n", dev->devnum, fmt->iface, fmt->altsetting); return -EIO; } subs->interface = -1; subs->format = 0; } /* set interface */ if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) { if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) { snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n", dev->devnum, fmt->iface, fmt->altsetting); return -EIO; } snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); subs->interface = fmt->iface; subs->format = fmt->altset_idx; } /* create a data pipe */ ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK; if (is_playback) subs->datapipe = usb_sndisocpipe(dev, ep); else subs->datapipe = usb_rcvisocpipe(dev, ep); if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH && get_endpoint(alts, 0)->bInterval >= 1 && get_endpoint(alts, 0)->bInterval <= 4) subs->datainterval = get_endpoint(alts, 0)->bInterval - 1; else subs->datainterval = 0; subs->syncpipe = subs->syncinterval = 0; subs->maxpacksize = fmt->maxpacksize; subs->fill_max = 0; /* we need a sync pipe in async OUT or adaptive IN mode */ /* check the number of EP, since some devices have broken * descriptors which fool us. if it has only one EP, * assume it as adaptive-out or sync-in. */ attr = fmt->ep_attr & EP_ATTR_MASK; if (((is_playback && attr == EP_ATTR_ASYNC) || (! is_playback && attr == EP_ATTR_ADAPTIVE)) && altsd->bNumEndpoints >= 2) { /* check sync-pipe endpoint */ /* ... and check descriptor size before accessing bSynchAddress because there is a version of the SB Audigy 2 NX firmware lacking the audio fields in the endpoint descriptors */ if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && get_endpoint(alts, 1)->bSynchAddress != 0)) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; } ep = get_endpoint(alts, 1)->bEndpointAddress; if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -