Skip to content

Commit 00272c6

Browse files
committed
ALSA: usb-audio: Avoid unnecessary interface re-setup
The current endpoint handling assumed (more or less) a unique 1:1 relation between the endpoint and the iface/altset. The exception was the sync EP without the implicit feedback which has usually the secondary EP of the same altset. This works fine for most devices, but it turned out that some unusual devices like Pinoeer's ones have both playback and capture endpoints in the same iface/altsetting and use both for the implicit feedback mode. For handling such a case, we need to extend the endpoint management to take the shared interface into account. This patch does that: it adds a new object snd_usb_iface_ref for managing the reference counts of the each USB interface that is used by each endpoint. The interface setup is performed only once for the (sharing) endpoints, and the doubly initialization is avoided. Along with this, the resource release of endpoints and interface refcounts are put into a single function, snd_usb_endpoint_free_all() instead of looping in the caller side. Fixes: bf6313a ("ALSA: usb-audio: Refactor endpoint management") Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 5d15f1e commit 00272c6

File tree

5 files changed

+77
-15
lines changed

5 files changed

+77
-15
lines changed

sound/usb/card.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,8 @@ lookup_device_name(u32 id)
450450
static void snd_usb_audio_free(struct snd_card *card)
451451
{
452452
struct snd_usb_audio *chip = card->private_data;
453-
struct snd_usb_endpoint *ep, *n;
454453

455-
list_for_each_entry_safe(ep, n, &chip->ep_list, list)
456-
snd_usb_endpoint_free(ep);
454+
snd_usb_endpoint_free_all(chip);
457455

458456
mutex_destroy(&chip->mutex);
459457
if (!atomic_read(&chip->shutdown))
@@ -611,6 +609,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
611609
chip->usb_id = usb_id;
612610
INIT_LIST_HEAD(&chip->pcm_list);
613611
INIT_LIST_HEAD(&chip->ep_list);
612+
INIT_LIST_HEAD(&chip->iface_ref_list);
614613
INIT_LIST_HEAD(&chip->midi_list);
615614
INIT_LIST_HEAD(&chip->mixer_list);
616615

sound/usb/card.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct audioformat {
4242
};
4343

4444
struct snd_usb_substream;
45+
struct snd_usb_iface_ref;
4546
struct snd_usb_endpoint;
4647
struct snd_usb_power_domain;
4748

@@ -58,6 +59,7 @@ struct snd_urb_ctx {
5859

5960
struct snd_usb_endpoint {
6061
struct snd_usb_audio *chip;
62+
struct snd_usb_iface_ref *iface_ref;
6163

6264
int opened; /* open refcount; protect with chip->mutex */
6365
atomic_t running; /* running status */

sound/usb/endpoint.c

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
#define EP_FLAG_RUNNING 1
2525
#define EP_FLAG_STOPPING 2
2626

27+
/* interface refcounting */
28+
struct snd_usb_iface_ref {
29+
unsigned char iface;
30+
bool need_setup;
31+
int opened;
32+
struct list_head list;
33+
};
34+
2735
/*
2836
* snd_usb_endpoint is a model that abstracts everything related to an
2937
* USB endpoint and its streaming.
@@ -488,6 +496,28 @@ static void snd_complete_urb(struct urb *urb)
488496
clear_bit(ctx->index, &ep->active_mask);
489497
}
490498

499+
/*
500+
* Find or create a refcount object for the given interface
501+
*
502+
* The objects are released altogether in snd_usb_endpoint_free_all()
503+
*/
504+
static struct snd_usb_iface_ref *
505+
iface_ref_find(struct snd_usb_audio *chip, int iface)
506+
{
507+
struct snd_usb_iface_ref *ip;
508+
509+
list_for_each_entry(ip, &chip->iface_ref_list, list)
510+
if (ip->iface == iface)
511+
return ip;
512+
513+
ip = kzalloc(sizeof(*ip), GFP_KERNEL);
514+
if (!ip)
515+
return NULL;
516+
ip->iface = iface;
517+
list_add_tail(&ip->list, &chip->iface_ref_list);
518+
return ip;
519+
}
520+
491521
/*
492522
* Get the existing endpoint object corresponding EP
493523
* Returns NULL if not present.
@@ -520,8 +550,8 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, int ep_num)
520550
*
521551
* Returns zero on success or a negative error code.
522552
*
523-
* New endpoints will be added to chip->ep_list and must be freed by
524-
* calling snd_usb_endpoint_free().
553+
* New endpoints will be added to chip->ep_list and freed by
554+
* calling snd_usb_endpoint_free_all().
525555
*
526556
* For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
527557
* bNumEndpoints > 1 beforehand.
@@ -658,6 +688,12 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
658688
usb_audio_dbg(chip, "Open EP 0x%x, iface=%d:%d, idx=%d\n",
659689
ep_num, ep->iface, ep->altsetting, ep->ep_idx);
660690

691+
ep->iface_ref = iface_ref_find(chip, ep->iface);
692+
if (!ep->iface_ref) {
693+
ep = NULL;
694+
goto unlock;
695+
}
696+
661697
ep->cur_audiofmt = fp;
662698
ep->cur_channels = fp->channels;
663699
ep->cur_rate = params_rate(params);
@@ -681,6 +717,11 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
681717
ep->implicit_fb_sync);
682718

683719
} else {
720+
if (WARN_ON(!ep->iface_ref)) {
721+
ep = NULL;
722+
goto unlock;
723+
}
724+
684725
if (!endpoint_compatible(ep, fp, params)) {
685726
usb_audio_err(chip, "Incompatible EP setup for 0x%x\n",
686727
ep_num);
@@ -692,6 +733,9 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
692733
ep_num, ep->opened);
693734
}
694735

736+
if (!ep->iface_ref->opened++)
737+
ep->iface_ref->need_setup = true;
738+
695739
ep->opened++;
696740

697741
unlock:
@@ -760,12 +804,16 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
760804
mutex_lock(&chip->mutex);
761805
usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n",
762806
ep->ep_num, ep->opened);
763-
if (!--ep->opened) {
807+
808+
if (!--ep->iface_ref->opened)
764809
endpoint_set_interface(chip, ep, false);
810+
811+
if (!--ep->opened) {
765812
ep->iface = 0;
766813
ep->altsetting = 0;
767814
ep->cur_audiofmt = NULL;
768815
ep->cur_rate = 0;
816+
ep->iface_ref = NULL;
769817
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
770818
}
771819
mutex_unlock(&chip->mutex);
@@ -775,6 +823,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
775823
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep)
776824
{
777825
ep->need_setup = true;
826+
if (ep->iface_ref)
827+
ep->iface_ref->need_setup = true;
778828
}
779829

780830
/*
@@ -1195,11 +1245,13 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
11951245
int err = 0;
11961246

11971247
mutex_lock(&chip->mutex);
1248+
if (WARN_ON(!ep->iface_ref))
1249+
goto unlock;
11981250
if (!ep->need_setup)
11991251
goto unlock;
12001252

1201-
/* No need to (re-)configure the sync EP belonging to the same altset */
1202-
if (ep->ep_idx) {
1253+
/* If the interface has been already set up, just set EP parameters */
1254+
if (!ep->iface_ref->need_setup) {
12031255
err = snd_usb_endpoint_set_params(chip, ep);
12041256
if (err < 0)
12051257
goto unlock;
@@ -1242,6 +1294,8 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
12421294
goto unlock;
12431295
}
12441296

1297+
ep->iface_ref->need_setup = false;
1298+
12451299
done:
12461300
ep->need_setup = false;
12471301
err = 1;
@@ -1387,15 +1441,21 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep)
13871441
}
13881442

13891443
/**
1390-
* snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
1444+
* snd_usb_endpoint_free_all: Free the resources of an snd_usb_endpoint
1445+
* @card: The chip
13911446
*
1392-
* @ep: the endpoint to free
1393-
*
1394-
* This free all resources of the given ep.
1447+
* This free all endpoints and those resources
13951448
*/
1396-
void snd_usb_endpoint_free(struct snd_usb_endpoint *ep)
1449+
void snd_usb_endpoint_free_all(struct snd_usb_audio *chip)
13971450
{
1398-
kfree(ep);
1451+
struct snd_usb_endpoint *ep, *en;
1452+
struct snd_usb_iface_ref *ip, *in;
1453+
1454+
list_for_each_entry_safe(ep, en, &chip->ep_list, list)
1455+
kfree(ep);
1456+
1457+
list_for_each_entry_safe(ip, in, &chip->iface_ref_list, list)
1458+
kfree(ip);
13991459
}
14001460

14011461
/*

sound/usb/endpoint.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
4242
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep);
4343
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
4444
void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
45-
void snd_usb_endpoint_free(struct snd_usb_endpoint *ep);
45+
void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
4646

4747
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
4848
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,

sound/usb/usbaudio.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct snd_usb_audio {
4444

4545
struct list_head pcm_list; /* list of pcm streams */
4646
struct list_head ep_list; /* list of audio-related endpoints */
47+
struct list_head iface_ref_list; /* list of interface refcounts */
4748
int pcm_devs;
4849

4950
struct list_head midi_list; /* list of midi interfaces */

0 commit comments

Comments
 (0)