Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/gadget/Kconfig | 4 + drivers/usb/gadget/Makefile | 3 + drivers/usb/gadget/f_sourcesink.c | 193 ++++++++++++++++++++++++++----------- drivers/usb/gadget/zero.c | 97 +++++++++++++++++-- 4 files changed, 235 insertions(+), 62 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d381d720..881cd63 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -500,6 +500,9 @@ config USB_LIBCOMPOSITE tristate depends on USB_GADGET +config USB_F_SOURCESINK + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -524,6 +527,7 @@ choice config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE + select USB_F_SOURCESINK help Gadget Zero is a two-configuration device. It either sinks and sources bulk data; or it loops back a configurable number of diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index fa65050..a68f306 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -76,3 +76,6 @@ obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o + +# USB Functions +obj-$(CONFIG_USB_F_SOURCESINK) += f_sourcesink.o diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 1afe562..8e5591e 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -16,11 +16,10 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/module.h> +#include <linux/usb/composite.h> -#include "g_zero.h" #include "gadget_chips.h" - /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral * controller drivers. @@ -62,24 +61,11 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f) } static unsigned pattern; -module_param(pattern, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none"); - -static unsigned isoc_interval = 4; -module_param(isoc_interval, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_interval, "1 - 16"); - -static unsigned isoc_maxpacket = 1024; -module_param(isoc_maxpacket, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); - +static unsigned isoc_interval; +static unsigned isoc_maxpacket; static unsigned isoc_mult; -module_param(isoc_mult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)"); - static unsigned isoc_maxburst; -module_param(isoc_maxburst, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); +static unsigned buflen; /*-------------------------------------------------------------------------*/ @@ -313,7 +299,75 @@ static struct usb_gadget_strings *sourcesink_strings[] = { /*-------------------------------------------------------------------------*/ -static int __init +static struct usb_request *alloc_ep_req(struct usb_ep *ep, int len) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + if (len) + req->length = len; + else + req->length = buflen; + req->buf = kmalloc(req->length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) +{ + int value; + + if (ep->driver_data) { + value = usb_ep_disable(ep); + if (value < 0) + DBG(cdev, "disable %s --> %d\n", + ep->name, value); + ep->driver_data = NULL; + } +} + +static void disable_endpoints(struct usb_composite_dev *cdev, + struct usb_ep *in, struct usb_ep *out, + struct usb_ep *iso_in, struct usb_ep *iso_out) +{ + disable_ep(cdev, in); + disable_ep(cdev, out); + if (iso_in) + disable_ep(cdev, iso_in); + if (iso_out) + disable_ep(cdev, iso_out); +} + +static int ss_check_param(void) +{ + if (pattern > 2) + return -EINVAL; + if (isoc_interval > 16 || isoc_interval == 0) + return -EINVAL; + if (isoc_maxpacket > 1024) + return -EINVAL; + if (isoc_mult > 2) + return -EINVAL; + if (isoc_maxburst > 15) + return -EINVAL; + if (!buflen) + return -EINVAL; + return 0; +} + + +static int sourcesink_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; @@ -321,6 +375,10 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) int id; int ret; + ret = ss_check_param(); + if (ret) + return ret; + /* allocate interface ID(s) */ id = usb_interface_id(c, f); if (id < 0) @@ -328,14 +386,6 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) source_sink_intf_alt0.bInterfaceNumber = id; source_sink_intf_alt1.bInterfaceNumber = id; - /* allocate string ID(s) */ - id = usb_string_id(cdev); - if (id < 0) - return id; - strings_sourcesink[0].id = id; - source_sink_intf_alt0.iInterface = id; - source_sink_intf_alt1.iInterface = id; - /* allocate bulk endpoints */ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); if (!ss->in_ep) { @@ -457,14 +507,11 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) return 0; } -static struct usb_function *global_ss_func; - static void -sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) +sourcesink_free_func(struct usb_function *f) { usb_free_all_descriptors(f); kfree(func_to_ss(f)); - global_ss_func = NULL; } /* optionally require specific source/sink data patterns */ @@ -768,6 +815,54 @@ static void sourcesink_disable(struct usb_function *f) } /*-------------------------------------------------------------------------*/ + +static const struct usbf_option fss_options[] = { + { + .type = USBF_OPTION_INT, + .name = "pattern", + }, { + .type = USBF_OPTION_INT, + .name = "isoc_interval", + }, { + .type = USBF_OPTION_INT, + .name = "isoc_maxpacket", + }, { + .type = USBF_OPTION_INT, + .name = "isoc_mult", + }, { + .type = USBF_OPTION_INT, + .name = "isoc_maxburst", + }, { + .type = USBF_OPTION_INT, + .name = "bulk_buflen", + }, +}; + +static int ss_configure(struct usb_function *f, struct usbf_option *options, + int num) +{ + int i; + + for (i = 0; i < num; i++) { + if (!strcmp("pattern", options->name)) + pattern = options->val.o_int; + else if (!strcmp("isoc_interval", options->name)) + isoc_interval = options->val.o_int; + else if (!strcmp("isoc_maxpacket", options->name)) + isoc_maxpacket = options->val.o_int; + else if (!strcmp("isoc_mult", options->name)) + isoc_mult = options->val.o_int; + else if (!strcmp("isoc_maxburst", options->name)) + isoc_maxburst = options->val.o_int; + else if (!strcmp("bulk_buflen", options->name)) + buflen = options->val.o_int; + else + return -EINVAL; + options++; + } + return 0; +} + static int sourcesink_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -840,41 +935,29 @@ static int sourcesink_setup(struct usb_function *f, return value; } -static int __init sourcesink_bind_config(struct usb_configuration *c) +static struct usb_function *source_sink_alloc(void) { - struct f_sourcesink *ss; - int status; + struct f_sourcesink *ss; ss = kzalloc(sizeof(*ss), GFP_KERNEL); if (!ss) - return -ENOMEM; - - global_ss_func = &ss->function; + return NULL; ss->function.name = "source/sink"; ss->function.bind = sourcesink_bind; - ss->function.unbind = sourcesink_unbind; ss->function.set_alt = sourcesink_set_alt; ss->function.get_alt = sourcesink_get_alt; ss->function.disable = sourcesink_disable; ss->function.setup = sourcesink_setup; + ss->function.strings = sourcesink_strings; - status = usb_add_function(c, &ss->function); - if (status) - kfree(ss); - return status; -} + ss->function.avail_options = fss_options; + ss->function.configure = ss_configure; + ss->function.avail_options_num = ARRAY_SIZE(fss_options); + ss->function.free_func = sourcesink_free_func; -static int ss_config_setup(struct usb_configuration *c, - const struct usb_ctrlrequest *ctrl) -{ - if (!global_ss_func) - return -EOPNOTSUPP; - switch (ctrl->bRequest) { - case 0x5b: - case 0x5c: - return global_ss_func->setup(global_ss_func, ctrl); - default: - return -EOPNOTSUPP; - } + return &ss->function; } + +DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 8ba0bee..bf4e37f 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -43,11 +43,12 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/device.h> +#include <linux/module.h> +#include <linux/err.h> #include "g_zero.h" #include "gadget_chips.h" - /*-------------------------------------------------------------------------*/ /* @@ -57,7 +58,6 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "f_sourcesink.c" #include "f_loopback.c" /*-------------------------------------------------------------------------*/ @@ -264,15 +264,80 @@ static struct usb_configuration loopback_driver = { /* .iConfiguration = DYNAMIC */ }; +static struct usb_function *func_ss; +static int ss_config_setup(struct usb_configuration *c, + const struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequest) { + case 0x5b: + case 0x5c: + return func_ss->setup(func_ss, ctrl); + default: + return -EOPNOTSUPP; + } +} + static struct usb_configuration sourcesink_driver = { .label = "source/sink", - .strings = sourcesink_strings, .setup = ss_config_setup, .bConfigurationValue = 3, .bmAttributes = USB_CONFIG_ATT_SELFPOWER, /* .iConfiguration = DYNAMIC */ }; +enum { + SS_OPT_PATTERN, + SS_OPT_ISOC_INT, + SS_OPT_ISOC_MAXP, + SS_OPT_ISOC_MULT, + SS_OPT_ISOC_MAXBURST, + SS_OPT_BULK_BUFLEN, +}; + +static struct usbf_option fss_options[] = { + [SS_OPT_PATTERN] = { + .name = "pattern", + }, + [SS_OPT_ISOC_INT] = { + .name = "isoc_interval", + .val.o_int = 4, + }, + [SS_OPT_ISOC_MAXP] = { + .name = "isoc_maxpacket", + .val.o_int = 1024, + }, + [SS_OPT_ISOC_MULT] = { + .name = "isoc_mult", + }, + [SS_OPT_ISOC_MAXBURST] = { + .name = "isoc_maxburst", + }, + [SS_OPT_BULK_BUFLEN] = { + .name = "bulk_buflen", + .val.o_int = 4096, + }, +}; + +module_param_named(pattern, fss_options[SS_OPT_PATTERN].val.o_int, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none"); + +module_param_named(isoc_interval, fss_options[SS_OPT_ISOC_INT].val.o_int, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_interval, "1 - 16"); + +module_param_named(isoc_maxpacket, fss_options[SS_OPT_ISOC_MAXP].val.o_int, + uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); + +module_param_named(isoc_mult, fss_options[SS_OPT_ISOC_MULT].val.o_int, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)"); + +module_param_named(isoc_maxburst, fss_options[SS_OPT_ISOC_MAXBURST].val.o_int, + uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); + static int __init zero_bind(struct usb_composite_dev *cdev) { int status; @@ -290,6 +355,16 @@ static int __init zero_bind(struct usb_composite_dev *cdev) setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev); + func_ss = usb_get_function("SourceSink"); + if (IS_ERR(func_ss)) + return PTR_ERR(func_ss); + + fss_options[SS_OPT_BULK_BUFLEN].val.o_int = buflen; + + status = usbf_configure(func_ss, fss_options); + if (status) + goto err_conf_fss; + sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id; loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id; @@ -316,24 +391,32 @@ static int __init zero_bind(struct usb_composite_dev *cdev) */ if (loopdefault) { usb_add_config(cdev, &loopback_driver, loopback_bind_config); - usb_add_config(cdev, &sourcesink_driver, - sourcesink_bind_config); + usb_add_config_only(cdev, &sourcesink_driver); } else { - usb_add_config(cdev, &sourcesink_driver, - sourcesink_bind_config); + usb_add_config_only(cdev, &sourcesink_driver); usb_add_config(cdev, &loopback_driver, loopback_bind_config); } + status = usb_add_function(&sourcesink_driver, func_ss); + if (status) + goto err_conf_fss; + usb_ep_autoconfig_reset(cdev->gadget); usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); return 0; +err_conf_fss: + usb_put_function(func_ss); + func_ss = NULL; + return status; } static int zero_unbind(struct usb_composite_dev *cdev) { del_timer_sync(&autoresume_timer); + if (!IS_ERR_OR_NULL(func_ss)) + usb_put_function(func_ss); return 0; } -- 1.7.10.4 -- 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