From 55c7a549117fff6da7d35a69e8d42d9c91cbf1e2 Mon Sep 17 00:00:00 2001 From: Ren Jianing Date: Fri, 12 Jun 2020 11:23:39 +0800 Subject: [PATCH] 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 Change-Id: Ia01572d92d0c3ae40b19e772571282b8806da9e5 --- drivers/usb/gadget/function/u_audio.c | 113 ++++++++++++++++++++++++++ drivers/usb/gadget/function/u_audio.h | 17 ++++ 2 files changed, 130 insertions(+) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 526fbb14d058..74d76748c54e 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -24,6 +24,8 @@ #define PRD_SIZE_MAX PAGE_SIZE #define MIN_PERIODS 4 +static struct class *audio_class; + struct uac_req { struct uac_rtd_params *pp; /* parent param */ struct usb_request *req; @@ -426,6 +428,9 @@ int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) for (i = 0; i < UAC_MAX_RATES; i++) { if (params->c_srate[i] == srate) { + audio_dev->usb_state[SET_SAMPLE_RATE_OUT] = true; + schedule_work(&audio_dev->work); + params->c_srate_active = srate; snd_ctl_notify(audio_dev->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); @@ -447,6 +452,9 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) for (i = 0; i < UAC_MAX_RATES; i++) { if (params->p_srate[i] == srate) { + audio_dev->usb_state[SET_SAMPLE_RATE_IN] = true; + schedule_work(&audio_dev->work); + params->p_srate_active = srate; snd_ctl_notify(audio_dev->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); @@ -471,6 +479,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); + ep = audio_dev->out_ep; prm = &uac->c_prm; config_ep_by_speed(gadget, &audio_dev->func, ep); @@ -508,6 +520,10 @@ void u_audio_stop_capture(struct g_audio *audio_dev) struct snd_uac_chip *uac = audio_dev->uac; 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); @@ -524,6 +540,10 @@ int u_audio_start_playback(struct g_audio *audio_dev) const struct usb_endpoint_descriptor *ep_desc; int req_len, i; + audio_dev->usb_state[SET_INTERFACE_IN] = true; + audio_dev->stream_state[STATE_IN] = true; + schedule_work(&audio_dev->work); + dev_dbg(dev, "start playback with rate %d\n", params->p_srate_active); ep = audio_dev->in_ep; prm = &uac->p_prm; @@ -585,9 +605,66 @@ void u_audio_stop_playback(struct g_audio *audio_dev) struct snd_uac_chip *uac = audio_dev->uac; 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); +static void g_audio_work(struct work_struct *data) +{ + struct g_audio *audio = container_of(data, struct g_audio, work); + struct uac_params *params = &audio->params; + struct usb_gadget *gadget = audio->gadget; + 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", + params->c_srate_active); + 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", + params->p_srate_active); + 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) { @@ -696,6 +773,17 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, } 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; @@ -721,6 +809,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) @@ -734,6 +826,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 643118bf4215..f4ab481aaf87 100644 --- a/drivers/usb/gadget/function/u_audio.h +++ b/drivers/usb/gadget/function/u_audio.h @@ -28,7 +28,24 @@ struct uac_params { int req_number; /* number of preallocated requests */ }; +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;