diff options
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r-- | drivers/usb/gadget/function/Makefile | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_hid.c | 374 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_midi.c | 364 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_ncm.c | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_rndis.c | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_hid.h | 42 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_midi.h | 40 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_uac1.c | 3 |
8 files changed, 670 insertions, 163 deletions
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 90701aa5a826..dd68091d92f0 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -38,3 +38,7 @@ usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o +usb_f_midi-y := f_midi.o +obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o +usb_f_hid-y := f_hid.o +obj-$(CONFIG_USB_F_HID) += usb_f_hid.o diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index ea2b9c374305..6e04e302dc3a 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/hid.h> +#include <linux/idr.h> #include <linux/cdev.h> #include <linux/mutex.h> #include <linux/poll.h> @@ -21,9 +22,14 @@ #include <linux/usb/g_hid.h> #include "u_f.h" +#include "u_hid.h" + +#define HIDG_MINORS 4 static int major, minors; static struct class *hidg_class; +static DEFINE_IDA(hidg_ida); +static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ /*-------------------------------------------------------------------------*/ /* HID gadget struct */ @@ -161,6 +167,26 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = { }; /*-------------------------------------------------------------------------*/ +/* Strings */ + +#define CT_FUNC_HID_IDX 0 + +static struct usb_string ct_func_string_defs[] = { + [CT_FUNC_HID_IDX].s = "HID Interface", + {}, /* end of list */ +}; + +static struct usb_gadget_strings ct_func_string_table = { + .language = 0x0409, /* en-US */ + .strings = ct_func_string_defs, +}; + +static struct usb_gadget_strings *ct_func_strings[] = { + &ct_func_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ /* Char Device */ static ssize_t f_hidg_read(struct file *file, char __user *buffer, @@ -552,13 +578,22 @@ const struct file_operations f_hidg_fops = { .llseek = noop_llseek, }; -static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) +static int hidg_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_ep *ep; struct f_hidg *hidg = func_to_hidg(f); + struct usb_string *us; + struct device *device; int status; dev_t dev; + /* maybe allocate device-global string IDs, and patch descriptors */ + us = usb_gstrings_attach(c->cdev, ct_func_strings, + ARRAY_SIZE(ct_func_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id; + /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) @@ -623,10 +658,16 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail_free_descs; - device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor); + device = device_create(hidg_class, NULL, dev, NULL, + "%s%d", "hidg", hidg->minor); + if (IS_ERR(device)) { + status = PTR_ERR(device); + goto del; + } return 0; - +del: + cdev_del(&hidg->cdev); fail_free_descs: usb_free_all_descriptors(f); fail: @@ -640,116 +681,313 @@ fail: return status; } -static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) +static inline int hidg_get_minor(void) { - struct f_hidg *hidg = func_to_hidg(f); + int ret; - device_destroy(hidg_class, MKDEV(major, hidg->minor)); - cdev_del(&hidg->cdev); + ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL); - /* disable/free request and end point */ - usb_ep_disable(hidg->in_ep); - usb_ep_dequeue(hidg->in_ep, hidg->req); - kfree(hidg->req->buf); - usb_ep_free_request(hidg->in_ep, hidg->req); - - usb_free_all_descriptors(f); + return ret; +} - kfree(hidg->report_desc); - kfree(hidg); +static inline struct f_hid_opts *to_f_hid_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_hid_opts, + func_inst.group); } -/*-------------------------------------------------------------------------*/ -/* Strings */ +CONFIGFS_ATTR_STRUCT(f_hid_opts); +CONFIGFS_ATTR_OPS(f_hid_opts); -#define CT_FUNC_HID_IDX 0 +static void hid_attr_release(struct config_item *item) +{ + struct f_hid_opts *opts = to_f_hid_opts(item); -static struct usb_string ct_func_string_defs[] = { - [CT_FUNC_HID_IDX].s = "HID Interface", - {}, /* end of list */ -}; + usb_put_function_instance(&opts->func_inst); +} -static struct usb_gadget_strings ct_func_string_table = { - .language = 0x0409, /* en-US */ - .strings = ct_func_string_defs, +static struct configfs_item_operations hidg_item_ops = { + .release = hid_attr_release, + .show_attribute = f_hid_opts_attr_show, + .store_attribute = f_hid_opts_attr_store, }; -static struct usb_gadget_strings *ct_func_strings[] = { - &ct_func_string_table, +#define F_HID_OPT(name, prec, limit) \ +static ssize_t f_hid_opts_##name##_show(struct f_hid_opts *opts, char *page)\ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_hid_opts_##name##_store(struct f_hid_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + u##prec num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou##prec(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_hid_opts_attribute f_hid_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_hid_opts_##name##_show,\ + f_hid_opts_##name##_store) + +F_HID_OPT(subclass, 8, 255); +F_HID_OPT(protocol, 8, 255); +F_HID_OPT(report_length, 16, 65536); + +static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = opts->report_desc_length; + memcpy(page, opts->report_desc, opts->report_desc_length); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_hid_opts_report_desc_store(struct f_hid_opts *opts, + const char *page, size_t len) +{ + int ret = -EBUSY; + char *d; + + mutex_lock(&opts->lock); + + if (opts->refcnt) + goto end; + if (len > PAGE_SIZE) { + ret = -ENOSPC; + goto end; + } + d = kmemdup(page, len, GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + kfree(opts->report_desc); + opts->report_desc = d; + opts->report_desc_length = len; + opts->report_desc_alloc = true; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_hid_opts_attribute f_hid_opts_report_desc = + __CONFIGFS_ATTR(report_desc, S_IRUGO | S_IWUSR, + f_hid_opts_report_desc_show, + f_hid_opts_report_desc_store); + +static struct configfs_attribute *hid_attrs[] = { + &f_hid_opts_subclass.attr, + &f_hid_opts_protocol.attr, + &f_hid_opts_report_length.attr, + &f_hid_opts_report_desc.attr, NULL, }; -/*-------------------------------------------------------------------------*/ -/* usb_configuration */ +static struct config_item_type hid_func_type = { + .ct_item_ops = &hidg_item_ops, + .ct_attrs = hid_attrs, + .ct_owner = THIS_MODULE, +}; -int __init hidg_bind_config(struct usb_configuration *c, - struct hidg_func_descriptor *fdesc, int index) +static inline void hidg_put_minor(int minor) { - struct f_hidg *hidg; - int status; + ida_simple_remove(&hidg_ida, minor); +} - if (index >= minors) - return -ENOENT; +static void hidg_free_inst(struct usb_function_instance *f) +{ + struct f_hid_opts *opts; - /* maybe allocate device-global string IDs, and patch descriptors */ - if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) - return status; - ct_func_string_defs[CT_FUNC_HID_IDX].id = status; - hidg_interface_desc.iInterface = status; + opts = container_of(f, struct f_hid_opts, func_inst); + + mutex_lock(&hidg_ida_lock); + + hidg_put_minor(opts->minor); + if (idr_is_empty(&hidg_ida.idr)) + ghid_cleanup(); + + mutex_unlock(&hidg_ida_lock); + + if (opts->report_desc_alloc) + kfree(opts->report_desc); + + kfree(opts); +} + +static struct usb_function_instance *hidg_alloc_inst(void) +{ + struct f_hid_opts *opts; + struct usb_function_instance *ret; + int status = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = hidg_free_inst; + ret = &opts->func_inst; + + mutex_lock(&hidg_ida_lock); + + if (idr_is_empty(&hidg_ida.idr)) { + status = ghid_setup(NULL, HIDG_MINORS); + if (status) { + ret = ERR_PTR(status); + kfree(opts); + goto unlock; + } + } + + opts->minor = hidg_get_minor(); + if (opts->minor < 0) { + ret = ERR_PTR(opts->minor); + kfree(opts); + if (idr_is_empty(&hidg_ida.idr)) + ghid_cleanup(); + goto unlock; } + config_group_init_type_name(&opts->func_inst.group, "", &hid_func_type); + +unlock: + mutex_unlock(&hidg_ida_lock); + return ret; +} + +static void hidg_free(struct usb_function *f) +{ + struct f_hidg *hidg; + struct f_hid_opts *opts; + + hidg = func_to_hidg(f); + opts = container_of(f->fi, struct f_hid_opts, func_inst); + kfree(hidg->report_desc); + kfree(hidg); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_hidg *hidg = func_to_hidg(f); + + device_destroy(hidg_class, MKDEV(major, hidg->minor)); + cdev_del(&hidg->cdev); + + /* disable/free request and end point */ + usb_ep_disable(hidg->in_ep); + usb_ep_dequeue(hidg->in_ep, hidg->req); + kfree(hidg->req->buf); + usb_ep_free_request(hidg->in_ep, hidg->req); + + usb_free_all_descriptors(f); +} + +static struct usb_function *hidg_alloc(struct usb_function_instance *fi) +{ + struct f_hidg *hidg; + struct f_hid_opts *opts; /* allocate and initialize one new instance */ - hidg = kzalloc(sizeof *hidg, GFP_KERNEL); + hidg = kzalloc(sizeof(*hidg), GFP_KERNEL); if (!hidg) - return -ENOMEM; - - hidg->minor = index; - hidg->bInterfaceSubClass = fdesc->subclass; - hidg->bInterfaceProtocol = fdesc->protocol; - hidg->report_length = fdesc->report_length; - hidg->report_desc_length = fdesc->report_desc_length; - hidg->report_desc = kmemdup(fdesc->report_desc, - fdesc->report_desc_length, - GFP_KERNEL); - if (!hidg->report_desc) { - kfree(hidg); - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_hid_opts, func_inst); + + mutex_lock(&opts->lock); + ++opts->refcnt; + + hidg->minor = opts->minor; + hidg->bInterfaceSubClass = opts->subclass; + hidg->bInterfaceProtocol = opts->protocol; + hidg->report_length = opts->report_length; + hidg->report_desc_length = opts->report_desc_length; + if (opts->report_desc) { + hidg->report_desc = kmemdup(opts->report_desc, + opts->report_desc_length, + GFP_KERNEL); + if (!hidg->report_desc) { + kfree(hidg); + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOMEM); + } } + mutex_unlock(&opts->lock); + hidg->func.name = "hid"; - hidg->func.strings = ct_func_strings; hidg->func.bind = hidg_bind; hidg->func.unbind = hidg_unbind; hidg->func.set_alt = hidg_set_alt; hidg->func.disable = hidg_disable; hidg->func.setup = hidg_setup; + hidg->func.free_func = hidg_free; /* this could me made configurable at some point */ hidg->qlen = 4; - status = usb_add_function(c, &hidg->func); - if (status) - kfree(hidg); - - return status; + return &hidg->func; } -int __init ghid_setup(struct usb_gadget *g, int count) +DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fabien Chouteau"); + +int ghid_setup(struct usb_gadget *g, int count) { int status; dev_t dev; hidg_class = class_create(THIS_MODULE, "hidg"); + if (IS_ERR(hidg_class)) { + status = PTR_ERR(hidg_class); + hidg_class = NULL; + return status; + } status = alloc_chrdev_region(&dev, 0, count, "hidg"); - if (!status) { - major = MAJOR(dev); - minors = count; + if (status) { + class_destroy(hidg_class); + hidg_class = NULL; + return status; } - return status; + major = MAJOR(dev); + minors = count; + + return 0; } void ghid_cleanup(void) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 807b31c0edc3..a90440300735 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -20,6 +20,7 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/device.h> @@ -33,6 +34,7 @@ #include <linux/usb/midi.h> #include "u_f.h" +#include "u_midi.h" MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); @@ -99,7 +101,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); /* B.3.1 Standard AC Interface Descriptor */ -static struct usb_interface_descriptor ac_interface_desc __initdata = { +static struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -110,7 +112,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = { }; /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { +static struct uac1_ac_header_descriptor_1 ac_header_desc = { .bLength = UAC_DT_AC_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, @@ -121,7 +123,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { }; /* B.4.1 Standard MS Interface Descriptor */ -static struct usb_interface_descriptor ms_interface_desc __initdata = { +static struct usb_interface_descriptor ms_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -132,7 +134,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = { }; /* B.4.2 Class-Specific MS Interface Descriptor */ -static struct usb_ms_header_descriptor ms_header_desc __initdata = { +static struct usb_ms_header_descriptor ms_header_desc = { .bLength = USB_DT_MS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, @@ -387,29 +389,6 @@ static void f_midi_disable(struct usb_function *f) usb_ep_disable(midi->out_ep); } -static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = f->config->cdev; - struct f_midi *midi = func_to_midi(f); - struct snd_card *card; - - DBG(cdev, "unbind\n"); - - /* just to be sure */ - f_midi_disable(f); - - card = midi->card; - midi->card = NULL; - if (card) - snd_card_free(card); - - kfree(midi->id); - midi->id = NULL; - - usb_free_all_descriptors(f); - kfree(midi); -} - static int f_midi_snd_free(struct snd_device *device) { return 0; @@ -654,6 +633,14 @@ static struct snd_rawmidi_ops gmidi_out_ops = { .trigger = f_midi_out_trigger }; +static inline void f_midi_unregister_card(struct f_midi *midi) +{ + if (midi->card) { + snd_card_free(midi->card); + midi->card = NULL; + } +} + /* register as a sound "card" */ static int f_midi_register_card(struct f_midi *midi) { @@ -715,17 +702,13 @@ static int f_midi_register_card(struct f_midi *midi) return 0; fail: - if (midi->card) { - snd_card_free(midi->card); - midi->card = NULL; - } + f_midi_unregister_card(midi); return err; } /* MIDI function driver setup/binding */ -static int __init -f_midi_bind(struct usb_configuration *c, struct usb_function *f) +static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_descriptor_header **midi_function; struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; @@ -734,15 +717,23 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS]; struct usb_composite_dev *cdev = c->cdev; struct f_midi *midi = func_to_midi(f); + struct usb_string *us; int status, n, jack = 1, i = 0; + midi->gadget = cdev->gadget; + tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); + status = f_midi_register_card(midi); + if (status < 0) + goto fail_register; + /* maybe allocate device-global string ID */ - if (midi_string_defs[0].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) - goto fail; - midi_string_defs[0].id = status; + us = usb_gstrings_attach(c->cdev, midi_strings, + ARRAY_SIZE(midi_string_defs)); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto fail; } + ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id; /* We have two interfaces, AudioControl and MIDIStreaming */ status = usb_interface_id(c, f); @@ -892,6 +883,8 @@ fail_f_midi: kfree(midi_function); usb_free_descriptors(f->hs_descriptors); fail: + f_midi_unregister_card(midi); +fail_register: /* we might as well release our claims on endpoints */ if (midi->out_ep) midi->out_ep->driver_data = NULL; @@ -903,42 +896,235 @@ fail: return status; } -/** - * f_midi_bind_config - add USB MIDI function to a configuration - * @c: the configuration to supcard the USB audio function - * @index: the soundcard index to use for the ALSA device creation - * @id: the soundcard id to use for the ALSA device creation - * @buflen: the buffer length to use - * @qlen the number of read requests to pre-allocate - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - */ -int __init f_midi_bind_config(struct usb_configuration *c, - int index, char *id, - unsigned int in_ports, - unsigned int out_ports, - unsigned int buflen, - unsigned int qlen) +static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_midi_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_midi_opts); +CONFIGFS_ATTR_OPS(f_midi_opts); + +static void midi_attr_release(struct config_item *item) +{ + struct f_midi_opts *opts = to_f_midi_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations midi_item_ops = { + .release = midi_attr_release, + .show_attribute = f_midi_opts_attr_show, + .store_attribute = f_midi_opts_attr_store, +}; + +#define F_MIDI_OPT(name, test_limit, limit) \ +static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + u32 num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou32(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (test_limit && num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_midi_opts_attribute f_midi_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \ + f_midi_opts_##name##_store) + +F_MIDI_OPT(index, true, SNDRV_CARDS); +F_MIDI_OPT(buflen, false, 0); +F_MIDI_OPT(qlen, false, 0); +F_MIDI_OPT(in_ports, true, MAX_PORTS); +F_MIDI_OPT(out_ports, true, MAX_PORTS); + +static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = strlcpy(page, opts->id, PAGE_SIZE); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts, + const char *page, size_t len) +{ + int ret; + char *c; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + c = kstrndup(page, len, GFP_KERNEL); + if (!c) { + ret = -ENOMEM; + goto end; + } + if (opts->id_allocated) + kfree(opts->id); + opts->id = c; + opts->id_allocated = true; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_midi_opts_attribute f_midi_opts_id = + __CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show, + f_midi_opts_id_store); + +static struct configfs_attribute *midi_attrs[] = { + &f_midi_opts_index.attr, + &f_midi_opts_buflen.attr, + &f_midi_opts_qlen.attr, + &f_midi_opts_in_ports.attr, + &f_midi_opts_out_ports.attr, + &f_midi_opts_id.attr, + NULL, +}; + +static struct config_item_type midi_func_type = { + .ct_item_ops = &midi_item_ops, + .ct_attrs = midi_attrs, + .ct_owner = THIS_MODULE, +}; + +static void f_midi_free_inst(struct usb_function_instance *f) +{ + struct f_midi_opts *opts; + + opts = container_of(f, struct f_midi_opts, func_inst); + + if (opts->id_allocated) + kfree(opts->id); + + kfree(opts); +} + +static struct usb_function_instance *f_midi_alloc_inst(void) +{ + struct f_midi_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = f_midi_free_inst; + opts->index = SNDRV_DEFAULT_IDX1; + opts->id = SNDRV_DEFAULT_STR1; + opts->buflen = 256; + opts->qlen = 32; + opts->in_ports = 1; + opts->out_ports = 1; + + config_group_init_type_name(&opts->func_inst.group, "", + &midi_func_type); + + return &opts->func_inst; +} + +static void f_midi_free(struct usb_function *f) +{ + struct f_midi *midi; + struct f_midi_opts *opts; + int i; + + midi = func_to_midi(f); + opts = container_of(f->fi, struct f_midi_opts, func_inst); + kfree(midi->id); + mutex_lock(&opts->lock); + for (i = opts->in_ports - 1; i >= 0; --i) + kfree(midi->in_port[i]); + kfree(midi); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_midi *midi = func_to_midi(f); + struct snd_card *card; + + DBG(cdev, "unbind\n"); + + /* just to be sure */ + f_midi_disable(f); + + card = midi->card; + midi->card = NULL; + if (card) + snd_card_free(card); + + usb_free_all_descriptors(f); +} + +static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) { struct f_midi *midi; + struct f_midi_opts *opts; int status, i; + opts = container_of(fi, struct f_midi_opts, func_inst); + + mutex_lock(&opts->lock); /* sanity check */ - if (in_ports > MAX_PORTS || out_ports > MAX_PORTS) - return -EINVAL; + if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) { + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } /* allocate and initialize one new instance */ - midi = kzalloc(sizeof *midi, GFP_KERNEL); + midi = kzalloc(sizeof(*midi), GFP_KERNEL); if (!midi) { - status = -ENOMEM; - goto fail; + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOMEM); } - for (i = 0; i < in_ports; i++) { + for (i = 0; i < opts->in_ports; i++) { struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) { status = -ENOMEM; + mutex_unlock(&opts->lock); goto setup_fail; } @@ -948,39 +1134,37 @@ int __init f_midi_bind_config(struct usb_configuration *c, midi->in_port[i] = port; } - midi->gadget = c->cdev->gadget; - tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); - /* set up ALSA midi devices */ - midi->in_ports = in_ports; - midi->out_ports = out_ports; - status = f_midi_register_card(midi); - if (status < 0) - goto setup_fail; - - midi->func.name = "gmidi function"; - midi->func.strings = midi_strings; - midi->func.bind = f_midi_bind; - midi->func.unbind = f_midi_unbind; - midi->func.set_alt = f_midi_set_alt; - midi->func.disable = f_midi_disable; - - midi->id = kstrdup(id, GFP_KERNEL); - midi->index = index; - midi->buflen = buflen; - midi->qlen = qlen; - - status = usb_add_function(c, &midi->func); - if (status) - goto setup_fail; - - return 0; - + midi->id = kstrdup(opts->id, GFP_KERNEL); + if (opts->id && !midi->id) { + status = -ENOMEM; + mutex_unlock(&opts->lock); + goto kstrdup_fail; + } + midi->in_ports = opts->in_ports; + midi->out_ports = opts->out_ports; + midi->index = opts->index; + midi->buflen = opts->buflen; + midi->qlen = opts->qlen; + ++opts->refcnt; + mutex_unlock(&opts->lock); + + midi->func.name = "gmidi function"; + midi->func.bind = f_midi_bind; + midi->func.unbind = f_midi_unbind; + midi->func.set_alt = f_midi_set_alt; + midi->func.disable = f_midi_disable; + midi->func.free_func = f_midi_free; + + return &midi->func; + +kstrdup_fail: + f_midi_unregister_card(midi); setup_fail: for (--i; i >= 0; i--) kfree(midi->in_port[i]); kfree(midi); -fail: - return status; + return ERR_PTR(status); } +DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc); diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 16361b0a8b46..bdcda9f5148e 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1441,6 +1441,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, NULL); + if (status) + goto fail; + /* * NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index f13fc6a58565..829edf878dac 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -375,8 +375,7 @@ static struct sk_buff *rndis_add_header(struct gether *port, struct sk_buff *skb2; skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); - if (skb2) - rndis_add_hdr(skb2); + rndis_add_hdr(skb2); dev_kfree_skb(skb); return skb2; diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h new file mode 100644 index 000000000000..aaa0e368a159 --- /dev/null +++ b/drivers/usb/gadget/function/u_hid.h @@ -0,0 +1,42 @@ +/* + * u_hid.h + * + * Utility definitions for the hid function + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_HID_H +#define U_HID_H + +#include <linux/usb/composite.h> + +struct f_hid_opts { + struct usb_function_instance func_inst; + int minor; + unsigned char subclass; + unsigned char protocol; + unsigned short report_length; + unsigned short report_desc_length; + unsigned char *report_desc; + bool report_desc_alloc; + + /* + * Protect the data form concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +int ghid_setup(struct usb_gadget *g, int count); +void ghid_cleanup(void); + +#endif /* U_HID_H */ diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h new file mode 100644 index 000000000000..22510189758e --- /dev/null +++ b/drivers/usb/gadget/function/u_midi.h @@ -0,0 +1,40 @@ +/* + * u_midi.h + * + * Utility definitions for the midi function + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_MIDI_H +#define U_MIDI_H + +#include <linux/usb/composite.h> + +struct f_midi_opts { + struct usb_function_instance func_inst; + int index; + char *id; + bool id_allocated; + unsigned int in_ports; + unsigned int out_ports; + unsigned int buflen; + unsigned int qlen; + + /* + * Protect the data form concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +#endif /* U_MIDI_H */ + diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index a44a07f30281..53842a1b947f 100644 --- a/drivers/usb/gadget/function/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c @@ -213,9 +213,6 @@ static int gaudio_open_snd_dev(struct gaudio *card) fn_cap = opts->fn_cap; fn_cntl = opts->fn_cntl; - if (!card) - return -ENODEV; - /* Open control device */ snd = &card->control; snd->filp = filp_open(fn_cntl, O_RDWR, 0); |