ANDROID: usb: gadget: f_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 [AmitP: Folded following android-4.9 commit changes into this patch 95106e3d2071 ("ANDROID: usb: gadget: build audio_source function only if SND is enabled") 4de9e33e5046 ("ANDROID: usb: gadget: audio_source: fix comparison of distinct pointer types") Parts of 051584e76d12 ("ANDROID: usb: gadget: function: cleanup: Add blank line after declaration")] Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
This commit is contained in:
parent
4503b048c5
commit
4f5452d938
3 changed files with 245 additions and 11 deletions
drivers/usb/gadget
|
@ -215,6 +215,9 @@ config USB_F_PRINTER
|
|||
config USB_F_TCM
|
||||
tristate
|
||||
|
||||
config USB_F_AUDIO_SRC
|
||||
tristate
|
||||
|
||||
# this first set of drivers all depend on bulk-capable hardware.
|
||||
|
||||
config USB_CONFIGFS
|
||||
|
@ -368,6 +371,15 @@ config USB_CONFIGFS_F_FS
|
|||
implemented in kernel space (for instance Ethernet, serial or
|
||||
mass storage) and other are implemented in user space.
|
||||
|
||||
config USB_CONFIGFS_F_AUDIO_SRC
|
||||
bool "Audio Source gadget"
|
||||
depends on USB_CONFIGFS
|
||||
depends on SND
|
||||
select SND_PCM
|
||||
select USB_F_AUDIO_SRC
|
||||
help
|
||||
USB gadget Audio Source support
|
||||
|
||||
config USB_CONFIGFS_UEVENT
|
||||
bool "Uevent notification of Gadget state"
|
||||
depends on USB_CONFIGFS
|
||||
|
|
|
@ -11,3 +11,5 @@ libcomposite-y := usbstring.o config.o epautoconf.o
|
|||
libcomposite-y += composite.o functions.o configfs.o u_f.o
|
||||
|
||||
obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
|
||||
usb_f_audio_source-y := f_audio_source.o
|
||||
obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o
|
||||
|
|
|
@ -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,9 +277,40 @@ 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);
|
||||
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
|
@ -338,10 +378,9 @@ static void audio_send(struct audio_dev *audio)
|
|||
|
||||
/* compute number of frames to send */
|
||||
now = ktime_get();
|
||||
msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
|
||||
do_div(msecs, 1000000);
|
||||
frames = msecs * SAMPLE_RATE;
|
||||
do_div(frames, 1000);
|
||||
msecs = div_s64((ktime_to_ns(now) - ktime_to_ns(audio->start_time)),
|
||||
1000000);
|
||||
frames = div_s64((msecs * SAMPLE_RATE), 1000);
|
||||
|
||||
/* Readjust our frames_sent if we fall too far behind.
|
||||
* If we get too far behind it is better to drop some frames than
|
||||
|
@ -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");
|
||||
|
|
Loading…
Add table
Reference in a new issue