From 1abe6982f84ce92ded36d50a1173dd91a51b9bd4 Mon Sep 17 00:00:00 2001 From: Ren Jianing Date: Fri, 12 Jun 2020 11:23:39 +0800 Subject: [PATCH] BACKPORT: usb: gadget: u_audio: add uevent for set_alt and set_srate This patch add uevent to notify the application layer when it should playback or capture and setting the accuration sample rate. The event consists of three parts, namely USB_STATE, STREAM_DIRECTION and STREAM_STATE. For example: g_audio_work: sent uac uevent USB_STATE=SET_INTERFACE STREAM_DIRECTION=IN STREAM_STATE=OFF g_audio_work: sent uac uevent USB_STATE=SET_INTERFACE STREAM_DIRECTION=OUT STREAM_STATE=ON g_audio_work: sent uac uevent USB_STATE=SET_SAMPLE_RATE STREAM_DIRECTION=OUT SAMPLE_RATE=44100 Signed-off-by: Ren Jianing (backported from develop-4.19 commit 55c7a549117fff6da7d35a69e8d42d9c91cbf1e2) Change-Id: I9115a8aa57875a440df96b67bb02c3d50f237b29 Signed-off-by: Frank Wang --- drivers/usb/gadget/function/u_audio.c | 112 ++++++++++++++++++++++++++ drivers/usb/gadget/function/u_audio.h | 17 ++++ 2 files changed, 129 insertions(+) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index b29cae056f7f..0aac9700009c 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -97,6 +97,8 @@ static const struct snd_pcm_hardware uac_pcm_hardware = { .periods_min = MIN_PERIODS, }; +static struct class *audio_class; + static void u_audio_set_fback_frequency(enum usb_device_speed speed, struct usb_ep *out_ep, unsigned long long freq, @@ -520,6 +522,8 @@ int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) if (params->c_srates[i] == srate) { spin_lock_irqsave(&prm->lock, flags); prm->srate = srate; + audio_dev->usb_state[SET_SAMPLE_RATE_OUT] = true; + schedule_work(&audio_dev->work); spin_unlock_irqrestore(&prm->lock, flags); return 0; } @@ -559,6 +563,8 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) if (params->p_srates[i] == srate) { spin_lock_irqsave(&prm->lock, flags); prm->srate = srate; + audio_dev->usb_state[SET_SAMPLE_RATE_IN] = true; + schedule_work(&audio_dev->work); spin_unlock_irqrestore(&prm->lock, flags); return 0; } @@ -595,6 +601,10 @@ int u_audio_start_capture(struct g_audio *audio_dev) struct uac_params *params = &audio_dev->params; int req_len, i; + audio_dev->usb_state[SET_INTERFACE_OUT] = true; + audio_dev->stream_state[STATE_OUT] = true; + schedule_work(&audio_dev->work); + prm = &uac->c_prm; dev_dbg(dev, "start capture with rate %d\n", prm->srate); ep = audio_dev->out_ep; @@ -674,6 +684,10 @@ void u_audio_stop_capture(struct g_audio *audio_dev) if (audio_dev->in_ep_fback) free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); free_ep(&uac->c_prm, audio_dev->out_ep); + + audio_dev->usb_state[SET_INTERFACE_OUT] = true; + audio_dev->stream_state[STATE_OUT] = false; + schedule_work(&audio_dev->work); } EXPORT_SYMBOL_GPL(u_audio_stop_capture); @@ -691,6 +705,10 @@ int u_audio_start_playback(struct g_audio *audio_dev) int req_len, i; unsigned int p_pktsize; + audio_dev->usb_state[SET_INTERFACE_IN] = true; + audio_dev->stream_state[STATE_IN] = true; + schedule_work(&audio_dev->work); + prm = &uac->p_prm; dev_dbg(dev, "start playback with rate %d\n", prm->srate); ep = audio_dev->in_ep; @@ -754,6 +772,10 @@ void u_audio_stop_playback(struct g_audio *audio_dev) set_active(&uac->p_prm, false); free_ep(&uac->p_prm, audio_dev->in_ep); + + audio_dev->usb_state[SET_INTERFACE_IN] = true; + audio_dev->stream_state[STATE_IN] = false; + schedule_work(&audio_dev->work); } EXPORT_SYMBOL_GPL(u_audio_stop_playback); @@ -1152,6 +1174,60 @@ static struct snd_kcontrol_new u_audio_controls[] = { }, }; +static void g_audio_work(struct work_struct *data) +{ + struct g_audio *audio = container_of(data, struct g_audio, work); + struct usb_gadget *gadget = audio->gadget; + struct snd_uac_chip *uac = audio->uac; + struct uac_rtd_params *prm = &uac->c_prm; + struct device *dev = &gadget->dev; + char *uac_event[4] = { NULL, NULL, NULL, NULL }; + char srate_str[19]; + int i; + + for (i = 0; i < 4; i++) { + if (!audio->usb_state[i]) + continue; + + switch (i) { + case SET_INTERFACE_OUT: + uac_event[0] = "USB_STATE=SET_INTERFACE"; + uac_event[1] = "STREAM_DIRECTION=OUT"; + uac_event[2] = audio->stream_state[STATE_OUT] ? + "STREAM_STATE=ON" : "STREAM_STATE=OFF"; + break; + case SET_INTERFACE_IN: + uac_event[0] = "USB_STATE=SET_INTERFACE"; + uac_event[1] = "STREAM_DIRECTION=IN"; + uac_event[2] = audio->stream_state[STATE_IN] ? + "STREAM_STATE=ON" : "STREAM_STATE=OFF"; + break; + case SET_SAMPLE_RATE_OUT: + uac_event[0] = "USB_STATE=SET_SAMPLE_RATE"; + uac_event[1] = "STREAM_DIRECTION=OUT"; + snprintf(srate_str, sizeof(srate_str), "SAMPLE_RATE=%d", + prm->srate); + uac_event[2] = srate_str; + break; + case SET_SAMPLE_RATE_IN: + uac_event[0] = "USB_STATE=SET_SAMPLE_RATE"; + uac_event[1] = "STREAM_DIRECTION=IN"; + snprintf(srate_str, sizeof(srate_str), "SAMPLE_RATE=%d", + prm->srate); + uac_event[2] = srate_str; + break; + default: + break; + } + + audio->usb_state[i] = false; + kobject_uevent_env(&audio->device->kobj, KOBJ_CHANGE, + uac_event); + dev_dbg(dev, "%s: sent uac uevent %s %s %s\n", __func__, + uac_event[0], uac_event[1], uac_event[2]); + } +} + int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, const char *card_name) { @@ -1394,6 +1470,17 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, NULL, 0, BUFF_SIZE_MAX); err = snd_card_register(card); + if (err < 0) + goto snd_fail; + + g_audio->device = device_create(audio_class, NULL, MKDEV(0, 0), NULL, + "%s", g_audio->uac->card->longname); + if (IS_ERR(g_audio->device)) { + err = PTR_ERR(g_audio->device); + goto snd_fail; + } + + INIT_WORK(&g_audio->work, g_audio_work); if (!err) return 0; @@ -1419,6 +1506,10 @@ void g_audio_cleanup(struct g_audio *g_audio) if (!g_audio || !g_audio->uac) return; + cancel_work_sync(&g_audio->work); + device_destroy(g_audio->device->class, g_audio->device->devt); + g_audio->device = NULL; + uac = g_audio->uac; card = uac->card; if (card) @@ -1432,6 +1523,27 @@ void g_audio_cleanup(struct g_audio *g_audio) } EXPORT_SYMBOL_GPL(g_audio_cleanup); +static int __init u_audio_init(void) +{ + int err = 0; + + audio_class = class_create(THIS_MODULE, "u_audio"); + if (IS_ERR(audio_class)) { + err = PTR_ERR(audio_class); + audio_class = NULL; + } + + return err; +} +module_init(u_audio_init); + +static void __exit u_audio_exit(void) +{ + if (audio_class) + class_destroy(audio_class); +} +module_exit(u_audio_exit); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities"); MODULE_AUTHOR("Ruslan Bilovol"); diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h index 9512b8fccfaa..8f5076e0799c 100644 --- a/drivers/usb/gadget/function/u_audio.h +++ b/drivers/usb/gadget/function/u_audio.h @@ -57,7 +57,24 @@ struct uac_params { int fb_max; /* upper frequency drift feedback limit per-mil */ }; +enum usb_state_index { + SET_INTERFACE_OUT, + SET_INTERFACE_IN, + SET_SAMPLE_RATE_OUT, + SET_SAMPLE_RATE_IN, +}; + +enum stream_state_index { + STATE_OUT, + STATE_IN, +}; + struct g_audio { + struct device *device; + bool usb_state[4]; + bool stream_state[2]; + struct work_struct work; + struct usb_function func; struct usb_gadget *gadget;