summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBadhri Jagan Sridharan <Badhri@google.com>2014-11-23 13:51:28 -0800
committerJohn Stultz <john.stultz@linaro.org>2016-02-16 13:52:09 -0800
commit3541608288141d2c7763315fab6e8be44db32349 (patch)
treecd468a743d915a6c5ffa8ff49902fcfa1bba1d3f
parentdbaad39b68c88745a59d53e45403332bd1761462 (diff)
usb:gadget:audio_source: Move to USB_FUNCTION API
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
-rw-r--r--drivers/usb/gadget/Kconfig10
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/f_audio_source.c234
3 files changed, 239 insertions, 7 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 6977b224218c..f550989173bf 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -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
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 502c379cce80..386ed6343e33 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -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
diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c
index 4b9786b48950..39645be93502 100644
--- a/drivers/usb/gadget/f_audio_source.c
+++ b/drivers/usb/gadget/f_audio_source.c
@@ -21,6 +21,13 @@
#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");