usb:gadget:audio_source: Move to USB_FUNCTION API
authorBadhri Jagan Sridharan <Badhri@google.com>
Sun, 23 Nov 2014 21:51:28 +0000 (13:51 -0800)
committerJohn Stultz <john.stultz@linaro.org>
Tue, 16 Feb 2016 21:52:09 +0000 (13:52 -0800)
This patch adds support to use audio_source
gadget function through DECLARE_USB_FUNCTION_INIT
interface.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
Change-Id: I1fc6c9ea07105ae4eb785eebd3bb925bfdd8bc6b

drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/f_audio_source.c

index 6977b224218ce6c32e602e3e08c6e9f6ba8181b1..f550989173bfc1d93bf22db6a7bd56a6b365d772 100644 (file)
@@ -203,6 +203,9 @@ config USB_F_MTP
        tristate
 
 config USB_F_PTP
+        tristate
+
+config USB_F_AUDIO_SRC
        tristate
 
 choice
@@ -391,6 +394,13 @@ config USB_CONFIGFS_F_PTP
        help
          USB gadget PTP support
 
+config USB_CONFIGFS_F_AUDIO_SRC
+       boolean "Audio Source gadget"
+       depends on USB_CONFIGFS
+       select USB_F_AUDIO_SRC
+       help
+         USB gadget Audio Source support
+
 config USB_CONFIGFS_UEVENT
        boolean "Uevent notification of Gadget state"
        depends on USB_CONFIGFS
index 502c379cce801897c3642a730f7d8b318b936e61..386ed6343e3391483f3700c3c23ae0429ad850bb 100644 (file)
@@ -15,3 +15,5 @@ usb_f_mtp-y                   := f_mtp.o
 obj-$(CONFIG_USB_F_MTP)                += usb_f_mtp.o
 usb_f_ptp-y                    := f_ptp.o
 obj-$(CONFIG_USB_F_PTP)                += usb_f_ptp.o
+usb_f_audio_source-y           := f_audio_source.o
+obj-$(CONFIG_USB_F_AUDIO_SRC)  += usb_f_audio_source.o
index 4b9786b489506adfff9e798d09d1a88e087b15e0..39645be93502f5b2fa464581853b0cb586e00ca8 100644 (file)
 #include <sound/initval.h>
 #include <sound/pcm.h>
 
+#include <linux/usb.h>
+#include <linux/usb_usual.h>
+#include <linux/usb/ch9.h>
+#include <linux/configfs.h>
+#include <linux/usb/composite.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 #define SAMPLE_RATE 44100
 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000)
 
@@ -32,6 +39,7 @@
 #define AUDIO_AC_INTERFACE     0
 #define AUDIO_AS_INTERFACE     1
 #define AUDIO_NUM_INTERFACES   2
+#define MAX_INST_NAME_LEN     40
 
 /* B.3.1  Standard AC Interface Descriptor */
 static struct usb_interface_descriptor ac_interface_desc = {
@@ -259,6 +267,7 @@ struct audio_dev {
        ktime_t                         start_time;
        /* number of frames sent since start_time */
        s64                             frames_sent;
+       struct audio_source_config      *config;
 };
 
 static inline struct audio_dev *func_to_audio(struct usb_function *f)
@@ -268,6 +277,36 @@ static inline struct audio_dev *func_to_audio(struct usb_function *f)
 
 /*-------------------------------------------------------------------------*/
 
+struct audio_source_instance {
+       struct usb_function_instance func_inst;
+       const char *name;
+       struct audio_source_config *config;
+       struct device *audio_device;
+};
+
+static void audio_source_attr_release(struct config_item *item);
+
+static struct configfs_item_operations audio_source_item_ops = {
+       .release        = audio_source_attr_release,
+};
+
+static struct config_item_type audio_source_func_type = {
+       .ct_item_ops    = &audio_source_item_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+static ssize_t audio_source_pcm_show(struct device *dev,
+               struct device_attribute *attr, char *buf);
+
+static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL);
+
+static struct device_attribute *audio_source_function_attributes[] = {
+       &dev_attr_pcm,
+       NULL
+};
+
+/*--------------------------------------------------------------------------*/
+
 static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
 {
        struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
@@ -561,6 +600,13 @@ static void audio_build_desc(struct audio_dev *audio)
        memcpy(sam_freq, &rate, 3);
 }
 
+
+static int snd_card_setup(struct usb_configuration *c,
+       struct audio_source_config *config);
+static struct audio_source_instance *to_fi_audio_source(
+       const struct usb_function_instance *fi);
+
+
 /* audio function driver setup/binding */
 static int
 audio_bind(struct usb_configuration *c, struct usb_function *f)
@@ -571,6 +617,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f)
        struct usb_ep *ep;
        struct usb_request *req;
        int i;
+       int err;
+
+       if (IS_ENABLED(CONFIG_USB_CONFIGFS)) {
+               struct audio_source_instance *fi_audio =
+                               to_fi_audio_source(f->fi);
+               struct audio_source_config *config =
+                               fi_audio->config;
+
+               err = snd_card_setup(c, config);
+               if (err)
+                       return err;
+       }
 
        audio_build_desc(audio);
 
@@ -636,6 +694,16 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f)
        audio->pcm = NULL;
        audio->substream = NULL;
        audio->in_ep = NULL;
+
+       if (IS_ENABLED(CONFIG_USB_CONFIGFS)) {
+               struct audio_source_instance *fi_audio =
+                               to_fi_audio_source(f->fi);
+               struct audio_source_config *config =
+                               fi_audio->config;
+
+               config->card = -1;
+               config->device = -1;
+       }
 }
 
 static void audio_pcm_playback_start(struct audio_dev *audio)
@@ -779,8 +847,6 @@ int audio_source_bind_config(struct usb_configuration *c,
                struct audio_source_config *config)
 {
        struct audio_dev *audio;
-       struct snd_card *card;
-       struct snd_pcm *pcm;
        int err;
 
        config->card = -1;
@@ -788,6 +854,31 @@ int audio_source_bind_config(struct usb_configuration *c,
 
        audio = &_audio_dev;
 
+       err = snd_card_setup(c, config);
+       if (err)
+               return err;
+
+       err = usb_add_function(c, &audio->func);
+       if (err)
+               goto add_fail;
+
+       return 0;
+
+add_fail:
+       snd_card_free(audio->card);
+       return err;
+}
+
+static int snd_card_setup(struct usb_configuration *c,
+               struct audio_source_config *config)
+{
+       struct audio_dev *audio;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       int err;
+
+       audio = &_audio_dev;
+
        err = snd_card_new(&c->cdev->gadget->dev,
                        SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
                        THIS_MODULE, 0, &card);
@@ -817,18 +908,147 @@ int audio_source_bind_config(struct usb_configuration *c,
        if (err)
                goto register_fail;
 
-       err = usb_add_function(c, &audio->func);
-       if (err)
-               goto add_fail;
-
        config->card = pcm->card->number;
        config->device = pcm->device;
        audio->card = card;
        return 0;
 
-add_fail:
 register_fail:
 pcm_fail:
        snd_card_free(audio->card);
        return err;
 }
+
+static struct audio_source_instance *to_audio_source_instance(
+                                       struct config_item *item)
+{
+       return container_of(to_config_group(item), struct audio_source_instance,
+               func_inst.group);
+}
+
+static struct audio_source_instance *to_fi_audio_source(
+                                       const struct usb_function_instance *fi)
+{
+       return container_of(fi, struct audio_source_instance, func_inst);
+}
+
+static void audio_source_attr_release(struct config_item *item)
+{
+       struct audio_source_instance *fi_audio = to_audio_source_instance(item);
+
+       usb_put_function_instance(&fi_audio->func_inst);
+}
+
+static int audio_source_set_inst_name(struct usb_function_instance *fi,
+                                       const char *name)
+{
+       struct audio_source_instance *fi_audio;
+       char *ptr;
+       int name_len;
+
+       name_len = strlen(name) + 1;
+       if (name_len > MAX_INST_NAME_LEN)
+               return -ENAMETOOLONG;
+
+       ptr = kstrndup(name, name_len, GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       fi_audio = to_fi_audio_source(fi);
+       fi_audio->name = ptr;
+
+       return 0;
+}
+
+static void audio_source_free_inst(struct usb_function_instance *fi)
+{
+       struct audio_source_instance *fi_audio;
+
+       fi_audio = to_fi_audio_source(fi);
+       device_destroy(fi_audio->audio_device->class,
+                       fi_audio->audio_device->devt);
+       kfree(fi_audio->name);
+       kfree(fi_audio->config);
+}
+
+static ssize_t audio_source_pcm_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct audio_source_instance *fi_audio = dev_get_drvdata(dev);
+       struct audio_source_config *config = fi_audio->config;
+
+       /* print PCM card and device numbers */
+       return sprintf(buf, "%d %d\n", config->card, config->device);
+}
+
+struct device *create_function_device(char *name);
+
+static struct usb_function_instance *audio_source_alloc_inst(void)
+{
+       struct audio_source_instance *fi_audio;
+       struct device_attribute **attrs;
+       struct device_attribute *attr;
+       struct device *dev;
+       void *err_ptr;
+       int err = 0;
+
+       fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL);
+       if (!fi_audio)
+               return ERR_PTR(-ENOMEM);
+
+       fi_audio->func_inst.set_inst_name = audio_source_set_inst_name;
+       fi_audio->func_inst.free_func_inst = audio_source_free_inst;
+
+       fi_audio->config = kzalloc(sizeof(struct audio_source_config),
+                                                       GFP_KERNEL);
+       if (!fi_audio->config) {
+               err_ptr = ERR_PTR(-ENOMEM);
+               goto fail_audio;
+       }
+
+       config_group_init_type_name(&fi_audio->func_inst.group, "",
+                                               &audio_source_func_type);
+       dev = create_function_device("f_audio_source");
+
+       if (IS_ERR(dev)) {
+               err_ptr = dev;
+               goto fail_audio_config;
+       }
+
+       fi_audio->config->card = -1;
+       fi_audio->config->device = -1;
+       fi_audio->audio_device = dev;
+
+       attrs = audio_source_function_attributes;
+       if (attrs) {
+               while ((attr = *attrs++) && !err)
+                       err = device_create_file(dev, attr);
+               if (err) {
+                       err_ptr = ERR_PTR(-EINVAL);
+                       goto fail_device;
+               }
+       }
+
+       dev_set_drvdata(dev, fi_audio);
+       _audio_dev.config = fi_audio->config;
+
+       return  &fi_audio->func_inst;
+
+fail_device:
+       device_destroy(dev->class, dev->devt);
+fail_audio_config:
+       kfree(fi_audio->config);
+fail_audio:
+       kfree(fi_audio);
+       return err_ptr;
+
+}
+
+static struct usb_function *audio_source_alloc(struct usb_function_instance *fi)
+{
+       return &_audio_dev.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(audio_source, audio_source_alloc_inst,
+                       audio_source_alloc);
+MODULE_LICENSE("GPL");