Reuse existing u_audio core utilities making f_uac1 much simpler. This also drops previous f_uac1 approach (write audio samples directly to existing ALSA sound card) and moves to more generic/flexible one - create an f_uac1 ALSA sound card that represents USB Audio function and allows to be used by userspace tools. As a side effect, using u_audio it will be much easier to create gadget -> PC Host audio stream in the future Signed-off-by: Ruslan Bilovol <ruslan.bilovol@xxxxxxxxx> --- drivers/usb/gadget/Kconfig | 8 +- drivers/usb/gadget/function/Makefile | 2 +- drivers/usb/gadget/function/f_uac1.c | 431 +++++++++++------------------------ drivers/usb/gadget/function/u_uac1.c | 314 ------------------------- drivers/usb/gadget/function/u_uac1.h | 65 +----- drivers/usb/gadget/legacy/Kconfig | 2 +- drivers/usb/gadget/legacy/audio.c | 42 ++-- 7 files changed, 162 insertions(+), 702 deletions(-) delete mode 100644 drivers/usb/gadget/function/u_uac1.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 42d8508..fd6ee1d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -389,12 +389,16 @@ config USB_CONFIGFS_F_UAC1 depends on SND select USB_LIBCOMPOSITE select SND_PCM + select USB_U_AUDIO select USB_F_UAC1 help This Audio function implements 1 AudioControl interface, 1 AudioStreaming Interface each for USB-OUT and USB-IN. - This driver requires a real Audio codec to be present - on the device. + This driver doesn't expect any real Audio codec to be present + on the device - the audio streams are simply sinked to + a virtual ALSA sound card created. The user-space + application may choose to do whatever it wants with the data + received from the USB Host. config USB_CONFIGFS_F_UAC2 bool "Audio Class 2.0" diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index b29f2ae..bd9d7d5 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -33,7 +33,7 @@ obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o usb_f_fs-y := f_fs.o obj-$(CONFIG_USB_F_FS) += usb_f_fs.o obj-$(CONFIG_USB_U_AUDIO) += u_audio.o -usb_f_uac1-y := f_uac1.o u_uac1.o +usb_f_uac1-y := f_uac1.o obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index ba498af..120bba9 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -4,6 +4,8 @@ * Copyright (C) 2008 Bryan Wu <cooloney@xxxxxxxxxx> * Copyright (C) 2008 Analog Devices, Inc * + * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@xxxxxxxxx> + * * Enter bugs at http://blackfin.uclinux.org/ * * Licensed under the GPL-2 or later. @@ -15,8 +17,20 @@ #include <linux/device.h> #include <linux/atomic.h> +#include "u_audio.h" #include "u_uac1.h" +struct f_uac1 { + struct gaudio gaudio; + u8 ac_intf, as_out_intf; + u8 ac_alt, as_out_alt; /* needed for get_alt() */ +}; + +static inline struct f_uac1 *func_to_uac1(struct usb_function *f) +{ + return container_of(f, struct f_uac1, gaudio.func); +} + /* * DESCRIPTORS ... most are static, but strings and full * configuration descriptors are built on demand. @@ -198,130 +212,6 @@ static struct usb_gadget_strings *uac1_strings[] = { * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ -/*-------------------------------------------------------------------------*/ -struct f_audio_buf { - u8 *buf; - int actual; - struct list_head list; -}; - -static struct f_audio_buf *f_audio_buffer_alloc(int buf_size) -{ - struct f_audio_buf *copy_buf; - - copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); - if (!copy_buf) - return ERR_PTR(-ENOMEM); - - copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC); - if (!copy_buf->buf) { - kfree(copy_buf); - return ERR_PTR(-ENOMEM); - } - - return copy_buf; -} - -static void f_audio_buffer_free(struct f_audio_buf *audio_buf) -{ - kfree(audio_buf->buf); - kfree(audio_buf); -} -/*-------------------------------------------------------------------------*/ - -struct f_audio { - struct gaudio card; - - /* endpoints handle full and/or high speeds */ - struct usb_ep *out_ep; - - spinlock_t lock; - struct f_audio_buf *copy_buf; - struct work_struct playback_work; - struct list_head play_queue; -}; - -static inline struct f_audio *func_to_audio(struct usb_function *f) -{ - return container_of(f, struct f_audio, card.func); -} - -/*-------------------------------------------------------------------------*/ - -static void f_audio_playback_work(struct work_struct *data) -{ - struct f_audio *audio = container_of(data, struct f_audio, - playback_work); - struct f_audio_buf *play_buf; - - spin_lock_irq(&audio->lock); - if (list_empty(&audio->play_queue)) { - spin_unlock_irq(&audio->lock); - return; - } - play_buf = list_first_entry(&audio->play_queue, - struct f_audio_buf, list); - list_del(&play_buf->list); - spin_unlock_irq(&audio->lock); - - u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); - f_audio_buffer_free(play_buf); -} - -static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct f_audio *audio = req->context; - struct usb_composite_dev *cdev = audio->card.func.config->cdev; - struct f_audio_buf *copy_buf = audio->copy_buf; - struct f_uac1_opts *opts; - int audio_buf_size; - int err; - - opts = container_of(audio->card.func.fi, struct f_uac1_opts, - func_inst); - audio_buf_size = opts->audio_buf_size; - - if (!copy_buf) - return -EINVAL; - - /* Copy buffer is full, add it to the play_queue */ - if (audio_buf_size - copy_buf->actual < req->actual) { - list_add_tail(©_buf->list, &audio->play_queue); - schedule_work(&audio->playback_work); - copy_buf = f_audio_buffer_alloc(audio_buf_size); - if (IS_ERR(copy_buf)) - return -ENOMEM; - } - - memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); - copy_buf->actual += req->actual; - audio->copy_buf = copy_buf; - - err = usb_ep_queue(ep, req, GFP_ATOMIC); - if (err) - ERROR(cdev, "%s queue req: %d\n", ep->name, err); - - return 0; - -} - -static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct f_audio *audio = req->context; - int status = req->status; - struct usb_ep *out_ep = audio->out_ep; - - switch (status) { - - case 0: /* normal completion? */ - if (ep == out_ep) - f_audio_out_ep_complete(ep, req); - break; - default: - break; - } -} - static int audio_set_endpoint_req(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -432,118 +322,88 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { - struct f_audio *audio = func_to_audio(f); struct usb_composite_dev *cdev = f->config->cdev; - struct usb_ep *out_ep = audio->out_ep; - struct usb_request *req; - struct f_uac1_opts *opts; - int req_buf_size, req_count, audio_buf_size; - int i = 0, err = 0; - - DBG(cdev, "intf %d, alt %d\n", intf, alt); + struct usb_gadget *gadget = cdev->gadget; + struct device *dev = &gadget->dev; + struct f_uac1 *uac1 = func_to_uac1(f); + int ret = 0; + + /* No i/f has more than 2 alt settings */ + if (alt > 1) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } - opts = container_of(f->fi, struct f_uac1_opts, func_inst); - req_buf_size = opts->req_buf_size; - req_count = opts->req_count; - audio_buf_size = opts->audio_buf_size; - - if (intf == 1) { - if (alt == 1) { - err = config_ep_by_speed(cdev->gadget, f, out_ep); - if (err) - return err; - - usb_ep_enable(out_ep); - audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); - if (IS_ERR(audio->copy_buf)) - return -ENOMEM; - - /* - * allocate a bunch of read buffers - * and queue them all at once. - */ - for (i = 0; i < req_count && err == 0; i++) { - req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); - if (req) { - req->buf = kzalloc(req_buf_size, - GFP_ATOMIC); - if (req->buf) { - req->length = req_buf_size; - req->context = audio; - req->complete = - f_audio_complete; - err = usb_ep_queue(out_ep, - req, GFP_ATOMIC); - if (err) - ERROR(cdev, - "%s queue req: %d\n", - out_ep->name, err); - } else - err = -ENOMEM; - } else - err = -ENOMEM; - } - - } else { - struct f_audio_buf *copy_buf = audio->copy_buf; - if (copy_buf) { - list_add_tail(©_buf->list, - &audio->play_queue); - schedule_work(&audio->playback_work); - } + if (intf == uac1->ac_intf) { + /* Control I/f has only 1 AltSetting - 0 */ + if (alt) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; } + return 0; } - return err; + if (intf == uac1->as_out_intf) { + uac1->as_out_alt = alt; + + if (alt) + ret = gaudio_start_capture(&uac1->gaudio); + else + gaudio_stop_capture(&uac1->gaudio); + } else { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + + return ret; } -static void f_audio_disable(struct usb_function *f) +static int f_audio_get_alt(struct usb_function *f, unsigned intf) { - return; + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct device *dev = &gadget->dev; + struct f_uac1 *uac1 = func_to_uac1(f); + + if (intf == uac1->ac_intf) + return uac1->ac_alt; + else if (intf == uac1->as_out_intf) + return uac1->as_out_alt; + else + dev_err(dev, "%s:%d Invalid Interface %d!\n", + __func__, __LINE__, intf); + + return -EINVAL; } -/*-------------------------------------------------------------------------*/ -static void f_audio_build_desc(struct f_audio *audio) +static void f_audio_disable(struct usb_function *f) { - struct gaudio *card = &audio->card; - u8 *sam_freq; - int rate; + struct f_uac1 *uac1 = func_to_uac1(f); - /* Set channel numbers */ - input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); - as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); + uac1->as_out_alt = 0; - /* Set sample rates */ - rate = u_audio_get_playback_rate(card); - sam_freq = as_type_i_desc.tSamFreq[0]; - memcpy(sam_freq, &rate, 3); - - /* Todo: Set Sample bits and other parameters */ - - return; + gaudio_stop_capture(&uac1->gaudio); } +/*-------------------------------------------------------------------------*/ + /* audio function driver setup/binding */ -static int -f_audio_bind(struct usb_configuration *c, struct usb_function *f) +static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; - struct f_audio *audio = func_to_audio(f); + struct usb_gadget *gadget = cdev->gadget; + struct f_uac1 *uac1 = func_to_uac1(f); + struct gaudio *audio = func_to_gaudio(f); struct usb_string *us; int status; struct usb_ep *ep = NULL; struct f_uac1_opts *audio_opts; + u8 *sam_freq; + int rate; audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); - audio->card.gadget = c->cdev->gadget; - /* set up ASLA audio devices */ - if (!audio_opts->bound) { - status = gaudio_setup(&audio->card); - if (status < 0) - return status; - audio_opts->bound = true; - } + us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); if (IS_ERR(us)) return PTR_ERR(us); @@ -554,20 +414,35 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id; as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id; + /* Set channel numbers */ + input_terminal_desc.bNrChannels = num_channels(audio_opts->c_chmask); + input_terminal_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask); + as_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask); + as_type_i_desc.bSubframeSize = audio_opts->c_ssize; + as_type_i_desc.bBitResolution = audio_opts->c_ssize * 8; - f_audio_build_desc(audio); + /* Set sample rates */ + rate = audio_opts->c_srate; + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) goto fail; ac_interface_desc.bInterfaceNumber = status; + uac1->ac_intf = status; + uac1->ac_alt = 0; status = usb_interface_id(c, f); if (status < 0) goto fail; as_interface_alt_0_desc.bInterfaceNumber = status; as_interface_alt_1_desc.bInterfaceNumber = status; + uac1->as_out_intf = status; + uac1->as_out_alt = 0; + + audio->gadget = gadget; status = -ENODEV; @@ -578,17 +453,26 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->out_ep = ep; audio->out_ep->desc = &as_out_ep_desc; - status = -ENOMEM; - /* copy descriptors, and track endpoint copies */ status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL, NULL); if (status) goto fail; + + audio->out_ep_maxpsize = as_out_ep_desc.wMaxPacketSize; + audio->params.c_chmask = audio_opts->c_chmask; + audio->params.c_srate = audio_opts->c_srate; + audio->params.c_ssize = audio_opts->c_ssize; + + status = gaudio_setup(audio, "UAC1_PCM", "UAC1_Gadget"); + if (status) + goto err_card_register; + return 0; +err_card_register: + usb_free_all_descriptors(f); fail: - gaudio_cleanup(&audio->card); return status; } @@ -611,7 +495,7 @@ static struct configfs_item_operations f_uac1_item_ops = { .release = f_uac1_attr_release, }; -#define UAC1_INT_ATTRIBUTE(name) \ +#define UAC1_ATTRIBUTE(name) \ static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ char *page) \ { \ @@ -652,64 +536,14 @@ end: \ \ CONFIGFS_ATTR(f_uac1_opts_, name) -UAC1_INT_ATTRIBUTE(req_buf_size); -UAC1_INT_ATTRIBUTE(req_count); -UAC1_INT_ATTRIBUTE(audio_buf_size); - -#define UAC1_STR_ATTRIBUTE(name) \ -static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ - char *page) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - int result; \ - \ - mutex_lock(&opts->lock); \ - result = sprintf(page, "%s\n", opts->name); \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - int ret = -EBUSY; \ - char *tmp; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) \ - goto end; \ - \ - tmp = kstrndup(page, len, GFP_KERNEL); \ - if (tmp) { \ - ret = -ENOMEM; \ - goto end; \ - } \ - if (opts->name##_alloc) \ - kfree(opts->name); \ - opts->name##_alloc = true; \ - opts->name = tmp; \ - ret = len; \ - \ -end: \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac1_opts_, name) - -UAC1_STR_ATTRIBUTE(fn_play); -UAC1_STR_ATTRIBUTE(fn_cap); -UAC1_STR_ATTRIBUTE(fn_cntl); +UAC1_ATTRIBUTE(c_chmask); +UAC1_ATTRIBUTE(c_srate); +UAC1_ATTRIBUTE(c_ssize); static struct configfs_attribute *f_uac1_attrs[] = { - &f_uac1_opts_attr_req_buf_size, - &f_uac1_opts_attr_req_count, - &f_uac1_opts_attr_audio_buf_size, - &f_uac1_opts_attr_fn_play, - &f_uac1_opts_attr_fn_cap, - &f_uac1_opts_attr_fn_cntl, + &f_uac1_opts_attr_c_chmask, + &f_uac1_opts_attr_c_srate, + &f_uac1_opts_attr_c_ssize, NULL, }; @@ -724,12 +558,6 @@ static void f_audio_free_inst(struct usb_function_instance *f) struct f_uac1_opts *opts; opts = container_of(f, struct f_uac1_opts, func_inst); - if (opts->fn_play_alloc) - kfree(opts->fn_play); - if (opts->fn_cap_alloc) - kfree(opts->fn_cap); - if (opts->fn_cntl_alloc) - kfree(opts->fn_cntl); kfree(opts); } @@ -747,21 +575,18 @@ static struct usb_function_instance *f_audio_alloc_inst(void) config_group_init_type_name(&opts->func_inst.group, "", &f_uac1_func_type); - opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; - opts->req_count = UAC1_REQ_COUNT; - opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE; - opts->fn_play = FILE_PCM_PLAYBACK; - opts->fn_cap = FILE_PCM_CAPTURE; - opts->fn_cntl = FILE_CONTROL; + opts->c_chmask = UAC1_DEF_CCHMASK; + opts->c_srate = UAC1_DEF_CSRATE; + opts->c_ssize = UAC1_DEF_CSSIZE; return &opts->func_inst; } static void f_audio_free(struct usb_function *f) { - struct f_audio *audio = func_to_audio(f); + struct gaudio *audio; struct f_uac1_opts *opts; - gaudio_cleanup(&audio->card); + audio = func_to_gaudio(f); opts = container_of(f->fi, struct f_uac1_opts, func_inst); kfree(audio); mutex_lock(&opts->lock); @@ -771,40 +596,42 @@ static void f_audio_free(struct usb_function *f) static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) { + struct gaudio *audio = func_to_gaudio(f); + + gaudio_cleanup(audio); usb_free_all_descriptors(f); + + audio->gadget = NULL; } static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) { - struct f_audio *audio; + struct f_uac1 *uac1; struct f_uac1_opts *opts; /* allocate and initialize one new instance */ - audio = kzalloc(sizeof(*audio), GFP_KERNEL); - if (!audio) + uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL); + if (!uac1) return ERR_PTR(-ENOMEM); - audio->card.func.name = "g_audio"; - opts = container_of(fi, struct f_uac1_opts, func_inst); mutex_lock(&opts->lock); ++opts->refcnt; mutex_unlock(&opts->lock); - INIT_LIST_HEAD(&audio->play_queue); - spin_lock_init(&audio->lock); - - audio->card.func.bind = f_audio_bind; - audio->card.func.unbind = f_audio_unbind; - audio->card.func.set_alt = f_audio_set_alt; - audio->card.func.setup = f_audio_setup; - audio->card.func.disable = f_audio_disable; - audio->card.func.free_func = f_audio_free; - INIT_WORK(&audio->playback_work, f_audio_playback_work); + uac1->gaudio.func.name = "g_audio"; + uac1->gaudio.func.bind = f_audio_bind; + uac1->gaudio.func.unbind = f_audio_unbind; + uac1->gaudio.func.set_alt = f_audio_set_alt; + uac1->gaudio.func.get_alt = f_audio_get_alt; + uac1->gaudio.func.setup = f_audio_setup; + uac1->gaudio.func.disable = f_audio_disable; + uac1->gaudio.func.free_func = f_audio_free; - return &audio->card.func; + return &uac1->gaudio.func; } DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bryan Wu"); +MODULE_AUTHOR("Ruslan Bilovol"); diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c deleted file mode 100644 index c78c841..0000000 --- a/drivers/usb/gadget/function/u_uac1.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * u_uac1.c -- ALSA audio utilities for Gadget stack - * - * Copyright (C) 2008 Bryan Wu <cooloney@xxxxxxxxxx> - * Copyright (C) 2008 Analog Devices, Inc - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/delay.h> -#include <linux/ctype.h> -#include <linux/random.h> -#include <linux/syscalls.h> - -#include "u_uac1.h" - -/* - * This component encapsulates the ALSA devices for USB audio gadget - */ - -/*-------------------------------------------------------------------------*/ - -/** - * Some ALSA internal helper functions - */ -static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) -{ - struct snd_interval t; - t.empty = 0; - t.min = t.max = val; - t.openmin = t.openmax = 0; - t.integer = 1; - return snd_interval_refine(i, &t); -} - -static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, - int dir) -{ - int changed; - if (hw_is_mask(var)) { - struct snd_mask *m = hw_param_mask(params, var); - if (val == 0 && dir < 0) { - changed = -EINVAL; - snd_mask_none(m); - } else { - if (dir > 0) - val++; - else if (dir < 0) - val--; - changed = snd_mask_refine_set( - hw_param_mask(params, var), val); - } - } else if (hw_is_interval(var)) { - struct snd_interval *i = hw_param_interval(params, var); - if (val == 0 && dir < 0) { - changed = -EINVAL; - snd_interval_none(i); - } else if (dir == 0) - changed = snd_interval_refine_set(i, val); - else { - struct snd_interval t; - t.openmin = 1; - t.openmax = 1; - t.empty = 0; - t.integer = 0; - if (dir < 0) { - t.min = val - 1; - t.max = val; - } else { - t.min = val; - t.max = val+1; - } - changed = snd_interval_refine(i, &t); - } - } else - return -EINVAL; - if (changed) { - params->cmask |= 1 << var; - params->rmask |= 1 << var; - } - return changed; -} -/*-------------------------------------------------------------------------*/ - -/** - * Set default hardware params - */ -static int playback_default_hw_params(struct gaudio_snd_dev *snd) -{ - struct snd_pcm_substream *substream = snd->substream; - struct snd_pcm_hw_params *params; - snd_pcm_sframes_t result; - - /* - * SNDRV_PCM_ACCESS_RW_INTERLEAVED, - * SNDRV_PCM_FORMAT_S16_LE - * CHANNELS: 2 - * RATE: 48000 - */ - snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; - snd->format = SNDRV_PCM_FORMAT_S16_LE; - snd->channels = 2; - snd->rate = 48000; - - params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) - return -ENOMEM; - - _snd_pcm_hw_params_any(params); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - snd->access, 0); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - snd->format, 0); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, - snd->channels, 0); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, - snd->rate, 0); - - snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); - snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params); - - result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); - if (result < 0) { - ERROR(snd->card, - "Preparing sound card failed: %d\n", (int)result); - kfree(params); - return result; - } - - /* Store the hardware parameters */ - snd->access = params_access(params); - snd->format = params_format(params); - snd->channels = params_channels(params); - snd->rate = params_rate(params); - - kfree(params); - - INFO(snd->card, - "Hardware params: access %x, format %x, channels %d, rate %d\n", - snd->access, snd->format, snd->channels, snd->rate); - - return 0; -} - -/** - * Playback audio buffer data by ALSA PCM device - */ -size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) -{ - struct gaudio_snd_dev *snd = &card->playback; - struct snd_pcm_substream *substream = snd->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - mm_segment_t old_fs; - ssize_t result; - snd_pcm_sframes_t frames; - -try_again: - if (runtime->status->state == SNDRV_PCM_STATE_XRUN || - runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_pcm_kernel_ioctl(substream, - SNDRV_PCM_IOCTL_PREPARE, NULL); - if (result < 0) { - ERROR(card, "Preparing sound card failed: %d\n", - (int)result); - return result; - } - } - - frames = bytes_to_frames(runtime, count); - old_fs = get_fs(); - set_fs(KERNEL_DS); - result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames); - if (result != frames) { - ERROR(card, "Playback error: %d\n", (int)result); - set_fs(old_fs); - goto try_again; - } - set_fs(old_fs); - - return 0; -} - -int u_audio_get_playback_channels(struct gaudio *card) -{ - return card->playback.channels; -} - -int u_audio_get_playback_rate(struct gaudio *card) -{ - return card->playback.rate; -} - -/** - * Open ALSA PCM and control device files - * Initial the PCM or control device - */ -static int gaudio_open_snd_dev(struct gaudio *card) -{ - struct snd_pcm_file *pcm_file; - struct gaudio_snd_dev *snd; - struct f_uac1_opts *opts; - char *fn_play, *fn_cap, *fn_cntl; - - opts = container_of(card->func.fi, struct f_uac1_opts, func_inst); - fn_play = opts->fn_play; - fn_cap = opts->fn_cap; - fn_cntl = opts->fn_cntl; - - /* Open control device */ - snd = &card->control; - snd->filp = filp_open(fn_cntl, O_RDWR, 0); - if (IS_ERR(snd->filp)) { - int ret = PTR_ERR(snd->filp); - ERROR(card, "unable to open sound control device file: %s\n", - fn_cntl); - snd->filp = NULL; - return ret; - } - snd->card = card; - - /* Open PCM playback device and setup substream */ - snd = &card->playback; - snd->filp = filp_open(fn_play, O_WRONLY, 0); - if (IS_ERR(snd->filp)) { - int ret = PTR_ERR(snd->filp); - - ERROR(card, "No such PCM playback device: %s\n", fn_play); - snd->filp = NULL; - return ret; - } - pcm_file = snd->filp->private_data; - snd->substream = pcm_file->substream; - snd->card = card; - playback_default_hw_params(snd); - - /* Open PCM capture device and setup substream */ - snd = &card->capture; - snd->filp = filp_open(fn_cap, O_RDONLY, 0); - if (IS_ERR(snd->filp)) { - ERROR(card, "No such PCM capture device: %s\n", fn_cap); - snd->substream = NULL; - snd->card = NULL; - snd->filp = NULL; - } else { - pcm_file = snd->filp->private_data; - snd->substream = pcm_file->substream; - snd->card = card; - } - - return 0; -} - -/** - * Close ALSA PCM and control device files - */ -static int gaudio_close_snd_dev(struct gaudio *gau) -{ - struct gaudio_snd_dev *snd; - - /* Close control device */ - snd = &gau->control; - if (snd->filp) - filp_close(snd->filp, NULL); - - /* Close PCM playback device and setup substream */ - snd = &gau->playback; - if (snd->filp) - filp_close(snd->filp, NULL); - - /* Close PCM capture device and setup substream */ - snd = &gau->capture; - if (snd->filp) - filp_close(snd->filp, NULL); - - return 0; -} - -/** - * gaudio_setup - setup ALSA interface and preparing for USB transfer - * - * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using. - * - * Returns negative errno, or zero on success - */ -int gaudio_setup(struct gaudio *card) -{ - int ret; - - ret = gaudio_open_snd_dev(card); - if (ret) - ERROR(card, "we need at least one control device\n"); - - return ret; - -} - -/** - * gaudio_cleanup - remove ALSA device interface - * - * This is called to free all resources allocated by @gaudio_setup(). - */ -void gaudio_cleanup(struct gaudio *the_card) -{ - if (the_card) - gaudio_close_snd_dev(the_card); -} - diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 5c2ac8e..fb871d6 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -9,74 +9,29 @@ * Licensed under the GPL-2 or later. */ -#ifndef __U_AUDIO_H -#define __U_AUDIO_H +#ifndef __U_UAC1_H +#define __U_UAC1_H #include <linux/device.h> #include <linux/err.h> #include <linux/usb/audio.h> #include <linux/usb/composite.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> - -#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" -#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" -#define FILE_CONTROL "/dev/snd/controlC0" - #define UAC1_OUT_EP_MAX_PACKET_SIZE 200 -#define UAC1_REQ_COUNT 256 -#define UAC1_AUDIO_BUF_SIZE 48000 - -/* - * This represents the USB side of an audio card device, managed by a USB - * function which provides control and stream interfaces. - */ - -struct gaudio_snd_dev { - struct gaudio *card; - struct file *filp; - struct snd_pcm_substream *substream; - int access; - int format; - int channels; - int rate; -}; +#define UAC1_DEF_CCHMASK 0x3 +#define UAC1_DEF_CSRATE 48000 +#define UAC1_DEF_CSSIZE 2 -struct gaudio { - struct usb_function func; - struct usb_gadget *gadget; - - /* ALSA sound device interfaces */ - struct gaudio_snd_dev control; - struct gaudio_snd_dev playback; - struct gaudio_snd_dev capture; - - /* TODO */ -}; struct f_uac1_opts { struct usb_function_instance func_inst; - int req_buf_size; - int req_count; - int audio_buf_size; - char *fn_play; - char *fn_cap; - char *fn_cntl; + int c_chmask; + int c_srate; + int c_ssize; unsigned bound:1; - unsigned fn_play_alloc:1; - unsigned fn_cap_alloc:1; - unsigned fn_cntl_alloc:1; + struct mutex lock; int refcnt; }; -int gaudio_setup(struct gaudio *card); -void gaudio_cleanup(struct gaudio *the_card); - -size_t u_audio_playback(struct gaudio *card, void *buf, size_t count); -int u_audio_get_playback_channels(struct gaudio *card); -int u_audio_get_playback_rate(struct gaudio *card); - -#endif /* __U_AUDIO_H */ +#endif /* __U_UAC1_H */ diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 5344064..5781383 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -56,7 +56,7 @@ config USB_AUDIO select SND_PCM select USB_F_UAC1 if GADGET_UAC1 select USB_F_UAC2 if !GADGET_UAC1 - select USB_U_AUDIO if USB_F_UAC2 + select USB_U_AUDIO help This Gadget Audio driver is compatible with USB Audio Class specification 2.0. It implements 1 AudioControl interface, diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 5d7b3c6..84cce80 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -55,29 +55,20 @@ MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); #else #include "u_uac1.h" -static char *fn_play = FILE_PCM_PLAYBACK; -module_param(fn_play, charp, S_IRUGO); -MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); - -static char *fn_cap = FILE_PCM_CAPTURE; -module_param(fn_cap, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); - -static char *fn_cntl = FILE_CONTROL; -module_param(fn_cntl, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cntl, "Control device file name"); - -static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; -module_param(req_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); +/* Capture(USB-OUT) Default Stereo - Fl/Fr */ +static int c_chmask = UAC1_DEF_CCHMASK; +module_param(c_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); -static int req_count = UAC1_REQ_COUNT; -module_param(req_count, int, S_IRUGO); -MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); +/* Capture Default 48 KHz */ +static int c_srate = UAC1_DEF_CSRATE; +module_param(c_srate, uint, S_IRUGO); +MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); -static int audio_buf_size = UAC1_AUDIO_BUF_SIZE; -module_param(audio_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); +/* Capture Default 16bits/sample */ +static int c_ssize = UAC1_DEF_CSSIZE; +module_param(c_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); #endif /* string IDs are assigned dynamically */ @@ -231,12 +222,9 @@ static int audio_bind(struct usb_composite_dev *cdev) uac2_opts->c_ssize = c_ssize; #else uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst); - uac1_opts->fn_play = fn_play; - uac1_opts->fn_cap = fn_cap; - uac1_opts->fn_cntl = fn_cntl; - uac1_opts->req_buf_size = req_buf_size; - uac1_opts->req_count = req_count; - uac1_opts->audio_buf_size = audio_buf_size; + uac1_opts->c_chmask = c_chmask; + uac1_opts->c_srate = c_srate; + uac1_opts->c_ssize = c_ssize; #endif status = usb_string_ids_tab(cdev, strings_dev); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html