RFC v2 for snd-usb endpoint logic rework
cover RFC v2 for snd-usb endpoint logic rework
This is v2 of the patch bulk I posted some weeks ago, which incoporates the points that were discussed. Thanks to everyone who commented and helped with sugesstions.
This version work really well here, but as last time, it needs a lot more testing. What's still missing are some cleanups and more documentation, I'll do that once the acutal content is agreed upon.
Patches should apply cleanly to the latest mainline git + Takashi's sound tree.
Thanks for any feedback for this next round,
Daniel
Daniel Mack (5):
ALSA: snd-usb: add snd_usb_audio-wide mutex
ALSA: snd-usb: implement new endpoint streaming model
ALSA: snd-usb: switch over to new endpoint streaming logic
ALSA: snd-usb: remove old streaming logic
ALSA: snd-usb: add support for implicit feedback
sound/usb/card.c | 8 +- sound/usb/card.h | 65 +++ sound/usb/endpoint.c | 1434 +++++++++++++++++++++++++------------------------- sound/usb/endpoint.h | 31 +- sound/usb/pcm.c | 444 +++++++++++++--- sound/usb/stream.c | 31 +- sound/usb/usbaudio.h | 2 + 7 files changed, 1214 insertions(+), 801 deletions(-)
Signature · 1 line
1.7.5.4
PATCH 1/5 ALSA: snd-usb: add snd_usb_audio-wide mutex
--- sound/usb/card.c | 2 ++ sound/usb/usbaudio.h | 1 + 2 files changed, 3 insertions(+), 0 deletions(-)
sound/usb/card.c +2 -0
@@ -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;
sound/usb/usbaudio.h +1 -0
@@ -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;
Signature · 1 line
1.7.5.4
PATCH 2/5 ALSA: snd-usb: implement new endpoint streaming model
In reply to Daniel MackIn order to split changes properly, this patch only adds the new implementation but leaves the old one around, so the rest of the driver does not behave differently. The switch to actually use the new thing is submitted separately.
--- sound/usb/card.h | 60 ++++ sound/usb/endpoint.c | 911 +++++++++++++++++++++++++++++++++++++++++++++++++- sound/usb/endpoint.h | 25 ++ sound/usb/usbaudio.h | 1 + 4 files changed, 986 insertions(+), 11 deletions(-)
sound/usb/card.h +60 -0
@@ -29,13 +29,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 {
@@ -45,6 +49,62 @@ 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_* */ +int activated; + +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; +struct tasklet_struct outbound_tasklet; + +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;
sound/usb/endpoint.c +900 -11
977 lines not on this page · raw message
sound/usb/endpoint.h +25 -0
@@ -18,4 +18,29 @@ 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 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 */
sound/usb/usbaudio.h +1 -0
@@ -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 */
Signature · 1 line
1.7.5.4
PATCH 3/5 ALSA: snd-usb: switch over to new endpoint streaming logic
In reply to Daniel Mack--- sound/usb/card.c | 6 +- sound/usb/card.h | 5 + sound/usb/endpoint.c | 40 ----- sound/usb/endpoint.h | 3 - sound/usb/pcm.c | 423 +++++++++++++++++++++++++++++++++++++++++--------- sound/usb/stream.c | 31 ++++- 6 files changed, 389 insertions(+), 119 deletions(-)
sound/usb/card.c +5 -1
@@ -131,7 +131,6 @@ 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 ; } }
@@ -350,6 +349,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 +567,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);
sound/usb/card.h +5 -0
@@ -146,6 +146,11 @@ 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 int data_endpoint_started:1; + unsigned int sync_endpoint_started:1;u64 formats;/* format bitmasks (all or'ed) */ unsigned int num_formats;/* number of supported audio formats (list) */
sound/usb/endpoint.c +0 -40
@@ -908,46 +908,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) {
sound/usb/endpoint.h +0 -3
@@ -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
sound/usb/pcm.c +351 -72
500 lines not on this page · raw message
sound/usb/stream.c +28 -3
@@ -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 )
Signature · 1 line
1.7.5.4
PATCH 4/5 ALSA: snd-usb: remove old streaming logic
In reply to Daniel Mack--- sound/usb/endpoint.c | 917 +++----------------------------------------------- sound/usb/endpoint.h | 15 - 2 files changed, 41 insertions(+), 891 deletions(-)
sound/usb/endpoint.c +41 -876
@@ -49,882 +49,6 @@ 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) { -snd_printd (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); -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 ; -} - -static const char *usb_error_string (int err) -{ -switch (err) { -case -ENODEV : -return "no device" ; -case -ENOENT : -return "endpoint not enabled" ; -case -EPIPE : -return "endpoint stalled" ; -case -ENOSPC : -return "not enough bandwidth" ; -case -ESHUTDOWN : -return "device disabled" ; -case -EHOSTUNREACH : -return "device suspended" ; -case -EINVAL : -case -EAGAIN : -case -EFBIG : -case -EMSGSIZE : -return "internal error" ; -default : -return "unknown error" ; - } -} - -/* -* 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 &&
@@ -1244,6 +368,47 @@ __exit_unlock:return ep; } +static const char *usb_error_string (int err) +{ +switch (err) { +case -ENODEV : +return "no device" ; +case -ENOENT : +return "endpoint not enabled" ; +case -EPIPE : +return "endpoint stalled" ; +case -ENOSPC : +return "not enough bandwidth" ; +case -ESHUTDOWN : +return "device disabled" ; +case -EHOSTUNREACH : +return "device suspended" ; +case -EINVAL : +case -EAGAIN : +case -EFBIG : +case -EMSGSIZE : +return "internal error" ; +default : +return "unknown error" ; + } +} + +/* +* release a urb data +*/ +static void release_urb_ctx (struct snd_urb_ctx *u) +{ +if (!u->urb) +return ; + +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 ; +} +/* * wait until all urbs are processed. */
sound/usb/endpoint.h +0 -15
@@ -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
Signature · 1 line
1.7.5.4
PATCH 5/5 ALSA: snd-usb: add support for implicit feedback
In reply to Daniel Mack--- sound/usb/pcm.c | 37 ++++++++++++++++++++++++++----------- 1 files changed, 26 insertions(+), 11 deletions(-)
sound/usb/pcm.c +26 -11
@@ -298,7 +298,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))
@@ -323,25 +323,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 ;
@@ -349,16 +358,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 ;
Signature · 1 line
1.7.5.4
Re: [PATCH 5/5] ALSA: snd-usb: add support for implicit feedback
Quoted 32 lines · Daniel Mack
--- sound/usb/pcm.c | 37 ++++++++++++++++++++++++++----------- 1 files changed, 26 insertions(+), 11 deletions(-)sound/usb/pcm.c +6
-7@@ -298,7 +298,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))@@ -323,25 +323,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) {
sound/usb/pcm.c static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) · line 340
Quoted 10 lines
sound/usb/pcm.c +5
-0+ 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; }
Eeks, something went wrong here in my patch rebasing/squashing. It shouldn't matter for the review though - I'll fix that up next time.
Quoted 10 lines · Daniel Mack
This is v2 of the patch bulk I posted some weeks ago, which incoporates the points that were discussed. Thanks to everyone who commented and helped with sugesstions.
This version work really well here, but as last time, it needs a lot more testing. What's still missing are some cleanups and more documentation, I'll do that once the acutal content is agreed upon.
Patches should apply cleanly to the latest mainline git + Takashi's sound tree.
The gist has also been updated.
A message that is not in the archive (sent off-list or not yet received): CAE-xCyPZ5T50CFvcV59rop7+Lkkzp0m+6L+SZv2r47EJw6AiTQ@mail.gmail.com
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi Daniel,
Compiled and running!
So far so good! I haven't pushed the jack parameters more than I would usually though... I'm still at 16ms latency (Frame/period: 256 ; Sample Rate: 48000 ; Periods Buffer: 3).
That was the minimum I could go with the previous driver, so I'll try to push it a bit tomorrow and see how it goes.
Is there any particular testing you would like to see done?
Another thing, in your previous post, you asked to try the driver with other sound card.
I have an Edirol UA-25Ex that never worked on this machine (I have the dredded EHCI USB port, it's the reason why I bought the FTU in the first place).
The error I used to get while starting Jack in Full Duplex with this sound card was
"Cannot submit datapipe for urb 0, error -28: not enough bandwidth"
But, "apparently".......now it works!
I tried it not later than last week with Kernel 3.0 and it still wasn't working.
But with 3.2 and your patch, (and after running the FTU on the very same USB port), I got the UA-25Ex to work in full duplex for the first time ever.
I don't know if it is due to the new version of the kernel, or your patch, or something else, but this is huge because a lot of people used to have this problem with EHCI and USB 1 sound cards.
(or may be it was simply fixed between 3.0 and 3.2 and I simply missed it!).
So the second question is: what do you think?
Any info you would like to see posted here? (dmesg trace....anything else?).
Anyway, thanks a lot for everything :)
Aurélien
Quoted 49 lines · Daniel Mack
Hi Aurélien,
Quoted 7 lines · Aurélien Leblond
I finally got some time to test this driver!
I'm not sure I understand where to get Takashi's sound tree.I tried to apply your patch against a clone of
git://git.alsa-project.org/alsa-kmirror.gitalsa-kmirror, but then GitCompile failed.Quoted 5 lines
I then tried to use
git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git, but I'm not even sure what I'm pulling to be honest...Would you mind just guiding me on how to prepare the source code?
No problem :)
Clone Linus' tree:
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Make a local branch and pull in Takashi's tree:
cd linux
git checkout -b alsa
git pullgit://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
masterMake another branch on top of that
git checkout -b usbaudio
Then apply my patch(es). For the next round, just kill the local usbaudio branch again and start over:
git reset --hard
git checkout alsa
git branch -D usbaudio
git checkout -b usbaudioThen apply the next round :)
Quoted 1 line
Thanks in advance, and thanks for all the work you have already done!
No problem. I'm planning to continue work on this these days. It's just that I'm constantly distracted by other things :-/
Daniel
Quoted 8 lines
Another thing, in your previous post, you asked to try the driver with other sound card.
I have an Edirol UA-25Ex that never worked on this machine (I have the dredded EHCI USB port, it's the reason why I bought the FTU in the first place).
The error I used to get while starting Jack in Full Duplex with this sound card was
"Cannot submit datapipe for urb 0, error -28: not enough bandwidth"
Sorry about that, I think I answered my own question
http://lists.linuxaudio.org/pipermail/linux-audio-user/2011-November/081814.html
Aurélien
Quoted 10 lines · Daniel Mack
This is v2 of the patch bulk I posted some weeks ago, which incoporates the points that were discussed. Thanks to everyone who commented and helped with sugesstions.
This version work really well here, but as last time, it needs a lot more testing. What's still missing are some cleanups and more documentation, I'll do that once the acutal content is agreed upon.
Patches should apply cleanly to the latest mainline git + Takashi's sound tree.
Did anyone find some time to test this, also with 'regular' USB devices?
Daniel
Quoted 1 line · Daniel Mack
Did anyone find some time to test this, also with 'regular' USB devices?
With an Edirol UA-1A (stereo, 16 bits, 44.1 kHz, adaptive), the first attempt to start playback fails (no sound), but the program can be exited:
5:1:1: add audio endpoint 0x1
5:2:1: add audio endpoint 0x82
usbcore: registered new interface driver snd-usb-audio Creating new playback data endpoint #1 snd_usb_add_endpoint() calling usb_set_interface() snd_usb_hw_params(): format changed stop_endpoints(ffff880212680018,0,1)
-- data ffff880213470000
-- sync (null)is_playback? 1 impf 0, total_packs 24 urb_packs 8
ep ffff880213470000:: buffer_size 1536 maxsize 192 packets 8 ep ffff880213470000:: buffer_size 1536 maxsize 192 packets 8 ep ffff880213470000:: buffer_size 1536 maxsize 192 packets 8
Setting params for ep #1 (type 0, 3 urbs) -> 0 Returning from snd_usb_hw_params() with 0 activate_endpoints(ffff880212680018) snd_usb_endpoint_activate() calling usb_set_interface(1,1) Starting data EP @ffff880213470000 snd_usb_endpoint_start(): pipein? 0, impl? 0 Submmiting urb 0/3 (in? 0 buf ffff8800df822000 len 1408) ret (0) Submmiting urb 1/3 (in? 0 buf ffff8800df823000 len 1412) ret (0) Submmiting urb 2/3 (in? 0 buf ffff8800df824000 len 1412) ret (0)
=================================
[ INFO: inconsistent lock state ]
3.2.0-rc2+ #319
---------------------------------
inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage.
mplayer/1605 [HC1[1]:SC0[0]:HE0:SE1] takes:
(&(&ep->lock)->rlock){?.+...}, at: [<ffffffffa0088558>] prepare_outbound_urb_sizes+0x3f/0x9b [snd_usb_audio]
{HARDIRQ-ON-W} state was registered at:
[<ffffffff810623e4>] __lock_acquire+0x6eb/0x8f7 [<ffffffff81062a19>] lock_acquire+0x57/0x6d [<ffffffff81471905>] _raw_spin_lock+0x3b/0x4a [<ffffffffa0088c38>] snd_usb_endpoint_start+0x8d/0x3c4 [snd_usb_audio] [<ffffffffa008f509>] start_endpoints+0x54/0xb9 [snd_usb_audio] [<ffffffffa008f6ab>] snd_usb_pcm_prepare+0x13d/0x141 [snd_usb_audio] [<ffffffffa0069f84>] snd_pcm_do_prepare+0x16/0x28 [snd_pcm] [<ffffffffa0069bb6>] snd_pcm_action_single+0x2e/0x65 [snd_pcm] [<ffffffffa006ba88>] snd_pcm_action_nonatomic+0x4a/0x63 [snd_pcm] [<ffffffffa006e4cc>] snd_pcm_common_ioctl1+0x419/0xaf3 [snd_pcm] [<ffffffffa006f0a9>] snd_pcm_playback_ioctl1+0x25c/0x279 [snd_pcm] [<ffffffffa006f727>] snd_pcm_playback_ioctl+0x2a/0x2e [snd_pcm] [<ffffffff810d3c92>] do_vfs_ioctl+0x47f/0x4ce [<ffffffff810d3d23>] sys_ioctl+0x42/0x65 [<ffffffff81472abb>] system_call_fastpath+0x16/0x1b irq event stamp: 109796 hardirqs last enabled at (109795): [<ffffffff8147238a>] retint_swapgs+0xe/0x13 hardirqs last disabled at (109796): [<ffffffff814722eb>] common_interrupt+0x6b/0x70 softirqs last enabled at (109792): [<ffffffff8103c46e>] __do_softirq+0x135/0x14b softirqs last disabled at (109787): [<ffffffff81473fec>] call_softirq+0x1c/0x30
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock(&(&ep->lock)->rlock);
<Interrupt>
lock(&(&ep->lock)->rlock);*** DEADLOCK ***
no locks held by mplayer/1605.
stack backtrace: Pid: 1605, comm: mplayer Not tainted 3.2.0-rc2+ #319 Call Trace: <IRQ> [<ffffffff810375d4>] ? console_unlock+0x1b2/0x200 [<ffffffff8105f568>] print_usage_bug+0x296/0x2a7 [<ffffffff8100c74d>] ? save_stack_trace+0x2a/0x47 [<ffffffff810603bd>] ? print_irq_inversion_bug+0x1d6/0x1d6 [<ffffffff8105f86a>] mark_lock+0x2f1/0x528 [<ffffffff8106236b>] __lock_acquire+0x672/0x8f7 [<ffffffff81062a19>] lock_acquire+0x57/0x6d [<ffffffffa0088558>] ? prepare_outbound_urb_sizes+0x3f/0x9b [snd_usb_audio] [<ffffffff81471a08>] _raw_spin_lock_irqsave+0x46/0x58 [<ffffffffa0088558>] ? prepare_outbound_urb_sizes+0x3f/0x9b [snd_usb_audio] [<ffffffffa0088558>] prepare_outbound_urb_sizes+0x3f/0x9b [snd_usb_audio] [<ffffffffa0089162>] snd_complete_urb+0x1f3/0x209 [snd_usb_audio] [<ffffffff8137b1f4>] usb_hcd_giveback_urb+0x8c/0xc0 [<ffffffffa0000d02>] ehci_urb_done+0xf7/0x10c [ehci_hcd] [<ffffffffa0003b58>] ehci_work+0x7e3/0x9c0 [ehci_hcd] [<ffffffffa0005abf>] ehci_irq+0x317/0x348 [ehci_hcd] [<ffffffff8105fe62>] ? trace_hardirqs_on+0xd/0xf [<ffffffff81472043>] ? _raw_spin_unlock_irq+0x2b/0x40 [<ffffffff81041210>] ? run_timer_softirq+0x288/0x29a [<ffffffff8106257f>] ? __lock_acquire+0x886/0x8f7 [<ffffffff810544c5>] ? sched_clock_local+0x1c/0x82 [<ffffffff8137a746>] usb_hcd_irq+0x2f/0x7a [<ffffffff8107bf0d>] handle_irq_event_percpu+0x29/0x124 [<ffffffff8107c044>] handle_irq_event+0x3c/0x5c [<ffffffff8107e6ad>] handle_edge_irq+0xc7/0xec [<ffffffff81003633>] handle_irq+0x83/0x8c [<ffffffff81002e70>] do_IRQ+0x48/0xaf [<ffffffff814722f0>] common_interrupt+0x70/0x70 <EOI> [<ffffffff8147238a>] ? retint_swapgs+0xe/0x13
cannot submit urb (err = -27)
cannot submit urb (err = -27)
cannot submit urb (err = -27) snd_usb_substream_playback_trigger(): cmd 1
START!snd_usb_substream_playback_trigger(): cmd 0 stop_endpoints(ffff880212680018,0,0) stop_endpoints(ffff880212680018,0,1) stop_endpoints(ffff880212680018,0,1) stop_endpoints(ffff880212680018,1,1) deactivate_endpoints(ffff880212680018) snd_usb_endpoint_deactivate() calling usb_set_interface(1,0)
The following attempt succeeds at playing, but when I seek in the file (stop/prepare/start without hw_params), sound becomes somewhat corrupted (wrong packet sizes I'd guess), and the computer hangs when I attempt to exit the program:
Re-using EP 1 in iface 1,1 @ffff880213470000 snd_usb_hw_params(): format changed stop_endpoints(ffff880212680018,0,1)
-- data ffff880213470000
-- sync (null)is_playback? 1 impf 0, total_packs 24 urb_packs 8
ep ffff880213470000:: buffer_size 1536 maxsize 192 packets 8 ep ffff880213470000:: buffer_size 1536 maxsize 192 packets 8 ep ffff880213470000:: buffer_size 1536 maxsize 192 packets 8
Setting params for ep #1 (type 0, 3 urbs) -> 0 Returning from snd_usb_hw_params() with 0 activate_endpoints(ffff880212680018) snd_usb_endpoint_activate() calling usb_set_interface(1,1) Starting data EP @ffff880213470000 snd_usb_endpoint_start(): pipein? 0, impl? 0 Submmiting urb 0/3 (in? 0 buf ffff8800df85d000 len 1408) ret (0) Submmiting urb 1/3 (in? 0 buf ffff8800df85e000 len 1412) ret (0) Submmiting urb 2/3 (in? 0 buf ffff8800df85f000 len 1412) ret (0) snd_usb_substream_playback_trigger(): cmd 1
START!
[...]
INFO: rcu_sched detected stalls on CPUs/tasks: { 4 5} (detected by 2, t=15002 jiffies)
Pid: 0, comm: kworker/0:1 Not tainted 3.2.0-rc2+ #319
Call Trace:
<IRQ> [<ffffffff8108129d>] __rcu_pending+0x228/0x34e
[<ffffffff8105c293>] ? tick_nohz_handler+0xcc/0xcc
[<ffffffff8108147e>] rcu_check_callbacks+0xbb/0xed
[<ffffffff810415c9>] update_process_times+0x3c/0x73
[<ffffffff8105c300>] tick_sched_timer+0x6d/0x8d
[<ffffffff810526ad>] __run_hrtimer+0x75/0xd0
[<ffffffff8105294e>] hrtimer_interrupt+0xdb/0x1b6
[<ffffffff81027caf>] ? account_system_vtime+0x88/0x96
[<ffffffff810182a9>] smp_apic_timer_interrupt+0x81/0x94
[<ffffffff81473570>] apic_timer_interrupt+0x70/0x80
<EOI> [<ffffffff8105ba26>] ? tick_broadcast_oneshot_control+0x5f/0x10a
[<ffffffff8105abd9>] ? clockevents_register_device+0xbb/0x138
[<ffffffff81008cee>] ? default_idle+0x27/0x43
[<ffffffff81008cf0>] ? default_idle+0x29/0x43
[<ffffffff81008cee>] ? default_idle+0x27/0x43
[<ffffffff81008e55>] amd_e400_idle+0xcd/0xf4
[<ffffffff81001430>] cpu_idle+0x73/0xb3
[<ffffffff81469d8b>] start_secondary+0x225/0x227Regards,
Clemens
Quoted 6 lines · Clemens Ladisch
Quoted 1 line · Daniel Mack
Did anyone find some time to test this, also with 'regular' USB devices?
With an Edirol UA-1A (stereo, 16 bits, 44.1 kHz, adaptive), the first attempt to start playback fails (no sound), but the program can be exited:
[...]
Thanks for the feedback!
I'm confident I found these hickups, and I'm about to resend a new version in a few minutes. I'd appreciate if you could repeat your tests with your hardware :)
Daniel