diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 8124af33b738..ebdcdc4e1040 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -244,7 +244,10 @@ struct audio_source_config { int device; }; +static struct class *audio_source_class; + struct audio_dev { + struct device *dev; struct usb_function func; struct snd_card *card; struct snd_pcm *pcm; @@ -253,6 +256,8 @@ struct audio_dev { struct list_head idle_reqs; struct usb_ep *in_ep; + struct work_struct work; + spinlock_t lock; /* beginning, end and current position in our buffer */ @@ -260,6 +265,8 @@ struct audio_dev { void *buffer_end; void *buffer_pos; + unsigned int alt; + /* byte size of a "period" */ unsigned int period; /* bytes sent since last call to snd_pcm_period_elapsed */ @@ -278,6 +285,84 @@ static inline struct audio_dev *func_to_audio(struct usb_function *f) return container_of(f, struct audio_dev, func); } +static void audio_source_work(struct work_struct *data) +{ + struct audio_dev *audio = container_of(data, struct audio_dev, work); + char *set_interface[3] = { "USB_STATE=SET_INTERFACE", NULL, NULL}; + char **uevent_envp = NULL; + + if (audio->alt) + set_interface[1] = "1"; + else + set_interface[1] = "0"; + uevent_envp = set_interface; + + if (uevent_envp) { + kobject_uevent_env(&audio->dev->kobj, KOBJ_CHANGE, + uevent_envp); + pr_info("%s: sent uevent %s\n", __func__, + uevent_envp[0]); + } else { + pr_info("%s: did not send uevent set interface\n", + __func__); + } +} + +static ssize_t +alt_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct audio_dev *audio = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", audio->alt); +} + +static ssize_t +alt_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned int value; + struct audio_dev *audio = dev_get_drvdata(dev); + + if (sscanf(buf, "%d\n", &value) != 1) + return -1; + + if (audio->alt != value) { + audio->alt = value; + schedule_work(&audio->work); + } + return size; +} +static DEVICE_ATTR_RW(alt); + +static struct device_attribute *audio_source_attributes[] = { + &dev_attr_alt, + NULL +}; + +static int audio_source_create_device(struct audio_dev *audio) +{ + struct device_attribute **attrs = audio_source_attributes; + struct device_attribute *attr; + int err; + + audio->dev = device_create(audio_source_class, NULL, + MKDEV(0, 0), NULL, "audio_source0"); + if (IS_ERR(audio->dev)) + return PTR_ERR(audio->dev); + + dev_set_drvdata(audio->dev, audio); + + while ((attr = *attrs++)) { + err = device_create_file(audio->dev, attr); + if (err) { + device_destroy(audio_source_class, audio->dev->devt); + return err; + } + } + return 0; +} + /*-------------------------------------------------------------------------*/ struct audio_source_instance { @@ -574,6 +659,11 @@ static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ret) return ret; + if (audio->alt != alt) { + audio->alt = alt; + schedule_work(&audio->work); + } + usb_ep_enable(audio->in_ep); return 0; } @@ -636,6 +726,13 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) err = snd_card_setup(c, config); if (err) return err; + + audio_source_class = class_create(THIS_MODULE, "audio_source"); + if (IS_ERR(audio_source_class)) + return PTR_ERR(audio_source_class); + + INIT_WORK(&audio->work, audio_source_work); + audio_source_create_device(audio); } audio_build_desc(audio); @@ -697,11 +794,16 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f) while ((req = audio_req_get(audio))) audio_request_free(req, audio->in_ep); + cancel_work_sync(&audio->work); + device_destroy(audio_source_class, audio->dev->devt); + class_destroy(audio_source_class); + snd_card_free_when_closed(audio->card); audio->card = NULL; audio->pcm = NULL; audio->substream = NULL; audio->in_ep = NULL; + audio->dev = NULL; if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { struct audio_source_instance *fi_audio = @@ -879,6 +981,13 @@ int audio_source_bind_config(struct usb_configuration *c, if (err) goto add_fail; + audio_source_class = class_create(THIS_MODULE, "audio_source"); + if (IS_ERR(audio_source_class)) + return PTR_ERR(audio_source_class); + + INIT_WORK(&audio->work, audio_source_work); + audio_source_create_device(audio); + return 0; add_fail: