From 596580d0ee1d17af70920a7bb06c963418014dd1 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 12 Apr 2012 13:51:10 +0200 Subject: ALSA: snd-usb: add snd_usb_audio-wide mutex This is needed for new card-wide list operations. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/card.c | 2 ++ sound/usb/usbaudio.h | 1 + 2 files changed, 3 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index 4a7be7b98331..6bc88b7ce4fd 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -276,6 +276,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) static int snd_usb_audio_free(struct snd_usb_audio *chip) { + mutex_destroy(&chip->mutex); kfree(chip); return 0; } @@ -336,6 +337,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, return -ENOMEM; } + mutex_init(&chip->mutex); mutex_init(&chip->shutdown_mutex); chip->index = idx; chip->dev = dev; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 3e2b03577936..a16c21d2d7ed 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -36,6 +36,7 @@ struct snd_usb_audio { struct snd_card *card; struct usb_interface *pm_intf; u32 usb_id; + struct mutex mutex; struct mutex shutdown_mutex; unsigned int shutdown:1; unsigned int probing:1; -- cgit v1.2.3 From 8fdff6a319e7dac757c558bd283dc4577e68cde7 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 12 Apr 2012 13:51:11 +0200 Subject: ALSA: snd-usb: implement new endpoint streaming model This patch adds a new generic streaming logic for audio over USB. It defines a model (snd_usb_endpoint) that handles everything that is related to an USB endpoint and its streaming. There are functions to activate and deactivate an endpoint (which call usb_set_interface()), and to start and stop its URBs. It also has function pointers to be called when data was received or is about to be sent, and pointer to a sync slave (another snd_usb_endpoint) that is informed when data has been received. A snd_usb_endpoint knows about its state and implements a refcounting, so only the first user will actually start the URBs and only the last one to stop it will tear them down again. With this sort of abstraction, the actual streaming is decoupled from the pcm handling, which makes the "implicit feedback" mechanisms easy to implement. In order to split changes properly, this patch only adds the new implementation but leaves the old one around, so the the driver doesn't change its behaviour. The switch to actually use the new code is submitted separately. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/card.h | 58 ++++ sound/usb/endpoint.c | 928 ++++++++++++++++++++++++++++++++++++++++++++++++++- sound/usb/endpoint.h | 26 ++ sound/usb/usbaudio.h | 1 + 4 files changed, 1002 insertions(+), 11 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.h b/sound/usb/card.h index da5fa1ac4eda..9acbd4a1228d 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -30,13 +30,17 @@ struct audioformat { }; struct snd_usb_substream; +struct snd_usb_endpoint; struct snd_urb_ctx { struct urb *urb; unsigned int buffer_size; /* size of data buffer, if data URB */ struct snd_usb_substream *subs; + struct snd_usb_endpoint *ep; int index; /* index for urb array */ int packets; /* number of packets per urb */ + int packet_size[MAX_PACKS_HS]; /* size of packets for next submission */ + struct list_head ready_list; }; struct snd_urb_ops { @@ -46,6 +50,60 @@ struct snd_urb_ops { int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u); }; +struct snd_usb_endpoint { + struct snd_usb_audio *chip; + + int use_count; + int ep_num; /* the referenced endpoint number */ + int type; /* SND_USB_ENDPOINT_TYPE_* */ + unsigned long flags; + + void (*prepare_data_urb) (struct snd_usb_substream *subs, + struct urb *urb); + void (*retire_data_urb) (struct snd_usb_substream *subs, + struct urb *urb); + + struct snd_usb_substream *data_subs; + struct snd_usb_endpoint *sync_master; + struct snd_usb_endpoint *sync_slave; + + struct snd_urb_ctx urb[MAX_URBS]; + + struct snd_usb_packet_info { + uint32_t packet_size[MAX_PACKS_HS]; + int packets; + } next_packet[MAX_URBS]; + int next_packet_read_pos, next_packet_write_pos; + struct list_head ready_playback_urbs; + + unsigned int nurbs; /* # urbs */ + unsigned long active_mask; /* bitmask of active urbs */ + unsigned long unlink_mask; /* bitmask of unlinked urbs */ + char *syncbuf; /* sync buffer for all sync URBs */ + dma_addr_t sync_dma; /* DMA address of syncbuf */ + + unsigned int pipe; /* the data i/o pipe */ + 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 */ + int freqshift; /* how much to shift the feedback value to get Q16.16 */ + 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 syncmaxsize; /* sync endpoint packet size */ + unsigned int fill_max:1; /* fill max packet size always */ + unsigned int datainterval; /* log_2 of data packet interval */ + unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ + unsigned char silence_value; + unsigned int stride; + int iface, alt_idx; + + spinlock_t lock; + struct list_head list; +}; + struct snd_usb_substream { struct snd_usb_stream *stream; struct usb_device *dev; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 08dcce53720b..ea25265427ad 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -20,9 +20,11 @@ #include #include #include +#include #include #include +#include #include "usbaudio.h" #include "helper.h" @@ -30,6 +32,9 @@ #include "endpoint.h" #include "pcm.h" +#define EP_FLAG_ACTIVATED 0 +#define EP_FLAG_RUNNING 1 + /* * convert a sampling rate into our full speed format (fs/1000 in Q16.16) * this will overflow at approx 524 kHz @@ -51,7 +56,7 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate) /* * unlink active urbs. */ -static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep) +static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep) { struct snd_usb_audio *chip = subs->stream->chip; unsigned int i; @@ -113,7 +118,7 @@ static void release_urb_ctx(struct snd_urb_ctx *u) /* * wait until all urbs are processed. */ -static int wait_clear_urbs(struct snd_usb_substream *subs) +static int wait_clear_urbs_old(struct snd_usb_substream *subs) { unsigned long end_time = jiffies + msecs_to_jiffies(1000); unsigned int i; @@ -148,8 +153,8 @@ void snd_usb_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); + deactivate_urbs_old(subs, force, 1); + wait_clear_urbs_old(subs); for (i = 0; i < MAX_URBS; i++) release_urb_ctx(&subs->dataurb[i]); @@ -164,7 +169,7 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force) /* * complete callback from data urb */ -static void snd_complete_urb(struct urb *urb) +static void snd_complete_urb_old(struct urb *urb) { struct snd_urb_ctx *ctx = urb->context; struct snd_usb_substream *subs = ctx->subs; @@ -318,7 +323,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, 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; + u->urb->complete = snd_complete_urb_old; } if (subs->syncpipe) { @@ -856,7 +861,7 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru __error: // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); - deactivate_urbs(subs, 0, 0); + deactivate_urbs_old(subs, 0, 0); return -EPIPE; } @@ -917,7 +922,7 @@ int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int subs->ops.prepare = prepare_playback_urb; return 0; case SNDRV_PCM_TRIGGER_STOP: - return deactivate_urbs(subs, 0, 0); + return deactivate_urbs_old(subs, 0, 0); case SNDRV_PCM_TRIGGER_PAUSE_PUSH: subs->ops.prepare = prepare_nodata_playback_urb; return 0; @@ -935,7 +940,7 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c subs->ops.retire = retire_capture_urb; return start_urbs(subs, substream->runtime); case SNDRV_PCM_TRIGGER_STOP: - return deactivate_urbs(subs, 0, 0); + return deactivate_urbs_old(subs, 0, 0); case SNDRV_PCM_TRIGGER_PAUSE_PUSH: subs->ops.retire = retire_paused_capture_urb; return 0; @@ -951,8 +956,8 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime) { /* clear urbs (to be sure) */ - deactivate_urbs(subs, 0, 1); - wait_clear_urbs(subs); + deactivate_urbs_old(subs, 0, 1); + wait_clear_urbs_old(subs); /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ @@ -964,3 +969,904 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs, return 0; } +int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep) +{ + return ep->sync_master && + ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA && + ep->type == SND_USB_ENDPOINT_TYPE_DATA && + usb_pipeout(ep->pipe); +} + +/* determine the number of frames in the next packet */ +static int next_packet_size(struct snd_usb_endpoint *ep) +{ + unsigned long flags; + int ret; + + if (ep->fill_max) + return ep->maxframesize; + + spin_lock_irqsave(&ep->lock, flags); + ep->phase = (ep->phase & 0xffff) + + (ep->freqm << ep->datainterval); + ret = min(ep->phase >> 16, ep->maxframesize); + spin_unlock_irqrestore(&ep->lock, flags); + + return ret; +} + +static void retire_outbound_urb(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *urb_ctx) +{ + if (ep->retire_data_urb) + ep->retire_data_urb(ep->data_subs, urb_ctx->urb); +} + +static void retire_inbound_urb(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *urb_ctx) +{ + struct urb *urb = urb_ctx->urb; + + if (ep->sync_slave) + snd_usb_handle_sync_urb(ep->sync_slave, ep, urb); + + if (ep->retire_data_urb) + ep->retire_data_urb(ep->data_subs, urb); +} + +static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx) +{ + int i; + + for (i = 0; i < ctx->packets; ++i) + ctx->packet_size[i] = next_packet_size(ep); +} + +/* + * Prepare a PLAYBACK urb for submission to the bus. + */ +static void prepare_outbound_urb(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx) +{ + int i; + struct urb *urb = ctx->urb; + unsigned char *cp = urb->transfer_buffer; + + urb->dev = ep->chip->dev; /* we need to set this at each time */ + + switch (ep->type) { + case SND_USB_ENDPOINT_TYPE_DATA: + if (ep->prepare_data_urb) { + ep->prepare_data_urb(ep->data_subs, urb); + } else { + /* no data provider, so send silence */ + unsigned int offs = 0; + for (i = 0; i < ctx->packets; ++i) { + int counts = ctx->packet_size[i]; + urb->iso_frame_desc[i].offset = offs * ep->stride; + urb->iso_frame_desc[i].length = counts * ep->stride; + offs += counts; + } + + urb->number_of_packets = ctx->packets; + urb->transfer_buffer_length = offs * ep->stride; + memset(urb->transfer_buffer, ep->silence_value, + offs * ep->stride); + } + break; + + case SND_USB_ENDPOINT_TYPE_SYNC: + if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) { + /* + * fill the length and offset of each urb descriptor. + * the fixed 12.13 frequency is passed as 16.16 through the pipe. + */ + urb->iso_frame_desc[0].length = 4; + urb->iso_frame_desc[0].offset = 0; + cp[0] = ep->freqn; + cp[1] = ep->freqn >> 8; + cp[2] = ep->freqn >> 16; + cp[3] = ep->freqn >> 24; + } else { + /* + * fill the length and offset of each urb descriptor. + * the fixed 10.14 frequency is passed through the pipe. + */ + urb->iso_frame_desc[0].length = 3; + urb->iso_frame_desc[0].offset = 0; + cp[0] = ep->freqn >> 2; + cp[1] = ep->freqn >> 10; + cp[2] = ep->freqn >> 18; + } + + break; + } +} + +/* + * Prepare a CAPTURE or SYNC urb for submission to the bus. + */ +static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *urb_ctx) +{ + int i, offs; + struct urb *urb = urb_ctx->urb; + + urb->dev = ep->chip->dev; /* we need to set this at each time */ + + switch (ep->type) { + case SND_USB_ENDPOINT_TYPE_DATA: + offs = 0; + for (i = 0; i < urb_ctx->packets; i++) { + urb->iso_frame_desc[i].offset = offs; + urb->iso_frame_desc[i].length = ep->curpacksize; + offs += ep->curpacksize; + } + + urb->transfer_buffer_length = offs; + urb->number_of_packets = urb_ctx->packets; + break; + + case SND_USB_ENDPOINT_TYPE_SYNC: + urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize); + urb->iso_frame_desc[0].offset = 0; + break; + } +} + +static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) +{ + while (test_bit(EP_FLAG_RUNNING, &ep->flags)) { + + unsigned long flags; + struct snd_usb_packet_info *packet; + struct snd_urb_ctx *ctx = NULL; + struct urb *urb; + int err, i; + + spin_lock_irqsave(&ep->lock, flags); + if (ep->next_packet_read_pos != ep->next_packet_write_pos) { + packet = ep->next_packet + ep->next_packet_read_pos; + ep->next_packet_read_pos++; + ep->next_packet_read_pos %= MAX_URBS; + + /* take URB out of FIFO */ + if (!list_empty(&ep->ready_playback_urbs)) + ctx = list_first_entry(&ep->ready_playback_urbs, + struct snd_urb_ctx, ready_list); + } + spin_unlock_irqrestore(&ep->lock, flags); + + if (ctx == NULL) + return; + + list_del_init(&ctx->ready_list); + urb = ctx->urb; + + /* copy over the length information */ + for (i = 0; i < packet->packets; i++) + ctx->packet_size[i] = packet->packet_size[i]; + + prepare_outbound_urb(ep, ctx); + + err = usb_submit_urb(ctx->urb, GFP_ATOMIC); + if (err < 0) + snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n", + ctx->index, err, ctx->urb); + else + set_bit(ctx->index, &ep->active_mask); + } +} + +/* + * complete callback for urbs + */ +static void snd_complete_urb(struct urb *urb) +{ + struct snd_urb_ctx *ctx = urb->context; + struct snd_usb_endpoint *ep = ctx->ep; + int err; + + if (unlikely(urb->status == -ENOENT || /* unlinked */ + urb->status == -ENODEV || /* device removed */ + urb->status == -ECONNRESET || /* unlinked */ + urb->status == -ESHUTDOWN || /* device disabled */ + ep->chip->shutdown)) /* device disconnected */ + goto exit_clear; + + if (usb_pipeout(ep->pipe)) { + retire_outbound_urb(ep, ctx); + /* can be stopped during retire callback */ + if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags))) + goto exit_clear; + + if (snd_usb_endpoint_implict_feedback_sink(ep)) { + unsigned long flags; + + spin_lock_irqsave(&ep->lock, flags); + list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + spin_unlock_irqrestore(&ep->lock, flags); + queue_pending_output_urbs(ep); + + goto exit_clear; + } + + prepare_outbound_urb_sizes(ep, ctx); + prepare_outbound_urb(ep, ctx); + } else { + retire_inbound_urb(ep, ctx); + /* can be stopped during retire callback */ + if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags))) + goto exit_clear; + + prepare_inbound_urb(ep, ctx); + } + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err == 0) + return; + + snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err); + //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + +exit_clear: + clear_bit(ctx->index, &ep->active_mask); +} + +struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int ep_num, int direction, int type) +{ + struct list_head *p; + struct snd_usb_endpoint *ep; + int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK; + + mutex_lock(&chip->mutex); + + list_for_each(p, &chip->ep_list) { + ep = list_entry(p, struct snd_usb_endpoint, list); + if (ep->ep_num == ep_num && + ep->iface == alts->desc.bInterfaceNumber && + ep->alt_idx == alts->desc.bAlternateSetting) { + snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n", + ep_num, ep->iface, ep->alt_idx, ep); + goto __exit_unlock; + } + } + + snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n", + is_playback ? "playback" : "capture", + type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync", + ep_num); + + /* select the alt setting once so the endpoints become valid */ + ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber, + alts->desc.bAlternateSetting); + if (ret < 0) { + snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n", + __func__, ret); + ep = NULL; + goto __exit_unlock; + } + + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + goto __exit_unlock; + + ep->chip = chip; + spin_lock_init(&ep->lock); + ep->type = type; + ep->ep_num = ep_num; + ep->iface = alts->desc.bInterfaceNumber; + ep->alt_idx = alts->desc.bAlternateSetting; + INIT_LIST_HEAD(&ep->ready_playback_urbs); + ep_num &= USB_ENDPOINT_NUMBER_MASK; + + if (is_playback) + ep->pipe = usb_sndisocpipe(chip->dev, ep_num); + else + ep->pipe = usb_rcvisocpipe(chip->dev, ep_num); + + if (type == SND_USB_ENDPOINT_TYPE_SYNC) { + if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 1)->bRefresh >= 1 && + get_endpoint(alts, 1)->bRefresh <= 9) + ep->syncinterval = get_endpoint(alts, 1)->bRefresh; + else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL) + ep->syncinterval = 1; + else if (get_endpoint(alts, 1)->bInterval >= 1 && + get_endpoint(alts, 1)->bInterval <= 16) + ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1; + else + ep->syncinterval = 3; + + ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); + } + + list_add_tail(&ep->list, &chip->ep_list); + +__exit_unlock: + mutex_unlock(&chip->mutex); + + return ep; +} + +/* + * wait until all urbs are processed. + */ +static int wait_clear_urbs(struct snd_usb_endpoint *ep) +{ + unsigned long end_time = jiffies + msecs_to_jiffies(1000); + unsigned int i; + int alive; + + do { + alive = 0; + for (i = 0; i < ep->nurbs; i++) + if (test_bit(i, &ep->active_mask)) + alive++; + + if (!alive) + break; + + schedule_timeout_uninterruptible(1); + } while (time_before(jiffies, end_time)); + + if (alive) + snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n", + alive, ep->ep_num); + + return 0; +} + +/* + * unlink active urbs. + */ +static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep) +{ + unsigned long flags; + unsigned int i; + int async; + + if (!force && ep->chip->shutdown) /* to be sure... */ + return -EBADFD; + + async = !can_sleep && ep->chip->async_unlink; + + clear_bit(EP_FLAG_RUNNING, &ep->flags); + + INIT_LIST_HEAD(&ep->ready_playback_urbs); + ep->next_packet_read_pos = 0; + ep->next_packet_write_pos = 0; + + if (!async && in_interrupt()) + return 0; + + for (i = 0; i < ep->nurbs; i++) { + if (test_bit(i, &ep->active_mask)) { + if (!test_and_set_bit(i, &ep->unlink_mask)) { + struct urb *u = ep->urb[i].urb; + if (async) + usb_unlink_urb(u); + else + usb_kill_urb(u); + } + } + } + + return 0; +} + +/* + * release an endpoint's urbs + */ +static void release_urbs(struct snd_usb_endpoint *ep, int force) +{ + int i; + + /* route incoming urbs to nirvana */ + ep->retire_data_urb = NULL; + ep->prepare_data_urb = NULL; + + /* stop urbs */ + deactivate_urbs(ep, force, 1); + wait_clear_urbs(ep); + + for (i = 0; i < ep->nurbs; i++) + release_urb_ctx(&ep->urb[i]); + + if (ep->syncbuf) + usb_free_coherent(ep->chip->dev, SYNC_URBS * 4, + ep->syncbuf, ep->sync_dma); + + ep->syncbuf = NULL; + ep->nurbs = 0; +} + +static int data_ep_set_params(struct snd_usb_endpoint *ep, + struct snd_pcm_hw_params *hw_params, + struct audioformat *fmt, + struct snd_usb_endpoint *sync_ep) +{ + unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms; + int period_bytes = params_period_bytes(hw_params); + int format = params_format(hw_params); + int is_playback = usb_pipeout(ep->pipe); + int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) * + params_channels(hw_params); + + ep->datainterval = fmt->datainterval; + ep->stride = frame_bits >> 3; + ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0; + + /* calculate max. frequency */ + if (ep->maxpacksize) { + /* whatever fits into a max. size packet */ + maxsize = ep->maxpacksize; + ep->freqmax = (maxsize / (frame_bits >> 3)) + << (16 - ep->datainterval); + } else { + /* no max. packet size: just take 25% higher than nominal */ + ep->freqmax = ep->freqn + (ep->freqn >> 2); + maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3)) + >> (16 - ep->datainterval); + } + + if (ep->fill_max) + ep->curpacksize = ep->maxpacksize; + else + ep->curpacksize = maxsize; + + if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) + packs_per_ms = 8 >> ep->datainterval; + else + packs_per_ms = 1; + + if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) { + urb_packs = max(ep->chip->nrpacks, 1); + urb_packs = min(urb_packs, (unsigned int) MAX_PACKS); + } else { + urb_packs = 1; + } + + urb_packs *= packs_per_ms; + + if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep)) + urb_packs = min(urb_packs, 1U << sync_ep->syncinterval); + + /* decide how many packets to be used */ + if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) { + unsigned int minsize, maxpacks; + /* determine how small a packet can be */ + minsize = (ep->freqn >> (16 - ep->datainterval)) + * (frame_bits >> 3); + /* with sync from device, assume it can be 12% lower */ + if (sync_ep) + minsize -= minsize >> 3; + minsize = max(minsize, 1u); + total_packs = (period_bytes + minsize - 1) / minsize; + /* we need at least two URBs for queueing */ + if (total_packs < 2) { + total_packs = 2; + } else { + /* and we don't want too long a queue either */ + maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); + total_packs = min(total_packs, maxpacks); + } + } else { + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; + total_packs = MAX_URBS * urb_packs; + } + + ep->nurbs = (total_packs + urb_packs - 1) / urb_packs; + if (ep->nurbs > MAX_URBS) { + /* too much... */ + ep->nurbs = MAX_URBS; + total_packs = MAX_URBS * urb_packs; + } else if (ep->nurbs < 2) { + /* too little - we need at least two packets + * to ensure contiguous playback/capture + */ + ep->nurbs = 2; + } + + /* allocate and initialize data urbs */ + for (i = 0; i < ep->nurbs; i++) { + struct snd_urb_ctx *u = &ep->urb[i]; + u->index = i; + u->ep = ep; + u->packets = (i + 1) * total_packs / ep->nurbs + - i * total_packs / ep->nurbs; + u->buffer_size = maxsize * u->packets; + + if (fmt->fmt_type == UAC_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_alloc_coherent(ep->chip->dev, u->buffer_size, + GFP_KERNEL, &u->urb->transfer_dma); + if (!u->urb->transfer_buffer) + goto out_of_memory; + u->urb->pipe = ep->pipe; + u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + u->urb->interval = 1 << ep->datainterval; + u->urb->context = u; + u->urb->complete = snd_complete_urb; + INIT_LIST_HEAD(&u->ready_list); + } + + return 0; + +out_of_memory: + release_urbs(ep, 0); + return -ENOMEM; +} + +static int sync_ep_set_params(struct snd_usb_endpoint *ep, + struct snd_pcm_hw_params *hw_params, + struct audioformat *fmt) +{ + int i; + + ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4, + GFP_KERNEL, &ep->sync_dma); + if (!ep->syncbuf) + return -ENOMEM; + + for (i = 0; i < SYNC_URBS; i++) { + struct snd_urb_ctx *u = &ep->urb[i]; + u->index = i; + u->ep = ep; + u->packets = 1; + u->urb = usb_alloc_urb(1, GFP_KERNEL); + if (!u->urb) + goto out_of_memory; + u->urb->transfer_buffer = ep->syncbuf + i * 4; + u->urb->transfer_dma = ep->sync_dma + i * 4; + u->urb->transfer_buffer_length = 4; + u->urb->pipe = ep->pipe; + u->urb->transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; + u->urb->number_of_packets = 1; + u->urb->interval = 1 << ep->syncinterval; + u->urb->context = u; + u->urb->complete = snd_complete_urb; + } + + ep->nurbs = SYNC_URBS; + + return 0; + +out_of_memory: + release_urbs(ep, 0); + return -ENOMEM; +} + +int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, + struct snd_pcm_hw_params *hw_params, + struct audioformat *fmt, + struct snd_usb_endpoint *sync_ep) +{ + int err; + + if (ep->use_count != 0) { + snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n", + ep->ep_num); + return -EBUSY; + } + + /* release old buffers, if any */ + release_urbs(ep, 0); + + ep->datainterval = fmt->datainterval; + ep->maxpacksize = fmt->maxpacksize; + ep->fill_max = fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX; + + if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) + ep->freqn = get_usb_full_speed_rate(params_rate(hw_params)); + else + ep->freqn = get_usb_high_speed_rate(params_rate(hw_params)); + + /* calculate the frequency in 16.16 format */ + ep->freqm = ep->freqn; + ep->freqshift = INT_MIN; + + ep->phase = 0; + + switch (ep->type) { + case SND_USB_ENDPOINT_TYPE_DATA: + err = data_ep_set_params(ep, hw_params, fmt, sync_ep); + break; + case SND_USB_ENDPOINT_TYPE_SYNC: + err = sync_ep_set_params(ep, hw_params, fmt); + break; + default: + err = -EINVAL; + } + + snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n", + ep->ep_num, ep->type, ep->nurbs, err); + + return err; +} + +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) +{ + int err; + unsigned int i; + + if (ep->chip->shutdown) + return -EBADFD; + + /* already running? */ + if (++ep->use_count != 1) + return 0; + + if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags))) + return -EINVAL; + + /* just to be sure */ + deactivate_urbs(ep, 0, 1); + wait_clear_urbs(ep); + + ep->active_mask = 0; + ep->unlink_mask = 0; + ep->phase = 0; + + /* + * If this endpoint has a data endpoint as implicit feedback source, + * don't start the urbs here. Instead, mark them all as available, + * wait for the record urbs to arrive and queue from that context. + */ + + set_bit(EP_FLAG_RUNNING, &ep->flags); + + if (snd_usb_endpoint_implict_feedback_sink(ep)) { + for (i = 0; i < ep->nurbs; i++) { + struct snd_urb_ctx *ctx = ep->urb + i; + list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + } + + return 0; + } + + for (i = 0; i < ep->nurbs; i++) { + struct urb *urb = ep->urb[i].urb; + + if (snd_BUG_ON(!urb)) + goto __error; + + if (usb_pipeout(ep->pipe)) { + prepare_outbound_urb_sizes(ep, urb->context); + prepare_outbound_urb(ep, urb->context); + } else { + prepare_inbound_urb(ep, urb->context); + } + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n", + i, err, usb_error_string(err)); + goto __error; + } + set_bit(i, &ep->active_mask); + } + + return 0; + +__error: + clear_bit(EP_FLAG_RUNNING, &ep->flags); + ep->use_count--; + deactivate_urbs(ep, 0, 0); + return -EPIPE; +} + +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, + int force, int can_sleep, int wait) +{ + if (!ep) + return; + + if (snd_BUG_ON(ep->use_count == 0)) + return; + + if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags))) + return; + + if (--ep->use_count == 0) { + deactivate_urbs(ep, force, can_sleep); + ep->data_subs = NULL; + ep->sync_slave = NULL; + ep->retire_data_urb = NULL; + ep->prepare_data_urb = NULL; + + if (wait) + wait_clear_urbs(ep); + } +} + +int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep) +{ + if (ep->use_count != 0) + return 0; + + if (!ep->chip->shutdown && + !test_and_set_bit(EP_FLAG_ACTIVATED, &ep->flags)) { + int ret; + + ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx); + if (ret < 0) { + snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n", + __func__, ret); + clear_bit(EP_FLAG_ACTIVATED, &ep->flags); + return ret; + } + + return 0; + } + + return -EBUSY; +} + +int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) +{ + if (!ep) + return -EINVAL; + + if (ep->use_count != 0) + return 0; + + if (!ep->chip->shutdown && + test_and_clear_bit(EP_FLAG_ACTIVATED, &ep->flags)) { + int ret; + + ret = usb_set_interface(ep->chip->dev, ep->iface, 0); + if (ret < 0) { + snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n", + __func__, ret); + return ret; + } + + return 0; + } + + return -EBUSY; +} + +void snd_usb_endpoint_free(struct list_head *head) +{ + struct snd_usb_endpoint *ep; + + ep = list_entry(head, struct snd_usb_endpoint, list); + release_urbs(ep, 1); + kfree(ep); +} + +/* + * process after playback sync complete + * + * Full speed devices report feedback values in 10.14 format as samples per + * frame, high speed devices in 16.16 format as samples per microframe. + * Because the Audio Class 1 spec was written before USB 2.0, many high speed + * devices use a wrong interpretation, some others use an entirely different + * format. Therefore, we cannot predict what format any particular device uses + * and must detect it automatically. + */ +void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, + struct snd_usb_endpoint *sender, + const struct urb *urb) +{ + int shift; + unsigned int f; + unsigned long flags; + + snd_BUG_ON(ep == sender); + + if (snd_usb_endpoint_implict_feedback_sink(ep) && + ep->use_count != 0) { + + /* implicit feedback case */ + int i, bytes = 0; + struct snd_urb_ctx *in_ctx; + struct snd_usb_packet_info *out_packet; + + in_ctx = urb->context; + + /* Count overall packet size */ + for (i = 0; i < in_ctx->packets; i++) + if (urb->iso_frame_desc[i].status == 0) + bytes += urb->iso_frame_desc[i].actual_length; + + /* + * skip empty packets. At least M-Audio's Fast Track Ultra stops + * streaming once it received a 0-byte OUT URB + */ + if (bytes == 0) + return; + + spin_lock_irqsave(&ep->lock, flags); + out_packet = ep->next_packet + ep->next_packet_write_pos; + + /* + * Iterate through the inbound packet and prepare the lengths + * for the output packet. The OUT packet we are about to send + * will have the same amount of payload than the IN packet we + * just received. + */ + + out_packet->packets = in_ctx->packets; + for (i = 0; i < in_ctx->packets; i++) { + if (urb->iso_frame_desc[i].status == 0) + out_packet->packet_size[i] = + urb->iso_frame_desc[i].actual_length / ep->stride; + else + out_packet->packet_size[i] = 0; + } + + ep->next_packet_write_pos++; + ep->next_packet_write_pos %= MAX_URBS; + spin_unlock_irqrestore(&ep->lock, flags); + queue_pending_output_urbs(ep); + + return; + } + + /* parse sync endpoint packet */ + + if (urb->iso_frame_desc[0].status != 0 || + urb->iso_frame_desc[0].actual_length < 3) + return; + + f = le32_to_cpup(urb->transfer_buffer); + if (urb->iso_frame_desc[0].actual_length == 3) + f &= 0x00ffffff; + else + f &= 0x0fffffff; + + if (f == 0) + return; + + if (unlikely(ep->freqshift == INT_MIN)) { + /* + * The first time we see a feedback value, determine its format + * by shifting it left or right until it matches the nominal + * frequency value. This assumes that the feedback does not + * differ from the nominal value more than +50% or -25%. + */ + shift = 0; + while (f < ep->freqn - ep->freqn / 4) { + f <<= 1; + shift++; + } + while (f > ep->freqn + ep->freqn / 2) { + f >>= 1; + shift--; + } + ep->freqshift = shift; + } else if (ep->freqshift >= 0) + f <<= ep->freqshift; + else + f >>= -ep->freqshift; + + if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) { + /* + * If the frequency looks valid, set it. + * This value is referred to in prepare_playback_urb(). + */ + spin_lock_irqsave(&ep->lock, flags); + ep->freqm = f; + spin_unlock_irqrestore(&ep->lock, flags); + } else { + /* + * Out of range; maybe the shift value is wrong. + * Reset it so that we autodetect again the next time. + */ + ep->freqshift = INT_MIN; + } +} + diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 88eb63a636eb..9f083d7d6cf1 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -18,4 +18,30 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs, int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd); int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd); + +#define SND_USB_ENDPOINT_TYPE_DATA 0 +#define SND_USB_ENDPOINT_TYPE_SYNC 1 + +struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int ep_num, int direction, int type); + +int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, + struct snd_pcm_hw_params *hw_params, + struct audioformat *fmt, + struct snd_usb_endpoint *sync_ep); + +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, + int force, int can_sleep, int wait); +int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); +void snd_usb_endpoint_free(struct list_head *head); + +int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep); + +void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, + struct snd_usb_endpoint *sender, + const struct urb *urb); + #endif /* __USBAUDIO_ENDPOINT_H */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index a16c21d2d7ed..b8233ebe250f 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -47,6 +47,7 @@ struct snd_usb_audio { int num_suspended_intf; struct list_head pcm_list; /* list of pcm streams */ + struct list_head ep_list; /* list of audio-related endpoints */ int pcm_devs; struct list_head midi_list; /* list of midi interfaces */ -- cgit v1.2.3 From edcd3633e72a1590c4cf46befe5e6cd03b5aec3e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 12 Apr 2012 13:51:12 +0200 Subject: ALSA: snd-usb: switch over to new endpoint streaming logic With the previous commit that added the new streaming model, all endpoint and streaming related code is now in endpoint.c, and pcm.c only acts as a wrapper for handling the packet's payload. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/card.c | 8 +- sound/usb/card.h | 4 + sound/usb/endpoint.c | 40 ----- sound/usb/endpoint.h | 3 - sound/usb/pcm.c | 418 ++++++++++++++++++++++++++++++++++++++++++--------- sound/usb/stream.c | 31 +++- 6 files changed, 385 insertions(+), 119 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index 6bc88b7ce4fd..d5b5c3388e28 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -131,8 +131,9 @@ static void snd_usb_stream_disconnect(struct list_head *head) subs = &as->substream[idx]; if (!subs->num_formats) continue; - snd_usb_release_substream_urbs(subs, 1); subs->interface = -1; + subs->data_endpoint = NULL; + subs->sync_endpoint = NULL; } } @@ -350,6 +351,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); INIT_LIST_HEAD(&chip->pcm_list); + INIT_LIST_HEAD(&chip->ep_list); INIT_LIST_HEAD(&chip->midi_list); INIT_LIST_HEAD(&chip->mixer_list); @@ -567,6 +569,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, list_for_each(p, &chip->pcm_list) { snd_usb_stream_disconnect(p); } + /* release the endpoint resources */ + list_for_each(p, &chip->ep_list) { + snd_usb_endpoint_free(p); + } /* release the midi resources */ list_for_each(p, &chip->midi_list) { snd_usbmidi_disconnect(p); diff --git a/sound/usb/card.h b/sound/usb/card.h index 9acbd4a1228d..8a08687e5bc0 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -145,6 +145,10 @@ struct snd_usb_substream { 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 */ + /* data and sync endpoints for this stream */ + struct snd_usb_endpoint *data_endpoint; + struct snd_usb_endpoint *sync_endpoint; + unsigned long flags; u64 formats; /* format bitmasks (all or'ed) */ unsigned int num_formats; /* number of supported audio formats (list) */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index ea25265427ad..1b0ed22cff73 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -912,46 +912,6 @@ void snd_usb_init_substream(struct snd_usb_stream *as, subs->fmt_type = fp->fmt_type; } -int snd_usb_substream_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_old(subs, 0, 0); - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - subs->ops.prepare = prepare_nodata_playback_urb; - return 0; - } - - return -EINVAL; -} - -int snd_usb_substream_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_old(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; - } - - return -EINVAL; -} - int snd_usb_substream_prepare(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime) { diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 9f083d7d6cf1..e540768de3d5 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -15,9 +15,6 @@ void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force); int snd_usb_substream_prepare(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime); -int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd); -int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd); - #define SND_USB_ENDPOINT_TYPE_DATA 0 #define SND_USB_ENDPOINT_TYPE_SYNC 1 diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 0eed6115c2d4..0f107834c100 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,9 @@ #include "clock.h" #include "power.h" +#define SUBSTREAM_FLAG_DATA_EP_STARTED 0 +#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 + /* return the estimated delay based on USB frame counters */ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, unsigned int rate) @@ -208,6 +212,84 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, } } +static int start_endpoints(struct snd_usb_substream *subs) +{ + int err; + + if (!subs->data_endpoint) + return -EINVAL; + + if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { + struct snd_usb_endpoint *ep = subs->data_endpoint; + + snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep); + + ep->data_subs = subs; + err = snd_usb_endpoint_start(ep); + if (err < 0) { + clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); + return err; + } + } + + if (subs->sync_endpoint && + !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { + struct snd_usb_endpoint *ep = subs->sync_endpoint; + + snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep); + + ep->sync_slave = subs->data_endpoint; + err = snd_usb_endpoint_start(ep); + if (err < 0) { + clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); + return err; + } + } + + return 0; +} + +static void stop_endpoints(struct snd_usb_substream *subs, + int force, int can_sleep, int wait) +{ + if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) + snd_usb_endpoint_stop(subs->sync_endpoint, + force, can_sleep, wait); + + if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) + snd_usb_endpoint_stop(subs->data_endpoint, + force, can_sleep, wait); +} + +static int activate_endpoints(struct snd_usb_substream *subs) +{ + if (subs->sync_endpoint) { + int ret; + + ret = snd_usb_endpoint_activate(subs->sync_endpoint); + if (ret < 0) + return ret; + } + + return snd_usb_endpoint_activate(subs->data_endpoint); +} + +static int deactivate_endpoints(struct snd_usb_substream *subs) +{ + int reta, retb; + + reta = snd_usb_endpoint_deactivate(subs->sync_endpoint); + retb = snd_usb_endpoint_deactivate(subs->data_endpoint); + + if (reta < 0) + return reta; + + if (retb < 0) + return retb; + + return 0; +} + /* * find a matching format and set up the interface */ @@ -232,40 +314,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) 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->altset_idx = 0; - } - - /* set interface */ - if (subs->interface != fmt->iface || subs->altset_idx != 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->altset_idx = 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); - subs->datainterval = fmt->datainterval; - subs->syncpipe = subs->syncinterval = 0; - subs->maxpacksize = fmt->maxpacksize; - subs->syncmaxsize = 0; - subs->fill_max = 0; + subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, + alts, fmt->endpoint, subs->direction, + SND_USB_ENDPOINT_TYPE_DATA); + if (!subs->data_endpoint) + return -EINVAL; /* we need a sync pipe in async OUT or adaptive IN mode */ /* check the number of EP, since some devices have broken @@ -276,6 +329,15 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && altsd->bNumEndpoints >= 2) { + switch (subs->stream->chip->usb_id) { + case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ + case USB_ID(0x0763, 0x2081): + ep = 0x81; + iface = usb_ifnum_to_if(dev, 2); + alts = &iface->altsetting[1]; + goto add_sync_ep; + } + /* check sync-pipe endpoint */ /* ... and check descriptor size before accessing bSynchAddress because there is a version of the SB Audigy 2 NX firmware lacking @@ -295,28 +357,16 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; } - 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; - subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); - } - - /* always fill max packet size */ - if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX) - subs->fill_max = 1; +add_sync_ep: + subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, + alts, ep, !subs->direction, + SND_USB_ENDPOINT_TYPE_SYNC); + + if (!subs->sync_endpoint) + return -EINVAL; + + subs->data_endpoint->sync_master = subs->sync_endpoint; + } if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0) return err; @@ -390,12 +440,22 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, if (changed) { mutex_lock(&subs->stream->chip->shutdown_mutex); /* format changed */ - snd_usb_release_substream_urbs(subs, 0); - /* influenced: period_bytes, channels, rate, format, */ - ret = snd_usb_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)); + stop_endpoints(subs, 0, 0, 0); + deactivate_endpoints(subs); + + ret = activate_endpoints(subs); + if (ret < 0) + goto unlock; + + ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt, + subs->sync_endpoint); + if (ret < 0) + goto unlock; + + if (subs->sync_endpoint) + ret = snd_usb_endpoint_set_params(subs->sync_endpoint, + hw_params, fmt, NULL); +unlock: mutex_unlock(&subs->stream->chip->shutdown_mutex); } @@ -415,7 +475,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_rate = 0; subs->period_bytes = 0; mutex_lock(&subs->stream->chip->shutdown_mutex); - snd_usb_release_substream_urbs(subs, 0); + stop_endpoints(subs, 0, 1, 1); mutex_unlock(&subs->stream->chip->shutdown_mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -435,19 +495,28 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) return -ENXIO; } + if (snd_BUG_ON(!subs->data_endpoint)) + return -EIO; + /* some unit conversions in runtime */ - subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); - subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); + subs->data_endpoint->maxframesize = + bytes_to_frames(runtime, subs->data_endpoint->maxpacksize); + subs->data_endpoint->curframesize = + bytes_to_frames(runtime, subs->data_endpoint->curpacksize); /* reset the pointer */ subs->hwptr_done = 0; subs->transfer_done = 0; - subs->phase = 0; subs->last_delay = 0; subs->last_frame_number = 0; runtime->delay = 0; - return snd_usb_substream_prepare(subs, runtime); + /* 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) + return start_endpoints(subs); + + return 0; } static struct snd_pcm_hardware snd_usb_hardware = @@ -842,16 +911,171 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) { + int ret; struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_usb_substream *subs = &as->substream[direction]; - if (!as->chip->shutdown && subs->interface >= 0) { - usb_set_interface(subs->dev, subs->interface, 0); - subs->interface = -1; - } + stop_endpoints(subs, 0, 0, 0); + ret = deactivate_endpoints(subs); subs->pcm_substream = NULL; snd_usb_autosuspend(subs->stream->chip); - return 0; + + return ret; +} + +/* Since a URB can handle only a single linear buffer, we must use double + * buffering when the data to be transferred overflows the buffer boundary. + * To avoid inconsistencies when updating hwptr_done, we use double buffering + * for all URBs. + */ +static void retire_capture_urb(struct snd_usb_substream *subs, + struct urb *urb) +{ + struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + unsigned int stride, frames, bytes, oldptr; + int i, period_elapsed = 0; + unsigned long flags; + unsigned char *cp; + + 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 && printk_ratelimit()) { + snd_printdd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status); + // continue; + } + bytes = urb->iso_frame_desc[i].actual_length; + frames = bytes / stride; + if (!subs->txfr_quirk) + bytes = frames * stride; + if (bytes % (runtime->sample_bits >> 3) != 0) { +#ifdef CONFIG_SND_DEBUG_VERBOSE + int oldbytes = bytes; +#endif + bytes = frames * stride; + snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n", + oldbytes, bytes); + } + /* update the current pointer */ + spin_lock_irqsave(&subs->lock, flags); + oldptr = subs->hwptr_done; + subs->hwptr_done += bytes; + if (subs->hwptr_done >= runtime->buffer_size * stride) + subs->hwptr_done -= runtime->buffer_size * stride; + frames = (bytes + (oldptr % stride)) / stride; + subs->transfer_done += frames; + 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 + bytes > runtime->buffer_size * stride) { + unsigned int bytes1 = + runtime->buffer_size * stride - oldptr; + memcpy(runtime->dma_area + oldptr, cp, bytes1); + memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1); + } else { + memcpy(runtime->dma_area + oldptr, cp, bytes); + } + } + + if (period_elapsed) + snd_pcm_period_elapsed(subs->pcm_substream); +} + +static void prepare_playback_urb(struct snd_usb_substream *subs, + struct urb *urb) +{ + struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + struct snd_urb_ctx *ctx = urb->context; + unsigned int counts, frames, bytes; + int i, stride, period_elapsed = 0; + unsigned long flags; + + stride = runtime->frame_bits >> 3; + + frames = 0; + urb->number_of_packets = 0; + spin_lock_irqsave(&subs->lock, flags); + for (i = 0; i < ctx->packets; i++) { + counts = ctx->packet_size[i]; + /* set up descriptor */ + urb->iso_frame_desc[i].offset = frames * stride; + urb->iso_frame_desc[i].length = counts * stride; + frames += counts; + urb->number_of_packets++; + subs->transfer_done += counts; + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + period_elapsed = 1; + if (subs->fmt_type == UAC_FORMAT_TYPE_II) { + if (subs->transfer_done > 0) { + /* FIXME: fill-max mode is not + * supported yet */ + frames -= subs->transfer_done; + counts -= subs->transfer_done; + urb->iso_frame_desc[i].length = + counts * stride; + subs->transfer_done = 0; + } + i++; + if (i < ctx->packets) { + /* add a transfer delimiter */ + urb->iso_frame_desc[i].offset = + frames * stride; + urb->iso_frame_desc[i].length = 0; + urb->number_of_packets++; + } + break; + } + } + if (period_elapsed && + !snd_usb_endpoint_implict_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */ + break; + } + bytes = frames * stride; + if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { + /* err, the transferred area goes over buffer boundary. */ + unsigned int bytes1 = + runtime->buffer_size * stride - subs->hwptr_done; + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done, bytes1); + memcpy(urb->transfer_buffer + bytes1, + runtime->dma_area, bytes - bytes1); + } else { + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done, bytes); + } + subs->hwptr_done += bytes; + if (subs->hwptr_done >= runtime->buffer_size * stride) + subs->hwptr_done -= runtime->buffer_size * stride; + runtime->delay += frames; + spin_unlock_irqrestore(&subs->lock, flags); + urb->transfer_buffer_length = bytes; + if (period_elapsed) + snd_pcm_period_elapsed(subs->pcm_substream); +} + +/* + * process after playback data complete + * - decrease the delay count again + */ +static void retire_playback_urb(struct snd_usb_substream *subs, + struct urb *urb) +{ + unsigned long flags; + struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + int stride = runtime->frame_bits >> 3; + int processed = urb->transfer_buffer_length / stride; + + spin_lock_irqsave(&subs->lock, flags); + if (processed > runtime->delay) + runtime->delay = 0; + else + runtime->delay -= processed; + spin_unlock_irqrestore(&subs->lock, flags); } static int snd_usb_playback_open(struct snd_pcm_substream *substream) @@ -874,6 +1098,56 @@ static int snd_usb_capture_close(struct snd_pcm_substream *substream) return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE); } +static int snd_usb_substream_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->data_endpoint->prepare_data_urb = prepare_playback_urb; + subs->data_endpoint->retire_data_urb = retire_playback_urb; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + stop_endpoints(subs, 0, 0, 0); + return 0; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + subs->data_endpoint->prepare_data_urb = NULL; + subs->data_endpoint->retire_data_urb = NULL; + return 0; + } + + return -EINVAL; +} + +int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int err; + struct snd_usb_substream *subs = substream->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = start_endpoints(subs); + if (err < 0) + return err; + + subs->data_endpoint->retire_data_urb = retire_capture_urb; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + stop_endpoints(subs, 0, 0, 0); + return 0; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + subs->data_endpoint->retire_data_urb = NULL; + return 0; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + subs->data_endpoint->retire_data_urb = retire_capture_urb; + return 0; + } + + return -EINVAL; +} + static struct snd_pcm_ops snd_usb_playback_ops = { .open = snd_usb_playback_open, .close = snd_usb_playback_close, diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 5ff8010b2d6f..6b7d7a2b7baa 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -73,6 +73,31 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) } } +/* + * initialize the substream instance. + */ + +static void snd_usb_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; + subs->txfr_quirk = as->chip->txfr_quirk; + + snd_usb_set_pcm_ops(as->pcm, stream); + + list_add_tail(&fp->list, &subs->fmt_list); + subs->formats |= fp->formats; + subs->num_formats++; + subs->fmt_type = fp->fmt_type; +} /* * add this endpoint to the chip instance. @@ -94,9 +119,9 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, if (as->fmt_type != fp->fmt_type) continue; subs = &as->substream[stream]; - if (!subs->endpoint) + if (!subs->data_endpoint) continue; - if (subs->endpoint == fp->endpoint) { + if (subs->data_endpoint->ep_num == fp->endpoint) { list_add_tail(&fp->list, &subs->fmt_list); subs->num_formats++; subs->formats |= fp->formats; @@ -109,7 +134,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, if (as->fmt_type != fp->fmt_type) continue; subs = &as->substream[stream]; - if (subs->endpoint) + if (subs->data_endpoint) continue; err = snd_pcm_new_stream(as->pcm, stream, 1); if (err < 0) -- cgit v1.2.3 From d399ff9593e088d33fb38f5206c6427825892baa Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 12 Apr 2012 13:51:13 +0200 Subject: ALSA: snd-usb: remove old streaming logic Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 850 +-------------------------------------------------- sound/usb/endpoint.h | 15 - 2 files changed, 6 insertions(+), 859 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 1b0ed22cff73..64853f76c10c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -53,728 +53,17 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate) return ((rate << 10) + 62) / 125; } -/* - * unlink active urbs. - */ -static int deactivate_urbs_old(struct snd_usb_substream *subs, int force, int can_sleep) -{ - struct snd_usb_audio *chip = subs->stream->chip; - unsigned int i; - int async; - - subs->running = 0; - - if (!force && subs->stream->chip->shutdown) /* to be sure... */ - return -EBADFD; - - async = !can_sleep && chip->async_unlink; - - if (!async && in_interrupt()) - return 0; - - for (i = 0; i < subs->nurbs; i++) { - if (test_bit(i, &subs->active_mask)) { - if (!test_and_set_bit(i, &subs->unlink_mask)) { - struct urb *u = subs->dataurb[i].urb; - if (async) - usb_unlink_urb(u); - else - usb_kill_urb(u); - } - } - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - if (test_bit(i+16, &subs->active_mask)) { - if (!test_and_set_bit(i+16, &subs->unlink_mask)) { - struct urb *u = subs->syncurb[i].urb; - if (async) - usb_unlink_urb(u); - else - usb_kill_urb(u); - } - } - } - } - return 0; -} - - /* * release a urb data */ static void release_urb_ctx(struct snd_urb_ctx *u) { - if (u->urb) { - if (u->buffer_size) - usb_free_coherent(u->subs->dev, u->buffer_size, - u->urb->transfer_buffer, - u->urb->transfer_dma); - usb_free_urb(u->urb); - u->urb = NULL; - } -} - -/* - * wait until all urbs are processed. - */ -static int wait_clear_urbs_old(struct snd_usb_substream *subs) -{ - unsigned long end_time = jiffies + msecs_to_jiffies(1000); - unsigned int i; - int alive; - - do { - alive = 0; - for (i = 0; i < subs->nurbs; i++) { - if (test_bit(i, &subs->active_mask)) - alive++; - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - if (test_bit(i + 16, &subs->active_mask)) - alive++; - } - } - if (! alive) - break; - schedule_timeout_uninterruptible(1); - } while (time_before(jiffies, end_time)); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); - return 0; -} - -/* - * release a substream - */ -void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force) -{ - int i; - - /* stop urbs (to be sure) */ - deactivate_urbs_old(subs, force, 1); - wait_clear_urbs_old(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_free_coherent(subs->dev, SYNC_URBS * 4, - subs->syncbuf, subs->sync_dma); - subs->syncbuf = NULL; - subs->nurbs = 0; -} - -/* - * complete callback from data urb - */ -static void snd_complete_urb_old(struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - struct snd_usb_substream *subs = ctx->subs; - struct snd_pcm_substream *substream = ctx->subs->pcm_substream; - int err = 0; - - if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) || - !subs->running || /* can be stopped during retire callback */ - (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 || - (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { - clear_bit(ctx->index, &subs->active_mask); - if (err < 0) { - snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - } - } -} - - -/* - * complete callback from sync urb - */ -static void snd_complete_sync_urb(struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - struct snd_usb_substream *subs = ctx->subs; - struct snd_pcm_substream *substream = ctx->subs->pcm_substream; - int err = 0; - - if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) || - !subs->running || /* can be stopped during retire callback */ - (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 || - (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { - clear_bit(ctx->index + 16, &subs->active_mask); - if (err < 0) { - snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - } - } -} - - -/* - * initialize a substream for plaback/capture - */ -int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, - unsigned int period_bytes, - unsigned int rate, - unsigned int frame_bits) -{ - unsigned int maxsize, i; - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - unsigned int urb_packs, total_packs, packs_per_ms; - struct snd_usb_audio *chip = subs->stream->chip; - - /* 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; - subs->freqshift = INT_MIN; - /* 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_FULL) - packs_per_ms = 8 >> subs->datainterval; - else - packs_per_ms = 1; - - if (is_playback) { - urb_packs = max(chip->nrpacks, 1); - urb_packs = min(urb_packs, (unsigned int)MAX_PACKS); - } else - urb_packs = 1; - urb_packs *= packs_per_ms; - if (subs->syncpipe) - urb_packs = min(urb_packs, 1U << subs->syncinterval); - - /* decide how many packets to be used */ - if (is_playback) { - unsigned int minsize, maxpacks; - /* 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; - /* we need at least two URBs for queueing */ - if (total_packs < 2) { - total_packs = 2; - } else { - /* and we don't want too long a queue either */ - maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); - total_packs = min(total_packs, maxpacks); - } - } else { - while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) - urb_packs >>= 1; - 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; - } else if (subs->nurbs < 2) { - /* too little - we need at least two packets - * to ensure contiguous playback/capture - */ - subs->nurbs = 2; - } - - /* 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 = (i + 1) * total_packs / subs->nurbs - - i * total_packs / subs->nurbs; - u->buffer_size = maxsize * u->packets; - if (subs->fmt_type == UAC_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_alloc_coherent(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_old; - } - - if (subs->syncpipe) { - /* allocate and initialize sync urbs */ - subs->syncbuf = usb_alloc_coherent(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: - snd_usb_release_substream_urbs(subs, 0); - return -ENOMEM; -} - -/* - * 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, frames, bytes, 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 && printk_ratelimit()) { - snd_printdd("frame %d active: %d\n", i, urb->iso_frame_desc[i].status); - // continue; - } - bytes = urb->iso_frame_desc[i].actual_length; - frames = bytes / stride; - if (!subs->txfr_quirk) - bytes = frames * stride; - if (bytes % (runtime->sample_bits >> 3) != 0) { -#ifdef CONFIG_SND_DEBUG_VERBOSE - int oldbytes = bytes; -#endif - bytes = frames * stride; - snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n", - oldbytes, bytes); - } - /* update the current pointer */ - spin_lock_irqsave(&subs->lock, flags); - oldptr = subs->hwptr_done; - subs->hwptr_done += bytes; - if (subs->hwptr_done >= runtime->buffer_size * stride) - subs->hwptr_done -= runtime->buffer_size * stride; - frames = (bytes + (oldptr % stride)) / stride; - subs->transfer_done += frames; - 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 + bytes > runtime->buffer_size * stride) { - unsigned int bytes1 = - runtime->buffer_size * stride - oldptr; - memcpy(runtime->dma_area + oldptr, cp, bytes1); - memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1); - } else { - memcpy(runtime->dma_area + oldptr, cp, bytes); - } - } - 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 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 = min(4u, ctx->subs->syncmaxsize); - urb->iso_frame_desc[0].offset = 0; - return 0; -} - -/* - * process after playback sync complete - * - * Full speed devices report feedback values in 10.14 format as samples per - * frame, high speed devices in 16.16 format as samples per microframe. - * Because the Audio Class 1 spec was written before USB 2.0, many high speed - * devices use a wrong interpretation, some others use an entirely different - * format. Therefore, we cannot predict what format any particular device uses - * and must detect it automatically. - */ -static int retire_playback_sync_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - int shift; - unsigned long flags; - - if (urb->iso_frame_desc[0].status != 0 || - urb->iso_frame_desc[0].actual_length < 3) - return 0; - - f = le32_to_cpup(urb->transfer_buffer); - if (urb->iso_frame_desc[0].actual_length == 3) - f &= 0x00ffffff; - else - f &= 0x0fffffff; - if (f == 0) - return 0; - - if (unlikely(subs->freqshift == INT_MIN)) { - /* - * The first time we see a feedback value, determine its format - * by shifting it left or right until it matches the nominal - * frequency value. This assumes that the feedback does not - * differ from the nominal value more than +50% or -25%. - */ - shift = 0; - while (f < subs->freqn - subs->freqn / 4) { - f <<= 1; - shift++; - } - while (f > subs->freqn + subs->freqn / 2) { - f >>= 1; - shift--; - } - subs->freqshift = shift; - } - else if (subs->freqshift >= 0) - f <<= subs->freqshift; - else - f >>= -subs->freqshift; - - if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) { - /* - * If the frequency looks valid, set it. - * This value is referred to in prepare_playback_urb(). - */ - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } else { - /* - * Out of range; maybe the shift value is wrong. - * Reset it so that we autodetect again the next time. - */ - subs->freqshift = INT_MIN; - } - - return 0; -} - -/* determine the number of frames in the next packet */ -static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) -{ - if (subs->fill_max) - return subs->maxframesize; - else { - subs->phase = (subs->phase & 0xffff) - + (subs->freqm << subs->datainterval); - return min(subs->phase >> 16, subs->maxframesize); - } -} - -/* - * Prepare urb for streaming before playback starts or when paused. - * - * We don't have any data, so we send silence. - */ -static int prepare_nodata_playback_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int i, offs, counts; - struct snd_urb_ctx *ctx = urb->context; - int stride = runtime->frame_bits >> 3; - - offs = 0; - urb->dev = ctx->subs->dev; - for (i = 0; i < ctx->packets; ++i) { - counts = snd_usb_audio_next_packet_size(subs); - urb->iso_frame_desc[i].offset = offs * stride; - urb->iso_frame_desc[i].length = counts * stride; - offs += counts; - } - urb->number_of_packets = ctx->packets; - urb->transfer_buffer_length = offs * stride; - memset(urb->transfer_buffer, - runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0, - offs * stride); - return 0; -} - -/* - * prepare urb for playback data pipe - * - * Since a URB can handle only a single linear buffer, we must use double - * buffering when the data to be transferred overflows the buffer boundary. - * To avoid inconsistencies when updating hwptr_done, we use double buffering - * for all URBs. - */ -static int prepare_playback_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - int i, stride; - unsigned int counts, frames, bytes; - unsigned long flags; - int period_elapsed = 0; - struct snd_urb_ctx *ctx = urb->context; - - stride = runtime->frame_bits >> 3; - - frames = 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++) { - counts = snd_usb_audio_next_packet_size(subs); - /* set up descriptor */ - urb->iso_frame_desc[i].offset = frames * stride; - urb->iso_frame_desc[i].length = counts * stride; - frames += counts; - urb->number_of_packets++; - subs->transfer_done += counts; - if (subs->transfer_done >= runtime->period_size) { - subs->transfer_done -= runtime->period_size; - period_elapsed = 1; - if (subs->fmt_type == UAC_FORMAT_TYPE_II) { - if (subs->transfer_done > 0) { - /* FIXME: fill-max mode is not - * supported yet */ - frames -= subs->transfer_done; - counts -= subs->transfer_done; - urb->iso_frame_desc[i].length = - counts * stride; - subs->transfer_done = 0; - } - i++; - if (i < ctx->packets) { - /* add a transfer delimiter */ - urb->iso_frame_desc[i].offset = - frames * stride; - urb->iso_frame_desc[i].length = 0; - urb->number_of_packets++; - } - break; - } - } - if (period_elapsed) /* finish at the period boundary */ - break; - } - bytes = frames * stride; - if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { - /* err, the transferred area goes over buffer boundary. */ - unsigned int bytes1 = - runtime->buffer_size * stride - subs->hwptr_done; - memcpy(urb->transfer_buffer, - runtime->dma_area + subs->hwptr_done, bytes1); - memcpy(urb->transfer_buffer + bytes1, - runtime->dma_area, bytes - bytes1); - } else { - memcpy(urb->transfer_buffer, - runtime->dma_area + subs->hwptr_done, bytes); - } - subs->hwptr_done += bytes; - if (subs->hwptr_done >= runtime->buffer_size * stride) - subs->hwptr_done -= runtime->buffer_size * stride; - - /* update delay with exact number of samples queued */ - runtime->delay = subs->last_delay; - runtime->delay += frames; - subs->last_delay = runtime->delay; - - /* realign last_frame_number */ - subs->last_frame_number = usb_get_current_frame_number(subs->dev); - subs->last_frame_number &= 0xFF; /* keep 8 LSBs */ - - spin_unlock_irqrestore(&subs->lock, flags); - urb->transfer_buffer_length = bytes; - if (period_elapsed) - snd_pcm_period_elapsed(subs->pcm_substream); - return 0; -} - -/* - * process after playback data complete - * - decrease the delay count again - */ -static int retire_playback_urb(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned long flags; - int stride = runtime->frame_bits >> 3; - int processed = urb->transfer_buffer_length / stride; - int est_delay; - - spin_lock_irqsave(&subs->lock, flags); - - est_delay = snd_usb_pcm_delay(subs, runtime->rate); - /* update delay with exact number of samples played */ - if (processed > subs->last_delay) - subs->last_delay = 0; - else - subs->last_delay -= processed; - runtime->delay = subs->last_delay; - - /* - * Report when delay estimate is off by more than 2ms. - * The error should be lower than 2ms since the estimate relies - * on two reads of a counter updated every ms. - */ - if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2) - snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n", - est_delay, subs->last_delay); - - spin_unlock_irqrestore(&subs->lock, flags); - return 0; + if (u->buffer_size) + usb_free_coherent(u->ep->chip->dev, u->buffer_size, + u->urb->transfer_buffer, + u->urb->transfer_dma); + usb_free_urb(u->urb); + u->urb = NULL; } static const char *usb_error_string(int err) @@ -802,133 +91,6 @@ static const char *usb_error_string(int err) } } -/* - * set up and start data/sync urbs - */ -static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime) -{ - unsigned int i; - int err; - - if (subs->stream->chip->shutdown) - return -EBADFD; - - for (i = 0; i < subs->nurbs; i++) { - if (snd_BUG_ON(!subs->dataurb[i].urb)) - return -EINVAL; - if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) { - snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i); - goto __error; - } - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - if (snd_BUG_ON(!subs->syncurb[i].urb)) - return -EINVAL; - if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) { - snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i); - goto __error; - } - } - } - - subs->active_mask = 0; - subs->unlink_mask = 0; - subs->running = 1; - for (i = 0; i < subs->nurbs; i++) { - err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC); - if (err < 0) { - snd_printk(KERN_ERR "cannot submit datapipe " - "for urb %d, error %d: %s\n", - i, err, usb_error_string(err)); - goto __error; - } - set_bit(i, &subs->active_mask); - } - if (subs->syncpipe) { - for (i = 0; i < SYNC_URBS; i++) { - err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC); - if (err < 0) { - snd_printk(KERN_ERR "cannot submit syncpipe " - "for urb %d, error %d: %s\n", - i, err, usb_error_string(err)); - goto __error; - } - set_bit(i + 16, &subs->active_mask); - } - } - return 0; - - __error: - // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); - deactivate_urbs_old(subs, 0, 0); - return -EPIPE; -} - - -/* - */ -static struct snd_urb_ops audio_urb_ops[2] = { - { - .prepare = prepare_nodata_playback_urb, - .retire = retire_playback_urb, - .prepare_sync = prepare_playback_sync_urb, - .retire_sync = retire_playback_sync_urb, - }, - { - .prepare = prepare_capture_urb, - .retire = retire_capture_urb, - .prepare_sync = prepare_capture_sync_urb, - .retire_sync = retire_capture_sync_urb, - }, -}; - -/* - * initialize the substream instance. - */ - -void snd_usb_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; - subs->txfr_quirk = as->chip->txfr_quirk; - subs->ops = audio_urb_ops[stream]; - if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH) - subs->ops.prepare_sync = prepare_capture_sync_urb_hs; - - snd_usb_set_pcm_ops(as->pcm, stream); - - list_add_tail(&fp->list, &subs->fmt_list); - subs->formats |= fp->formats; - subs->endpoint = fp->endpoint; - subs->num_formats++; - subs->fmt_type = fp->fmt_type; -} - -int snd_usb_substream_prepare(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime) -{ - /* clear urbs (to be sure) */ - deactivate_urbs_old(subs, 0, 1); - wait_clear_urbs_old(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); - } - - return 0; -} - int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep) { return ep->sync_master && diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index e540768de3d5..ee2723fb174f 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -1,21 +1,6 @@ #ifndef __USBAUDIO_ENDPOINT_H #define __USBAUDIO_ENDPOINT_H -void snd_usb_init_substream(struct snd_usb_stream *as, - int stream, - struct audioformat *fp); - -int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, - unsigned int period_bytes, - unsigned int rate, - unsigned int frame_bits); - -void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force); - -int snd_usb_substream_prepare(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime); - - #define SND_USB_ENDPOINT_TYPE_DATA 0 #define SND_USB_ENDPOINT_TYPE_SYNC 1 -- cgit v1.2.3 From c75a8a7ae565d7cd9baa87a504ba9162e355b4b0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 12 Apr 2012 13:51:14 +0200 Subject: ALSA: snd-usb: add support for implicit feedback Implicit feedback is a streaming mode that does not rely on dedicated sync endpoints but uses the information provided by record streams to clock output streams. Now that the streaming logic is decoupled from the PCM streams, this is easy to implement. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 0f107834c100..2d3a04d829b7 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -301,7 +301,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) struct usb_interface *iface; unsigned int ep, attr; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - int err; + int err, implicit_fb = 0; iface = usb_ifnum_to_if(dev, fmt->iface); if (WARN_ON(!iface)) @@ -326,25 +326,34 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) * assume it as adaptive-out or sync-in. */ attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; - if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || - (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && - altsd->bNumEndpoints >= 2) { - switch (subs->stream->chip->usb_id) { - case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ - case USB_ID(0x0763, 0x2081): + + switch (subs->stream->chip->usb_id) { + case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ + case USB_ID(0x0763, 0x2081): + if (is_playback) { + implicit_fb = 1; ep = 0x81; iface = usb_ifnum_to_if(dev, 2); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + alts = &iface->altsetting[1]; goto add_sync_ep; } + } + if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || + (!is_playback && attr == USB_ENDPOINT_SYNC_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)) { + get_endpoint(alts, 1)->bSynchAddress != 0 && + !implicit_fb)) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; @@ -352,16 +361,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) 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)))) { + (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)) || + ( is_playback && !implicit_fb))) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; } + + implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK) + == USB_ENDPOINT_USAGE_IMPLICIT_FB; + add_sync_ep: subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, ep, !subs->direction, - SND_USB_ENDPOINT_TYPE_SYNC); - + implicit_fb ? + SND_USB_ENDPOINT_TYPE_DATA : + SND_USB_ENDPOINT_TYPE_SYNC); if (!subs->sync_endpoint) return -EINVAL; -- cgit v1.2.3 From 94c27215bc5b7941a52d3d3a02d904bfdcd5fe5e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 12 Apr 2012 13:51:15 +0200 Subject: ALSA: snd-usb: add some documentation Document the new streaming code and some of the functions so that contributers can catch up easier. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 171 insertions(+), 11 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 64853f76c10c..d1ef3048dbba 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -35,6 +35,32 @@ #define EP_FLAG_ACTIVATED 0 #define EP_FLAG_RUNNING 1 +/* + * snd_usb_endpoint is a model that abstracts everything related to an + * USB endpoint and its streaming. + * + * There are functions to activate and deactivate the streaming URBs and + * optinal callbacks to let the pcm logic handle the actual content of the + * packets for playback and record. Thus, the bus streaming and the audio + * handlers are fully decoupled. + * + * There are two different types of endpoints in for audio applications. + * + * SND_USB_ENDPOINT_TYPE_DATA handles full audio data payload for both + * inbound and outbound traffic. + * + * SND_USB_ENDPOINT_TYPE_SYNC are for inbound traffic only and expect the + * payload to carry Q16.16 formatted sync information (3 or 4 bytes). + * + * Each endpoint has to be configured (by calling + * snd_usb_endpoint_set_params()) before it can be used. + * + * The model incorporates a reference counting, so that multiple users + * can call snd_usb_endpoint_start() and snd_usb_endpoint_stop(), and + * only the first user will effectively start the URBs, and only the last + * one will tear them down again. + */ + /* * convert a sampling rate into our full speed format (fs/1000 in Q16.16) * this will overflow at approx 524 kHz @@ -91,6 +117,14 @@ static const char *usb_error_string(int err) } } +/** + * snd_usb_endpoint_implicit_feedback_sink: Report endpoint usage type + * + * @ep: The endpoint + * + * Determine whether an endpoint is driven by an implicit feedback + * data endpoint source. + */ int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep) { return ep->sync_master && @@ -99,7 +133,13 @@ int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep) usb_pipeout(ep->pipe); } -/* determine the number of frames in the next packet */ +/* + * For streaming based on information derived from sync endpoints, + * prepare_outbound_urb_sizes() will call next_packet_size() to + * determine the number of samples to be sent in the next packet. + * + * For implicit feedback, next_packet_size() is unused. + */ static int next_packet_size(struct snd_usb_endpoint *ep) { unsigned long flags; @@ -237,6 +277,19 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, } } +/* + * Send output urbs that have been prepared previously. Urbs are dequeued + * from ep->ready_playback_urbs and in case there there aren't any available + * or there are no packets that have been prepared, this function does + * nothing. + * + * The reason why the functionality of sending and preparing urbs is separated + * is that host controllers don't guarantee an ordering in returing inbound + * and outbound packets to their submitters. + * + * This function is only used for implicit feedback endpoints. For endpoints + * driven by sync endpoints, urbs are submitted from their completion handler. + */ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) { while (test_bit(EP_FLAG_RUNNING, &ep->flags)) { @@ -270,6 +323,7 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) for (i = 0; i < packet->packets; i++) ctx->packet_size[i] = packet->packet_size[i]; + /* call the data handler to fill in playback data */ prepare_outbound_urb(ep, ctx); err = usb_submit_urb(ctx->urb, GFP_ATOMIC); @@ -336,6 +390,22 @@ exit_clear: clear_bit(ctx->index, &ep->active_mask); } +/** + * snd_usb_add_endpoint: Add an endpoint to an audio chip + * + * @chip: The chip + * @alts: The USB host interface + * @ep_num: The number of the endpoint to use + * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE + * @type: SND_USB_ENDPOINT_TYPE_DATA or SND_USB_ENDPOINT_TYPE_SYNC + * + * If the requested endpoint has not been added to the given chip before, + * a new instance is created. Otherwise, a pointer to the previoulsy + * created instance is returned. In case of any error, NULL is returned. + * + * New endpoints will be added to chip->ep_list and must be freed by + * calling snd_usb_endpoint_free(). + */ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, struct usb_host_interface *alts, int ep_num, int direction, int type) @@ -506,6 +576,9 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) ep->nurbs = 0; } +/* + * configure a data endpoint + */ static int data_ep_set_params(struct snd_usb_endpoint *ep, struct snd_pcm_hw_params *hw_params, struct audioformat *fmt, @@ -629,6 +702,9 @@ out_of_memory: return -ENOMEM; } +/* + * configure a sync endpoint + */ static int sync_ep_set_params(struct snd_usb_endpoint *ep, struct snd_pcm_hw_params *hw_params, struct audioformat *fmt) @@ -669,6 +745,15 @@ out_of_memory: return -ENOMEM; } +/** + * snd_usb_endpoint_set_params: configure an snd_endpoint + * + * @ep: the endpoint to configure + * + * Determine the number of of URBs to be used on this endpoint. + * An endpoint must be configured before it can be started. + * An endpoint that is already running can not be reconfigured. + */ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct snd_pcm_hw_params *hw_params, struct audioformat *fmt, @@ -717,6 +802,19 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, return err; } +/** + * snd_usb_endpoint_start: start an snd_usb_endpoint + * + * @ep: the endpoint to start + * + * A call to this function will increment the use count of the endpoint. + * In case this not already running, the URBs for this endpoint will be + * submitted. Otherwise, this function does nothing. + * + * Must be balanced to calls of snd_usb_endpoint_stop(). + * + * Returns an error if the URB submission failed, 0 in all other cases. + */ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) { int err; @@ -788,6 +886,17 @@ __error: return -EPIPE; } +/** + * snd_usb_endpoint_stop: stop an snd_usb_endpoint + * + * @ep: the endpoint to stop (may be NULL) + * + * A call to this function will decrement the use count of the endpoint. + * In case the last user has requested the endpoint stop, the URBs will + * actually deactivated. + * + * Must be balanced to calls of snd_usb_endpoint_start(). + */ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, int force, int can_sleep, int wait) { @@ -812,6 +921,19 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, } } +/** + * snd_usb_endpoint_activate: activate an snd_usb_endpoint + * + * @ep: the endpoint to activate + * + * If the endpoint is not currently in use, this functions will select the + * correct alternate interface setting for the interface of this endpoint. + * + * In case of any active users, this functions does nothing. + * + * Returns an error if usb_set_interface() failed, 0 in all other + * cases. + */ int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep) { if (ep->use_count != 0) @@ -835,6 +957,19 @@ int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep) return -EBUSY; } +/** + * snd_usb_endpoint_deactivate: deactivate an snd_usb_endpoint + * + * @ep: the endpoint to deactivate + * + * If the endpoint is not currently in use, this functions will select the + * alternate interface setting 0 for the interface of this endpoint. + * + * In case of any active users, this functions does nothing. + * + * Returns an error if usb_set_interface() failed, 0 in all other + * cases. + */ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) { if (!ep) @@ -860,6 +995,13 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) return -EBUSY; } +/** snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint + * + * @ep: the list header of the endpoint to free + * + * This function does not care for the endpoint's use count but will tear + * down all the streaming URBs immediately and free all resources. + */ void snd_usb_endpoint_free(struct list_head *head) { struct snd_usb_endpoint *ep; @@ -869,15 +1011,15 @@ void snd_usb_endpoint_free(struct list_head *head) kfree(ep); } -/* - * process after playback sync complete - * - * Full speed devices report feedback values in 10.14 format as samples per - * frame, high speed devices in 16.16 format as samples per microframe. - * Because the Audio Class 1 spec was written before USB 2.0, many high speed - * devices use a wrong interpretation, some others use an entirely different - * format. Therefore, we cannot predict what format any particular device uses - * and must detect it automatically. +/** + * snd_usb_handle_sync_urb: parse an USB sync packet + * + * @ep: the endpoint to handle the packet + * @sender: the sending endpoint + * @urb: the received packet + * + * This function is called from the context of an endpoint that received + * the packet and is used to let another endpoint object handle the payload. */ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, struct snd_usb_endpoint *sender, @@ -889,6 +1031,11 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, snd_BUG_ON(ep == sender); + /* + * In case the endpoint is operating in implicit feedback mode, prepare + * and a new outbound URB that has the same layout as the received + * packet and add it to the list of pending urbs. + */ if (snd_usb_endpoint_implict_feedback_sink(ep) && ep->use_count != 0) { @@ -938,7 +1085,20 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, return; } - /* parse sync endpoint packet */ + /* + * process after playback sync complete + * + * Full speed devices report feedback values in 10.14 format as samples + * per frame, high speed devices in 16.16 format as samples per + * microframe. + * + * Because the Audio Class 1 spec was written before USB 2.0, many high + * speed devices use a wrong interpretation, some others use an + * entirely different format. + * + * Therefore, we cannot predict what format any particular device uses + * and must detect it automatically. + */ if (urb->iso_frame_desc[0].status != 0 || urb->iso_frame_desc[0].actual_length < 3) -- cgit v1.2.3 From c5ee4ec82801d11c67836565539acd3b47100208 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Apr 2012 10:27:28 +0200 Subject: ALSA: usb: Remove unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/usb/endpoint.c: In function ‘deactivate_urbs’: sound/usb/endpoint.c:520:16: warning: unused variable ‘flags’ [-Wunused-variable] Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index d1ef3048dbba..8b695d5562e6 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -517,7 +517,6 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) */ static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep) { - unsigned long flags; unsigned int i; int async; -- cgit v1.2.3 From 85f71932e5f22c8f4a28e09b2c27083839aa5bed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Apr 2012 12:41:54 +0200 Subject: ALSA: usb: Fix fill_max flag set ep->fill_max is a 1 bit flag, thus it has to be boolean. sound/usb/endpoint.c: In function 'snd_usb_endpoint_set_params': sound/usb/endpoint.c:785: warning: overflow in implicit constant conversion Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 8b695d5562e6..12e5a951a143 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -771,7 +771,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, ep->datainterval = fmt->datainterval; ep->maxpacksize = fmt->maxpacksize; - ep->fill_max = fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX; + ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX); if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) ep->freqn = get_usb_full_speed_rate(params_rate(hw_params)); -- cgit v1.2.3 From 22026c1a7be900cc6dabd6be37a77bb217d2d837 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Apr 2012 12:57:39 +0200 Subject: ALSA: usb: Remove obsoleted fields from struct snd_usb_substream Many fields have been moved to struct snd_usb_endpoint. Also fix the proc output to correspond to the new structure. Signed-off-by: Takashi Iwai --- sound/usb/card.h | 15 --------------- sound/usb/proc.c | 32 +++++++++++++++++++++----------- 2 files changed, 21 insertions(+), 26 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.h b/sound/usb/card.h index 8a08687e5bc0..77d2eec5e897 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -115,21 +115,6 @@ struct snd_usb_substream { unsigned int cur_rate; /* current rate (for hw_params callback) */ unsigned int period_bytes; /* current period bytes (for hw_params callback) */ unsigned int altset_idx; /* USB data format: index of alternate setting */ - 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 */ - int freqshift; /* how much to shift the feedback value to get Q16.16 */ - 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 syncmaxsize; /* sync endpoint packet size */ - unsigned int fill_max: 1; /* fill max packet size always */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ unsigned int fmt_type; /* USB audio format type (1-3) */ diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 961c9a250686..06e23d89d111 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -115,6 +115,25 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s } } +static void proc_dump_ep_status(struct snd_usb_substream *subs, + struct snd_usb_endpoint *ep, + struct snd_info_buffer *buffer) +{ + if (!ep) + return; + snd_iprintf(buffer, " Packet Size = %d\n", ep->curpacksize); + snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n", + snd_usb_get_speed(subs->dev) == USB_SPEED_FULL + ? get_full_speed_hz(ep->freqm) + : get_high_speed_hz(ep->freqm), + ep->freqm >> 16, ep->freqm & 0xffff); + if (ep->freqshift != INT_MIN) { + int res = 16 - ep->freqshift; + snd_iprintf(buffer, " Feedback Format = %d.%d\n", + (ep->syncmaxsize > 3 ? 32 : 24) - res, res); + } +} + static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer) { if (subs->running) { @@ -126,17 +145,8 @@ static void proc_dump_substream_status(struct snd_usb_substream *subs, struct sn 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); - if (subs->freqshift != INT_MIN) - snd_iprintf(buffer, " Feedback Format = %d.%d\n", - (subs->syncmaxsize > 3 ? 32 : 24) - - (16 - subs->freqshift), - 16 - subs->freqshift); + proc_dump_ep_status(subs, subs->data_endpoint, buffer); + proc_dump_ep_status(subs, subs->sync_endpoint, buffer); } else { snd_iprintf(buffer, " Status: Stop\n"); } -- cgit v1.2.3 From 284a8dd6f0fb16029198b5b34437499d504b567c Mon Sep 17 00:00:00 2001 From: Mark Hills Date: Sat, 14 Apr 2012 17:19:23 +0100 Subject: ALSA: snd-usb-audio: Skip un-parseable mixer units instead of erroring Some interfaces reference endpoints which do not exists. To accomodate these, do not fail completely, but skip over them. This allows the Electrix Ebox-44 with earlier firmware to be detected and used for audio. Signed-off-by: Mark Hills Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index ab23869c01bb..c374c7242ab2 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1388,7 +1388,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r for (pin = 0; pin < input_pins; pin++) { err = parse_audio_unit(state, desc->baSourceID[pin]); if (err < 0) - return err; + continue; err = check_input_term(state, desc->baSourceID[pin], &iterm); if (err < 0) return err; -- cgit v1.2.3 From 7536c301f8817214629e80fa06b5b5c93df3ad52 Mon Sep 17 00:00:00 2001 From: Mark Hills Date: Sat, 14 Apr 2012 17:19:24 +0100 Subject: ALSA: snd-usb-audio: Replace mixer for Electrix Ebox-44 The mixer units from the firmware are corrupt, and even where they are valid they presents mono controls as L and R channels of stereo. Signed-off-by: Mark Hills Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 13 ++++++++++ sound/usb/mixer_quirks.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index f1324c423835..41daaa24c25f 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -288,6 +288,15 @@ static struct usbmix_name_map scratch_live_map[] = { { 0 } /* terminator */ }; +static struct usbmix_name_map ebox44_map[] = { + { 4, NULL }, /* FU */ + { 6, NULL }, /* MU */ + { 7, NULL }, /* FU */ + { 10, NULL }, /* FU */ + { 11, NULL }, /* MU */ + { 0 } +}; + /* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+" * most importand difference is SU[8], it should be set to "Capture Source" * to make alsamixer and PA working properly. @@ -371,6 +380,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = scratch_live_map, .ignore_ctl_error = 1, }, + { + .id = USB_ID(0x200c, 0x1018), + .map = ebox44_map, + }, { 0 } /* terminator */ }; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ab125ee0b0f0..e2072edc7772 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -557,6 +557,69 @@ static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) return 0; } +static int snd_ebox44_create_ctl(struct usb_mixer_interface *mixer, + int unitid, int control, int cmask, + int val_type, const char *name) +{ + struct usb_mixer_elem_info *cval; + struct snd_kcontrol *kctl; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (!cval) + return -ENOMEM; + + cval->id = unitid; + cval->mixer = mixer; + + cval->val_type = val_type; + cval->channels = 1; + cval->control = control; + cval->cmask = cmask; + + /* Volume controls will override these values */ + cval->min = 0; + cval->max = 1; + cval->res = 0; + + cval->dBmin = 0; + cval->dBmax = 0; + + kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); + if (!kctl) { + kfree(cval); + return -ENOMEM; + } + + snprintf(kctl->id.name, sizeof(kctl->id.name), name); + kctl->private_free = usb_mixer_elem_free; + return snd_usb_mixer_add_control(mixer, kctl); +} + +/* + * Create mixer for Electrix Ebox-44 + * + * The mixer units from this device are corrupt, and even where they + * are valid they presents mono controls as L and R channels of + * stereo. So we create a good mixer in code. + */ + +static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer) +{ + snd_ebox44_create_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Headphone Playback Switch"); + snd_ebox44_create_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16, "Headphone A Mix Playback Volume"); + snd_ebox44_create_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16, "Headphone B Mix Playback Volume"); + + snd_ebox44_create_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Output Playback Switch"); + snd_ebox44_create_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16, "Output A Playback Volume"); + snd_ebox44_create_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16, "Output B Playback Volume"); + + snd_ebox44_create_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Input Capture Switch"); + snd_ebox44_create_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16, "Input A Capture Volume"); + snd_ebox44_create_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16, "Input B Capture Volume"); + + return 0; +} + void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, unsigned char samplerate_id) { @@ -619,6 +682,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) snd_nativeinstruments_ta10_mixers, ARRAY_SIZE(snd_nativeinstruments_ta10_mixers)); break; + + case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */ + err = snd_ebox44_create_mixer(mixer); + break; } return err; -- cgit v1.2.3 From c89a5d9cacf2b862fea986a30b75359f6cf997c1 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 21 Apr 2012 13:52:12 +0200 Subject: ALSA: snd-usb: remove refactorization left-overs Drop some struct members and definitions that became obsolete during the refactorization of the driver. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/card.h | 13 ------------- sound/usb/proc.c | 6 +----- 2 files changed, 1 insertion(+), 18 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.h b/sound/usb/card.h index 77d2eec5e897..0d37238b8457 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -43,13 +43,6 @@ struct snd_urb_ctx { struct list_head ready_list; }; -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_endpoint { struct snd_usb_audio *chip; @@ -125,11 +118,6 @@ struct snd_usb_substream { 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 */ /* data and sync endpoints for this stream */ struct snd_usb_endpoint *data_endpoint; struct snd_usb_endpoint *sync_endpoint; @@ -141,7 +129,6 @@ struct snd_usb_substream { struct snd_pcm_hw_constraint_list rate_list; /* limited rates */ spinlock_t lock; - struct snd_urb_ops ops; /* callbacks (must be filled at init) */ int last_frame_number; /* stored frame number */ int last_delay; /* stored delay */ }; diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 06e23d89d111..ebc1a5b5b3f1 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -25,6 +25,7 @@ #include "usbaudio.h" #include "helper.h" #include "card.h" +#include "endpoint.h" #include "proc.h" /* convert our full speed USB rate into sampling rate in Hz */ @@ -137,14 +138,9 @@ static void proc_dump_ep_status(struct snd_usb_substream *subs, 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->altset_idx); - 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"); proc_dump_ep_status(subs, subs->data_endpoint, buffer); proc_dump_ep_status(subs, subs->sync_endpoint, buffer); } else { -- cgit v1.2.3 From 8a4d1d397b37f5aab1f830c0c7ca1fcdebf7ad9e Mon Sep 17 00:00:00 2001 From: Felix Homann Date: Mon, 23 Apr 2012 20:24:23 +0200 Subject: ALSA: usb-audio: Unify M-Audio Fast Track Ultra and Ebox-44 mixer quirks. Merge snd_maudio_ftu_create_ctl() and snd_ebox44_create_ctl() into snd_create_std_mono_ctl(). As opposed to the ftu and ebox-44 specific functions, a TLV callback can be specified for controls created by snd_create_std_mono_ctl(). [fixed minor checkpatch.pl warnings by tiwai] Signed-off-by: Felix Homann Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 210 ++++++++++++++++++++++++++++------------------- 1 file changed, 125 insertions(+), 85 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e2072edc7772..1c3da65f1af1 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -42,6 +42,85 @@ extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; +/* private_free callback */ +static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +{ + kfree(kctl->private_data); + kctl->private_data = NULL; +} + +/* This function allows for the creation of standard UAC controls. + * See the quirks for M-Audio FTUs or Ebox-44. + * If you don't want to set a TLV callback pass NULL. + * + * Since there doesn't seem to be a devices that needs a multichannel + * version, we keep it mono for simplicity. + */ +static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, + unsigned int unitid, + unsigned int control, + unsigned int cmask, + int val_type, + const char *name, + snd_kcontrol_tlv_rw_t *tlv_callback) +{ + int err; + struct usb_mixer_elem_info *cval; + struct snd_kcontrol *kctl; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (!cval) + return -ENOMEM; + + cval->id = unitid; + cval->mixer = mixer; + cval->val_type = val_type; + cval->channels = 1; + cval->control = control; + cval->cmask = cmask; + + /* FIXME: Do we need this? + * The following values are for compatibility with + * Ebox-44 mixer. + * But the corresponding ebox-44 function says: + * "Volume controls will override these values" + * + * These values don't have any effect at all for + * M-Audio FTUs. + * So I think, we can safely omit the range settings here. + */ + cval->min = 0; + cval->max = 1; + cval->res = 0; + cval->dBmin = 0; + cval->dBmax = 0; + + /* Create control */ + kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); + if (!kctl) { + kfree(cval); + return -ENOMEM; + } + + /* Set name */ + snprintf(kctl->id.name, sizeof(kctl->id.name), name); + kctl->private_free = usb_mixer_elem_free; + + /* set TLV */ + if (tlv_callback) { + kctl->tlv.c = tlv_callback; + kctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } + /* Add control to mixer */ + err = snd_usb_mixer_add_control(mixer, kctl); + if (err < 0) + return err; + + return 0; +} + /* * Sound Blaster remote control configuration * @@ -496,59 +575,37 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, /* M-Audio FastTrack Ultra quirks */ -/* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) -{ - kfree(kctl->private_data); - kctl->private_data = NULL; -} - -static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer, - int in, int out, const char *name) -{ - struct usb_mixer_elem_info *cval; - struct snd_kcontrol *kctl; - - cval = kzalloc(sizeof(*cval), GFP_KERNEL); - if (!cval) - return -ENOMEM; - - cval->id = 5; - cval->mixer = mixer; - cval->val_type = USB_MIXER_S16; - cval->channels = 1; - cval->control = out + 1; - cval->cmask = 1 << in; - - kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); - if (!kctl) { - kfree(cval); - return -ENOMEM; - } - - snprintf(kctl->id.name, sizeof(kctl->id.name), name); - kctl->private_free = usb_mixer_elem_free; - return snd_usb_mixer_add_control(mixer, kctl); -} - -static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) +/* Create a volume control for FTU devices*/ +static int snd_maudio_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) { char name[64]; + unsigned int control, cmask; int in, out, err; + const unsigned int id = 5; + const int val_type = USB_MIXER_S16; + for (out = 0; out < 8; out++) { + control = out + 1; for (in = 0; in < 8; in++) { + cmask = 1 << in; snprintf(name, sizeof(name), - "AIn%d - Out%d Capture Volume", in + 1, out + 1); - err = snd_maudio_ftu_create_ctl(mixer, in, out, name); + "AIn%d - Out%d Capture Volume", + in + 1, out + 1); + err = snd_create_std_mono_ctl(mixer, id, control, + cmask, val_type, name, + NULL); if (err < 0) return err; } - for (in = 8; in < 16; in++) { + cmask = 1 << in; snprintf(name, sizeof(name), - "DIn%d - Out%d Playback Volume", in - 7, out + 1); - err = snd_maudio_ftu_create_ctl(mixer, in, out, name); + "DIn%d - Out%d Playback Volume", + in - 7, out + 1); + err = snd_create_std_mono_ctl(mixer, id, control, + cmask, val_type, name, + NULL); if (err < 0) return err; } @@ -557,44 +614,18 @@ static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) return 0; } -static int snd_ebox44_create_ctl(struct usb_mixer_interface *mixer, - int unitid, int control, int cmask, - int val_type, const char *name) +static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) { - struct usb_mixer_elem_info *cval; - struct snd_kcontrol *kctl; - - cval = kzalloc(sizeof(*cval), GFP_KERNEL); - if (!cval) - return -ENOMEM; - - cval->id = unitid; - cval->mixer = mixer; - - cval->val_type = val_type; - cval->channels = 1; - cval->control = control; - cval->cmask = cmask; - - /* Volume controls will override these values */ - cval->min = 0; - cval->max = 1; - cval->res = 0; - - cval->dBmin = 0; - cval->dBmax = 0; + int err; - kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); - if (!kctl) { - kfree(cval); - return -ENOMEM; - } + err = snd_maudio_ftu_create_volume_ctls(mixer); + if (err < 0) + return err; - snprintf(kctl->id.name, sizeof(kctl->id.name), name); - kctl->private_free = usb_mixer_elem_free; - return snd_usb_mixer_add_control(mixer, kctl); + return 0; } + /* * Create mixer for Electrix Ebox-44 * @@ -605,17 +636,26 @@ static int snd_ebox44_create_ctl(struct usb_mixer_interface *mixer, static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer) { - snd_ebox44_create_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Headphone Playback Switch"); - snd_ebox44_create_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16, "Headphone A Mix Playback Volume"); - snd_ebox44_create_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16, "Headphone B Mix Playback Volume"); - - snd_ebox44_create_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Output Playback Switch"); - snd_ebox44_create_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16, "Output A Playback Volume"); - snd_ebox44_create_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16, "Output B Playback Volume"); - - snd_ebox44_create_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Input Capture Switch"); - snd_ebox44_create_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16, "Input A Capture Volume"); - snd_ebox44_create_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16, "Input B Capture Volume"); + snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN, + "Headphone Playback Switch", NULL); + snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16, + "Headphone A Mix Playback Volume", NULL); + snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16, + "Headphone B Mix Playback Volume", NULL); + + snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN, + "Output Playback Switch", NULL); + snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16, + "Output A Playback Volume", NULL); + snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16, + "Output B Playback Volume", NULL); + + snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN, + "Input Capture Switch", NULL); + snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16, + "Input A Capture Volume", NULL); + snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16, + "Input B Capture Volume", NULL); return 0; } -- cgit v1.2.3 From 285de9c08b07af8f2c1ab263705952672b3cf46c Mon Sep 17 00:00:00 2001 From: Felix Homann Date: Mon, 23 Apr 2012 20:24:24 +0200 Subject: ALSA: usb-audio: Rename and export mixer_vol_tlv Rename mixer_vol_tlv to snd_usb_mixer_vol_tlv and export it to make it reuseable in mixer_quirks.c. Signed-off-by: Felix Homann Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 4 ++-- sound/usb/mixer.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index c374c7242ab2..bb56f5353fe0 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -486,7 +486,7 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, /* * TLV callback for mixer volume controls */ -static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, +int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv) { struct usb_mixer_elem_info *cval = kcontrol->private_data; @@ -1158,7 +1158,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, if (control == UAC_FU_VOLUME) { check_mapped_dB(map, cval); if (cval->dBmin < cval->dBmax || !cval->initialized) { - kctl->tlv.c = mixer_vol_tlv; + kctl->tlv.c = snd_usb_mixer_vol_tlv; kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 81b2d8a32fb0..a7f3d45a8acf 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -68,4 +68,7 @@ int snd_usb_mixer_activate(struct usb_mixer_interface *mixer); int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl); +int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv); + #endif /* __USBMIXER_H */ -- cgit v1.2.3 From 25ee7ef8faa24024197056ba49a502597b02c0c5 Mon Sep 17 00:00:00 2001 From: Felix Homann Date: Mon, 23 Apr 2012 20:24:25 +0200 Subject: ALSA: usb-audio: Add TLV to M-Audio Fast Track Ultra controls This adds db gain information to M-Audio Fast Track Ultra (8R) devices. Signed-off-by: Felix Homann Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 1c3da65f1af1..045a0d9ff8a2 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -594,7 +594,7 @@ static int snd_maudio_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) in + 1, out + 1); err = snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, name, - NULL); + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -605,7 +605,7 @@ static int snd_maudio_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) in - 7, out + 1); err = snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, name, - NULL); + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } -- cgit v1.2.3 From cfe8f97c8243cec6614524779424f2de9c335c5c Mon Sep 17 00:00:00 2001 From: Felix Homann Date: Mon, 23 Apr 2012 20:24:26 +0200 Subject: ALSA: usb-audio: Rename Fast Track Ultra mixer quirk functions This is in preparation for more FTU controls to come. Should help keeping names a bit shorter. Signed-off-by: Felix Homann Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 045a0d9ff8a2..6671e64a7630 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -575,8 +575,8 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, /* M-Audio FastTrack Ultra quirks */ -/* Create a volume control for FTU devices*/ -static int snd_maudio_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) +/* Create volume controls for FTU devices*/ +static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) { char name[64]; unsigned int control, cmask; @@ -614,11 +614,11 @@ static int snd_maudio_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) return 0; } -static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) +static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) { int err; - err = snd_maudio_ftu_create_volume_ctls(mixer); + err = snd_ftu_create_volume_ctls(mixer); if (err < 0) return err; @@ -703,7 +703,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ - err = snd_maudio_ftu_create_mixer(mixer); + err = snd_ftu_create_mixer(mixer); break; case USB_ID(0x0b05, 0x1739): -- cgit v1.2.3 From d34bf1485192b9cedb7b0dec8988324099b6a0b1 Mon Sep 17 00:00:00 2001 From: Felix Homann Date: Mon, 23 Apr 2012 20:24:27 +0200 Subject: ALSA: usb-audio: M-Audio Fast Track Ultra: Add effect controls This adds controls for the effects section on the FTU devices. Some of these controls need volume quirks. They are added to mixer.c. [fixed missing break by tiwai] Signed-off-by: Felix Homann Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 20 ++++ sound/usb/mixer_quirks.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index bb56f5353fe0..3d70245ab442 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -770,6 +770,26 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { switch (cval->mixer->chip->usb_id) { + case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ + case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ + if (strcmp(kctl->id.name, "Effect Duration") == 0) { + snd_printk(KERN_INFO + "usb-audio: set quirk for FTU Effect Duration\n"); + cval->min = 0x0000; + cval->max = 0x7f00; + cval->res = 0x0100; + break; + } + if (strcmp(kctl->id.name, "Effect Volume") == 0 || + strcmp(kctl->id.name, "Effect Feedback Volume") == 0) { + snd_printk(KERN_INFO + "usb-audio: set quirks for FTU Effect Feedback/Volume\n"); + cval->min = 0x00; + cval->max = 0x7f; + break; + } + break; + case USB_ID(0x0471, 0x0101): case USB_ID(0x0471, 0x0104): case USB_ID(0x0471, 0x0105): diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 6671e64a7630..b44df6e5109b 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -574,6 +574,186 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, } /* M-Audio FastTrack Ultra quirks */ +/* FTU Effect switch */ +struct snd_ftu_eff_switch_priv_val { + struct usb_mixer_interface *mixer; + int cached_value; + int is_cached; +}; + +static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char *texts[8] = {"Room 1", + "Room 2", + "Room 3", + "Hall 1", + "Hall 2", + "Plate", + "Delay", + "Echo" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip; + struct usb_mixer_interface *mixer; + struct snd_ftu_eff_switch_priv_val *pval; + int err; + unsigned char value[2]; + + const int id = 6; + const int validx = 1; + const int val_len = 2; + + value[0] = 0x00; + value[1] = 0x00; + + pval = (struct snd_ftu_eff_switch_priv_val *) + kctl->private_value; + + if (pval->is_cached) { + ucontrol->value.enumerated.item[0] = pval->cached_value; + return 0; + } + + mixer = (struct usb_mixer_interface *) pval->mixer; + if (snd_BUG_ON(!mixer)) + return -EINVAL; + + chip = (struct snd_usb_audio *) mixer->chip; + if (snd_BUG_ON(!chip)) + return -EINVAL; + + + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), + value, val_len); + if (err < 0) + return err; + + ucontrol->value.enumerated.item[0] = value[0]; + pval->cached_value = value[0]; + pval->is_cached = 1; + + return 0; +} + +static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip; + struct snd_ftu_eff_switch_priv_val *pval; + + struct usb_mixer_interface *mixer; + int changed, cur_val, err, new_val; + unsigned char value[2]; + + + const int id = 6; + const int validx = 1; + const int val_len = 2; + + changed = 0; + + pval = (struct snd_ftu_eff_switch_priv_val *) + kctl->private_value; + cur_val = pval->cached_value; + new_val = ucontrol->value.enumerated.item[0]; + + mixer = (struct usb_mixer_interface *) pval->mixer; + if (snd_BUG_ON(!mixer)) + return -EINVAL; + + chip = (struct snd_usb_audio *) mixer->chip; + if (snd_BUG_ON(!chip)) + return -EINVAL; + + if (!pval->is_cached) { + /* Read current value */ + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), + value, val_len); + if (err < 0) + return err; + + cur_val = value[0]; + pval->cached_value = cur_val; + pval->is_cached = 1; + } + /* update value if needed */ + if (cur_val != new_val) { + value[0] = new_val; + value[1] = 0; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), + value, val_len); + if (err < 0) + return err; + + pval->cached_value = new_val; + pval->is_cached = 1; + changed = 1; + } + + return changed; +} + +static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer) +{ + static struct snd_kcontrol_new template = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Effect Program Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ftu_eff_switch_info, + .get = snd_ftu_eff_switch_get, + .put = snd_ftu_eff_switch_put + }; + + int err; + struct snd_kcontrol *kctl; + struct snd_ftu_eff_switch_priv_val *pval; + + pval = kzalloc(sizeof(*pval), GFP_KERNEL); + if (!pval) + return -ENOMEM; + + pval->cached_value = 0; + pval->is_cached = 0; + pval->mixer = mixer; + + template.private_value = (unsigned long) pval; + kctl = snd_ctl_new1(&template, mixer->chip); + if (!kctl) { + kfree(pval); + return -ENOMEM; + } + + err = snd_ctl_add(mixer->chip->card, kctl); + if (err < 0) + return err; + + return 0; +} /* Create volume controls for FTU devices*/ static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) @@ -614,6 +794,102 @@ static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) return 0; } +/* This control needs a volume quirk, see mixer.c */ +static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Volume"; + const unsigned int id = 6; + const int val_type = USB_MIXER_U8; + const unsigned int control = 2; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Duration"; + const unsigned int id = 6; + const int val_type = USB_MIXER_S16; + const unsigned int control = 3; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Feedback Volume"; + const unsigned int id = 6; + const int val_type = USB_MIXER_U8; + const unsigned int control = 4; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, NULL); +} + +static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer) +{ + unsigned int cmask; + int err, ch; + char name[48]; + + const unsigned int id = 7; + const int val_type = USB_MIXER_S16; + const unsigned int control = 7; + + for (ch = 0; ch < 4; ++ch) { + cmask = 1 << ch; + snprintf(name, sizeof(name), + "Effect Return %d Volume", ch + 1); + err = snd_create_std_mono_ctl(mixer, id, control, + cmask, val_type, name, + snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + + return 0; +} + +static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer) +{ + unsigned int cmask; + int err, ch; + char name[48]; + + const unsigned int id = 5; + const int val_type = USB_MIXER_S16; + const unsigned int control = 9; + + for (ch = 0; ch < 8; ++ch) { + cmask = 1 << ch; + snprintf(name, sizeof(name), + "Effect Send AIn%d Volume", ch + 1); + err = snd_create_std_mono_ctl(mixer, id, control, cmask, + val_type, name, + snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + for (ch = 8; ch < 16; ++ch) { + cmask = 1 << ch; + snprintf(name, sizeof(name), + "Effect Send DIn%d Volume", ch - 7); + err = snd_create_std_mono_ctl(mixer, id, control, cmask, + val_type, name, + snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + return 0; +} + static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) { int err; @@ -622,6 +898,29 @@ static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) if (err < 0) return err; + err = snd_ftu_create_effect_switch(mixer); + if (err < 0) + return err; + err = snd_ftu_create_effect_volume_ctl(mixer); + if (err < 0) + return err; + + err = snd_ftu_create_effect_duration_ctl(mixer); + if (err < 0) + return err; + + err = snd_ftu_create_effect_feedback_ctl(mixer); + if (err < 0) + return err; + + err = snd_ftu_create_effect_return_ctls(mixer); + if (err < 0) + return err; + + err = snd_ftu_create_effect_send_ctls(mixer); + if (err < 0) + return err; + return 0; } -- cgit v1.2.3 From baba2e0d2bf5d68e4ec0d8a4d700724ef866249f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Apr 2012 08:07:38 +0200 Subject: ALSA: usb-audio: Add missing error checks in snd_ebox44_create_mixer() Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index b44df6e5109b..12125ca7167f 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -935,26 +935,46 @@ static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer) { - snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN, + int err; + + err = snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Headphone Playback Switch", NULL); - snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16, + if (err < 0) + return err; + err = snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16, "Headphone A Mix Playback Volume", NULL); - snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16, + if (err < 0) + return err; + err = snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16, "Headphone B Mix Playback Volume", NULL); + if (err < 0) + return err; - snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN, + err = snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Output Playback Switch", NULL); - snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16, + if (err < 0) + return err; + err = snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16, "Output A Playback Volume", NULL); - snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16, + if (err < 0) + return err; + err = snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16, "Output B Playback Volume", NULL); + if (err < 0) + return err; - snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN, + err = snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN, "Input Capture Switch", NULL); - snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16, + if (err < 0) + return err; + err = snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16, "Input A Capture Volume", NULL); - snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16, + if (err < 0) + return err; + err = snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16, "Input B Capture Volume", NULL); + if (err < 0) + return err; return 0; } -- cgit v1.2.3 From 68853fa30cdb6a9a92f7ab46c34aedb24b2f9d56 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 24 Apr 2012 08:10:10 +0200 Subject: ALSA: usb-audio: sound/usb/endpoint.c: suppress warning sound/usb/endpoint.c: In function 'queue_pending_output_urbs': sound/usb/endpoint.c:298: warning: 'packet' may be used uninitialized in this function Cc: Daniel Mack Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 12e5a951a143..83a36d0d2a2d 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -295,7 +295,7 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) while (test_bit(EP_FLAG_RUNNING, &ep->flags)) { unsigned long flags; - struct snd_usb_packet_info *packet; + struct snd_usb_packet_info *uninitialized_var(packet); struct snd_urb_ctx *ctx = NULL; struct urb *urb; int err, i; -- cgit v1.2.3 From 07a5e9d4fd0218e9243875c87f85287a2a3b38c0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 24 Apr 2012 19:31:24 +0200 Subject: ALSA: snd-usb: fix some typos in endpoint.c documentation Also be more specific about some details while at it. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 58 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 25 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 83a36d0d2a2d..e6906901debb 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -40,25 +40,26 @@ * USB endpoint and its streaming. * * There are functions to activate and deactivate the streaming URBs and - * optinal callbacks to let the pcm logic handle the actual content of the + * optional callbacks to let the pcm logic handle the actual content of the * packets for playback and record. Thus, the bus streaming and the audio * handlers are fully decoupled. * - * There are two different types of endpoints in for audio applications. + * There are two different types of endpoints in audio applications. * * SND_USB_ENDPOINT_TYPE_DATA handles full audio data payload for both * inbound and outbound traffic. * - * SND_USB_ENDPOINT_TYPE_SYNC are for inbound traffic only and expect the - * payload to carry Q16.16 formatted sync information (3 or 4 bytes). + * SND_USB_ENDPOINT_TYPE_SYNC endpoints are for inbound traffic only and + * expect the payload to carry Q10.14 / Q16.16 formatted sync information + * (3 or 4 bytes). * - * Each endpoint has to be configured (by calling - * snd_usb_endpoint_set_params()) before it can be used. + * Each endpoint has to be configured prior to being used by calling + * snd_usb_endpoint_set_params(). * * The model incorporates a reference counting, so that multiple users * can call snd_usb_endpoint_start() and snd_usb_endpoint_stop(), and * only the first user will effectively start the URBs, and only the last - * one will tear them down again. + * one to stop it will tear the URBs down again. */ /* @@ -120,7 +121,7 @@ static const char *usb_error_string(int err) /** * snd_usb_endpoint_implicit_feedback_sink: Report endpoint usage type * - * @ep: The endpoint + * @ep: The snd_usb_endpoint * * Determine whether an endpoint is driven by an implicit feedback * data endpoint source. @@ -278,17 +279,18 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, } /* - * Send output urbs that have been prepared previously. Urbs are dequeued + * Send output urbs that have been prepared previously. URBs are dequeued * from ep->ready_playback_urbs and in case there there aren't any available * or there are no packets that have been prepared, this function does * nothing. * - * The reason why the functionality of sending and preparing urbs is separated - * is that host controllers don't guarantee an ordering in returing inbound - * and outbound packets to their submitters. + * The reason why the functionality of sending and preparing URBs is separated + * is that host controllers don't guarantee the order in which they return + * inbound and outbound packets to their submitters. * * This function is only used for implicit feedback endpoints. For endpoints - * driven by sync endpoints, urbs are submitted from their completion handler. + * driven by dedicated sync endpoints, URBs are immediately re-submitted + * from their completion handler. */ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) { @@ -391,7 +393,7 @@ exit_clear: } /** - * snd_usb_add_endpoint: Add an endpoint to an audio chip + * snd_usb_add_endpoint: Add an endpoint to an USB audio chip * * @chip: The chip * @alts: The USB host interface @@ -745,11 +747,14 @@ out_of_memory: } /** - * snd_usb_endpoint_set_params: configure an snd_endpoint + * snd_usb_endpoint_set_params: configure an snd_usb_endpoint * - * @ep: the endpoint to configure + * @ep: the snd_usb_endpoint to configure + * @hw_params: the hardware parameters + * @fmt: the USB audio format information + * @sync_ep: the sync endpoint to use, if any * - * Determine the number of of URBs to be used on this endpoint. + * Determine the number of URBs to be used on this endpoint. * An endpoint must be configured before it can be started. * An endpoint that is already running can not be reconfigured. */ @@ -807,7 +812,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * @ep: the endpoint to start * * A call to this function will increment the use count of the endpoint. - * In case this not already running, the URBs for this endpoint will be + * In case it is not already running, the URBs for this endpoint will be * submitted. Otherwise, this function does nothing. * * Must be balanced to calls of snd_usb_endpoint_stop(). @@ -840,7 +845,8 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) /* * If this endpoint has a data endpoint as implicit feedback source, * don't start the urbs here. Instead, mark them all as available, - * wait for the record urbs to arrive and queue from that context. + * wait for the record urbs to return and queue the playback urbs + * from that context. */ set_bit(EP_FLAG_RUNNING, &ep->flags); @@ -892,7 +898,7 @@ __error: * * A call to this function will decrement the use count of the endpoint. * In case the last user has requested the endpoint stop, the URBs will - * actually deactivated. + * actually be deactivated. * * Must be balanced to calls of snd_usb_endpoint_start(). */ @@ -994,7 +1000,8 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) return -EBUSY; } -/** snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint +/** + * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint * * @ep: the list header of the endpoint to free * @@ -1032,8 +1039,9 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, /* * In case the endpoint is operating in implicit feedback mode, prepare - * and a new outbound URB that has the same layout as the received - * packet and add it to the list of pending urbs. + * a new outbound URB that has the same layout as the received packet + * and add it to the list of pending urbs. queue_pending_output_urbs() + * will take care of them later. */ if (snd_usb_endpoint_implict_feedback_sink(ep) && ep->use_count != 0) { @@ -1063,8 +1071,8 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, /* * Iterate through the inbound packet and prepare the lengths * for the output packet. The OUT packet we are about to send - * will have the same amount of payload than the IN packet we - * just received. + * will have the same amount of payload bytes than the IN + * packet we just received. */ out_packet->packets = in_ctx->packets; -- cgit v1.2.3 From 7df4a691fb6645405c9d3dad8d27f8e5e3451e00 Mon Sep 17 00:00:00 2001 From: Mark Hills Date: Fri, 11 May 2012 18:31:55 +0100 Subject: ALSA: usb-audio: Fix comment Explained by Takashi in > The reason is because get_min_max*() isn't called in the place you > created these controls, and get_min_max() would be called only for > integer volumes later even if uninitialized. A short cut for booleans. Signed-off-by: Mark Hills Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 12125ca7167f..41f4b6911920 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -79,16 +79,8 @@ static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, cval->control = control; cval->cmask = cmask; - /* FIXME: Do we need this? - * The following values are for compatibility with - * Ebox-44 mixer. - * But the corresponding ebox-44 function says: - * "Volume controls will override these values" - * - * These values don't have any effect at all for - * M-Audio FTUs. - * So I think, we can safely omit the range settings here. - */ + /* get_min_max() is called only for integer volumes later, + * so provide a short-cut for booleans */ cval->min = 0; cval->max = 1; cval->res = 0; -- cgit v1.2.3 From e182534d4bd3a779941f2868f35e1f66a8d36cea Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 May 2012 17:11:06 +0200 Subject: ALSA: usb-audio - Call get_min_max_*() after determining the name string get_min_max_with_quirks() must be called after the control id name string is determined, but the current code changes the id name string after calling the function. Reported-by: Christian Melki Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 3d70245ab442..4f40ba823163 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1141,9 +1141,6 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); - /* get min/max values */ - get_min_max_with_quirks(cval, 0, kctl); - switch (control) { case UAC_FU_MUTE: case UAC_FU_VOLUME: @@ -1175,17 +1172,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); - if (control == UAC_FU_VOLUME) { - check_mapped_dB(map, cval); - if (cval->dBmin < cval->dBmax || !cval->initialized) { - kctl->tlv.c = snd_usb_mixer_vol_tlv; - kctl->vd[0].access |= - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; - } - } break; - default: if (! len) strlcpy(kctl->id.name, audio_feature_info[control-1].name, @@ -1193,6 +1180,19 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, break; } + /* get min/max values */ + get_min_max_with_quirks(cval, 0, kctl); + + if (control == UAC_FU_VOLUME) { + check_mapped_dB(map, cval); + if (cval->dBmin < cval->dBmax || !cval->initialized) { + kctl->tlv.c = snd_usb_mixer_vol_tlv; + kctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } + } + range = (cval->max - cval->min) / cval->res; /* Are there devices with volume range more than 255? I use a bit more * to be sure. 384 is a resolution magic number found on Logitech -- cgit v1.2.3 From 97f8d3b6503082416898f893a442a78f8819c42a Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 21 May 2012 12:47:36 +0200 Subject: ALSA: snd-usb: fix stream info output in /proc Set some substream struct members to make the proc interface code work again. Signed-off-by: Daniel Mack Reported-by: Felix Homann Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 2d3a04d829b7..24839d932648 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -474,6 +474,11 @@ unlock: mutex_unlock(&subs->stream->chip->shutdown_mutex); } + if (ret == 0) { + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; + } + return ret; } @@ -1123,13 +1128,16 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: subs->data_endpoint->prepare_data_urb = prepare_playback_urb; subs->data_endpoint->retire_data_urb = retire_playback_urb; + subs->running = 1; return 0; case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs, 0, 0, 0); + subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: subs->data_endpoint->prepare_data_urb = NULL; subs->data_endpoint->retire_data_urb = NULL; + subs->running = 0; return 0; } @@ -1148,15 +1156,19 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c return err; subs->data_endpoint->retire_data_urb = retire_capture_urb; + subs->running = 1; return 0; case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs, 0, 0, 0); + subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: subs->data_endpoint->retire_data_urb = NULL; + subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: subs->data_endpoint->retire_data_urb = retire_capture_urb; + subs->running = 1; return 0; } -- cgit v1.2.3