diff options
Diffstat (limited to 'drivers/usb/gadget')
99 files changed, 2917 insertions, 958 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b3592bcb0f96..76521555e3c1 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -190,6 +190,7 @@ config USB_F_MASS_STORAGE tristate config USB_F_FS + select DMA_SHARED_BUFFER tristate config USB_F_UAC1 @@ -210,6 +211,8 @@ config USB_F_MIDI config USB_F_MIDI2 tristate + select SND_UMP + select SND_UMP_LEGACY_RAWMIDI config USB_F_HID tristate @@ -444,8 +447,6 @@ config USB_CONFIGFS_F_MIDI2 depends on USB_CONFIGFS depends on SND select USB_LIBCOMPOSITE - select SND_UMP - select SND_UMP_LEGACY_RAWMIDI select USB_F_MIDI2 help The MIDI 2.0 function driver provides the generic emulated diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 0ace45b66a31..869ad99afb48 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -19,7 +19,7 @@ #include <linux/usb/composite.h> #include <linux/usb/otg.h> #include <linux/usb/webusb.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "u_os_desc.h" @@ -1050,10 +1050,11 @@ static int set_config(struct usb_composite_dev *cdev, else usb_gadget_set_remote_wakeup(gadget, 0); done: - if (power <= USB_SELF_POWER_VBUS_MAX_DRAW) - usb_gadget_set_selfpowered(gadget); - else + if (power > USB_SELF_POWER_VBUS_MAX_DRAW || + (c && !(c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))) usb_gadget_clear_selfpowered(gadget); + else + usb_gadget_set_selfpowered(gadget); usb_gadget_vbus_draw(gadget, power); if (result >= 0 && cdev->delayed_status) @@ -1844,7 +1845,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->desc.bcdUSB = cpu_to_le16(0x0200); } - value = min(w_length, (u16) sizeof cdev->desc); + value = min_t(u16, w_length, sizeof(cdev->desc)); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: @@ -1863,19 +1864,19 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_STRING: value = get_string(cdev, req->buf, w_index, w_value & 0xff); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_BOS: if (gadget_is_superspeed(gadget) || gadget->lpm_capable || cdev->use_webusb) { value = bos_desc(cdev); - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); } break; case USB_DT_OTG: @@ -1930,7 +1931,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *(u8 *)req->buf = cdev->config->bConfigurationValue; else *(u8 *)req->buf = 0; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; /* function drivers must handle get/set altsetting */ @@ -1976,7 +1977,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value < 0) break; *((u8 *)req->buf) = value; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; case USB_REQ_GET_STATUS: if (gadget_is_otg(gadget) && gadget->hnp_polling_support && @@ -2111,6 +2112,18 @@ unknown: memset(buf, 0, w_length); buf[5] = 0x01; switch (ctrl->bRequestType & USB_RECIP_MASK) { + /* + * The Microsoft CompatID OS Descriptor Spec(w_index = 0x4) and + * Extended Prop OS Desc Spec(w_index = 0x5) state that the + * HighByte of wValue is the InterfaceNumber and the LowByte is + * the PageNumber. This high/low byte ordering is incorrectly + * documented in the Spec. USB analyzer output on the below + * request packets show the high/low byte inverted i.e LowByte + * is the InterfaceNumber and the HighByte is the PageNumber. + * Since we dont support >64KB CompatID/ExtendedProp descriptors, + * PageNumber is set to 0. Hence verify that the HighByte is 0 + * for below two cases. + */ case USB_RECIP_DEVICE: if (w_index != 0x4 || (w_value >> 8)) break; @@ -2603,7 +2616,10 @@ void composite_suspend(struct usb_gadget *gadget) cdev->suspended = 1; - usb_gadget_set_selfpowered(gadget); + if (cdev->config && + cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER) + usb_gadget_set_selfpowered(gadget); + usb_gadget_vbus_draw(gadget, 2); } @@ -2637,8 +2653,11 @@ void composite_resume(struct usb_gadget *gadget) else maxpower = min(maxpower, 900U); - if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW) + if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW || + !(cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) usb_gadget_clear_selfpowered(gadget); + else + usb_gadget_set_selfpowered(gadget); usb_gadget_vbus_draw(gadget, maxpower); } else { @@ -2799,5 +2818,6 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev, } EXPORT_SYMBOL_GPL(usb_composite_overwrite_options); +MODULE_DESCRIPTION("infrastructure for Composite USB Gadgets"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b1f625245713..95f144a54ed9 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -57,11 +57,11 @@ EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf); * usb_gadget_config_buf - builts a complete configuration descriptor * @config: Header for the descriptor, including characteristics such * as power requirements and number of interfaces. - * @desc: Null-terminated vector of pointers to the descriptors (interface, - * endpoint, etc) defining all functions in this device configuration. * @buf: Buffer for the resulting configuration descriptor. * @length: Length of buffer. If this is not big enough to hold the * entire configuration descriptor, an error code will be returned. + * @desc: Null-terminated vector of pointers to the descriptors (interface, + * endpoint, etc) defining all functions in this device configuration. * * This copies descriptors into the response buffer, building a descriptor * for that configuration. It returns the buffer length or a negative diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index ce3cfa1f36f5..fba2a56dae97 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -6,13 +6,13 @@ #include <linux/kstrtox.h> #include <linux/nls.h> #include <linux/usb/composite.h> +#include <linux/usb/func_utils.h> #include <linux/usb/gadget_configfs.h> #include <linux/usb/webusb.h> #include "configfs.h" -#include "u_f.h" #include "u_os_desc.h" -int check_user_usb_string(const char *name, +static int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) { u16 num; @@ -115,9 +115,12 @@ static int usb_string_copy(const char *s, char **s_copy) int ret; char *str; char *copy = *s_copy; + ret = strlen(s); if (ret > USB_MAX_STRING_LEN) return -EOVERFLOW; + if (ret < 1) + return -EINVAL; if (copy) { str = copy; @@ -824,11 +827,15 @@ static ssize_t gadget_string_s_store(struct config_item *item, const char *page, { struct gadget_string *string = to_gadget_string(item); int size = min(sizeof(string->string), len + 1); + ssize_t cpy_len; if (len > USB_MAX_STRING_LEN) return -EINVAL; - return strscpy(string->string, page, size); + cpy_len = strscpy(string->string, page, size); + if (cpy_len > 0 && string->string[cpy_len - 1] == '\n') + string->string[cpy_len - 1] = 0; + return len; } CONFIGFS_ATTR(gadget_string_, s); @@ -899,7 +906,7 @@ static struct configfs_group_operations gadget_language_langid_group_ops = { .drop_item = gadget_language_string_drop, }; -static struct config_item_type gadget_language_type = { +static const struct config_item_type gadget_language_type = { .ct_item_ops = &gadget_language_langid_item_ops, .ct_group_ops = &gadget_language_langid_group_ops, .ct_attrs = gadget_language_langid_attrs, @@ -958,7 +965,7 @@ static struct configfs_group_operations gadget_language_group_ops = { .drop_item = &gadget_language_drop, }; -static struct config_item_type gadget_language_strings_type = { +static const struct config_item_type gadget_language_strings_type = { .ct_group_ops = &gadget_language_group_ops, .ct_owner = THIS_MODULE, }; @@ -1103,7 +1110,7 @@ static struct configfs_attribute *webusb_attrs[] = { NULL, }; -static struct config_item_type webusb_type = { +static const struct config_item_type webusb_type = { .ct_attrs = webusb_attrs, .ct_owner = THIS_MODULE, }; @@ -1181,7 +1188,7 @@ static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, struct gadget_info *gi = os_desc_item_to_gadget_info(item); int res, l; - l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); + l = min_t(int, len, OS_STRING_QW_SIGN_LEN >> 1); if (page[l - 1] == '\n') --l; @@ -1260,7 +1267,7 @@ static struct configfs_item_operations os_desc_ops = { .drop_link = os_desc_unlink, }; -static struct config_item_type os_desc_type = { +static const struct config_item_type os_desc_type = { .ct_item_ops = &os_desc_ops, .ct_attrs = os_desc_attrs, .ct_owner = THIS_MODULE, diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 87917a7d4a9b..7ce1637276f0 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -41,6 +41,10 @@ obj-$(CONFIG_USB_F_UAC1_LEGACY) += usb_f_uac1_legacy.o 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 uvc_configfs.o +ifneq ($(CONFIG_TRACING),) + CFLAGS_uvc_trace.o := -I$(src) + usb_f_uvc-y += uvc_trace.o +endif 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 diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index f616059c5e1e..7061720b9732 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -41,6 +41,7 @@ struct f_acm { struct gserial port; u8 ctrl_id, data_id; u8 port_num; + u8 bInterfaceProtocol; u8 pending; @@ -89,7 +90,7 @@ acm_iad_descriptor = { .bInterfaceCount = 2, // control + data .bFunctionClass = USB_CLASS_COMM, .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + /* .bFunctionProtocol = DYNAMIC */ /* .iFunction = DYNAMIC */ }; @@ -101,7 +102,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = { .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + /* .bInterfaceProtocol = DYNAMIC */ /* .iInterface = DYNAMIC */ }; @@ -663,6 +664,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) goto fail; acm->notify = ep; + acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol; + acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol; + /* allocate notification */ acm->notify_req = gs_alloc_req(ep, sizeof(struct usb_cdc_notification) + 2, @@ -719,8 +723,14 @@ static void acm_unbind(struct usb_configuration *c, struct usb_function *f) static void acm_free_func(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); + struct f_serial_opts *opts; + + opts = container_of(f->fi, struct f_serial_opts, func_inst); kfree(acm); + mutex_lock(&opts->lock); + opts->instances--; + mutex_unlock(&opts->lock); } static void acm_resume(struct usb_function *f) @@ -761,7 +771,11 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) acm->port.func.disable = acm_disable; opts = container_of(fi, struct f_serial_opts, func_inst); + mutex_lock(&opts->lock); acm->port_num = opts->port_num; + acm->bInterfaceProtocol = opts->protocol; + opts->instances++; + mutex_unlock(&opts->lock); acm->port.func.unbind = acm_unbind; acm->port.func.free_func = acm_free_func; acm->port.func.resume = acm_resume; @@ -812,11 +826,42 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page) CONFIGFS_ATTR_RO(f_acm_, port_num); +static ssize_t f_acm_protocol_show(struct config_item *item, char *page) +{ + return sprintf(page, "%u\n", to_f_serial_opts(item)->protocol); +} + +static ssize_t f_acm_protocol_store(struct config_item *item, + const char *page, size_t count) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + int ret; + + mutex_lock(&opts->lock); + + if (opts->instances) { + ret = -EBUSY; + goto out; + } + + ret = kstrtou8(page, 0, &opts->protocol); + if (ret) + goto out; + ret = count; + +out: + mutex_unlock(&opts->lock); + return ret; +} + +CONFIGFS_ATTR(f_acm_, protocol); + static struct configfs_attribute *acm_attrs[] = { #ifdef CONFIG_U_SERIAL_CONSOLE &f_acm_attr_console, #endif &f_acm_attr_port_num, + &f_acm_attr_protocol, NULL, }; @@ -832,6 +877,7 @@ static void acm_free_instance(struct usb_function_instance *fi) opts = container_of(fi, struct f_serial_opts, func_inst); gserial_free_line(opts->port_num); + mutex_destroy(&opts->lock); kfree(opts); } @@ -843,7 +889,9 @@ static struct usb_function_instance *acm_alloc_instance(void) opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); + opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER; opts->func_inst.free_func_inst = acm_free_instance; + mutex_init(&opts->lock); ret = gserial_alloc_line(&opts->port_num); if (ret) { kfree(opts); @@ -854,4 +902,5 @@ static struct usb_function_instance *acm_alloc_instance(void) return &opts->func_inst; } DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); +MODULE_DESCRIPTION("USB CDC serial (ACM) function driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index f55f60639e42..80841de845b0 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> +#include <linux/string_choices.h> #include "u_ether.h" #include "u_ether_configfs.h" @@ -387,8 +388,7 @@ static void ecm_do_notify(struct f_ecm *ecm) event->wLength = 0; req->length = sizeof *event; - DBG(cdev, "notify connect %s\n", - ecm->is_open ? "true" : "false"); + DBG(cdev, "notify connect %s\n", str_true_false(ecm->is_open)); ecm->notify_state = ECM_NOTIFY_SPEED; break; @@ -966,5 +966,6 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); +MODULE_DESCRIPTION("USB CDC Ethernet (ECM) link function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index 3b445bd88498..6de81ea17274 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -674,5 +674,6 @@ static struct usb_function *eem_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc); +MODULE_DESCRIPTION("USB CDC Ethernet (EEM) link function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 6bff6cb93789..2dea9e42a0f8 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -15,6 +15,9 @@ /* #define VERBOSE_DEBUG */ #include <linux/blkdev.h> +#include <linux/dma-buf.h> +#include <linux/dma-fence.h> +#include <linux/dma-resv.h> #include <linux/pagemap.h> #include <linux/export.h> #include <linux/fs_parser.h> @@ -25,11 +28,12 @@ #include <linux/sched/signal.h> #include <linux/uio.h> #include <linux/vmalloc.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/usb/ccid.h> #include <linux/usb/composite.h> #include <linux/usb/functionfs.h> +#include <linux/usb/func_utils.h> #include <linux/aio.h> #include <linux/kthread.h> @@ -37,11 +41,15 @@ #include <linux/eventfd.h> #include "u_fs.h" -#include "u_f.h" #include "u_os_desc.h" #include "configfs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ +#define MAX_ALT_SETTINGS 2 /* Allow up to 2 alt settings to be set. */ + +#define DMABUF_ENQUEUE_TIMEOUT_MS 5000 + +MODULE_IMPORT_NS("DMA_BUF"); /* Reference counter handling */ static void ffs_data_get(struct ffs_data *ffs); @@ -75,6 +83,7 @@ struct ffs_function { short *interfaces_nums; struct usb_function function; + int cur_alt[MAX_CONFIG_INTERFACES]; }; @@ -98,6 +107,7 @@ static int __must_check ffs_func_eps_enable(struct ffs_function *func); static int ffs_func_bind(struct usb_configuration *, struct usb_function *); static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); +static int ffs_func_get_alt(struct usb_function *f, unsigned int intf); static void ffs_func_disable(struct usb_function *); static int ffs_func_setup(struct usb_function *, const struct usb_ctrlrequest *); @@ -124,6 +134,25 @@ struct ffs_ep { u8 num; }; +struct ffs_dmabuf_priv { + struct list_head entry; + struct kref ref; + struct ffs_data *ffs; + struct dma_buf_attachment *attach; + struct sg_table *sgt; + enum dma_data_direction dir; + spinlock_t lock; + u64 context; + struct usb_request *req; /* P: ffs->eps_lock */ + struct usb_ep *ep; /* P: ffs->eps_lock */ +}; + +struct ffs_dma_fence { + struct dma_fence base; + struct ffs_dmabuf_priv *priv; + struct work_struct work; +}; + struct ffs_epfile { /* Protects ep->ep and ep->req. */ struct mutex mutex; @@ -197,6 +226,11 @@ struct ffs_epfile { unsigned char isoc; /* P: ffs->eps_lock */ unsigned char _pad; + + /* Protects dmabufs */ + struct mutex dmabufs_mutex; + struct list_head dmabufs; /* P: dmabufs_mutex */ + atomic_t seqno; }; struct ffs_buffer { @@ -422,7 +456,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, } /* FFS_SETUP_PENDING and not stall */ - len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength)); spin_unlock_irq(&ffs->ev.waitq.lock); @@ -556,7 +590,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ return __ffs_ep0_read_events(ffs, buf, - min(n, (size_t)ffs->ev.count)); + min_t(size_t, n, ffs->ev.count)); case FFS_SETUP_PENDING: if (ffs->ev.setup.bRequestType & USB_DIR_IN) { @@ -565,7 +599,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, goto done_mutex; } - len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength)); spin_unlock_irq(&ffs->ev.waitq.lock); @@ -688,7 +722,6 @@ static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait) } static const struct file_operations ffs_ep0_operations = { - .llseek = no_llseek, .open = ffs_ep0_open, .write = ffs_ep0_write, @@ -821,6 +854,7 @@ static void ffs_user_copy_worker(struct work_struct *work) work); int ret = io_data->status; bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD; + unsigned long flags; if (io_data->read && ret > 0) { kthread_use_mm(io_data->mm); @@ -833,6 +867,11 @@ static void ffs_user_copy_worker(struct work_struct *work) if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd) eventfd_signal(io_data->ffs->ffs_eventfd); + spin_lock_irqsave(&io_data->ffs->eps_lock, flags); + usb_ep_free_request(io_data->ep, io_data->req); + io_data->req = NULL; + spin_unlock_irqrestore(&io_data->ffs->eps_lock, flags); + if (io_data->read) kfree(io_data->to_free); ffs_free_buffer(io_data); @@ -846,7 +885,6 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep, struct ffs_data *ffs = io_data->ffs; io_data->status = req->status ? req->status : req->actual; - usb_ep_free_request(_ep, req); INIT_WORK(&io_data->work, ffs_user_copy_worker); queue_work(ffs->io_completion_wq, &io_data->work); @@ -934,31 +972,44 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, return ret; } -static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) +static struct ffs_ep *ffs_epfile_wait_ep(struct file *file) { struct ffs_epfile *epfile = file->private_data; - struct usb_request *req; struct ffs_ep *ep; - char *data = NULL; - ssize_t ret, data_len = -EINVAL; - int halt; - - /* Are we still active? */ - if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) - return -ENODEV; + int ret; /* Wait for endpoint to be enabled */ ep = epfile->ep; if (!ep) { if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + return ERR_PTR(-EAGAIN); ret = wait_event_interruptible( epfile->ffs->wait, (ep = epfile->ep)); if (ret) - return -EINTR; + return ERR_PTR(-EINTR); } + return ep; +} + +static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) +{ + struct ffs_epfile *epfile = file->private_data; + struct usb_request *req; + struct ffs_ep *ep; + char *data = NULL; + ssize_t ret, data_len = -EINVAL; + int halt; + + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + + ep = ffs_epfile_wait_ep(file); + if (IS_ERR(ep)) + return PTR_ERR(ep); + /* Do we halt? */ halt = (!io_data->read == !epfile->in); if (halt && epfile->isoc) @@ -1258,10 +1309,58 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) return res; } +static void ffs_dmabuf_release(struct kref *ref) +{ + struct ffs_dmabuf_priv *priv = container_of(ref, struct ffs_dmabuf_priv, ref); + struct dma_buf_attachment *attach = priv->attach; + struct dma_buf *dmabuf = attach->dmabuf; + + pr_vdebug("FFS DMABUF release\n"); + dma_resv_lock(dmabuf->resv, NULL); + dma_buf_unmap_attachment(attach, priv->sgt, priv->dir); + dma_resv_unlock(dmabuf->resv); + + dma_buf_detach(attach->dmabuf, attach); + dma_buf_put(dmabuf); + kfree(priv); +} + +static void ffs_dmabuf_get(struct dma_buf_attachment *attach) +{ + struct ffs_dmabuf_priv *priv = attach->importer_priv; + + kref_get(&priv->ref); +} + +static void ffs_dmabuf_put(struct dma_buf_attachment *attach) +{ + struct ffs_dmabuf_priv *priv = attach->importer_priv; + + kref_put(&priv->ref, ffs_dmabuf_release); +} + static int ffs_epfile_release(struct inode *inode, struct file *file) { struct ffs_epfile *epfile = inode->i_private; + struct ffs_dmabuf_priv *priv, *tmp; + struct ffs_data *ffs = epfile->ffs; + + mutex_lock(&epfile->dmabufs_mutex); + + /* Close all attached DMABUFs */ + list_for_each_entry_safe(priv, tmp, &epfile->dmabufs, entry) { + /* Cancel any pending transfer */ + spin_lock_irq(&ffs->eps_lock); + if (priv->ep && priv->req) + usb_ep_dequeue(priv->ep, priv->req); + spin_unlock_irq(&ffs->eps_lock); + + list_del(&priv->entry); + ffs_dmabuf_put(priv->attach); + } + + mutex_unlock(&epfile->dmabufs_mutex); __ffs_epfile_read_buffer_free(epfile); ffs_data_closed(epfile->ffs); @@ -1269,6 +1368,357 @@ ffs_epfile_release(struct inode *inode, struct file *file) return 0; } +static void ffs_dmabuf_cleanup(struct work_struct *work) +{ + struct ffs_dma_fence *dma_fence = + container_of(work, struct ffs_dma_fence, work); + struct ffs_dmabuf_priv *priv = dma_fence->priv; + struct dma_buf_attachment *attach = priv->attach; + struct dma_fence *fence = &dma_fence->base; + + ffs_dmabuf_put(attach); + dma_fence_put(fence); +} + +static void ffs_dmabuf_signal_done(struct ffs_dma_fence *dma_fence, int ret) +{ + struct ffs_dmabuf_priv *priv = dma_fence->priv; + struct dma_fence *fence = &dma_fence->base; + bool cookie = dma_fence_begin_signalling(); + + dma_fence_get(fence); + fence->error = ret; + dma_fence_signal(fence); + dma_fence_end_signalling(cookie); + + /* + * The fence will be unref'd in ffs_dmabuf_cleanup. + * It can't be done here, as the unref functions might try to lock + * the resv object, which would deadlock. + */ + INIT_WORK(&dma_fence->work, ffs_dmabuf_cleanup); + queue_work(priv->ffs->io_completion_wq, &dma_fence->work); +} + +static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep, + struct usb_request *req) +{ + pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status); + ffs_dmabuf_signal_done(req->context, req->status); + usb_ep_free_request(ep, req); +} + +static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence) +{ + return "functionfs"; +} + +static const char *ffs_dmabuf_get_timeline_name(struct dma_fence *fence) +{ + return ""; +} + +static void ffs_dmabuf_fence_release(struct dma_fence *fence) +{ + struct ffs_dma_fence *dma_fence = + container_of(fence, struct ffs_dma_fence, base); + + kfree(dma_fence); +} + +static const struct dma_fence_ops ffs_dmabuf_fence_ops = { + .get_driver_name = ffs_dmabuf_get_driver_name, + .get_timeline_name = ffs_dmabuf_get_timeline_name, + .release = ffs_dmabuf_fence_release, +}; + +static int ffs_dma_resv_lock(struct dma_buf *dmabuf, bool nonblock) +{ + if (!nonblock) + return dma_resv_lock_interruptible(dmabuf->resv, NULL); + + if (!dma_resv_trylock(dmabuf->resv)) + return -EBUSY; + + return 0; +} + +static struct dma_buf_attachment * +ffs_dmabuf_find_attachment(struct ffs_epfile *epfile, struct dma_buf *dmabuf) +{ + struct device *dev = epfile->ffs->gadget->dev.parent; + struct dma_buf_attachment *attach = NULL; + struct ffs_dmabuf_priv *priv; + + mutex_lock(&epfile->dmabufs_mutex); + + list_for_each_entry(priv, &epfile->dmabufs, entry) { + if (priv->attach->dev == dev + && priv->attach->dmabuf == dmabuf) { + attach = priv->attach; + break; + } + } + + if (attach) + ffs_dmabuf_get(attach); + + mutex_unlock(&epfile->dmabufs_mutex); + + return attach ?: ERR_PTR(-EPERM); +} + +static int ffs_dmabuf_attach(struct file *file, int fd) +{ + bool nonblock = file->f_flags & O_NONBLOCK; + struct ffs_epfile *epfile = file->private_data; + struct usb_gadget *gadget = epfile->ffs->gadget; + struct dma_buf_attachment *attach; + struct ffs_dmabuf_priv *priv; + enum dma_data_direction dir; + struct sg_table *sg_table; + struct dma_buf *dmabuf; + int err; + + if (!gadget || !gadget->sg_supported) + return -EPERM; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + attach = dma_buf_attach(dmabuf, gadget->dev.parent); + if (IS_ERR(attach)) { + err = PTR_ERR(attach); + goto err_dmabuf_put; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + goto err_dmabuf_detach; + } + + dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + err = ffs_dma_resv_lock(dmabuf, nonblock); + if (err) + goto err_free_priv; + + sg_table = dma_buf_map_attachment(attach, dir); + dma_resv_unlock(dmabuf->resv); + + if (IS_ERR(sg_table)) { + err = PTR_ERR(sg_table); + goto err_free_priv; + } + + attach->importer_priv = priv; + + priv->sgt = sg_table; + priv->dir = dir; + priv->ffs = epfile->ffs; + priv->attach = attach; + spin_lock_init(&priv->lock); + kref_init(&priv->ref); + priv->context = dma_fence_context_alloc(1); + + mutex_lock(&epfile->dmabufs_mutex); + list_add(&priv->entry, &epfile->dmabufs); + mutex_unlock(&epfile->dmabufs_mutex); + + return 0; + +err_free_priv: + kfree(priv); +err_dmabuf_detach: + dma_buf_detach(dmabuf, attach); +err_dmabuf_put: + dma_buf_put(dmabuf); + + return err; +} + +static int ffs_dmabuf_detach(struct file *file, int fd) +{ + struct ffs_epfile *epfile = file->private_data; + struct ffs_data *ffs = epfile->ffs; + struct device *dev = ffs->gadget->dev.parent; + struct ffs_dmabuf_priv *priv, *tmp; + struct dma_buf *dmabuf; + int ret = -EPERM; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + mutex_lock(&epfile->dmabufs_mutex); + + list_for_each_entry_safe(priv, tmp, &epfile->dmabufs, entry) { + if (priv->attach->dev == dev + && priv->attach->dmabuf == dmabuf) { + /* Cancel any pending transfer */ + spin_lock_irq(&ffs->eps_lock); + if (priv->ep && priv->req) + usb_ep_dequeue(priv->ep, priv->req); + spin_unlock_irq(&ffs->eps_lock); + + list_del(&priv->entry); + + /* Unref the reference from ffs_dmabuf_attach() */ + ffs_dmabuf_put(priv->attach); + ret = 0; + break; + } + } + + mutex_unlock(&epfile->dmabufs_mutex); + dma_buf_put(dmabuf); + + return ret; +} + +static int ffs_dmabuf_transfer(struct file *file, + const struct usb_ffs_dmabuf_transfer_req *req) +{ + bool nonblock = file->f_flags & O_NONBLOCK; + struct ffs_epfile *epfile = file->private_data; + struct dma_buf_attachment *attach; + struct ffs_dmabuf_priv *priv; + struct ffs_dma_fence *fence; + struct usb_request *usb_req; + enum dma_resv_usage resv_dir; + struct dma_buf *dmabuf; + unsigned long timeout; + struct ffs_ep *ep; + bool cookie; + u32 seqno; + long retl; + int ret; + + if (req->flags & ~USB_FFS_DMABUF_TRANSFER_MASK) + return -EINVAL; + + dmabuf = dma_buf_get(req->fd); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + if (req->length > dmabuf->size || req->length == 0) { + ret = -EINVAL; + goto err_dmabuf_put; + } + + attach = ffs_dmabuf_find_attachment(epfile, dmabuf); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + goto err_dmabuf_put; + } + + priv = attach->importer_priv; + + ep = ffs_epfile_wait_ep(file); + if (IS_ERR(ep)) { + ret = PTR_ERR(ep); + goto err_attachment_put; + } + + ret = ffs_dma_resv_lock(dmabuf, nonblock); + if (ret) + goto err_attachment_put; + + /* Make sure we don't have writers */ + timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS); + retl = dma_resv_wait_timeout(dmabuf->resv, + dma_resv_usage_rw(epfile->in), + true, timeout); + if (retl == 0) + retl = -EBUSY; + if (retl < 0) { + ret = (int)retl; + goto err_resv_unlock; + } + + ret = dma_resv_reserve_fences(dmabuf->resv, 1); + if (ret) + goto err_resv_unlock; + + fence = kmalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) { + ret = -ENOMEM; + goto err_resv_unlock; + } + + fence->priv = priv; + + spin_lock_irq(&epfile->ffs->eps_lock); + + /* In the meantime, endpoint got disabled or changed. */ + if (epfile->ep != ep) { + ret = -ESHUTDOWN; + goto err_fence_put; + } + + usb_req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC); + if (!usb_req) { + ret = -ENOMEM; + goto err_fence_put; + } + + /* + * usb_ep_queue() guarantees that all transfers are processed in the + * order they are enqueued, so we can use a simple incrementing + * sequence number for the dma_fence. + */ + seqno = atomic_add_return(1, &epfile->seqno); + + dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops, + &priv->lock, priv->context, seqno); + + resv_dir = epfile->in ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ; + + dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir); + dma_resv_unlock(dmabuf->resv); + + /* Now that the dma_fence is in place, queue the transfer. */ + + usb_req->length = req->length; + usb_req->buf = NULL; + usb_req->sg = priv->sgt->sgl; + usb_req->num_sgs = sg_nents_for_len(priv->sgt->sgl, req->length); + usb_req->sg_was_mapped = true; + usb_req->context = fence; + usb_req->complete = ffs_epfile_dmabuf_io_complete; + + cookie = dma_fence_begin_signalling(); + ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC); + dma_fence_end_signalling(cookie); + if (!ret) { + priv->req = usb_req; + priv->ep = ep->ep; + } else { + pr_warn("FFS: Failed to queue DMABUF: %d\n", ret); + ffs_dmabuf_signal_done(fence, ret); + usb_ep_free_request(ep->ep, usb_req); + } + + spin_unlock_irq(&epfile->ffs->eps_lock); + dma_buf_put(dmabuf); + + return ret; + +err_fence_put: + spin_unlock_irq(&epfile->ffs->eps_lock); + dma_fence_put(&fence->base); +err_resv_unlock: + dma_resv_unlock(dmabuf->resv); +err_attachment_put: + ffs_dmabuf_put(attach); +err_dmabuf_put: + dma_buf_put(dmabuf); + + return ret; +} + static long ffs_epfile_ioctl(struct file *file, unsigned code, unsigned long value) { @@ -1279,18 +1729,49 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) return -ENODEV; - /* Wait for endpoint to be enabled */ - ep = epfile->ep; - if (!ep) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + switch (code) { + case FUNCTIONFS_DMABUF_ATTACH: + { + int fd; - ret = wait_event_interruptible( - epfile->ffs->wait, (ep = epfile->ep)); - if (ret) - return -EINTR; + if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { + ret = -EFAULT; + break; + } + + return ffs_dmabuf_attach(file, fd); + } + case FUNCTIONFS_DMABUF_DETACH: + { + int fd; + + if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { + ret = -EFAULT; + break; + } + + return ffs_dmabuf_detach(file, fd); + } + case FUNCTIONFS_DMABUF_TRANSFER: + { + struct usb_ffs_dmabuf_transfer_req req; + + if (copy_from_user(&req, (void __user *)value, sizeof(req))) { + ret = -EFAULT; + break; + } + + return ffs_dmabuf_transfer(file, &req); + } + default: + break; } + /* Wait for endpoint to be enabled */ + ep = ffs_epfile_wait_ep(file); + if (IS_ERR(ep)) + return PTR_ERR(ep); + spin_lock_irq(&epfile->ffs->eps_lock); /* In the meantime, endpoint got disabled or changed. */ @@ -1348,7 +1829,6 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, } static const struct file_operations ffs_epfile_operations = { - .llseek = no_llseek, .open = ffs_epfile_open, .write_iter = ffs_epfile_write_iter, @@ -1805,7 +2285,7 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) struct usb_gadget_strings **lang; int first_id; - if (WARN_ON(ffs->state != FFS_ACTIVE + if ((ffs->state != FFS_ACTIVE || test_and_set_bit(FFS_FL_BOUND, &ffs->flags))) return -EBADFD; @@ -1863,6 +2343,8 @@ static int ffs_epfiles_create(struct ffs_data *ffs) for (i = 1; i <= count; ++i, ++epfile) { epfile->ffs = ffs; mutex_init(&epfile->mutex); + mutex_init(&epfile->dmabufs_mutex); + INIT_LIST_HEAD(&epfile->dmabufs); if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else @@ -1994,7 +2476,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, static int __must_check ffs_do_single_desc(char *data, unsigned len, ffs_entity_callback entity, - void *priv, int *current_class) + void *priv, int *current_class, int *current_subclass) { struct usb_descriptor_header *_ds = (void *)data; u8 length; @@ -2051,6 +2533,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, if (ds->iInterface) __entity(STRING, ds->iInterface); *current_class = ds->bInterfaceClass; + *current_subclass = ds->bInterfaceSubClass; } break; @@ -2075,6 +2558,12 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, if (length != sizeof(struct ccid_descriptor)) goto inv_length; break; + } else if (*current_class == USB_CLASS_APP_SPEC && + *current_subclass == USB_SUBCLASS_DFU) { + pr_vdebug("dfu functional descriptor\n"); + if (length != sizeof(struct usb_dfu_functional_descriptor)) + goto inv_length; + break; } else { pr_vdebug("unknown descriptor: %d for class %d\n", _ds->bDescriptorType, *current_class); @@ -2137,6 +2626,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, const unsigned _len = len; unsigned long num = 0; int current_class = -1; + int current_subclass = -1; for (;;) { int ret; @@ -2156,7 +2646,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, return _len - len; ret = ffs_do_single_desc(data, len, entity, priv, - ¤t_class); + ¤t_class, ¤t_subclass); if (ret < 0) { pr_debug("%s returns %d\n", __func__, ret); return ret; @@ -3231,6 +3721,15 @@ static void ffs_reset_work(struct work_struct *work) ffs_data_reset(ffs); } +static int ffs_func_get_alt(struct usb_function *f, + unsigned int interface) +{ + struct ffs_function *func = ffs_func_from_usb(f); + int intf = ffs_func_revmap_intf(func, interface); + + return (intf < 0) ? intf : func->cur_alt[interface]; +} + static int ffs_func_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { @@ -3238,11 +3737,12 @@ static int ffs_func_set_alt(struct usb_function *f, struct ffs_data *ffs = func->ffs; int ret = 0, intf; - if (alt != (unsigned)-1) { - intf = ffs_func_revmap_intf(func, interface); - if (intf < 0) - return intf; - } + if (alt > MAX_ALT_SETTINGS) + return -EINVAL; + + intf = ffs_func_revmap_intf(func, interface); + if (intf < 0) + return intf; if (ffs->func) ffs_func_eps_disable(ffs->func); @@ -3257,22 +3757,34 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->state != FFS_ACTIVE) return -ENODEV; - if (alt == (unsigned)-1) { - ffs->func = NULL; - ffs_event_add(ffs, FUNCTIONFS_DISABLE); - return 0; - } - ffs->func = func; ret = ffs_func_eps_enable(func); - if (ret >= 0) + if (ret >= 0) { ffs_event_add(ffs, FUNCTIONFS_ENABLE); + func->cur_alt[interface] = alt; + } return ret; } static void ffs_func_disable(struct usb_function *f) { - ffs_func_set_alt(f, 0, (unsigned)-1); + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + + if (ffs->func) + ffs_func_eps_disable(ffs->func); + + if (ffs->state == FFS_DEACTIVATED) { + ffs->state = FFS_CLOSING; + INIT_WORK(&ffs->reset_work, ffs_reset_work); + schedule_work(&ffs->reset_work); + return; + } + + if (ffs->state == FFS_ACTIVE) { + ffs->func = NULL; + ffs_event_add(ffs, FUNCTIONFS_DISABLE); + } } static int ffs_func_setup(struct usb_function *f, @@ -3330,7 +3842,7 @@ static int ffs_func_setup(struct usb_function *f, __ffs_event_add(ffs, FUNCTIONFS_SETUP); spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); - return creq->wLength == 0 ? USB_GADGET_DELAYED_STATUS : 0; + return ffs->ev.setup.wLength == 0 ? USB_GADGET_DELAYED_STATUS : 0; } static bool ffs_func_req_match(struct usb_function *f, @@ -3445,6 +3957,25 @@ static inline struct f_fs_opts *to_ffs_opts(struct config_item *item) func_inst.group); } +static ssize_t f_fs_opts_ready_show(struct config_item *item, char *page) +{ + struct f_fs_opts *opts = to_ffs_opts(item); + int ready; + + ffs_dev_lock(); + ready = opts->dev->desc_ready; + ffs_dev_unlock(); + + return sprintf(page, "%d\n", ready); +} + +CONFIGFS_ATTR_RO(f_fs_opts_, ready); + +static struct configfs_attribute *ffs_attrs[] = { + &f_fs_opts_attr_ready, + NULL, +}; + static void ffs_attr_release(struct config_item *item) { struct f_fs_opts *opts = to_ffs_opts(item); @@ -3458,6 +3989,7 @@ static struct configfs_item_operations ffs_item_ops = { static const struct config_item_type ffs_func_type = { .ct_item_ops = &ffs_item_ops, + .ct_attrs = ffs_attrs, .ct_owner = THIS_MODULE, }; @@ -3573,6 +4105,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) func->function.bind = ffs_func_bind; func->function.unbind = ffs_func_unbind; func->function.set_alt = ffs_func_set_alt; + func->function.get_alt = ffs_func_get_alt; func->function.disable = ffs_func_disable; func->function.setup = ffs_func_setup; func->function.req_match = ffs_func_req_match; @@ -3797,5 +4330,6 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) } DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc); +MODULE_DESCRIPTION("user mode file system API for USB composite function controllers"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michal Nazarewicz"); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 3c8a9dd585c0..740311c4fa24 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -15,13 +15,21 @@ #include <linux/uaccess.h> #include <linux/wait.h> #include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/usb/func_utils.h> #include <linux/usb/g_hid.h> +#include <uapi/linux/usb/g_hid.h> -#include "u_f.h" #include "u_hid.h" #define HIDG_MINORS 4 +/* + * Most operating systems seem to allow for 5000ms timeout, we will allow + * userspace half that time to respond before we return an empty report. + */ +#define GET_REPORT_TIMEOUT_MS 2500 + static int major, minors; static const struct class hidg_class = { @@ -31,6 +39,11 @@ static const struct class hidg_class = { static DEFINE_IDA(hidg_ida); static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ +struct report_entry { + struct usb_hidg_report report_data; + struct list_head node; +}; + /*-------------------------------------------------------------------------*/ /* HID gadget struct */ @@ -75,6 +88,19 @@ struct f_hidg { wait_queue_head_t write_queue; struct usb_request *req; + /* get report */ + struct usb_request *get_req; + struct usb_hidg_report get_report; + bool get_report_returned; + int get_report_req_report_id; + int get_report_req_report_length; + spinlock_t get_report_spinlock; + wait_queue_head_t get_queue; /* Waiting for userspace response */ + wait_queue_head_t get_id_queue; /* Get ID came in */ + struct work_struct work; + struct workqueue_struct *workqueue; + struct list_head report_list; + struct device dev; struct cdev cdev; struct usb_function func; @@ -524,6 +550,174 @@ release_write_pending: return status; } +static struct report_entry *f_hidg_search_for_report(struct f_hidg *hidg, u8 report_id) +{ + struct list_head *ptr; + struct report_entry *entry; + + list_for_each(ptr, &hidg->report_list) { + entry = list_entry(ptr, struct report_entry, node); + if (entry->report_data.report_id == report_id) + return entry; + } + + return NULL; +} + +static void get_report_workqueue_handler(struct work_struct *work) +{ + struct f_hidg *hidg = container_of(work, struct f_hidg, work); + struct usb_composite_dev *cdev = hidg->func.config->cdev; + struct usb_request *req; + struct report_entry *ptr; + unsigned long flags; + + int status = 0; + + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + req = hidg->get_req; + if (!req) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return; + } + + req->zero = 0; + req->length = min_t(unsigned int, min_t(unsigned int, hidg->get_report_req_report_length, + hidg->report_length), + MAX_REPORT_LENGTH); + + /* Check if there is a response available for immediate response */ + ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id); + if (ptr && !ptr->report_data.userspace_req) { + /* Report exists in list and it is to be used for immediate response */ + req->buf = ptr->report_data.data; + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + hidg->get_report_returned = true; + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + } else { + /* + * Report does not exist in list or should not be immediately sent + * i.e. give userspace time to respond + */ + hidg->get_report_returned = false; + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + wake_up(&hidg->get_id_queue); +#define GET_REPORT_COND (!hidg->get_report_returned) + /* Wait until userspace has responded or timeout */ + status = wait_event_interruptible_timeout(hidg->get_queue, !GET_REPORT_COND, + msecs_to_jiffies(GET_REPORT_TIMEOUT_MS)); + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + req = hidg->get_req; + if (!req) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return; + } + if (status == 0 && !hidg->get_report_returned) { + /* GET_REPORT request was not serviced by userspace within timeout period */ + VDBG(cdev, "get_report : userspace timeout.\n"); + hidg->get_report_returned = true; + } + + /* Search again for report ID in list and respond to GET_REPORT request */ + ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id); + if (ptr) { + /* + * Either get an updated response just serviced by userspace + * or send the latest response in the list + */ + req->buf = ptr->report_data.data; + } else { + /* If there are no prevoiusly sent reports send empty report */ + req->buf = hidg->get_report.data; + memset(req->buf, 0x0, req->length); + } + + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + } + + if (status < 0) + VDBG(cdev, "usb_ep_queue error on ep0 responding to GET_REPORT\n"); +} + +static int f_hidg_get_report_id(struct file *file, __u8 __user *buffer) +{ + struct f_hidg *hidg = file->private_data; + int ret = 0; + + ret = put_user(hidg->get_report_req_report_id, buffer); + + return ret; +} + +static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer) +{ + struct f_hidg *hidg = file->private_data; + struct usb_composite_dev *cdev = hidg->func.config->cdev; + unsigned long flags; + struct report_entry *entry; + struct report_entry *ptr; + __u8 report_id; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + if (copy_from_user(&entry->report_data, buffer, + sizeof(struct usb_hidg_report))) { + ERROR(cdev, "copy_from_user error\n"); + kfree(entry); + return -EINVAL; + } + + report_id = entry->report_data.report_id; + + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + ptr = f_hidg_search_for_report(hidg, report_id); + + if (ptr) { + /* Report already exists in list - update it */ + if (copy_from_user(&ptr->report_data, buffer, + sizeof(struct usb_hidg_report))) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + ERROR(cdev, "copy_from_user error\n"); + kfree(entry); + return -EINVAL; + } + kfree(entry); + } else { + /* Report does not exist in list - add it */ + list_add_tail(&entry->node, &hidg->report_list); + } + + /* If there is no response pending then do nothing further */ + if (hidg->get_report_returned) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return 0; + } + + /* If this userspace response serves the current pending report */ + if (hidg->get_report_req_report_id == report_id) { + hidg->get_report_returned = true; + wake_up(&hidg->get_queue); + } + + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return 0; +} + +static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg) +{ + switch (code) { + case GADGET_HID_READ_GET_REPORT_ID: + return f_hidg_get_report_id(file, (__u8 __user *)arg); + case GADGET_HID_WRITE_GET_REPORT: + return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg); + default: + return -ENOTTY; + } +} + static __poll_t f_hidg_poll(struct file *file, poll_table *wait) { struct f_hidg *hidg = file->private_data; @@ -531,6 +725,8 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) poll_wait(file, &hidg->read_queue, wait); poll_wait(file, &hidg->write_queue, wait); + poll_wait(file, &hidg->get_queue, wait); + poll_wait(file, &hidg->get_id_queue, wait); if (WRITE_COND) ret |= EPOLLOUT | EPOLLWRNORM; @@ -543,12 +739,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) ret |= EPOLLIN | EPOLLRDNORM; } + if (GET_REPORT_COND) + ret |= EPOLLPRI; + return ret; } #undef WRITE_COND #undef READ_COND_SSREPORT #undef READ_COND_INTOUT +#undef GET_REPORT_COND static int f_hidg_release(struct inode *inode, struct file *fd) { @@ -641,6 +841,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req) wake_up(&hidg->read_queue); } +static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req) +{ +} + static int hidg_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -649,6 +853,7 @@ static int hidg_setup(struct usb_function *f, struct usb_request *req = cdev->req; int status = 0; __u16 value, length; + unsigned long flags; value = __le16_to_cpu(ctrl->wValue); length = __le16_to_cpu(ctrl->wLength); @@ -660,14 +865,20 @@ static int hidg_setup(struct usb_function *f, switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_GET_REPORT): - VDBG(cdev, "get_report\n"); + VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength); - /* send an empty report */ - length = min_t(unsigned, length, hidg->report_length); - memset(req->buf, 0x0, length); + /* + * Update GET_REPORT ID so that an ioctl can be used to determine what + * GET_REPORT the request was actually for. + */ + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + hidg->get_report_req_report_id = value & 0xff; + hidg->get_report_req_report_length = length; + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); - goto respond; - break; + queue_work(hidg->workqueue, &hidg->work); + + return status; case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_GET_PROTOCOL): @@ -793,6 +1004,14 @@ static void hidg_disable(struct usb_function *f) spin_unlock_irqrestore(&hidg->read_spinlock, flags); } + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + if (!hidg->get_report_returned) { + usb_ep_free_request(f->config->cdev->gadget->ep0, hidg->get_req); + hidg->get_req = NULL; + hidg->get_report_returned = true; + } + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + spin_lock_irqsave(&hidg->write_spinlock, flags); if (!hidg->write_pending) { free_ep_req(hidg->in_ep, hidg->req); @@ -902,6 +1121,14 @@ fail: return status; } +#ifdef CONFIG_COMPAT +static long f_hidg_compat_ioctl(struct file *file, unsigned int code, + unsigned long value) +{ + return f_hidg_ioctl(file, code, value); +} +#endif + static const struct file_operations f_hidg_fops = { .owner = THIS_MODULE, .open = f_hidg_open, @@ -909,6 +1136,10 @@ static const struct file_operations f_hidg_fops = { .write = f_hidg_write, .read = f_hidg_read, .poll = f_hidg_poll, + .unlocked_ioctl = f_hidg_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = f_hidg_compat_ioctl, +#endif .llseek = noop_llseek, }; @@ -919,6 +1150,15 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) struct usb_string *us; int status; + hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC); + if (!hidg->get_req) + return -ENOMEM; + + hidg->get_req->zero = 0; + hidg->get_req->complete = hidg_get_report_complete; + hidg->get_req->context = hidg; + hidg->get_report_returned = true; + /* maybe allocate device-global string IDs, and patch descriptors */ us = usb_gstrings_attach(c->cdev, ct_func_strings, ARRAY_SIZE(ct_func_string_defs)); @@ -1004,9 +1244,24 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg->write_pending = 1; hidg->req = NULL; spin_lock_init(&hidg->read_spinlock); + spin_lock_init(&hidg->get_report_spinlock); init_waitqueue_head(&hidg->write_queue); init_waitqueue_head(&hidg->read_queue); + init_waitqueue_head(&hidg->get_queue); + init_waitqueue_head(&hidg->get_id_queue); INIT_LIST_HEAD(&hidg->completed_out_req); + INIT_LIST_HEAD(&hidg->report_list); + + INIT_WORK(&hidg->work, get_report_workqueue_handler); + hidg->workqueue = alloc_workqueue("report_work", + WQ_FREEZABLE | + WQ_MEM_RECLAIM, + 1); + + if (!hidg->workqueue) { + status = -ENOMEM; + goto fail; + } /* create char device */ cdev_init(&hidg->cdev, &f_hidg_fops); @@ -1016,12 +1271,16 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail_free_descs: + destroy_workqueue(hidg->workqueue); usb_free_all_descriptors(f); fail: ERROR(f->config->cdev, "hidg_bind FAILED\n"); if (hidg->req != NULL) free_ep_req(hidg->in_ep, hidg->req); + usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req); + hidg->get_req = NULL; + return status; } @@ -1029,9 +1288,9 @@ static inline int hidg_get_minor(void) { int ret; - ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL); + ret = ida_alloc(&hidg_ida, GFP_KERNEL); if (ret >= HIDG_MINORS) { - ida_simple_remove(&hidg_ida, ret); + ida_free(&hidg_ida, ret); ret = -ENODEV; } @@ -1176,7 +1435,7 @@ static const struct config_item_type hid_func_type = { static inline void hidg_put_minor(int minor) { - ida_simple_remove(&hidg_ida, minor); + ida_free(&hidg_ida, minor); } static void hidg_free_inst(struct usb_function_instance *f) @@ -1256,7 +1515,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) struct f_hidg *hidg = func_to_hidg(f); cdev_device_del(&hidg->cdev, &hidg->dev); - + destroy_workqueue(hidg->workqueue); usb_free_all_descriptors(f); } @@ -1322,6 +1581,7 @@ err_unlock: } DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc); +MODULE_DESCRIPTION("USB HID function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Fabien Chouteau"); diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 17ac6ace0cff..49b009a7d5d7 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -14,9 +14,9 @@ #include <linux/module.h> #include <linux/err.h> #include <linux/usb/composite.h> +#include <linux/usb/func_utils.h> #include "g_zero.h" -#include "u_f.h" /* * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, @@ -593,4 +593,5 @@ void __exit lb_modexit(void) usb_function_unregister(&Loopbackusb_func); } +MODULE_DESCRIPTION("USB peripheral loopback configuration driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index c265a1f62fc1..2eae8fc2e0db 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -188,7 +188,7 @@ #include <linux/freezer.h> #include <linux/module.h> #include <linux/uaccess.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -500,7 +500,7 @@ static int fsg_setup(struct usb_function *f, *(u8 *)req->buf = _fsg_common_get_max_lun(fsg->common); /* Respond with data/status */ - req->length = min((u16)1, w_length); + req->length = min_t(u16, 1, w_length); return ep0_queue(fsg->common); } @@ -655,7 +655,7 @@ static int do_read(struct fsg_common *common) * And don't try to read past the end of the file. */ amount = min(amount_left, FSG_BUFLEN); - amount = min((loff_t)amount, + amount = min_t(loff_t, amount, curlun->file_length - file_offset); /* Wait for the next buffer to become available */ @@ -1005,7 +1005,7 @@ static int do_verify(struct fsg_common *common) * And don't try to read past the end of the file. */ amount = min(amount_left, FSG_BUFLEN); - amount = min((loff_t)amount, + amount = min_t(loff_t, amount, curlun->file_length - file_offset); if (amount == 0) { curlun->sense_data = @@ -2167,7 +2167,7 @@ unknown_cmnd: if (reply == -EINVAL) reply = 0; /* Error reply length */ if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { - reply = min((u32)reply, common->data_size_from_cmnd); + reply = min_t(u32, reply, common->data_size_from_cmnd); bh->inreq->length = reply; bh->state = BUF_STATE_FULL; common->residue -= reply; @@ -3050,7 +3050,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) if (!common->thread_task) { common->state = FSG_STATE_NORMAL; common->thread_task = - kthread_create(fsg_main_thread, common, "file-storage"); + kthread_run(fsg_main_thread, common, "file-storage"); if (IS_ERR(common->thread_task)) { ret = PTR_ERR(common->thread_task); common->thread_task = NULL; @@ -3059,7 +3059,6 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) } DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); - wake_up_process(common->thread_task); } fsg->gadget = gadget; @@ -3577,6 +3576,7 @@ static struct usb_function *fsg_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc); +MODULE_DESCRIPTION("Mass Storage USB Composite Function"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michal Nazarewicz"); diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 20c6fbd94f32..da82598fcef8 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -30,14 +30,15 @@ #include <sound/rawmidi.h> #include <linux/usb/ch9.h> +#include <linux/usb/func_utils.h> #include <linux/usb/gadget.h> #include <linux/usb/audio.h> #include <linux/usb/midi.h> -#include "u_f.h" #include "u_midi.h" MODULE_AUTHOR("Ben Williamson"); +MODULE_DESCRIPTION("USB MIDI class function driver"); MODULE_LICENSE("GPL v2"); static const char f_midi_shortname[] = "f_midi"; @@ -282,7 +283,7 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req) /* Our transmit completed. See if there's more to go. * f_midi_transmit eats req, don't queue it again. */ req->length = 0; - f_midi_transmit(midi); + queue_work(system_highpri_wq, &midi->work); return; } break; @@ -818,9 +819,9 @@ static int f_midi_register_card(struct f_midi *midi) goto fail; } - strcpy(card->driver, f_midi_longname); - strcpy(card->longname, f_midi_longname); - strcpy(card->shortname, f_midi_shortname); + strscpy(card->driver, f_midi_longname); + strscpy(card->longname, f_midi_longname); + strscpy(card->shortname, f_midi_shortname); /* Set up rawmidi */ snd_component_add(card, "MIDI"); @@ -832,7 +833,7 @@ static int f_midi_register_card(struct f_midi *midi) } midi->rmidi = rmidi; midi->in_last_port = 0; - strcpy(rmidi->name, card->shortname); + strscpy(rmidi->name, card->shortname); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -906,6 +907,15 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) status = -ENODEV; + /* + * Reset wMaxPacketSize with maximum packet size of FS bulk transfer before + * endpoint claim. This ensures that the wMaxPacketSize does not exceed the + * limit during bind retries where configured dwc3 TX/RX FIFO's maxpacket + * size of 512 bytes for IN/OUT endpoints in support HS speed only. + */ + bulk_in_desc.wMaxPacketSize = cpu_to_le16(64); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(64); + /* allocate instance-specific endpoints */ midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc); if (!midi->in_ep) @@ -999,11 +1009,11 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) } /* configure the endpoint descriptors ... */ - ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); - ms_out_desc.bNumEmbMIDIJack = midi->in_ports; + ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); + ms_out_desc.bNumEmbMIDIJack = midi->out_ports; - ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); - ms_in_desc.bNumEmbMIDIJack = midi->out_ports; + ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); + ms_in_desc.bNumEmbMIDIJack = midi->in_ports; /* ... and add them to the list */ endpoint_descriptor_index = i; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index ec8cd7c7bbfc..12e866fb311d 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -15,11 +15,11 @@ #include <sound/ump_convert.h> #include <linux/usb/ch9.h> +#include <linux/usb/func_utils.h> #include <linux/usb/gadget.h> #include <linux/usb/audio.h> #include <linux/usb/midi-v2.h> -#include "u_f.h" #include "u_midi2.h" struct f_midi2; @@ -150,6 +150,9 @@ struct f_midi2 { #define func_to_midi2(f) container_of(f, struct f_midi2, func) +/* convert from MIDI protocol number (1 or 2) to SNDRV_UMP_EP_INFO_PROTO_* */ +#define to_ump_protocol(v) (((v) & 3) << 8) + /* get EP name string */ static const char *ump_ep_name(const struct f_midi2_ep *ep) { @@ -564,8 +567,7 @@ static void reply_ump_stream_ep_config(struct f_midi2_ep *ep) .status = UMP_STREAM_MSG_STATUS_STREAM_CFG, }; - if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) == - SNDRV_UMP_EP_INFO_PROTO_MIDI2) + if (ep->info.protocol == 2) rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8; else rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8; @@ -627,25 +629,34 @@ static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data) return; case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST: if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) { - ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2; + ep->info.protocol = 2; DBG(midi2, "Switching Protocol to MIDI2\n"); } else { - ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1; + ep->info.protocol = 1; DBG(midi2, "Switching Protocol to MIDI1\n"); } - snd_ump_switch_protocol(ep->ump, ep->info.protocol); + snd_ump_switch_protocol(ep->ump, to_ump_protocol(ep->info.protocol)); reply_ump_stream_ep_config(ep); return; case UMP_STREAM_MSG_STATUS_FB_DISCOVERY: if (format) return; // invalid blk = (*data >> 8) & 0xff; - if (blk >= ep->num_blks) - return; - if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO) - reply_ump_stream_fb_info(ep, blk); - if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME) - reply_ump_stream_fb_name(ep, blk); + if (blk == 0xff) { + /* inquiry for all blocks */ + for (blk = 0; blk < ep->num_blks; blk++) { + if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO) + reply_ump_stream_fb_info(ep, blk); + if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME) + reply_ump_stream_fb_name(ep, blk); + } + } else if (blk < ep->num_blks) { + /* only the specified block */ + if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO) + reply_ump_stream_fb_info(ep, blk); + if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME) + reply_ump_stream_fb_name(ep, blk); + } return; } } @@ -1065,7 +1076,8 @@ static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep, group = midi2->out_cable_mapping[cable].group; bytes = midi1_packet_bytes[*buf & 0x0f]; for (c = 0; c < bytes; c++) { - snd_ump_convert_to_ump(cvt, group, ep->info.protocol, + snd_ump_convert_to_ump(cvt, group, + to_ump_protocol(ep->info.protocol), buf[c + 1]); if (cvt->ump_bytes) { snd_ump_receive(ep->ump, cvt->ump, @@ -1273,10 +1285,8 @@ static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, if (alt == 0) op_mode = MIDI_OP_MODE_MIDI1; - else if (alt == 1) - op_mode = MIDI_OP_MODE_MIDI2; else - op_mode = MIDI_OP_MODE_UNSET; + op_mode = MIDI_OP_MODE_MIDI2; if (midi2->operation_mode == op_mode) return 0; @@ -1375,7 +1385,7 @@ static void assign_block_descriptors(struct f_midi2 *midi2, desc->nNumGroupTrm = b->num_groups; desc->iBlockItem = ep->blks[blk].string_id; - if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) + if (ep->info.protocol == 2) desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0; else desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128; @@ -1552,7 +1562,7 @@ static int f_midi2_create_card(struct f_midi2 *midi2) if (midi2->info.static_block) ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS; ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8; - ump->info.protocol = (ep->info.protocol & 3) << 8; + ump->info.protocol = to_ump_protocol(ep->info.protocol); ump->info.version = 0x0101; ump->info.family_id = ep->info.family; ump->info.model_id = ep->info.model; @@ -1581,7 +1591,11 @@ static int f_midi2_create_card(struct f_midi2 *midi2) fb->info.midi_ci_version = b->midi_ci_version; fb->info.ui_hint = reverse_dir(b->ui_hint); fb->info.sysex8_streams = b->sysex8_streams; - fb->info.flags |= b->is_midi1; + if (b->is_midi1 < 2) + fb->info.flags |= b->is_midi1; + else + fb->info.flags |= SNDRV_UMP_BLOCK_IS_MIDI1 | + SNDRV_UMP_BLOCK_IS_LOWSPEED; strscpy(fb->info.name, ump_fb_name(b), sizeof(fb->info.name)); } @@ -2868,4 +2882,5 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc); +MODULE_DESCRIPTION("USB MIDI 2.0 class function driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 28f4e6552e84..f60576a65ca6 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -17,6 +17,7 @@ #include <linux/device.h> #include <linux/etherdevice.h> #include <linux/crc32.h> +#include <linux/string_choices.h> #include <linux/usb/cdc.h> @@ -558,7 +559,7 @@ static void ncm_do_notify(struct f_ncm *ncm) req->length = sizeof *event; DBG(cdev, "notify connect %s\n", - ncm->is_open ? "true" : "false"); + str_true_false(ncm->is_open)); ncm->notify_state = NCM_NOTIFY_NONE; break; @@ -878,7 +879,7 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (alt > 1) goto fail; - if (ncm->port.in_ep->enabled) { + if (ncm->netdev) { DBG(cdev, "reset ncm\n"); ncm->netdev = NULL; gether_disconnect(&ncm->port); @@ -1367,7 +1368,7 @@ static void ncm_disable(struct usb_function *f) DBG(cdev, "ncm deactivated\n"); - if (ncm->port.in_ep->enabled) { + if (ncm->netdev) { ncm->netdev = NULL; gether_disconnect(&ncm->port); } @@ -1797,5 +1798,6 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc); +MODULE_DESCRIPTION("USB CDC Network (NCM) link function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yauheni Kaliuta"); diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index dcb093210305..1305e2326cdf 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -487,4 +487,5 @@ static struct usb_function *obex_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc); MODULE_AUTHOR("Felipe Balbi"); +MODULE_DESCRIPTION("USB CDC OBEX function driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 0bebbdf3f213..0aa9e8224cae 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -729,4 +729,5 @@ void gphonet_cleanup(struct net_device *dev) DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc); MODULE_AUTHOR("Rémi Denis-Courmont"); +MODULE_DESCRIPTION("USB CDC Phonet function"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 076dd4c1be96..d295ade8fa67 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -37,7 +37,7 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/uaccess.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/usb/ch9.h> #include <linux/usb/composite.h> @@ -213,6 +213,7 @@ static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget, struct usb_endpoint_descriptor *ss) { switch (gadget->speed) { + case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: return ss; case USB_SPEED_HIGH: @@ -449,11 +450,8 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); - if (dev->interface < 0) { - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -ENODEV; - } + if (dev->interface < 0) + goto out_disabled; /* We will use this flag later to check if a printer reset happened * after we turn interrupts back on. @@ -461,6 +459,9 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) dev->reset_printer = 0; setup_rx_reqs(dev); + /* this dropped the lock - need to retest */ + if (dev->interface < 0) + goto out_disabled; bytes_copied = 0; current_rx_req = dev->current_rx_req; @@ -494,6 +495,8 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) wait_event_interruptible(dev->rx_wait, (likely(!list_empty(&dev->rx_buffers)))); spin_lock_irqsave(&dev->lock, flags); + if (dev->interface < 0) + goto out_disabled; } /* We have data to return then copy it to the caller's buffer.*/ @@ -537,6 +540,9 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) return -EAGAIN; } + if (dev->interface < 0) + goto out_disabled; + /* If we not returning all the data left in this RX request * buffer then adjust the amount of data left in the buffer. * Othewise if we are done with this RX request buffer then @@ -566,6 +572,11 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) return bytes_copied; else return -EAGAIN; + +out_disabled: + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -ENODEV; } static ssize_t @@ -586,11 +597,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); - if (dev->interface < 0) { - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -ENODEV; - } + if (dev->interface < 0) + goto out_disabled; /* Check if a printer reset happens while we have interrupts on */ dev->reset_printer = 0; @@ -613,6 +621,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) wait_event_interruptible(dev->tx_wait, (likely(!list_empty(&dev->tx_reqs)))); spin_lock_irqsave(&dev->lock, flags); + if (dev->interface < 0) + goto out_disabled; } while (likely(!list_empty(&dev->tx_reqs)) && len) { @@ -662,6 +672,9 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) return -EAGAIN; } + if (dev->interface < 0) + goto out_disabled; + list_add(&req->list, &dev->tx_reqs_active); /* here, we unlock, and only unlock, to avoid deadlock. */ @@ -674,6 +687,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) mutex_unlock(&dev->lock_printer_io); return -EAGAIN; } + if (dev->interface < 0) + goto out_disabled; } spin_unlock_irqrestore(&dev->lock, flags); @@ -685,6 +700,11 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) return bytes_copied; else return -EAGAIN; + +out_disabled: + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -ENODEV; } static int @@ -1312,9 +1332,9 @@ static inline int gprinter_get_minor(void) { int ret; - ret = ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); + ret = ida_alloc(&printer_ida, GFP_KERNEL); if (ret >= PRINTER_MINORS) { - ida_simple_remove(&printer_ida, ret); + ida_free(&printer_ida, ret); ret = -ENODEV; } @@ -1323,7 +1343,7 @@ static inline int gprinter_get_minor(void) static inline void gprinter_put_minor(int minor) { - ida_simple_remove(&printer_ida, minor); + ida_free(&printer_ida, minor); } static int gprinter_setup(int); @@ -1507,6 +1527,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc); +MODULE_DESCRIPTION("USB printer function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Craig Nadler"); diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index b47f99d17ee9..7cec19d65fb5 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -1013,5 +1013,6 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); +MODULE_DESCRIPTION("RNDIS link function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 65c50092aea2..8f7e7a2b2ff2 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -392,6 +392,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc); +MODULE_DESCRIPTION("generic USB serial function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Al Borchers"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 2edbd9b510d6..ec5fd25020fd 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -13,10 +13,10 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/usb/composite.h> +#include <linux/usb/func_utils.h> #include <linux/err.h> #include "g_zero.h" -#include "u_f.h" /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral @@ -1284,4 +1284,5 @@ static void __exit sslb_modexit(void) module_init(sslb_modinit); module_exit(sslb_modexit); +MODULE_DESCRIPTION("USB peripheral source/sink configuration driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 8ae9689ef2a0..ea3fdd842462 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -500,5 +500,6 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc); +MODULE_DESCRIPTION("\"CDC Subset\" Ethernet link function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 37befd6db001..5a2e1237f85c 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -12,6 +12,7 @@ #include <linux/string.h> #include <linux/configfs.h> #include <linux/ctype.h> +#include <linux/delay.h> #include <linux/usb/ch9.h> #include <linux/usb/composite.h> #include <linux/usb/gadget.h> @@ -19,7 +20,7 @@ #include <scsi/scsi_tcq.h> #include <target/target_core_base.h> #include <target/target_core_fabric.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "tcm.h" #include "u_tcm.h" @@ -50,7 +51,7 @@ static int bot_enqueue_cmd_cbw(struct f_uas *fu) if (fu->flags & USBG_BOT_CMD_PEND) return 0; - ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC); + ret = usb_ep_queue(fu->ep_out, fu->cmd[0].req, GFP_ATOMIC); if (!ret) fu->flags |= USBG_BOT_CMD_PEND; return ret; @@ -62,10 +63,11 @@ static void bot_status_complete(struct usb_ep *ep, struct usb_request *req) struct f_uas *fu = cmd->fu; transport_generic_free_cmd(&cmd->se_cmd, 0); - if (req->status < 0) { - pr_err("ERR %s(%d)\n", __func__, __LINE__); + if (req->status == -ESHUTDOWN) return; - } + + if (req->status < 0) + pr_err("ERR %s(%d)\n", __func__, __LINE__); /* CSW completed, wait for next CBW */ bot_enqueue_cmd_cbw(fu); @@ -136,7 +138,7 @@ static void bot_send_bad_status(struct usbg_cmd *cmd) } req->complete = bot_err_compl; req->context = cmd; - req->buf = fu->cmd.buf; + req->buf = fu->cmd[0].buf; usb_ep_queue(ep, req, GFP_KERNEL); } else { bot_enqueue_sense_code(fu, cmd); @@ -196,6 +198,11 @@ static void bot_read_compl(struct usb_ep *ep, struct usb_request *req) if (req->status < 0) pr_err("ERR %s(%d)\n", __func__, __LINE__); + if (req->status == -ESHUTDOWN) { + transport_generic_free_cmd(&cmd->se_cmd, 0); + return; + } + bot_send_status(cmd, true); } @@ -244,11 +251,8 @@ static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *); static int bot_send_write_request(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; - struct se_cmd *se_cmd = &cmd->se_cmd; - struct usb_gadget *gadget = fuas_to_gadget(fu); int ret; - init_completion(&cmd->write_complete); cmd->fu = fu; if (!cmd->data_len) { @@ -256,22 +260,6 @@ static int bot_send_write_request(struct usbg_cmd *cmd) return -EINVAL; } - if (!gadget->sg_supported) { - cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL); - if (!cmd->data_buf) - return -ENOMEM; - - fu->bot_req_out->buf = cmd->data_buf; - } else { - fu->bot_req_out->buf = NULL; - fu->bot_req_out->num_sgs = se_cmd->t_data_nents; - fu->bot_req_out->sg = se_cmd->t_data_sg; - } - - fu->bot_req_out->complete = usbg_data_write_cmpl; - fu->bot_req_out->length = se_cmd->data_length; - fu->bot_req_out->context = cmd; - ret = usbg_prepare_w_request(cmd, fu->bot_req_out); if (ret) goto cleanup; @@ -279,8 +267,6 @@ static int bot_send_write_request(struct usbg_cmd *cmd) if (ret) pr_err("%s(%d)\n", __func__, __LINE__); - wait_for_completion(&cmd->write_complete); - target_execute_cmd(se_cmd); cleanup: return ret; } @@ -292,14 +278,31 @@ static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req) struct f_uas *fu = req->context; int ret; + if (req->status == -ESHUTDOWN) + return; + fu->flags &= ~USBG_BOT_CMD_PEND; - if (req->status < 0) + if (req->status < 0) { + struct usb_gadget *gadget = fuas_to_gadget(fu); + + dev_err(&gadget->dev, "BOT command req err (%d)\n", req->status); + bot_enqueue_cmd_cbw(fu); return; + } ret = bot_submit_command(fu, req->buf, req->actual); - if (ret) + if (ret) { pr_err("%s(%d): %d\n", __func__, __LINE__, ret); + if (!(fu->flags & USBG_BOT_WEDGED)) + usb_ep_set_wedge(fu->ep_in); + + fu->flags |= USBG_BOT_WEDGED; + bot_enqueue_cmd_cbw(fu); + } else if (fu->flags & USBG_BOT_WEDGED) { + fu->flags &= ~USBG_BOT_WEDGED; + usb_ep_clear_halt(fu->ep_in); + } } static int bot_prepare_reqs(struct f_uas *fu) @@ -314,8 +317,8 @@ static int bot_prepare_reqs(struct f_uas *fu) if (!fu->bot_req_out) goto err_out; - fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL); - if (!fu->cmd.req) + fu->cmd[0].req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL); + if (!fu->cmd[0].req) goto err_cmd; fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); @@ -327,27 +330,27 @@ static int bot_prepare_reqs(struct f_uas *fu) fu->bot_status.req->complete = bot_status_complete; fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN); - fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL); - if (!fu->cmd.buf) + fu->cmd[0].buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL); + if (!fu->cmd[0].buf) goto err_buf; - fu->cmd.req->complete = bot_cmd_complete; - fu->cmd.req->buf = fu->cmd.buf; - fu->cmd.req->length = fu->ep_out->maxpacket; - fu->cmd.req->context = fu; + fu->cmd[0].req->complete = bot_cmd_complete; + fu->cmd[0].req->buf = fu->cmd[0].buf; + fu->cmd[0].req->length = fu->ep_out->maxpacket; + fu->cmd[0].req->context = fu; ret = bot_enqueue_cmd_cbw(fu); if (ret) goto err_queue; return 0; err_queue: - kfree(fu->cmd.buf); - fu->cmd.buf = NULL; + kfree(fu->cmd[0].buf); + fu->cmd[0].buf = NULL; err_buf: usb_ep_free_request(fu->ep_in, fu->bot_status.req); err_sts: - usb_ep_free_request(fu->ep_out, fu->cmd.req); - fu->cmd.req = NULL; + usb_ep_free_request(fu->ep_out, fu->cmd[0].req); + fu->cmd[0].req = NULL; err_cmd: usb_ep_free_request(fu->ep_out, fu->bot_req_out); fu->bot_req_out = NULL; @@ -372,16 +375,16 @@ static void bot_cleanup_old_alt(struct f_uas *fu) usb_ep_free_request(fu->ep_in, fu->bot_req_in); usb_ep_free_request(fu->ep_out, fu->bot_req_out); - usb_ep_free_request(fu->ep_out, fu->cmd.req); + usb_ep_free_request(fu->ep_out, fu->cmd[0].req); usb_ep_free_request(fu->ep_in, fu->bot_status.req); - kfree(fu->cmd.buf); + kfree(fu->cmd[0].buf); fu->bot_req_in = NULL; fu->bot_req_out = NULL; - fu->cmd.req = NULL; + fu->cmd[0].req = NULL; fu->bot_status.req = NULL; - fu->cmd.buf = NULL; + fu->cmd[0].buf = NULL; } static void bot_set_alt(struct f_uas *fu) @@ -441,14 +444,10 @@ static int usbg_bot_setup(struct usb_function *f, pr_err("No LUNs configured?\n"); return -EINVAL; } - /* - * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be - * accessed. The upper limit is 0xf - */ luns--; - if (luns > 0xf) { + if (luns > US_BULK_MAX_LUN_LIMIT) { pr_info_once("Limiting the number of luns to 16\n"); - luns = 0xf; + luns = US_BULK_MAX_LUN_LIMIT; } ret_lun = cdev->req->buf; *ret_lun = luns; @@ -457,6 +456,11 @@ static int usbg_bot_setup(struct usb_function *f, case US_BULK_RESET_REQUEST: /* XXX maybe we should remove previous requests for IN + OUT */ + if (fu->flags & USBG_BOT_WEDGED) { + fu->flags &= ~USBG_BOT_WEDGED; + usb_ep_clear_halt(fu->ep_in); + } + bot_enqueue_cmd_cbw(fu); return 0; } @@ -465,6 +469,45 @@ static int usbg_bot_setup(struct usb_function *f, /* Start uas.c code */ +static int tcm_to_uasp_response(enum tcm_tmrsp_table code) +{ + switch (code) { + case TMR_FUNCTION_FAILED: + return RC_TMF_FAILED; + case TMR_FUNCTION_COMPLETE: + case TMR_TASK_DOES_NOT_EXIST: + return RC_TMF_COMPLETE; + case TMR_LUN_DOES_NOT_EXIST: + return RC_INCORRECT_LUN; + case TMR_FUNCTION_REJECTED: + case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: + default: + return RC_TMF_NOT_SUPPORTED; + } +} + +static unsigned char uasp_to_tcm_func(int code) +{ + switch (code) { + case TMF_ABORT_TASK: + return TMR_ABORT_TASK; + case TMF_ABORT_TASK_SET: + return TMR_ABORT_TASK_SET; + case TMF_CLEAR_TASK_SET: + return TMR_CLEAR_TASK_SET; + case TMF_LOGICAL_UNIT_RESET: + return TMR_LUN_RESET; + case TMF_CLEAR_ACA: + return TMR_CLEAR_ACA; + case TMF_I_T_NEXUS_RESET: + case TMF_QUERY_TASK: + case TMF_QUERY_TASK_SET: + case TMF_QUERY_ASYNC_EVENT: + default: + return TMR_UNKNOWN; + } +} + static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream) { /* We have either all three allocated or none */ @@ -482,10 +525,14 @@ static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream) static void uasp_free_cmdreq(struct f_uas *fu) { - usb_ep_free_request(fu->ep_cmd, fu->cmd.req); - kfree(fu->cmd.buf); - fu->cmd.req = NULL; - fu->cmd.buf = NULL; + int i; + + for (i = 0; i < USBG_NUM_CMDS; i++) { + usb_ep_free_request(fu->ep_cmd, fu->cmd[i].req); + kfree(fu->cmd[i].buf); + fu->cmd[i].req = NULL; + fu->cmd[i].buf = NULL; + } } static void uasp_cleanup_old_alt(struct f_uas *fu) @@ -500,7 +547,7 @@ static void uasp_cleanup_old_alt(struct f_uas *fu) usb_ep_disable(fu->ep_status); usb_ep_disable(fu->ep_cmd); - for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++) + for (i = 0; i < USBG_NUM_CMDS; i++) uasp_cleanup_one_stream(fu, &fu->stream[i]); uasp_free_cmdreq(fu); } @@ -512,7 +559,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd) struct se_cmd *se_cmd = &cmd->se_cmd; struct f_uas *fu = cmd->fu; struct usb_gadget *gadget = fuas_to_gadget(fu); - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[se_cmd->map_tag]; if (!gadget->sg_supported) { cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC); @@ -532,6 +579,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd) } stream->req_in->is_last = 1; + stream->req_in->stream_id = cmd->tag; stream->req_in->complete = uasp_status_data_cmpl; stream->req_in->length = se_cmd->data_length; stream->req_in->context = cmd; @@ -544,7 +592,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd) { struct se_cmd *se_cmd = &cmd->se_cmd; struct sense_iu *iu = &cmd->sense_iu; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag]; cmd->state = UASP_QUEUE_COMMAND; iu->iu_id = IU_ID_STATUS; @@ -556,20 +604,76 @@ static void uasp_prepare_status(struct usbg_cmd *cmd) iu->len = cpu_to_be16(se_cmd->scsi_sense_length); iu->status = se_cmd->scsi_status; stream->req_status->is_last = 1; + stream->req_status->stream_id = cmd->tag; stream->req_status->context = cmd; stream->req_status->length = se_cmd->scsi_sense_length + 16; stream->req_status->buf = iu; stream->req_status->complete = uasp_status_data_cmpl; } +static void uasp_prepare_response(struct usbg_cmd *cmd) +{ + struct se_cmd *se_cmd = &cmd->se_cmd; + struct response_iu *rsp_iu = &cmd->response_iu; + struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag]; + + cmd->state = UASP_QUEUE_COMMAND; + rsp_iu->iu_id = IU_ID_RESPONSE; + rsp_iu->tag = cpu_to_be16(cmd->tag); + + if (cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) + rsp_iu->response_code = cmd->tmr_rsp; + else + rsp_iu->response_code = + tcm_to_uasp_response(se_cmd->se_tmr_req->response); + + /* + * The UASP driver must support all the task management functions listed + * in Table 20 of UAS-r04. To remain compliant while indicate that the + * TMR did not go through, report RC_TMF_FAILED instead of + * RC_TMF_NOT_SUPPORTED and print a warning to the user. + */ + switch (cmd->tmr_func) { + case TMF_ABORT_TASK: + case TMF_ABORT_TASK_SET: + case TMF_CLEAR_TASK_SET: + case TMF_LOGICAL_UNIT_RESET: + case TMF_CLEAR_ACA: + case TMF_I_T_NEXUS_RESET: + case TMF_QUERY_TASK: + case TMF_QUERY_TASK_SET: + case TMF_QUERY_ASYNC_EVENT: + if (rsp_iu->response_code == RC_TMF_NOT_SUPPORTED) { + struct usb_gadget *gadget = fuas_to_gadget(cmd->fu); + + dev_warn(&gadget->dev, "TMF function %d not supported\n", + cmd->tmr_func); + rsp_iu->response_code = RC_TMF_FAILED; + } + break; + default: + break; + } + + stream->req_status->is_last = 1; + stream->req_status->stream_id = cmd->tag; + stream->req_status->context = cmd; + stream->req_status->length = sizeof(struct response_iu); + stream->req_status->buf = rsp_iu; + stream->req_status->complete = uasp_status_data_cmpl; +} + +static void usbg_release_cmd(struct se_cmd *se_cmd); +static int uasp_send_tm_response(struct usbg_cmd *cmd); + static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) { struct usbg_cmd *cmd = req->context; - struct uas_stream *stream = cmd->stream; struct f_uas *fu = cmd->fu; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; int ret; - if (req->status < 0) + if (req->status == -ESHUTDOWN) goto cleanup; switch (cmd->state) { @@ -600,8 +704,37 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) break; case UASP_QUEUE_COMMAND: - transport_generic_free_cmd(&cmd->se_cmd, 0); - usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); + /* + * Overlapped command detected and cancelled. + * So send overlapped attempted status. + */ + if (cmd->tmr_rsp == RC_OVERLAPPED_TAG && + req->status == -ECONNRESET) { + uasp_send_tm_response(cmd); + return; + } + + hash_del(&stream->node); + + /* + * If no command submitted to target core here, just free the + * bitmap index. This is for the cases where f_tcm handles + * status response instead of the target core. + */ + if (cmd->tmr_rsp != RC_OVERLAPPED_TAG && + cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) { + struct se_session *se_sess; + + se_sess = fu->tpg->tpg_nexus->tvn_se_sess; + sbitmap_queue_clear(&se_sess->sess_tag_pool, + cmd->se_cmd.map_tag, + cmd->se_cmd.map_cpu); + } else { + transport_generic_free_cmd(&cmd->se_cmd, 0); + } + + usb_ep_queue(fu->ep_cmd, cmd->req, GFP_ATOMIC); + complete(&stream->cmd_completion); break; default: @@ -610,27 +743,38 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) return; cleanup: + hash_del(&stream->node); transport_generic_free_cmd(&cmd->se_cmd, 0); } static int uasp_send_status_response(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; struct sense_iu *iu = &cmd->sense_iu; iu->tag = cpu_to_be16(cmd->tag); - stream->req_status->complete = uasp_status_data_cmpl; - stream->req_status->context = cmd; cmd->fu = fu; uasp_prepare_status(cmd); return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC); } +static int uasp_send_tm_response(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; + struct response_iu *iu = &cmd->response_iu; + + iu->tag = cpu_to_be16(cmd->tag); + cmd->fu = fu; + uasp_prepare_response(cmd); + return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC); +} + static int uasp_send_read_response(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; struct sense_iu *iu = &cmd->sense_iu; int ret; @@ -674,11 +818,10 @@ static int uasp_send_write_request(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; struct se_cmd *se_cmd = &cmd->se_cmd; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[se_cmd->map_tag]; struct sense_iu *iu = &cmd->sense_iu; int ret; - init_completion(&cmd->write_complete); cmd->fu = fu; iu->tag = cpu_to_be16(cmd->tag); @@ -710,36 +853,31 @@ static int uasp_send_write_request(struct usbg_cmd *cmd) pr_err("%s(%d)\n", __func__, __LINE__); } - wait_for_completion(&cmd->write_complete); - target_execute_cmd(se_cmd); cleanup: return ret; } -static int usbg_submit_command(struct f_uas *, void *, unsigned int); +static int usbg_submit_command(struct f_uas *, struct usb_request *); static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req) { struct f_uas *fu = req->context; - int ret; - if (req->status < 0) + if (req->status == -ESHUTDOWN) return; - ret = usbg_submit_command(fu, req->buf, req->actual); - /* - * Once we tune for performance enqueue the command req here again so - * we can receive a second command while we processing this one. Pay - * attention to properly sync STAUS endpoint with DATA IN + OUT so you - * don't break HS. - */ - if (!ret) + if (req->status < 0) { + usb_ep_queue(fu->ep_cmd, req, GFP_ATOMIC); return; - usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); + } + + usbg_submit_command(fu, req); } static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream) { + init_completion(&stream->cmd_completion); + stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); if (!stream->req_in) goto out; @@ -764,66 +902,48 @@ out: return -ENOMEM; } -static int uasp_alloc_cmd(struct f_uas *fu) +static int uasp_alloc_cmd(struct f_uas *fu, int i) { - fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL); - if (!fu->cmd.req) + fu->cmd[i].req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL); + if (!fu->cmd[i].req) goto err; - fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL); - if (!fu->cmd.buf) + fu->cmd[i].buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL); + if (!fu->cmd[i].buf) goto err_buf; - fu->cmd.req->complete = uasp_cmd_complete; - fu->cmd.req->buf = fu->cmd.buf; - fu->cmd.req->length = fu->ep_cmd->maxpacket; - fu->cmd.req->context = fu; + fu->cmd[i].req->complete = uasp_cmd_complete; + fu->cmd[i].req->buf = fu->cmd[i].buf; + fu->cmd[i].req->length = fu->ep_cmd->maxpacket; + fu->cmd[i].req->context = fu; return 0; err_buf: - usb_ep_free_request(fu->ep_cmd, fu->cmd.req); + usb_ep_free_request(fu->ep_cmd, fu->cmd[i].req); err: return -ENOMEM; } -static void uasp_setup_stream_res(struct f_uas *fu, int max_streams) -{ - int i; - - for (i = 0; i < max_streams; i++) { - struct uas_stream *s = &fu->stream[i]; - - s->req_in->stream_id = i + 1; - s->req_out->stream_id = i + 1; - s->req_status->stream_id = i + 1; - } -} - static int uasp_prepare_reqs(struct f_uas *fu) { int ret; int i; - int max_streams; - if (fu->flags & USBG_USE_STREAMS) - max_streams = UASP_SS_EP_COMP_NUM_STREAMS; - else - max_streams = 1; - - for (i = 0; i < max_streams; i++) { + for (i = 0; i < USBG_NUM_CMDS; i++) { ret = uasp_alloc_stream_res(fu, &fu->stream[i]); if (ret) goto err_cleanup; } - ret = uasp_alloc_cmd(fu); - if (ret) - goto err_free_stream; - uasp_setup_stream_res(fu, max_streams); + for (i = 0; i < USBG_NUM_CMDS; i++) { + ret = uasp_alloc_cmd(fu, i); + if (ret) + goto err_free_stream; - ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); - if (ret) - goto err_free_stream; + ret = usb_ep_queue(fu->ep_cmd, fu->cmd[i].req, GFP_ATOMIC); + if (ret) + goto err_free_stream; + } return 0; @@ -914,6 +1034,8 @@ static int get_cmd_dir(const unsigned char *cdb) case READ_TOC: case READ_FORMAT_CAPACITIES: case REQUEST_SENSE: + case ATA_12: + case ATA_16: ret = DMA_FROM_DEVICE; break; @@ -957,7 +1079,18 @@ static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req) struct usbg_cmd *cmd = req->context; struct se_cmd *se_cmd = &cmd->se_cmd; - if (req->status < 0) { + cmd->state = UASP_QUEUE_COMMAND; + + if (req->status == -ESHUTDOWN) { + struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag]; + + hash_del(&stream->node); + target_put_sess_cmd(se_cmd); + transport_generic_free_cmd(&cmd->se_cmd, 0); + return; + } + + if (req->status) { pr_err("%s() state %d transfer failed\n", __func__, cmd->state); goto cleanup; } @@ -969,11 +1102,22 @@ static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req) se_cmd->data_length); } - complete(&cmd->write_complete); + cmd->flags |= USBG_CMD_PENDING_DATA_WRITE; + queue_work(cmd->fu->tpg->workqueue, &cmd->work); return; cleanup: - transport_generic_free_cmd(&cmd->se_cmd, 0); + target_put_sess_cmd(se_cmd); + + /* Command was aborted due to overlapped tag */ + if (cmd->state == UASP_QUEUE_COMMAND && + cmd->tmr_rsp == RC_OVERLAPPED_TAG) { + uasp_send_tm_response(cmd); + return; + } + + transport_send_check_condition_and_sense(se_cmd, + TCM_CHECK_CONDITION_ABORT_CMD, 0); } static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req) @@ -995,9 +1139,12 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req) } req->is_last = 1; + req->stream_id = cmd->tag; req->complete = usbg_data_write_cmpl; req->length = se_cmd->data_length; req->context = cmd; + + cmd->state = UASP_SEND_STATUS; return 0; } @@ -1037,36 +1184,153 @@ static int usbg_send_read_response(struct se_cmd *se_cmd) return uasp_send_read_response(cmd); } -static void usbg_cmd_work(struct work_struct *work) +static void usbg_aborted_task(struct se_cmd *se_cmd); + +static void usbg_submit_tmr(struct usbg_cmd *cmd) +{ + struct se_session *se_sess; + struct se_cmd *se_cmd; + int flags = TARGET_SCF_ACK_KREF; + + se_cmd = &cmd->se_cmd; + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; + + target_submit_tmr(se_cmd, se_sess, + cmd->response_iu.add_response_info, + cmd->unpacked_lun, NULL, uasp_to_tcm_func(cmd->tmr_func), + GFP_ATOMIC, cmd->tag, flags); +} + +static void usbg_submit_cmd(struct usbg_cmd *cmd) { - struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work); struct se_cmd *se_cmd; struct tcm_usbg_nexus *tv_nexus; struct usbg_tpg *tpg; int dir, flags = (TARGET_SCF_UNKNOWN_SIZE | TARGET_SCF_ACK_KREF); + /* + * Note: each command will spawn its own process, and each stage of the + * command is processed sequentially. Should this no longer be the case, + * locking is needed. + */ + if (cmd->flags & USBG_CMD_PENDING_DATA_WRITE) { + target_execute_cmd(&cmd->se_cmd); + cmd->flags &= ~USBG_CMD_PENDING_DATA_WRITE; + return; + } + se_cmd = &cmd->se_cmd; tpg = cmd->fu->tpg; tv_nexus = tpg->tpg_nexus; dir = get_cmd_dir(cmd->cmd_buf); - if (dir < 0) { - __target_init_cmd(se_cmd, - tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, - tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, - cmd->prio_attr, cmd->sense_iu.sense, - cmd->unpacked_lun, NULL); + if (dir < 0) goto out; - } target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun, 0, cmd->prio_attr, dir, flags); + return; out: + __target_init_cmd(se_cmd, + tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, + tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, + cmd->prio_attr, cmd->sense_iu.sense, + cmd->unpacked_lun, NULL); transport_send_check_condition_and_sense(se_cmd, - TCM_UNSUPPORTED_SCSI_OPCODE, 1); - transport_generic_free_cmd(&cmd->se_cmd, 0); + TCM_UNSUPPORTED_SCSI_OPCODE, 0); +} + +static void usbg_cmd_work(struct work_struct *work) +{ + struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work); + + /* + * Failure is detected by f_tcm here. Skip submitting the command to the + * target core if we already know the failing response and send the usb + * response to the host directly. + */ + if (cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) + goto skip; + + if (cmd->tmr_func) + usbg_submit_tmr(cmd); + else + usbg_submit_cmd(cmd); + + return; + +skip: + if (cmd->tmr_rsp == RC_OVERLAPPED_TAG) { + struct f_uas *fu = cmd->fu; + struct se_session *se_sess; + struct uas_stream *stream = NULL; + struct hlist_node *tmp; + struct usbg_cmd *active_cmd = NULL; + + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; + + hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, cmd->tag) { + int i = stream - &fu->stream[0]; + + active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i]; + if (active_cmd->tag == cmd->tag) + break; + } + + /* Sanity check */ + if (!stream || (active_cmd && active_cmd->tag != cmd->tag)) { + usbg_submit_command(cmd->fu, cmd->req); + return; + } + + reinit_completion(&stream->cmd_completion); + + /* + * A UASP command consists of the command, data, and status + * stages, each operating sequentially from different endpoints. + * + * Each USB endpoint operates independently, and depending on + * hardware implementation, a completion callback for a transfer + * from one endpoint may not reflect the order of completion on + * the wire. This is particularly true for devices with + * endpoints that have independent interrupts and event buffers. + * + * The driver must still detect misbehaving hosts and respond + * with an overlap status. To reduce false overlap failures, + * allow the active and matching stream ID a brief 1ms to + * complete before responding with an overlap command failure. + * Overlap failure should be rare. + */ + wait_for_completion_timeout(&stream->cmd_completion, msecs_to_jiffies(1)); + + /* If the previous stream is completed, retry the command. */ + if (!hash_hashed(&stream->node)) { + usbg_submit_command(cmd->fu, cmd->req); + return; + } + + /* + * The command isn't submitted to the target core, so we're safe + * to remove the bitmap index from the session tag pool. + */ + sbitmap_queue_clear(&se_sess->sess_tag_pool, + cmd->se_cmd.map_tag, + cmd->se_cmd.map_cpu); + + /* + * Overlap command tag detected. Cancel any pending transfer of + * the command submitted to target core. + */ + active_cmd->tmr_rsp = RC_OVERLAPPED_TAG; + usbg_aborted_task(&active_cmd->se_cmd); + + /* Send the response after the transfer is aborted. */ + return; + } + + uasp_send_tm_response(cmd); } static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu, @@ -1084,6 +1348,7 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu, memset(cmd, 0, sizeof(*cmd)); cmd->se_cmd.map_tag = tag; cmd->se_cmd.map_cpu = cpu; + cmd->se_cmd.cpuid = cpu; cmd->se_cmd.tag = cmd->tag = scsi_tag; cmd->fu = fu; @@ -1092,50 +1357,82 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu, static void usbg_release_cmd(struct se_cmd *); -static int usbg_submit_command(struct f_uas *fu, - void *cmdbuf, unsigned int len) +static int usbg_submit_command(struct f_uas *fu, struct usb_request *req) { - struct command_iu *cmd_iu = cmdbuf; + struct iu *iu = req->buf; struct usbg_cmd *cmd; struct usbg_tpg *tpg = fu->tpg; struct tcm_usbg_nexus *tv_nexus; + struct uas_stream *stream; + struct hlist_node *tmp; + struct command_iu *cmd_iu; u32 cmd_len; u16 scsi_tag; - if (cmd_iu->iu_id != IU_ID_COMMAND) { - pr_err("Unsupported type %d\n", cmd_iu->iu_id); - return -EINVAL; - } - tv_nexus = tpg->tpg_nexus; if (!tv_nexus) { pr_err("Missing nexus, ignoring command\n"); return -EINVAL; } - cmd_len = (cmd_iu->len & ~0x3) + 16; - if (cmd_len > USBG_MAX_CMD) - return -EINVAL; - - scsi_tag = be16_to_cpup(&cmd_iu->tag); + scsi_tag = be16_to_cpup(&iu->tag); cmd = usbg_get_cmd(fu, tv_nexus, scsi_tag); if (IS_ERR(cmd)) { pr_err("usbg_get_cmd failed\n"); return -ENOMEM; } - memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len); - if (fu->flags & USBG_USE_STREAMS) { - if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS) - goto err; - if (!cmd->tag) - cmd->stream = &fu->stream[0]; - else - cmd->stream = &fu->stream[cmd->tag - 1]; - } else { - cmd->stream = &fu->stream[0]; + cmd->req = req; + cmd->fu = fu; + cmd->tag = scsi_tag; + cmd->se_cmd.tag = scsi_tag; + cmd->tmr_func = 0; + cmd->tmr_rsp = RC_RESPONSE_UNKNOWN; + cmd->flags = 0; + + cmd_iu = (struct command_iu *)iu; + + /* Command and Task Management IUs share the same LUN offset */ + cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun); + + if (iu->iu_id != IU_ID_COMMAND && iu->iu_id != IU_ID_TASK_MGMT) { + cmd->tmr_rsp = RC_INVALID_INFO_UNIT; + goto skip; + } + + hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, scsi_tag) { + struct usbg_cmd *active_cmd; + struct se_session *se_sess; + int i = stream - &fu->stream[0]; + + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; + active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i]; + + if (active_cmd->tag == scsi_tag) { + cmd->tmr_rsp = RC_OVERLAPPED_TAG; + goto skip; + } } + stream = &fu->stream[cmd->se_cmd.map_tag]; + hash_add(fu->stream_hash, &stream->node, scsi_tag); + + if (iu->iu_id == IU_ID_TASK_MGMT) { + struct task_mgmt_iu *tm_iu; + + tm_iu = (struct task_mgmt_iu *)iu; + cmd->tmr_func = tm_iu->function; + goto skip; + } + + cmd_len = (cmd_iu->len & ~0x3) + 16; + if (cmd_len > USBG_MAX_CMD) { + target_free_tag(tv_nexus->tvn_se_sess, &cmd->se_cmd); + hash_del(&stream->node); + return -EINVAL; + } + memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len); + switch (cmd_iu->prio_attr & 0x7) { case UAS_HEAD_TAG: cmd->prio_attr = TCM_HEAD_TAG; @@ -1155,15 +1452,11 @@ static int usbg_submit_command(struct f_uas *fu, break; } - cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun); - +skip: INIT_WORK(&cmd->work, usbg_cmd_work); queue_work(tpg->workqueue, &cmd->work); return 0; -err: - usbg_release_cmd(&cmd->se_cmd); - return -EINVAL; } static void bot_cmd_work(struct work_struct *work) @@ -1172,30 +1465,40 @@ static void bot_cmd_work(struct work_struct *work) struct se_cmd *se_cmd; struct tcm_usbg_nexus *tv_nexus; struct usbg_tpg *tpg; + int flags = TARGET_SCF_ACK_KREF; int dir; + /* + * Note: each command will spawn its own process, and each stage of the + * command is processed sequentially. Should this no longer be the case, + * locking is needed. + */ + if (cmd->flags & USBG_CMD_PENDING_DATA_WRITE) { + target_execute_cmd(&cmd->se_cmd); + cmd->flags &= ~USBG_CMD_PENDING_DATA_WRITE; + return; + } + se_cmd = &cmd->se_cmd; tpg = cmd->fu->tpg; tv_nexus = tpg->tpg_nexus; dir = get_cmd_dir(cmd->cmd_buf); - if (dir < 0) { - __target_init_cmd(se_cmd, - tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, - tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, - cmd->prio_attr, cmd->sense_iu.sense, - cmd->unpacked_lun, NULL); + if (dir < 0) goto out; - } target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun, - cmd->data_len, cmd->prio_attr, dir, 0); + cmd->data_len, cmd->prio_attr, dir, flags); return; out: + __target_init_cmd(se_cmd, + tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, + tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, + cmd->prio_attr, cmd->sense_iu.sense, + cmd->unpacked_lun, NULL); transport_send_check_condition_and_sense(se_cmd, - TCM_UNSUPPORTED_SCSI_OPCODE, 1); - transport_generic_free_cmd(&cmd->se_cmd, 0); + TCM_UNSUPPORTED_SCSI_OPCODE, 0); } static int bot_submit_command(struct f_uas *fu, @@ -1239,6 +1542,7 @@ static int bot_submit_command(struct f_uas *fu, cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0; cmd->data_len = le32_to_cpu(cbw->DataTransferLength); cmd->se_cmd.tag = le32_to_cpu(cmd->bot_tag); + cmd->flags = 0; INIT_WORK(&cmd->work, bot_cmd_work); queue_work(tpg->workqueue, &cmd->work); @@ -1275,16 +1579,38 @@ static void usbg_release_cmd(struct se_cmd *se_cmd) se_cmd); struct se_session *se_sess = se_cmd->se_sess; + cmd->tag = 0; kfree(cmd->data_buf); target_free_tag(se_sess, se_cmd); } static void usbg_queue_tm_rsp(struct se_cmd *se_cmd) { + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, se_cmd); + + uasp_send_tm_response(cmd); } static void usbg_aborted_task(struct se_cmd *se_cmd) { + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, se_cmd); + struct f_uas *fu = cmd->fu; + struct usb_gadget *gadget = fuas_to_gadget(fu); + struct uas_stream *stream = &fu->stream[se_cmd->map_tag]; + int ret = 0; + + if (stream->req_out->status == -EINPROGRESS) + ret = usb_ep_dequeue(fu->ep_out, stream->req_out); + else if (stream->req_in->status == -EINPROGRESS) + ret = usb_ep_dequeue(fu->ep_in, stream->req_in); + else if (stream->req_status->status == -EINPROGRESS) + ret = usb_ep_dequeue(fu->ep_status, stream->req_status); + + if (ret) + dev_err(&gadget->dev, "Failed to abort cmd tag %d, (%d)\n", + cmd->tag, ret); + + cmd->state = UASP_QUEUE_COMMAND; } static const char *usbg_check_wwn(const char *name) @@ -1355,7 +1681,8 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn, goto unref_dep; mutex_init(&tpg->tpg_mutex); atomic_set(&tpg->tpg_port_count, 0); - tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1); + tpg->workqueue = alloc_workqueue("tcm_usb_gadget", + WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE); if (!tpg->workqueue) goto free_tpg; @@ -1746,7 +2073,7 @@ static struct usb_endpoint_descriptor uasp_ss_bi_desc = { static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = { .bLength = sizeof(uasp_bi_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, + .bMaxBurst = 15, .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS, .wBytesPerInterval = 0, }; @@ -1754,7 +2081,7 @@ static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = { static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = { .bLength = sizeof(bot_bi_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, + .bMaxBurst = 15, }; static struct usb_endpoint_descriptor uasp_bo_desc = { @@ -1789,12 +2116,14 @@ static struct usb_endpoint_descriptor uasp_ss_bo_desc = { static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = { .bLength = sizeof(uasp_bo_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 15, .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS, }; static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = { .bLength = sizeof(bot_bo_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 15, }; static struct usb_endpoint_descriptor uasp_status_desc = { @@ -1971,43 +2300,39 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f) bot_intf_desc.bInterfaceNumber = iface; uasp_intf_desc.bInterfaceNumber = iface; fu->iface = iface; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc, - &uasp_bi_ep_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_bi_desc); if (!ep) goto ep_fail; fu->ep_in = ep; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc, - &uasp_bo_ep_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_bo_desc); if (!ep) goto ep_fail; fu->ep_out = ep; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc, - &uasp_status_in_ep_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_status_desc); if (!ep) goto ep_fail; fu->ep_status = ep; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc, - &uasp_cmd_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_cmd_desc); if (!ep) goto ep_fail; fu->ep_cmd = ep; /* Assume endpoint addresses are the same for both speeds */ - uasp_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress; - uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress; + uasp_bi_desc.bEndpointAddress = uasp_fs_bi_desc.bEndpointAddress; + uasp_bo_desc.bEndpointAddress = uasp_fs_bo_desc.bEndpointAddress; uasp_status_desc.bEndpointAddress = - uasp_ss_status_desc.bEndpointAddress; - uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress; + uasp_fs_status_desc.bEndpointAddress; + uasp_cmd_desc.bEndpointAddress = uasp_fs_cmd_desc.bEndpointAddress; - uasp_fs_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress; - uasp_fs_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress; - uasp_fs_status_desc.bEndpointAddress = - uasp_ss_status_desc.bEndpointAddress; - uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress; + uasp_ss_bi_desc.bEndpointAddress = uasp_fs_bi_desc.bEndpointAddress; + uasp_ss_bo_desc.bEndpointAddress = uasp_fs_bo_desc.bEndpointAddress; + uasp_ss_status_desc.bEndpointAddress = + uasp_fs_status_desc.bEndpointAddress; + uasp_ss_cmd_desc.bEndpointAddress = uasp_fs_cmd_desc.bEndpointAddress; ret = usb_assign_descriptors(f, uasp_fs_function_desc, uasp_hs_function_desc, uasp_ss_function_desc, @@ -2051,9 +2376,14 @@ static void tcm_delayed_set_alt(struct work_struct *wq) static int tcm_get_alt(struct usb_function *f, unsigned intf) { - if (intf == bot_intf_desc.bInterfaceNumber) + struct f_uas *fu = to_f_uas(f); + + if (fu->iface != intf) + return -EOPNOTSUPP; + + if (fu->flags & USBG_IS_BOT) return USB_G_ALT_INT_BBB; - if (intf == uasp_intf_desc.bInterfaceNumber) + else if (fu->flags & USBG_IS_UAS) return USB_G_ALT_INT_UAS; return -EOPNOTSUPP; @@ -2063,6 +2393,9 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_uas *fu = to_f_uas(f); + if (fu->iface != intf) + return -EOPNOTSUPP; + if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) { struct guas_setup_wq *work; @@ -2271,6 +2604,8 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi) fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; fu->tpg = tpg_instances[i].tpg; + + hash_init(fu->stream_hash); mutex_unlock(&tpg_instances_lock); return &fu->function; @@ -2301,5 +2636,6 @@ static void __exit tcm_exit(void) } module_exit(tcm_exit); +MODULE_DESCRIPTION("Target based USB-Gadget"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sebastian Andrzej Siewior"); diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 7de74a3dd392..c87e74afc881 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -377,24 +377,10 @@ enum { STR_AS_OUT_IF_ALT1, STR_AS_IN_IF_ALT0, STR_AS_IN_IF_ALT1, + NUM_STR_DESCRIPTORS, }; -static struct usb_string strings_uac1[] = { - /* [STR_AC_IF].s = DYNAMIC, */ - [STR_USB_OUT_IT].s = "Playback Input terminal", - [STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels", - [STR_IO_OUT_OT].s = "Playback Output terminal", - [STR_IO_IN_IT].s = "Capture Input terminal", - [STR_IO_IN_IT_CH_NAMES].s = "Capture Channels", - [STR_USB_IN_OT].s = "Capture Output terminal", - [STR_FU_IN].s = "Capture Volume", - [STR_FU_OUT].s = "Playback Volume", - [STR_AS_OUT_IF_ALT0].s = "Playback Inactive", - [STR_AS_OUT_IF_ALT1].s = "Playback Active", - [STR_AS_IN_IF_ALT0].s = "Capture Inactive", - [STR_AS_IN_IF_ALT1].s = "Capture Active", - { }, -}; +static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {}; static struct usb_gadget_strings str_uac1 = { .language = 0x0409, /* en-us */ @@ -1265,6 +1251,20 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) strings_uac1[STR_AC_IF].s = audio_opts->function_name; + strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name; + strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name; + strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name; + strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name; + strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive"; + strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active"; + + strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name; + strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name; + strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name; + strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name; + strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive"; + strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active"; + us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); if (IS_ERR(us)) return PTR_ERR(us); @@ -1681,8 +1681,19 @@ UAC1_ATTRIBUTE(bool, c_volume_present); UAC1_ATTRIBUTE(s16, c_volume_min); UAC1_ATTRIBUTE(s16, c_volume_max); UAC1_ATTRIBUTE(s16, c_volume_res); + UAC1_ATTRIBUTE_STRING(function_name); +UAC1_ATTRIBUTE_STRING(p_it_name); +UAC1_ATTRIBUTE_STRING(p_it_ch_name); +UAC1_ATTRIBUTE_STRING(p_ot_name); +UAC1_ATTRIBUTE_STRING(p_fu_vol_name); + +UAC1_ATTRIBUTE_STRING(c_it_name); +UAC1_ATTRIBUTE_STRING(c_it_ch_name); +UAC1_ATTRIBUTE_STRING(c_ot_name); +UAC1_ATTRIBUTE_STRING(c_fu_vol_name); + static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_c_chmask, &f_uac1_opts_attr_c_srate, @@ -1706,6 +1717,16 @@ static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_function_name, + &f_uac1_opts_attr_p_it_name, + &f_uac1_opts_attr_p_it_ch_name, + &f_uac1_opts_attr_p_ot_name, + &f_uac1_opts_attr_p_fu_vol_name, + + &f_uac1_opts_attr_c_it_name, + &f_uac1_opts_attr_c_it_ch_name, + &f_uac1_opts_attr_c_ot_name, + &f_uac1_opts_attr_c_fu_vol_name, + NULL, }; @@ -1760,6 +1781,16 @@ static struct usb_function_instance *f_audio_alloc_inst(void) scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); + scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "Capture Input terminal"); + scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels"); + scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "Capture Output terminal"); + scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume"); + + scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "Playback Input terminal"); + scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels"); + scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "Playback Output terminal"); + scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume"); + return &opts->func_inst; } @@ -1823,5 +1854,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc); +MODULE_DESCRIPTION("USB Audio Class 1.0 Function (using u_audio API)"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ruslan Bilovol"); diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index e2d7f69128a0..49cf5aae90ca 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -1014,5 +1014,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(uac1_legacy, f_audio_alloc_inst, f_audio_alloc); +MODULE_DESCRIPTION("USB Audio class function driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bryan Wu"); diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 383f6854cfec..9b324821c93b 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -95,7 +95,9 @@ enum { STR_CLKSRC_IN, STR_CLKSRC_OUT, STR_USB_IT, + STR_USB_IT_CH, STR_IO_IT, + STR_IO_IT_CH, STR_USB_OT, STR_IO_OT, STR_FU_IN, @@ -104,25 +106,10 @@ enum { STR_AS_OUT_ALT1, STR_AS_IN_ALT0, STR_AS_IN_ALT1, + NUM_STR_DESCRIPTORS, }; -static struct usb_string strings_fn[] = { - /* [STR_ASSOC].s = DYNAMIC, */ - [STR_IF_CTRL].s = "Topology Control", - [STR_CLKSRC_IN].s = "Input Clock", - [STR_CLKSRC_OUT].s = "Output Clock", - [STR_USB_IT].s = "USBH Out", - [STR_IO_IT].s = "USBD Out", - [STR_USB_OT].s = "USBH In", - [STR_IO_OT].s = "USBD In", - [STR_FU_IN].s = "Capture Volume", - [STR_FU_OUT].s = "Playback Volume", - [STR_AS_OUT_ALT0].s = "Playback Inactive", - [STR_AS_OUT_ALT1].s = "Playback Active", - [STR_AS_IN_ALT0].s = "Capture Inactive", - [STR_AS_IN_ALT1].s = "Capture Active", - { }, -}; +static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {}; static const char *const speed_names[] = { [USB_SPEED_UNKNOWN] = "UNKNOWN", @@ -1049,6 +1036,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) return ret; strings_fn[STR_ASSOC].s = uac2_opts->function_name; + strings_fn[STR_IF_CTRL].s = uac2_opts->if_ctrl_name; + strings_fn[STR_CLKSRC_IN].s = uac2_opts->clksrc_in_name; + strings_fn[STR_CLKSRC_OUT].s = uac2_opts->clksrc_out_name; + + strings_fn[STR_USB_IT].s = uac2_opts->c_it_name; + strings_fn[STR_USB_IT_CH].s = uac2_opts->c_it_ch_name; + strings_fn[STR_IO_OT].s = uac2_opts->c_ot_name; + strings_fn[STR_FU_OUT].s = uac2_opts->c_fu_vol_name; + strings_fn[STR_AS_OUT_ALT0].s = "Playback Inactive"; + strings_fn[STR_AS_OUT_ALT1].s = "Playback Active"; + + strings_fn[STR_IO_IT].s = uac2_opts->p_it_name; + strings_fn[STR_IO_IT_CH].s = uac2_opts->p_it_ch_name; + strings_fn[STR_USB_OT].s = uac2_opts->p_ot_name; + strings_fn[STR_FU_IN].s = uac2_opts->p_fu_vol_name; + strings_fn[STR_AS_IN_ALT0].s = "Capture Inactive"; + strings_fn[STR_AS_IN_ALT1].s = "Capture Active"; us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); if (IS_ERR(us)) @@ -1072,7 +1076,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id; out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id; usb_out_it_desc.iTerminal = us[STR_USB_IT].id; + usb_out_it_desc.iChannelNames = us[STR_USB_IT_CH].id; io_in_it_desc.iTerminal = us[STR_IO_IT].id; + io_in_it_desc.iChannelNames = us[STR_IO_IT_CH].id; usb_in_ot_desc.iTerminal = us[STR_USB_OT].id; io_out_ot_desc.iTerminal = us[STR_IO_OT].id; std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id; @@ -1179,6 +1185,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) uac2->as_in_alt = 0; } + std_ac_if_desc.bNumEndpoints = 0; if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) { uac2->int_ep = usb_ep_autoconfig(gadget, &fs_ep_int_desc); if (!uac2->int_ep) { @@ -2055,7 +2062,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ const char *page, size_t len) \ { \ struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int ret = 0; \ + int ret = len; \ \ mutex_lock(&opts->lock); \ if (opts->refcnt) { \ @@ -2063,8 +2070,11 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ goto end; \ } \ \ - ret = scnprintf(opts->name, min(sizeof(opts->name), len), \ - "%s", page); \ + if (len && page[len - 1] == '\n') \ + len--; \ + \ + scnprintf(opts->name, min(sizeof(opts->name), len + 1), \ + "%s", page); \ \ end: \ mutex_unlock(&opts->lock); \ @@ -2097,10 +2107,24 @@ UAC2_ATTRIBUTE(s16, c_volume_max); UAC2_ATTRIBUTE(s16, c_volume_res); UAC2_ATTRIBUTE(u32, fb_max); UAC2_ATTRIBUTE_STRING(function_name); +UAC2_ATTRIBUTE_STRING(if_ctrl_name); +UAC2_ATTRIBUTE_STRING(clksrc_in_name); +UAC2_ATTRIBUTE_STRING(clksrc_out_name); + +UAC2_ATTRIBUTE_STRING(p_it_name); +UAC2_ATTRIBUTE_STRING(p_it_ch_name); +UAC2_ATTRIBUTE_STRING(p_ot_name); +UAC2_ATTRIBUTE_STRING(p_fu_vol_name); + +UAC2_ATTRIBUTE_STRING(c_it_name); +UAC2_ATTRIBUTE_STRING(c_it_ch_name); +UAC2_ATTRIBUTE_STRING(c_ot_name); +UAC2_ATTRIBUTE_STRING(c_fu_vol_name); UAC2_ATTRIBUTE(s16, p_terminal_type); UAC2_ATTRIBUTE(s16, c_terminal_type); + static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_p_chmask, &f_uac2_opts_attr_p_srate, @@ -2127,6 +2151,19 @@ static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_c_volume_res, &f_uac2_opts_attr_function_name, + &f_uac2_opts_attr_if_ctrl_name, + &f_uac2_opts_attr_clksrc_in_name, + &f_uac2_opts_attr_clksrc_out_name, + + &f_uac2_opts_attr_p_it_name, + &f_uac2_opts_attr_p_it_ch_name, + &f_uac2_opts_attr_p_ot_name, + &f_uac2_opts_attr_p_fu_vol_name, + + &f_uac2_opts_attr_c_it_name, + &f_uac2_opts_attr_c_it_ch_name, + &f_uac2_opts_attr_c_ot_name, + &f_uac2_opts_attr_c_fu_vol_name, &f_uac2_opts_attr_p_terminal_type, &f_uac2_opts_attr_c_terminal_type, @@ -2188,6 +2225,19 @@ static struct usb_function_instance *afunc_alloc_inst(void) opts->fb_max = FBACK_FAST_MAX; scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink"); + scnprintf(opts->if_ctrl_name, sizeof(opts->if_ctrl_name), "Topology Control"); + scnprintf(opts->clksrc_in_name, sizeof(opts->clksrc_in_name), "Input Clock"); + scnprintf(opts->clksrc_out_name, sizeof(opts->clksrc_out_name), "Output Clock"); + + scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "USBD Out"); + scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels"); + scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "USBH In"); + scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume"); + + scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "USBH Out"); + scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels"); + scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "USBD In"); + scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume"); opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE; opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE; @@ -2251,6 +2301,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc); +MODULE_DESCRIPTION("USB Audio Class 2.0 Function"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yadwinder Singh"); MODULE_AUTHOR("Jaswinder Singh"); diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 929666805bd2..aa6ab666741a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -465,7 +465,7 @@ uvc_register_video(struct uvc_device *uvc) memcpy(mem, desc, (desc)->bLength); \ *(dst)++ = mem; \ mem += (desc)->bLength; \ - } while (0); + } while (0) #define UVC_COPY_DESCRIPTORS(mem, dst, src) \ do { \ @@ -991,6 +991,8 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_info(f, "%s()\n", __func__); + kthread_cancel_work_sync(&video->hw_submit); + if (video->async_wq) destroy_workqueue(video->async_wq); @@ -1118,5 +1120,6 @@ err_config: } DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc); +MODULE_DESCRIPTION("USB Video Class Gadget driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Laurent Pinchart"); diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 29bf8664bf58..afd75d72412c 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -31,7 +31,7 @@ #include <asm/io.h> #include <asm/byteorder.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "u_rndis.h" @@ -869,12 +869,12 @@ EXPORT_SYMBOL_GPL(rndis_msg_parser); static inline int rndis_get_nr(void) { - return ida_simple_get(&rndis_ida, 0, 1000, GFP_KERNEL); + return ida_alloc_max(&rndis_ida, 999, GFP_KERNEL); } static inline void rndis_put_nr(int nr) { - ida_simple_remove(&rndis_ida, nr); + ida_free(&rndis_ida, nr); } struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c index 2a4163b0f6fe..75831f2c7abe 100644 --- a/drivers/usb/gadget/function/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c @@ -537,4 +537,5 @@ ssize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *file } EXPORT_SYMBOL_GPL(fsg_store_forced_eject); +MODULE_DESCRIPTION("Common definitions for mass storage functionality"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h index 0a544a82cbf8..11ac785d5eee 100644 --- a/drivers/usb/gadget/function/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h @@ -5,7 +5,7 @@ #include <linux/device.h> #include <linux/usb/storage.h> #include <scsi/scsi.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #ifndef DEBUG #undef VERBOSE_DEBUG @@ -131,7 +131,7 @@ static inline bool fsg_lun_is_open(struct fsg_lun *curlun) #define FSG_BUFLEN ((u32)16384) /* Maximal number of LUNs supported in mass storage function */ -#define FSG_MAX_LUNS 16 +#define FSG_MAX_LUNS (US_BULK_MAX_LUN_LIMIT + 1) enum fsg_buffer_state { BUF_STATE_SENDING = -2, diff --git a/drivers/usb/gadget/function/tcm.h b/drivers/usb/gadget/function/tcm.h index 3cd565794ad7..009974d81d66 100644 --- a/drivers/usb/gadget/function/tcm.h +++ b/drivers/usb/gadget/function/tcm.h @@ -4,6 +4,7 @@ #include <linux/kref.h> /* #include <linux/usb/uas.h> */ +#include <linux/hashtable.h> #include <linux/usb/composite.h> #include <linux/usb/uas.h> #include <linux/usb/storage.h> @@ -13,9 +14,11 @@ #define USBG_NAMELEN 32 #define fuas_to_gadget(f) (f->function.config->cdev->gadget) -#define UASP_SS_EP_COMP_LOG_STREAMS 4 +#define UASP_SS_EP_COMP_LOG_STREAMS 5 #define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS) +#define USBG_NUM_CMDS (UASP_SS_EP_COMP_NUM_STREAMS + 1) + enum { USB_G_STR_INT_UAS = 0, USB_G_STR_INT_BBB, @@ -24,7 +27,7 @@ enum { #define USB_G_ALT_INT_BBB 0 #define USB_G_ALT_INT_UAS 1 -#define USB_G_DEFAULT_SESSION_TAGS 128 +#define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS struct tcm_usbg_nexus { struct se_session *tvn_se_sess; @@ -72,15 +75,23 @@ struct usbg_cmd { struct se_cmd se_cmd; void *data_buf; /* used if no sg support available */ struct f_uas *fu; - struct completion write_complete; struct kref ref; + struct usb_request *req; + + u32 flags; +#define USBG_CMD_PENDING_DATA_WRITE BIT(0) + /* UAS only */ u16 tag; u16 prio_attr; struct sense_iu sense_iu; + struct response_iu response_iu; enum uas_state state; - struct uas_stream *stream; + + int tmr_func; + int tmr_rsp; +#define RC_RESPONSE_UNKNOWN 0xff /* BOT only */ __le32 bot_tag; @@ -93,6 +104,9 @@ struct uas_stream { struct usb_request *req_in; struct usb_request *req_out; struct usb_request *req_status; + + struct completion cmd_completion; + struct hlist_node node; }; struct usbg_cdb { @@ -116,15 +130,17 @@ struct f_uas { #define USBG_USE_STREAMS (1 << 2) #define USBG_IS_BOT (1 << 3) #define USBG_BOT_CMD_PEND (1 << 4) +#define USBG_BOT_WEDGED (1 << 5) - struct usbg_cdb cmd; + struct usbg_cdb cmd[USBG_NUM_CMDS]; struct usb_ep *ep_in; struct usb_ep *ep_out; /* UAS */ struct usb_ep *ep_status; struct usb_ep *ep_cmd; - struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS]; + struct uas_stream stream[USBG_NUM_CMDS]; + DECLARE_HASHTABLE(stream_hash, UASP_SS_EP_COMP_LOG_STREAMS); /* BOT */ struct bot_status bot_status; diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 4a42574b4a7f..ca8dbec65f73 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -57,13 +57,13 @@ struct uac_rtd_params { /* Volume/Mute controls and their state */ int fu_id; /* Feature Unit ID */ - struct snd_kcontrol *snd_kctl_volume; - struct snd_kcontrol *snd_kctl_mute; + struct snd_ctl_elem_id snd_kctl_volume_id; + struct snd_ctl_elem_id snd_kctl_mute_id; s16 volume_min, volume_max, volume_res; s16 volume; int mute; - struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */ + struct snd_ctl_elem_id snd_kctl_rate_id; /* read-only current rate */ int srate; /* selected samplerate */ int active; /* playback/capture running */ @@ -494,14 +494,13 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) static void set_active(struct uac_rtd_params *prm, bool active) { // notifying through the Rate ctrl - struct snd_kcontrol *kctl = prm->snd_kctl_rate; unsigned long flags; spin_lock_irqsave(&prm->lock, flags); if (prm->active != active) { prm->active = active; snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, - &kctl->id); + &prm->snd_kctl_rate_id); } spin_unlock_irqrestore(&prm->lock, flags); } @@ -593,16 +592,25 @@ int u_audio_start_capture(struct g_audio *audio_dev) struct usb_ep *ep, *ep_fback; struct uac_rtd_params *prm; struct uac_params *params = &audio_dev->params; - int req_len, i; + int req_len, i, ret; prm = &uac->c_prm; dev_dbg(dev, "start capture with rate %d\n", prm->srate); ep = audio_dev->out_ep; - config_ep_by_speed(gadget, &audio_dev->func, ep); + ret = config_ep_by_speed(gadget, &audio_dev->func, ep); + if (ret < 0) { + dev_err(dev, "config_ep_by_speed for out_ep failed (%d)\n", ret); + return ret; + } + req_len = ep->maxpacket; prm->ep_enabled = true; - usb_ep_enable(ep); + ret = usb_ep_enable(ep); + if (ret < 0) { + dev_err(dev, "usb_ep_enable failed for out_ep (%d)\n", ret); + return ret; + } for (i = 0; i < params->req_number; i++) { if (!prm->reqs[i]) { @@ -630,9 +638,18 @@ int u_audio_start_capture(struct g_audio *audio_dev) return 0; /* Setup feedback endpoint */ - config_ep_by_speed(gadget, &audio_dev->func, ep_fback); + ret = config_ep_by_speed(gadget, &audio_dev->func, ep_fback); + if (ret < 0) { + dev_err(dev, "config_ep_by_speed in_ep_fback failed (%d)\n", ret); + return ret; // TODO: Clean up out_ep + } + prm->fb_ep_enabled = true; - usb_ep_enable(ep_fback); + ret = usb_ep_enable(ep_fback); + if (ret < 0) { + dev_err(dev, "usb_ep_enable failed for in_ep_fback (%d)\n", ret); + return ret; // TODO: Clean up out_ep + } req_len = ep_fback->maxpacket; req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); @@ -688,13 +705,17 @@ int u_audio_start_playback(struct g_audio *audio_dev) struct uac_params *params = &audio_dev->params; unsigned int factor; const struct usb_endpoint_descriptor *ep_desc; - int req_len, i; + int req_len, i, ret; unsigned int p_pktsize; prm = &uac->p_prm; dev_dbg(dev, "start playback with rate %d\n", prm->srate); ep = audio_dev->in_ep; - config_ep_by_speed(gadget, &audio_dev->func, ep); + ret = config_ep_by_speed(gadget, &audio_dev->func, ep); + if (ret < 0) { + dev_err(dev, "config_ep_by_speed for in_ep failed (%d)\n", ret); + return ret; + } ep_desc = ep->desc; /* @@ -721,7 +742,11 @@ int u_audio_start_playback(struct g_audio *audio_dev) uac->p_residue_mil = 0; prm->ep_enabled = true; - usb_ep_enable(ep); + ret = usb_ep_enable(ep); + if (ret < 0) { + dev_err(dev, "usb_ep_enable failed for in_ep (%d)\n", ret); + return ret; + } for (i = 0; i < params->req_number; i++) { if (!prm->reqs[i]) { @@ -807,7 +832,7 @@ int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) if (change) snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, - &prm->snd_kctl_volume->id); + &prm->snd_kctl_volume_id); return 0; } @@ -856,7 +881,7 @@ int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) if (change) snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, - &prm->snd_kctl_mute->id); + &prm->snd_kctl_mute_id); return 0; } @@ -1115,35 +1140,35 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new u_audio_controls[] = { - [UAC_FBACK_CTRL] { + [UAC_FBACK_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Capture Pitch 1000000", .info = u_audio_pitch_info, .get = u_audio_pitch_get, .put = u_audio_pitch_put, }, - [UAC_P_PITCH_CTRL] { + [UAC_P_PITCH_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Playback Pitch 1000000", .info = u_audio_pitch_info, .get = u_audio_pitch_get, .put = u_audio_pitch_put, }, - [UAC_MUTE_CTRL] { + [UAC_MUTE_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later */ .info = u_audio_mute_info, .get = u_audio_mute_get, .put = u_audio_mute_put, }, - [UAC_VOLUME_CTRL] { + [UAC_VOLUME_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later */ .info = u_audio_volume_info, .get = u_audio_volume_get, .put = u_audio_volume_put, }, - [UAC_RATE_CTRL] { + [UAC_RATE_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "", /* will be filled later */ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, @@ -1243,7 +1268,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, if (err < 0) goto snd_fail; - strscpy(pcm->name, pcm_name, sizeof(pcm->name)); + strscpy(pcm->name, pcm_name); pcm->private_data = uac; uac->pcm = pcm; @@ -1257,7 +1282,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, if ((c_chmask && g_audio->in_ep_fback) || (p_chmask && params->p_fu.id) || (c_chmask && params->c_fu.id)) - strscpy(card->mixername, card_name, sizeof(card->driver)); + strscpy(card->mixername, card_name); if (c_chmask && g_audio->in_ep_fback) { kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL], @@ -1331,7 +1356,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, err = snd_ctl_add(card, kctl); if (err < 0) goto snd_fail; - prm->snd_kctl_mute = kctl; + prm->snd_kctl_mute_id = kctl->id; prm->mute = 0; } @@ -1359,7 +1384,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, err = snd_ctl_add(card, kctl); if (err < 0) goto snd_fail; - prm->snd_kctl_volume = kctl; + prm->snd_kctl_volume_id = kctl->id; prm->volume = fu->volume_max; prm->volume_max = fu->volume_max; prm->volume_min = fu->volume_min; @@ -1383,12 +1408,13 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, err = snd_ctl_add(card, kctl); if (err < 0) goto snd_fail; - prm->snd_kctl_rate = kctl; + prm->snd_kctl_rate_id = kctl->id; } - strscpy(card->driver, card_name, sizeof(card->driver)); - strscpy(card->shortname, card_name, sizeof(card->shortname)); - sprintf(card->longname, "%s %i", card_name, card->dev->id); + strscpy(card->driver, card_name); + strscpy(card->shortname, card_name); + snprintf(card->longname, sizeof(card->longname), "%s %i", + card_name, card->dev->id); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, NULL, 0, BUFF_SIZE_MAX); @@ -1420,6 +1446,8 @@ void g_audio_cleanup(struct g_audio *g_audio) return; uac = g_audio->uac; + g_audio->uac = NULL; + card = uac->card; if (card) snd_card_free_when_closed(card); diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 3c5a6f6ac341..f58590bf5e02 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -718,7 +718,7 @@ static const struct net_device_ops eth_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; -static struct device_type gadget_type = { +static const struct device_type gadget_type = { .name = "gadget", }; @@ -1032,7 +1032,7 @@ int gether_set_ifname(struct net_device *net, const char *name, int len) if (!p || p[1] != 'd' || strchr(p + 2, '%')) return -EINVAL; - strncpy(net->name, tmp, sizeof(net->name)); + strscpy(net->name, tmp); dev->ifname_set = true; return 0; @@ -1052,8 +1052,8 @@ void gether_suspend(struct gether *link) * There is a transfer in progress. So we trigger a remote * wakeup to inform the host. */ - ether_wakeup_host(dev->port_usb); - return; + if (!ether_wakeup_host(dev->port_usb)) + return; } spin_lock_irqsave(&dev->lock, flags); link->is_suspend = true; @@ -1163,8 +1163,6 @@ struct net_device *gether_connect(struct gether *link) if (netif_running(dev->net)) eth_start(dev, GFP_ATOMIC); - netif_device_attach(dev->net); - /* on error, disable any endpoints */ } else { (void) usb_ep_disable(link->out_ep); @@ -1202,7 +1200,7 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); - netif_device_detach(dev->net); + netif_stop_queue(dev->net); netif_carrier_off(dev->net); /* disable endpoints, forcing (synchronous) completion @@ -1247,5 +1245,6 @@ void gether_disconnect(struct gether *link) } EXPORT_SYMBOL_GPL(gether_disconnect); +MODULE_DESCRIPTION("Ethernet-over-USB link layer utilities for Gadget stack"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index a92eb6d90976..36fff45e8c9b 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -21,6 +21,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/export.h> #include <linux/module.h> #include <linux/console.h> @@ -28,6 +29,7 @@ #include <linux/kthread.h> #include <linux/workqueue.h> #include <linux/kfifo.h> +#include <linux/serial.h> #include "u_serial.h" @@ -126,6 +128,7 @@ struct gs_port { wait_queue_head_t close_wait; bool suspended; /* port suspended */ bool start_delayed; /* delay start when suspended */ + struct async_icount icount; /* REVISIT this state ... */ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ @@ -257,6 +260,7 @@ __acquires(&port->port_lock) break; } do_tty_wake = true; + port->icount.tx += len; req->length = len; list_del(&req->list); @@ -408,6 +412,7 @@ static void gs_rx_push(struct work_struct *work) size -= n; } + port->icount.rx += size; count = tty_insert_flip_string(&port->port, packet, size); if (count) @@ -575,9 +580,12 @@ static int gs_start_io(struct gs_port *port) * we didn't in gs_start_tx() */ tty_wakeup(port->port.tty); } else { - gs_free_requests(ep, head, &port->read_allocated); - gs_free_requests(port->port_usb->in, &port->write_pool, - &port->write_allocated); + /* Free reqs only if we are still connected */ + if (port->port_usb) { + gs_free_requests(ep, head, &port->read_allocated); + gs_free_requests(port->port_usb->in, &port->write_pool, + &port->write_allocated); + } status = -EIO; } @@ -851,6 +859,23 @@ static int gs_break_ctl(struct tty_struct *tty, int duration) return status; } +static int gs_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct gs_port *port = tty->driver_data; + struct async_icount cnow; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + cnow = port->icount; + spin_unlock_irqrestore(&port->port_lock, flags); + + icount->rx = cnow.rx; + icount->tx = cnow.tx; + + return 0; +} + static const struct tty_operations gs_tty_ops = { .open = gs_open, .close = gs_close, @@ -861,6 +886,7 @@ static const struct tty_operations gs_tty_ops = { .chars_in_buffer = gs_chars_in_buffer, .unthrottle = gs_unthrottle, .break_ctl = gs_break_ctl, + .get_icount = gs_get_icount, }; /*-------------------------------------------------------------------------*/ @@ -1441,6 +1467,7 @@ void gserial_suspend(struct gserial *gser) spin_lock(&port->port_lock); spin_unlock(&serial_port_lock); port->suspended = true; + port->start_delayed = true; spin_unlock_irqrestore(&port->port_lock, flags); } EXPORT_SYMBOL_GPL(gserial_suspend); @@ -1519,7 +1546,7 @@ static int __init userial_init(void) pr_debug("%s: registered %d ttyGS* device%s\n", __func__, MAX_U_SERIAL_PORTS, - (MAX_U_SERIAL_PORTS == 1) ? "" : "s"); + str_plural(MAX_U_SERIAL_PORTS)); return status; fail: @@ -1536,4 +1563,5 @@ static void __exit userial_cleanup(void) } module_exit(userial_cleanup); +MODULE_DESCRIPTION("utilities for USB gadget \"serial port\"/TTY support"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 901d99310bc4..e1274338ea61 100644 --- a/drivers/usb/gadget/function/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -17,6 +17,10 @@ struct f_serial_opts { struct usb_function_instance func_inst; u8 port_num; + u8 protocol; + + struct mutex lock; /* protect instances */ + int instances; }; /* diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index f7a616760e31..feb6eb76462f 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -52,7 +52,17 @@ struct f_uac1_opts { int req_number; unsigned bound:1; - char function_name[32]; + char function_name[USB_MAX_STRING_LEN]; + + char p_it_name[USB_MAX_STRING_LEN]; + char p_it_ch_name[USB_MAX_STRING_LEN]; + char p_ot_name[USB_MAX_STRING_LEN]; + char p_fu_vol_name[USB_MAX_STRING_LEN]; + + char c_it_name[USB_MAX_STRING_LEN]; + char c_it_ch_name[USB_MAX_STRING_LEN]; + char c_ot_name[USB_MAX_STRING_LEN]; + char c_fu_vol_name[USB_MAX_STRING_LEN]; struct mutex lock; int refcnt; diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 5e81bdd6c5fb..0df808289ded 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -68,7 +68,20 @@ struct f_uac2_opts { int fb_max; bool bound; - char function_name[32]; + char function_name[USB_MAX_STRING_LEN]; + char if_ctrl_name[USB_MAX_STRING_LEN]; + char clksrc_in_name[USB_MAX_STRING_LEN]; + char clksrc_out_name[USB_MAX_STRING_LEN]; + + char p_it_name[USB_MAX_STRING_LEN]; + char p_it_ch_name[USB_MAX_STRING_LEN]; + char p_ot_name[USB_MAX_STRING_LEN]; + char p_fu_vol_name[USB_MAX_STRING_LEN]; + + char c_it_name[USB_MAX_STRING_LEN]; + char c_it_ch_name[USB_MAX_STRING_LEN]; + char c_ot_name[USB_MAX_STRING_LEN]; + char c_fu_vol_name[USB_MAX_STRING_LEN]; s16 p_terminal_type; s16 c_terminal_type; diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index cb35687b11e7..6f44dd732315 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -71,6 +71,11 @@ extern unsigned int uvc_gadget_trace_param; #define UVCG_REQUEST_HEADER_LEN 12 +#define UVCG_REQ_MAX_INT_COUNT 16 +#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT) + +#define UVCG_STREAMING_MIN_BUFFERS 2 + /* ------------------------------------------------------------------------ * Structures */ @@ -91,16 +96,24 @@ struct uvc_video { struct work_struct pump; struct workqueue_struct *async_wq; + struct kthread_worker *kworker; + struct kthread_work hw_submit; + + atomic_t queued; + /* Frame parameters */ u8 bpp; u32 fcc; unsigned int width; unsigned int height; unsigned int imagesize; + unsigned int interval; struct mutex mutex; /* protects frame parameters */ unsigned int uvc_num_requests; + unsigned int reqs_per_frame; + /* Requests */ bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 7e704b2bcfd1..f131943254a4 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -13,6 +13,7 @@ #include "uvc_configfs.h" #include <linux/sort.h> +#include <linux/usb/uvc.h> #include <linux/usb/video.h> /* ----------------------------------------------------------------------------- @@ -92,10 +93,10 @@ static int __uvcg_iter_item_entries(const char *page, size_t len, while (pg - page < len) { i = 0; - while (i < sizeof(buf) && (pg - page < len) && + while (i < bufsize && (pg - page < len) && *pg != '\0' && *pg != '\n') buf[i++] = *pg++; - if (i == sizeof(buf)) { + if (i == bufsize) { ret = -EINVAL; goto out_free_buf; } @@ -1565,11 +1566,13 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { /* ----------------------------------------------------------------------------- * streaming/uncompressed * streaming/mjpeg + * streaming/framebased */ static const char * const uvcg_format_names[] = { "uncompressed", "mjpeg", + "framebased", }; static struct uvcg_color_matching * @@ -1776,6 +1779,9 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + uvcg_format_set_indices(to_config_group(target)); format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); @@ -1815,6 +1821,9 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) if (format_ptr->fmt == target_fmt) { list_del(&format_ptr->entry); @@ -1825,6 +1834,7 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, --target_fmt->linked; +out: mutex_unlock(&opts->lock); mutex_unlock(su_mutex); } @@ -2021,6 +2031,7 @@ UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); +UVCG_FRAME_ATTR(dw_bytes_perline, dwBytesPerLine, 32); #undef UVCG_FRAME_ATTR @@ -2034,7 +2045,7 @@ static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, int result, i; char *pg = page; - mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2104,7 +2115,7 @@ end: UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); -static struct configfs_attribute *uvcg_frame_attrs[] = { +static struct configfs_attribute *uvcg_frame_attrs1[] = { &uvcg_frame_attr_b_frame_index, &uvcg_frame_attr_bm_capabilities, &uvcg_frame_attr_w_width, @@ -2117,12 +2128,31 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { NULL, }; -static const struct config_item_type uvcg_frame_type = { +static struct configfs_attribute *uvcg_frame_attrs2[] = { + &uvcg_frame_attr_b_frame_index, + &uvcg_frame_attr_bm_capabilities, + &uvcg_frame_attr_w_width, + &uvcg_frame_attr_w_height, + &uvcg_frame_attr_dw_min_bit_rate, + &uvcg_frame_attr_dw_max_bit_rate, + &uvcg_frame_attr_dw_default_frame_interval, + &uvcg_frame_attr_dw_frame_interval, + &uvcg_frame_attr_dw_bytes_perline, + NULL, +}; + +static const struct config_item_type uvcg_frame_type1 = { .ct_item_ops = &uvcg_config_item_ops, - .ct_attrs = uvcg_frame_attrs, + .ct_attrs = uvcg_frame_attrs1, .ct_owner = THIS_MODULE, }; +static const struct config_item_type uvcg_frame_type2 = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_frame_attrs2, + .ct_owner = THIS_MODULE, +}; + static struct config_item *uvcg_frame_make(struct config_group *group, const char *name) { @@ -2144,6 +2174,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->frame.dw_max_bit_rate = 55296000; h->frame.dw_max_video_frame_buffer_size = 460800; h->frame.dw_default_frame_interval = 666666; + h->frame.dw_bytes_perline = 0; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2156,6 +2187,9 @@ static struct config_item *uvcg_frame_make(struct config_group *group, } else if (fmt->type == UVCG_MJPEG) { h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; h->fmt_type = UVCG_MJPEG; + } else if (fmt->type == UVCG_FRAMEBASED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED; + h->fmt_type = UVCG_FRAMEBASED; } else { mutex_unlock(&opts->lock); kfree(h); @@ -2174,7 +2208,10 @@ static struct config_item *uvcg_frame_make(struct config_group *group, ++fmt->num_frames; mutex_unlock(&opts->lock); - config_item_init_type_name(&h->item, name, &uvcg_frame_type); + if (fmt->type == UVCG_FRAMEBASED) + config_item_init_type_name(&h->item, name, &uvcg_frame_type2); + else + config_item_init_type_name(&h->item, name, &uvcg_frame_type1); return &h->item; } @@ -2214,9 +2251,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) list_for_each_entry(ci, &fmt->cg_children, ci_entry) { struct uvcg_frame *frm; - if (ci->ci_type != &uvcg_frame_type) - continue; - frm = to_uvcg_frame(ci); frm->frame.b_frame_index = i++; } @@ -2260,6 +2294,8 @@ static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item, struct f_uvc_opts *opts; struct config_item *opts_item; struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + const struct uvc_format_desc *format; + u8 tmpguidFormat[sizeof(ch->desc.guidFormat)]; int ret; mutex_lock(su_mutex); /* for navigating configfs hierarchy */ @@ -2273,7 +2309,16 @@ static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item, goto end; } - memcpy(ch->desc.guidFormat, page, + memcpy(tmpguidFormat, page, + min(sizeof(tmpguidFormat), len)); + + format = uvc_format_by_guid(tmpguidFormat); + if (!format) { + ret = -EINVAL; + goto end; + } + + memcpy(ch->desc.guidFormat, tmpguidFormat, min(sizeof(ch->desc.guidFormat), len)); ret = sizeof(ch->desc.guidFormat); @@ -2666,6 +2711,251 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { }; /* ----------------------------------------------------------------------------- + * streaming/framebased/<NAME> + */ + +static struct configfs_group_operations uvcg_framebased_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_FRAMEBASED_ATTR_RO(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +UVC_ATTR_RO(uvcg_framebased_, cname, aname) + +#define UVCG_FRAMEBASED_ATTR(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_framebased_##cname##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +UVC_ATTR(uvcg_framebased_, cname, aname) + +UVCG_FRAMEBASED_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_bits_per_pixel, bBitsPerPixel, 8); +UVCG_FRAMEBASED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_FRAMEBASED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); + +#undef UVCG_FRAMEBASED_ATTR +#undef UVCG_FRAMEBASED_ATTR_RO + +static ssize_t uvcg_framebased_guid_format_show(struct config_item *item, + char *page) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_framebased_guid_format_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVC_ATTR(uvcg_framebased_, guid_format, guidFormat); + +static inline ssize_t +uvcg_framebased_bma_controls_show(struct config_item *item, char *page) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_show(&u->fmt, page); +} + +static inline ssize_t +uvcg_framebased_bma_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_store(&u->fmt, page, len); +} + +UVC_ATTR(uvcg_framebased_, bma_controls, bmaControls); + +static struct configfs_attribute *uvcg_framebased_attrs[] = { + &uvcg_framebased_attr_b_format_index, + &uvcg_framebased_attr_b_default_frame_index, + &uvcg_framebased_attr_b_bits_per_pixel, + &uvcg_framebased_attr_b_aspect_ratio_x, + &uvcg_framebased_attr_b_aspect_ratio_y, + &uvcg_framebased_attr_bm_interface_flags, + &uvcg_framebased_attr_bma_controls, + &uvcg_framebased_attr_guid_format, + NULL, +}; + +static const struct config_item_type uvcg_framebased_type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_group_ops, + .ct_attrs = uvcg_framebased_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_framebased_make(struct config_group *group, + const char *name) +{ + static char guid[] = { /*Declear frame based as H264 format*/ + 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_framebased *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); + + h->desc.bLength = UVC_DT_FORMAT_FRAMEBASED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 0; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + h->desc.bVariableSize = 1; + + INIT_LIST_HEAD(&h->fmt.frames); + h->fmt.type = UVCG_FRAMEBASED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_framebased_type); + + return &h->fmt.group; +} + +static struct configfs_group_operations uvcg_framebased_grp_ops = { + .make_group = uvcg_framebased_make, +}; + +static const struct uvcg_config_group_type uvcg_framebased_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "framebased", +}; + +/* ----------------------------------------------------------------------------- * streaming/color_matching/default */ @@ -2900,6 +3190,7 @@ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, if (ret) return ret; grp = &f->fmt->group; + j = 0; list_for_each_entry(item, &grp->cg_children, ci_entry) { frm = to_uvcg_frame(item); ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); @@ -2953,6 +3244,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, container_of(fmt, struct uvcg_mjpeg, fmt); *size += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, fmt); + + *size += sizeof(f->desc); } else { return -EINVAL; } @@ -2963,6 +3259,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, int sz = sizeof(frm->dw_frame_interval); *size += sizeof(frm->frame); + /* + * framebased has duplicate member with uncompressed and + * mjpeg, so minus it + */ + *size -= sizeof(u32); *size += frm->frame.b_frame_interval_type * sz; } break; @@ -2979,6 +3280,27 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, return 0; } +static int __uvcg_copy_framebased_desc(void *dest, struct uvcg_frame *frm, + int sz) +{ + struct uvc_frame_framebased *desc = dest; + + desc->bLength = frm->frame.b_length; + desc->bDescriptorType = frm->frame.b_descriptor_type; + desc->bDescriptorSubType = frm->frame.b_descriptor_subtype; + desc->bFrameIndex = frm->frame.b_frame_index; + desc->bmCapabilities = frm->frame.bm_capabilities; + desc->wWidth = frm->frame.w_width; + desc->wHeight = frm->frame.w_height; + desc->dwMinBitRate = frm->frame.dw_min_bit_rate; + desc->dwMaxBitRate = frm->frame.dw_max_bit_rate; + desc->dwDefaultFrameInterval = frm->frame.dw_default_frame_interval; + desc->bFrameIntervalType = frm->frame.b_frame_interval_type; + desc->dwBytesPerLine = frm->frame.dw_bytes_perline; + + return 0; +} + /* * Fill an array of streaming descriptors. * @@ -3033,6 +3355,15 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, m->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &m->desc, sizeof(m->desc)); *dest += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, + fmt); + + f->desc.bFormatIndex = n + 1; + f->desc.bNumFrameDescriptors = fmt->num_frames; + memcpy(*dest, &f->desc, sizeof(f->desc)); + *dest += sizeof(f->desc); } else { return -EINVAL; } @@ -3042,8 +3373,11 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, struct uvcg_frame *frm = priv1; struct uvc_descriptor_header *h = *dest; - sz = sizeof(frm->frame); - memcpy(*dest, &frm->frame, sz); + sz = sizeof(frm->frame) - 4; + if (frm->fmt_type != UVCG_FRAMEBASED) + memcpy(*dest, &frm->frame, sz); + else + __uvcg_copy_framebased_desc(*dest, frm, sz); *dest += sz; sz = frm->frame.b_frame_interval_type * sizeof(*frm->dw_frame_interval); @@ -3054,7 +3388,10 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, frm->frame.b_frame_interval_type); else if (frm->fmt_type == UVCG_MJPEG) h->bLength = UVC_DT_FRAME_MJPEG_SIZE( - frm->frame.b_frame_interval_type); + frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_FRAMEBASED) + h->bLength = UVC_DT_FRAME_FRAMEBASED_SIZE( + frm->frame.b_frame_interval_type); } break; case UVCG_COLOR_MATCHING: { @@ -3273,6 +3610,7 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { &uvcg_streaming_header_grp_type, &uvcg_uncompressed_grp_type, &uvcg_mjpeg_grp_type, + &uvcg_framebased_grp_type, &uvcg_color_matching_grp_type, &uvcg_streaming_class_grp_type, NULL, diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index c6a690158138..2f78cd4f396f 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -49,6 +49,7 @@ container_of(group_ptr, struct uvcg_color_matching, group) enum uvcg_format_type { UVCG_UNCOMPRESSED = 0, UVCG_MJPEG, + UVCG_FRAMEBASED, }; struct uvcg_format { @@ -105,6 +106,7 @@ struct uvcg_frame { u32 dw_max_video_frame_buffer_size; u32 dw_default_frame_interval; u8 b_frame_interval_type; + u32 dw_bytes_perline; } __attribute__((packed)) frame; u32 *dw_frame_interval; }; @@ -143,6 +145,20 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) } /* ----------------------------------------------------------------------------- + * streaming/framebased/<NAME> + */ + +struct uvcg_framebased { + struct uvcg_format fmt; + struct uvc_format_framebased desc; +}; + +static inline struct uvcg_framebased *to_uvcg_framebased(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_framebased, fmt); +} + +/* ----------------------------------------------------------------------------- * control/extensions/<NAME> */ diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 0aa3d7e1f3cc..5eaeae3e2441 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -21,6 +21,7 @@ #include <media/videobuf2-vmalloc.h> #include "uvc.h" +#include "uvc_video.h" /* ------------------------------------------------------------------------ * Video buffers queue management. @@ -44,33 +45,23 @@ static int uvc_queue_setup(struct vb2_queue *vq, { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); struct uvc_video *video = container_of(queue, struct uvc_video, queue); - unsigned int req_size; - unsigned int nreq; if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) *nbuffers = UVC_MAX_VIDEO_BUFFERS; + if (*nbuffers < UVCG_STREAMING_MIN_BUFFERS) + *nbuffers = UVCG_STREAMING_MIN_BUFFERS; *nplanes = 1; sizes[0] = video->imagesize; - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); - - /* We divide by two, to increase the chance to run - * into fewer requests for smaller framesizes. - */ - nreq = DIV_ROUND_UP(DIV_ROUND_UP(sizes[0], 2), req_size); - nreq = clamp(nreq, 4U, 64U); - video->uvc_num_requests = nreq; - return 0; } static int uvc_buffer_prepare(struct vb2_buffer *vb) { struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_video *video = container_of(queue, struct uvc_video, queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf); @@ -91,10 +82,15 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) buf->mem = vb2_plane_vaddr(vb, 0); } buf->length = vb2_plane_size(vb, 0); - if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { buf->bytesused = 0; - else + } else { buf->bytesused = vb2_get_plane_payload(vb, 0); + buf->req_payload_size = + DIV_ROUND_UP(buf->bytesused + + (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), + video->reqs_per_frame); + } return 0; } diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 41f87b917f6b..b54becc570a3 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -39,6 +39,8 @@ struct uvc_buffer { unsigned int offset; unsigned int length; unsigned int bytesused; + /* req_payload_size: only used with isoc */ + unsigned int req_payload_size; }; #define UVC_QUEUE_DISCONNECTED (1 << 0) diff --git a/drivers/usb/gadget/function/uvc_trace.c b/drivers/usb/gadget/function/uvc_trace.c new file mode 100644 index 000000000000..d384f6d8221a --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * trace.c - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik <m.grzeschik@pengutronix.de> + */ + +#define CREATE_TRACE_POINTS +#include "uvc_trace.h" diff --git a/drivers/usb/gadget/function/uvc_trace.h b/drivers/usb/gadget/function/uvc_trace.h new file mode 100644 index 000000000000..04c33cf43cc2 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * trace.h - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik <m.grzeschik@pengutronix.de> + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM uvcg + +#if !defined(__UVCG_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __UVCG_TRACE_H + +#include <linux/types.h> +#include <linux/tracepoint.h> +#include <linux/usb/gadget.h> +#include <asm/byteorder.h> + +DECLARE_EVENT_CLASS(uvcg_video_req, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued), + TP_STRUCT__entry( + __field(struct usb_request *, req) + __field(u32, length) + __field(u32, queued) + ), + TP_fast_assign( + __entry->req = req; + __entry->length = req->length; + __entry->queued = queued; + ), + TP_printk("req %p length %u queued %u", + __entry->req, + __entry->length, + __entry->queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_complete, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_queue, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +#endif /* __UVCG_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE uvc_trace + +#include <trace/define_trace.h> diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index c7e5fa4f29e0..fc9a8d31a1e9 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -31,14 +31,23 @@ static const struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) { char guid[16] = UVC_GUID_FORMAT_MJPEG; const struct uvc_format_desc *format; - struct uvcg_uncompressed *unc; if (uformat->type == UVCG_UNCOMPRESSED) { + struct uvcg_uncompressed *unc; + unc = to_uvcg_uncompressed(&uformat->group.cg_item); if (!unc) return ERR_PTR(-EINVAL); memcpy(guid, unc->desc.guidFormat, sizeof(guid)); + } else if (uformat->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *unc; + + unc = to_uvcg_framebased(&uformat->group.cg_item); + if (!unc) + return ERR_PTR(-EINVAL); + + memcpy(guid, unc->desc.guidFormat, sizeof(guid)); } format = uvc_format_by_guid(guid); @@ -121,6 +130,9 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, list_for_each_entry(format, &uvc->header->formats, entry) { const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt); + if (IS_ERR(fmtdesc)) + continue; + if (fmtdesc->fcc == pixelformat) { uformat = format->fmt; break; @@ -240,6 +252,7 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt) struct uvc_video *video = &uvc->video; struct uvcg_format *uformat; struct uvcg_frame *uframe; + const struct uvc_format_desc *fmtdesc; u8 *fcc; if (fmt->type != video->queue.queue.type) @@ -260,12 +273,29 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt) if (!uframe) return -EINVAL; - fmt->fmt.pix.width = uframe->frame.w_width; - fmt->fmt.pix.height = uframe->frame.w_height; + if (uformat->type == UVCG_UNCOMPRESSED) { + struct uvcg_uncompressed *u = + to_uvcg_uncompressed(&uformat->group.cg_item); + if (!u) + return 0; + + v4l2_fill_pixfmt(&fmt->fmt.pix, fmt->fmt.pix.pixelformat, + uframe->frame.w_width, uframe->frame.w_height); + + if (fmt->fmt.pix.sizeimage != (uvc_v4l2_get_bytesperline(uformat, uframe) * + uframe->frame.w_height)) + return -EINVAL; + } else { + fmt->fmt.pix.width = uframe->frame.w_width; + fmt->fmt.pix.height = uframe->frame.w_height; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe); + fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe); + fmtdesc = to_uvc_format(uformat); + if (IS_ERR(fmtdesc)) + return PTR_ERR(fmtdesc); + fmt->fmt.pix.pixelformat = fmtdesc->fcc; + } fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe); - fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe); - fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; fmt->fmt.pix.priv = 0; @@ -293,6 +323,56 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) return ret; } +static int uvc_v4l2_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + /* Return the actual frame period. */ + timeperframe.numerator = video->interval; + timeperframe.denominator = 10000000; + v4l2_simplify_fraction(&timeperframe.numerator, + &timeperframe.denominator, 8, 333); + + uvcg_dbg(&uvc->func, "Getting frame interval of %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + parm->parm.output.timeperframe = timeperframe; + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + + return 0; +} + +static int uvc_v4l2_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + timeperframe = parm->parm.output.timeperframe; + + video->interval = v4l2_fraction_to_interval(timeperframe.numerator, + timeperframe.denominator); + + uvcg_dbg(&uvc->func, "Setting frame interval to %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + return 0; +} + static int uvc_v4l2_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) @@ -375,6 +455,9 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) return -EINVAL; fmtdesc = to_uvc_format(uformat); + if (IS_ERR(fmtdesc)) + return PTR_ERR(fmtdesc); + f->pixelformat = fmtdesc->fcc; return 0; @@ -472,6 +555,9 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) return ret; + if (uvc->state != UVC_STATE_STREAMING) + return 0; + uvc->state = UVC_STATE_CONNECTED; uvc_function_setup_continue(uvc, 1); return 0; @@ -563,6 +649,8 @@ const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_dqbuf = uvc_v4l2_dqbuf, .vidioc_streamon = uvc_v4l2_streamon, .vidioc_streamoff = uvc_v4l2_streamoff, + .vidioc_s_parm = uvc_v4l2_s_parm, + .vidioc_g_parm = uvc_v4l2_g_parm, .vidioc_subscribe_event = uvc_v4l2_subscribe_event, .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, .vidioc_default = uvc_v4l2_ioctl_default, diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index dd3241fc6939..fb77b0b21790 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -12,13 +12,14 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/video.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <media/v4l2-dev.h> #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" +#include "uvc_trace.h" /* -------------------------------------------------------------------------- * Video codecs @@ -35,6 +36,9 @@ uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, data[1] = UVC_STREAM_EOH | video->fid; + if (video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) + data[1] |= UVC_STREAM_ERR; + if (video->queue.buf_used == 0 && ts.tv_sec) { /* dwClockFrequency is 48 MHz */ u32 pts = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48; @@ -75,7 +79,7 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, /* Copy video data to the USB buffer. */ mem = buf->mem + queue->buf_used; - nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); + nbytes = min_t(unsigned int, len, buf->bytesused - queue->buf_used); memcpy(data, mem, nbytes); queue->buf_used += nbytes; @@ -101,7 +105,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, } /* Process video data. */ - len = min((int)(video->max_payload_size - video->payload_size), len); + len = min_t(int, video->max_payload_size - video->payload_size, len); ret = uvc_video_encode_data(video, buf, mem, len); video->payload_size += ret; @@ -133,7 +137,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, unsigned int pending = buf->bytesused - video->queue.buf_used; struct uvc_request *ureq = req->context; struct scatterlist *sg, *iter; - unsigned int len = video->req_size; + unsigned int len = buf->req_payload_size; unsigned int sg_left, part = 0; unsigned int i; int header_len; @@ -143,15 +147,15 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, /* Init the header. */ header_len = uvc_video_encode_header(video, buf, ureq->header, - video->req_size); + buf->req_payload_size); sg_set_buf(sg, ureq->header, header_len); len -= header_len; if (pending <= len) len = pending; - req->length = (len == pending) ? - len + header_len : video->req_size; + req->length = (len == pending) ? len + header_len : + buf->req_payload_size; /* Init the pending sgs with payload */ sg = sg_next(sg); @@ -199,7 +203,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, { void *mem = req->buf; struct uvc_request *ureq = req->context; - int len = video->req_size; + int len = buf->req_payload_size; int ret; /* Add the header. */ @@ -211,7 +215,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, ret = uvc_video_encode_data(video, buf, mem, len); len -= ret; - req->length = video->req_size - len; + req->length = buf->req_payload_size - len; if (buf->bytesused == video->queue.buf_used || video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) { @@ -266,6 +270,10 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) } } + atomic_inc(&video->queued); + + trace_uvcg_video_queue(req, atomic_read(&video->queued)); + return ret; } @@ -301,7 +309,7 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, */ if (list_empty(&video->req_free) || ureq->last_buf || !(video->req_int_count % - DIV_ROUND_UP(video->uvc_num_requests, 4))) { + min(DIV_ROUND_UP(video->uvc_num_requests, 4), UVCG_REQ_MAX_INT_COUNT))) { video->req_int_count = 0; req->no_interrupt = 0; } else { @@ -319,50 +327,6 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, return 0; } -/* - * Must only be called from uvcg_video_enable - since after that we only want to - * queue requests to the endpoint from the uvc_video_complete complete handler. - * This function is needed in order to 'kick start' the flow of requests from - * gadget driver to the usb controller. - */ -static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) -{ - struct usb_request *req = NULL; - unsigned long flags = 0; - unsigned int count = 0; - int ret = 0; - - /* - * We only queue half of the free list since we still want to have - * some free usb_requests in the free list for the video_pump async_wq - * thread to encode uvc buffers into. Otherwise we could get into a - * situation where the free list does not have any usb requests to - * encode into - we always end up queueing 0 length requests to the - * end point. - */ - unsigned int half_list_size = video->uvc_num_requests / 2; - - spin_lock_irqsave(&video->req_lock, flags); - /* - * Take these requests off the free list and queue them all to the - * endpoint. Since we queue 0 length requests with the req_lock held, - * there isn't any 'data' race involved here with the complete handler. - */ - while (count < half_list_size) { - req = list_first_entry(&video->req_free, struct usb_request, - list); - list_del(&req->list); - req->length = 0; - ret = uvcg_video_ep_queue(video, req); - if (ret < 0) { - uvcg_queue_cancel(&video->queue, 0); - break; - } - count++; - } - spin_unlock_irqrestore(&video->req_lock, flags); -} - static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { @@ -371,10 +335,9 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *last_buf; unsigned long flags; - bool is_bulk = video->max_payload_size; - int ret = 0; spin_lock_irqsave(&video->req_lock, flags); + atomic_dec(&video->queued); if (!video->is_enabled) { /* * When is_enabled is false, uvcg_video_disable() ensures @@ -397,7 +360,8 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) case -EXDEV: uvcg_dbg(&video->uvc->func, "VS request missed xfer.\n"); - queue->flags |= UVC_QUEUE_DROP_INCOMPLETE; + if (req->length != 0) + queue->flags |= UVC_QUEUE_DROP_INCOMPLETE; break; case -ESHUTDOWN: /* disconnect from host. */ @@ -425,7 +389,47 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) * we're still streaming before queueing the usb_request * back to req_free */ - if (video->is_enabled) { + if (!video->is_enabled) { + uvc_video_free_request(ureq, ep); + spin_unlock_irqrestore(&video->req_lock, flags); + uvcg_queue_cancel(queue, 0); + + return; + } + + list_add_tail(&req->list, &video->req_free); + /* + * Queue work to the wq as well since it is possible that a + * buffer may not have been completely encoded with the set of + * in-flight usb requests for whih the complete callbacks are + * firing. + * In that case, if we do not queue work to the worker thread, + * the buffer will never be marked as complete - and therefore + * not be returned to userpsace. As a result, + * dequeue -> queue -> dequeue flow of uvc buffers will not + * happen. Since there are is a new free request wake up the pump. + */ + queue_work(video->async_wq, &video->pump); + + trace_uvcg_video_complete(req, atomic_read(&video->queued)); + + spin_unlock_irqrestore(&video->req_lock, flags); + + kthread_queue_work(video->kworker, &video->hw_submit); +} + +static void uvcg_video_hw_submit(struct kthread_work *work) +{ + struct uvc_video *video = container_of(work, struct uvc_video, hw_submit); + bool is_bulk = video->max_payload_size; + unsigned long flags; + struct usb_request *req; + int ret = 0; + + while (true) { + if (!video->ep->enabled) + return; + spin_lock_irqsave(&video->req_lock, flags); /* * Here we check whether any request is available in the ready * list. If it is, queue it to the ep and add the current @@ -436,48 +440,44 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) * be a situation where the req_free list is completely out of * requests and cannot recover. */ - struct usb_request *to_queue = req; - - to_queue->length = 0; if (!list_empty(&video->req_ready)) { - to_queue = list_first_entry(&video->req_ready, - struct usb_request, list); - list_del(&to_queue->list); - list_add_tail(&req->list, &video->req_free); - /* - * Queue work to the wq as well since it is possible that a - * buffer may not have been completely encoded with the set of - * in-flight usb requests for whih the complete callbacks are - * firing. - * In that case, if we do not queue work to the worker thread, - * the buffer will never be marked as complete - and therefore - * not be returned to userpsace. As a result, - * dequeue -> queue -> dequeue flow of uvc buffers will not - * happen. - */ - queue_work(video->async_wq, &video->pump); + req = list_first_entry(&video->req_ready, + struct usb_request, list); + } else { + if (list_empty(&video->req_free) || + (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT)) { + spin_unlock_irqrestore(&video->req_lock, flags); + + return; + } + req = list_first_entry(&video->req_free, struct usb_request, + list); + req->length = 0; } + list_del(&req->list); + /* * Queue to the endpoint. The actual queueing to ep will * only happen on one thread - the async_wq for bulk endpoints * and this thread for isoc endpoints. */ - ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); + ret = uvcg_video_usb_req_queue(video, req, !is_bulk); if (ret < 0) { /* * Endpoint error, but the stream is still enabled. * Put request back in req_free for it to be cleaned * up later. */ - list_add_tail(&to_queue->list, &video->req_free); + list_add_tail(&req->list, &video->req_free); + /* + * There is a new free request - wake up the pump. + */ + queue_work(video->async_wq, &video->pump); + } - } else { - uvc_video_free_request(ureq, ep); - ret = 0; + + spin_unlock_irqrestore(&video->req_lock, flags); } - spin_unlock_irqrestore(&video->req_lock, flags); - if (ret < 0) - uvcg_queue_cancel(queue, 0); } static int @@ -491,23 +491,71 @@ uvc_video_free_requests(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; return 0; } +static void +uvc_video_prep_requests(struct uvc_video *video) +{ + struct uvc_device *uvc = container_of(video, struct uvc_device, video); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + unsigned int interval_duration = video->ep->desc->bInterval * 1250; + unsigned int max_req_size, req_size, header_size; + unsigned int nreq; + + max_req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult); + + if (!usb_endpoint_xfer_isoc(video->ep->desc)) { + video->req_size = max_req_size; + video->reqs_per_frame = video->uvc_num_requests = + DIV_ROUND_UP(video->imagesize, max_req_size); + + return; + } + + if (cdev->gadget->speed < USB_SPEED_HIGH) + interval_duration = video->ep->desc->bInterval * 10000; + + nreq = DIV_ROUND_UP(video->interval, interval_duration); + + header_size = nreq * UVCG_REQUEST_HEADER_LEN; + + req_size = DIV_ROUND_UP(video->imagesize + header_size, nreq); + + if (req_size > max_req_size) { + /* The prepared interval length and expected buffer size + * is not possible to stream with the currently configured + * isoc bandwidth. Fallback to the maximum. + */ + req_size = max_req_size; + } + video->req_size = req_size; + + /* We need to compensate the amount of requests to be + * allocated with the maximum amount of zero length requests. + * Since it is possible that hw_submit will initially + * enqueue some zero length requests and we then will not be + * able to fully encode one frame. + */ + video->uvc_num_requests = nreq + UVCG_REQ_MAX_ZERO_COUNT; + video->reqs_per_frame = nreq; +} + static int uvc_video_alloc_requests(struct uvc_video *video) { struct uvc_request *ureq; - unsigned int req_size; unsigned int i; int ret = -ENOMEM; - BUG_ON(video->req_size); - - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); + /* + * calculate in uvc_video_prep_requests + * - video->uvc_num_requests + * - video->req_size + */ + uvc_video_prep_requests(video); for (i = 0; i < video->uvc_num_requests; i++) { ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); @@ -518,7 +566,7 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->list, &video->ureqs); - ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + ureq->req_buffer = kmalloc(video->req_size, GFP_KERNEL); if (ureq->req_buffer == NULL) goto error; @@ -536,12 +584,10 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ sg_alloc_table(&ureq->sgt, - DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, + DIV_ROUND_UP(video->req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } - video->req_size = req_size; - return 0; error: @@ -594,10 +640,7 @@ static void uvcg_video_pump(struct work_struct *work) */ spin_lock_irqsave(&queue->irqlock, flags); buf = uvcg_queue_head(queue); - - if (buf != NULL) { - video->encode(req, video, buf); - } else { + if (!buf) { /* * Either the queue has been disconnected or no video buffer * available for bulk transfer. Either way, stop processing @@ -607,6 +650,8 @@ static void uvcg_video_pump(struct work_struct *work) break; } + video->encode(req, video, buf); + spin_unlock_irqrestore(&queue->irqlock, flags); spin_lock_irqsave(&video->req_lock, flags); @@ -623,14 +668,7 @@ static void uvcg_video_pump(struct work_struct *work) uvcg_queue_cancel(queue, 0); break; } - - /* The request is owned by the endpoint / ready list. */ - req = NULL; } - - if (!req) - return; - spin_lock_irqsave(&video->req_lock, flags); if (video->is_enabled) list_add_tail(&req->list, &video->req_free); @@ -702,7 +740,6 @@ uvcg_video_disable(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; spin_unlock_irqrestore(&video->req_lock, flags); /* @@ -755,7 +792,10 @@ int uvcg_video_enable(struct uvc_video *video) video->req_int_count = 0; - uvc_video_ep_queue_initial_requests(video); + atomic_set(&video->queued, 0); + + kthread_queue_work(video->kworker, &video->hw_submit); + queue_work(video->async_wq, &video->pump); return ret; } @@ -777,12 +817,24 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) if (!video->async_wq) return -EINVAL; + /* Allocate a kthread for asynchronous hw submit handler. */ + video->kworker = kthread_run_worker(0, "UVCG"); + if (IS_ERR(video->kworker)) { + uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n"); + return PTR_ERR(video->kworker); + } + + kthread_init_work(&video->hw_submit, uvcg_video_hw_submit); + + sched_set_fifo(video->kworker->task); + video->uvc = uvc; video->fcc = V4L2_PIX_FMT_YUYV; video->bpp = 16; video->width = 320; video->height = 240; video->imagesize = 320 * 240 * 2; + video->interval = 666666; /* Initialize the video buffers queue. */ uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index b62e45235e8e..d70fb5bc2357 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -434,6 +434,7 @@ static void __exit dbgp_exit(void) } MODULE_AUTHOR("Stephane Duverger"); +MODULE_DESCRIPTION("EHCI Debug Port device gadget"); MODULE_LICENSE("GPL"); module_init(dbgp_init); module_exit(dbgp_exit); diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index 265c392810d7..e4a419b19f45 100644 --- a/drivers/usb/gadget/legacy/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -31,6 +31,7 @@ /*-------------------------------------------------------------------------*/ MODULE_AUTHOR("Ben Williamson"); +MODULE_DESCRIPTION("USB MIDI Gadget Driver"); MODULE_LICENSE("GPL v2"); static const char longname[] = "MIDI Gadget"; diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c index 4ca67b2f8f24..3684546e8801 100644 --- a/drivers/usb/gadget/legacy/hid.c +++ b/drivers/usb/gadget/legacy/hid.c @@ -261,7 +261,7 @@ static struct usb_composite_driver hidg_driver = { }; static struct platform_driver hidg_plat_driver = { - .remove_new = hidg_plat_driver_remove, + .remove = hidg_plat_driver_remove, .driver = { .name = "hidg", }, diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 03179b1880fd..b6a30d88a800 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -20,6 +20,7 @@ #include <linux/uaccess.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/poll.h> #include <linux/kthread.h> #include <linux/aio.h> @@ -705,7 +706,6 @@ static const struct file_operations ep_io_operations = { .open = ep_open, .release = ep_release, - .llseek = no_llseek, .unlocked_ioctl = ep_ioctl, .read_iter = ep_read_iter, .write_iter = ep_write_iter, @@ -1183,7 +1183,7 @@ ep0_fasync (int f, struct file *fd, int on) { struct dev_data *dev = fd->private_data; // caller must F_SETOWN before signal delivery happens - VDEBUG (dev, "%s %s\n", __func__, on ? "on" : "off"); + VDEBUG(dev, "%s %s\n", __func__, str_on_off(on)); return fasync_helper (f, fd, on, &dev->fasync); } @@ -1939,7 +1939,6 @@ gadget_dev_open (struct inode *inode, struct file *fd) } static const struct file_operations ep0_operations = { - .llseek = no_llseek, .open = gadget_dev_open, .read = ep0_read, diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index 399fca32a8ac..20165e1582d9 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -782,7 +782,7 @@ static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value) if (ret < 0) goto free; - length = min(io.length, (unsigned int)ret); + length = min_t(unsigned int, io.length, ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; else @@ -1168,7 +1168,7 @@ static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value) if (ret < 0) goto free; - length = min(io.length, (unsigned int)ret); + length = min_t(unsigned int, io.length, ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; else @@ -1364,7 +1364,6 @@ static const struct file_operations raw_fops = { .unlocked_ioctl = raw_ioctl, .compat_ioctl = raw_ioctl, .release = raw_release, - .llseek = no_llseek, }; static struct miscdevice raw_misc_device = { diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 40870227999a..fc1e06246d9d 100644 --- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c @@ -19,7 +19,7 @@ #include <scsi/scsi_tcq.h> #include <target/target_core_base.h> #include <target/target_core_fabric.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "u_tcm.h" diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index 23312a07efb4..e25e0d8dd387 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -425,4 +425,5 @@ static struct usb_composite_driver zero_driver = { module_usb_composite_driver(zero_driver); MODULE_AUTHOR("David Brownell"); +MODULE_DESCRIPTION("Gadget Zero, for USB development"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c index 6aea1ecb3999..115d219c9c00 100644 --- a/drivers/usb/gadget/u_f.c +++ b/drivers/usb/gadget/u_f.c @@ -8,8 +8,8 @@ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> */ -#include "u_f.h" #include <linux/usb/ch9.h> +#include <linux/usb/func_utils.h> struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len) { diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h deleted file mode 100644 index e313c3b8dcb1..000000000000 --- a/drivers/usb/gadget/u_f.h +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * u_f.h - * - * Utility definitions for USB functions - * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> - */ - -#ifndef __U_F_H__ -#define __U_F_H__ - -#include <linux/usb/gadget.h> -#include <linux/overflow.h> - -/* Variable Length Array Macros **********************************************/ -#define vla_group(groupname) size_t groupname##__next = 0 -#define vla_group_size(groupname) groupname##__next - -#define vla_item(groupname, type, name, n) \ - size_t groupname##_##name##__offset = ({ \ - size_t offset = 0; \ - if (groupname##__next != SIZE_MAX) { \ - size_t align_mask = __alignof__(type) - 1; \ - size_t size = array_size(n, sizeof(type)); \ - offset = (groupname##__next + align_mask) & \ - ~align_mask; \ - if (check_add_overflow(offset, size, \ - &groupname##__next)) { \ - groupname##__next = SIZE_MAX; \ - offset = 0; \ - } \ - } \ - offset; \ - }) - -#define vla_item_with_sz(groupname, type, name, n) \ - size_t groupname##_##name##__sz = array_size(n, sizeof(type)); \ - size_t groupname##_##name##__offset = ({ \ - size_t offset = 0; \ - if (groupname##__next != SIZE_MAX) { \ - size_t align_mask = __alignof__(type) - 1; \ - offset = (groupname##__next + align_mask) & \ - ~align_mask; \ - if (check_add_overflow(offset, groupname##_##name##__sz,\ - &groupname##__next)) { \ - groupname##__next = SIZE_MAX; \ - offset = 0; \ - } \ - } \ - offset; \ - }) - -#define vla_ptr(ptr, groupname, name) \ - ((void *) ((char *)ptr + groupname##_##name##__offset)) - -struct usb_ep; -struct usb_request; - -/** - * alloc_ep_req - returns a usb_request allocated by the gadget driver and - * allocates the request's buffer. - * - * @ep: the endpoint to allocate a usb_request - * @len: usb_requests's buffer suggested size - * - * In case @ep direction is OUT, the @len will be aligned to ep's - * wMaxPacketSize. In order to avoid memory leaks or drops, *always* use - * usb_requests's length (req->length) to refer to the allocated buffer size. - * Requests allocated via alloc_ep_req() *must* be freed by free_ep_req(). - */ -struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len); - -/* Frees a usb_request previously allocated by alloc_ep_req() */ -static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - WARN_ON(req->buf == NULL); - kfree(req->buf); - req->buf = NULL; - usb_ep_free_request(ep, req); -} - -#endif /* __U_F_H__ */ diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h index 5d7d35c8cc31..f8b9f0faa9b1 100644 --- a/drivers/usb/gadget/u_os_desc.h +++ b/drivers/usb/gadget/u_os_desc.h @@ -13,7 +13,7 @@ #ifndef __U_OS_DESC_H__ #define __U_OS_DESC_H__ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/nls.h> #define USB_EXT_PROP_DW_SIZE 0 diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index f60a019bb173..f2685f89b3e5 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -428,7 +428,7 @@ MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids); static struct platform_driver ast_vhub_driver = { .probe = ast_vhub_probe, - .remove_new = ast_vhub_remove, + .remove = ast_vhub_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_vhub_dt_ids, diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index a63e4af60a56..02fe1a08d575 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -22,6 +22,7 @@ #include <linux/usb/gadget.h> #include <linux/of.h> #include <linux/regmap.h> +#include <linux/string_choices.h> #include <linux/dma-mapping.h> #include <linux/bcd.h> #include <linux/version.h> @@ -219,7 +220,7 @@ static int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep, if (wValue == USB_DEVICE_REMOTE_WAKEUP) { ep->vhub->wakeup_en = is_set; EPDBG(ep, "Hub remote wakeup %s\n", - is_set ? "enabled" : "disabled"); + str_enabled_disabled(is_set)); return std_req_complete; } diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index 3916c8e2ba01..353bfb1ff0a1 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -66,8 +66,8 @@ #define USB_UPSTREAM_EN BIT(0) /* Main config reg */ -#define UDC_CFG_SET_ADDR(x) ((x) & 0x3f) -#define UDC_CFG_ADDR_MASK (0x3f) +#define UDC_CFG_SET_ADDR(x) ((x) & UDC_CFG_ADDR_MASK) +#define UDC_CFG_ADDR_MASK GENMASK(6, 0) /* Interrupt ctrl & status reg */ #define UDC_IRQ_EP_POOL_NAK BIT(17) @@ -156,7 +156,7 @@ #define AST_EP_DMA_DESC_PID_DATA1 (2 << 14) #define AST_EP_DMA_DESC_PID_MDATA (3 << 14) #define EP_DESC1_IN_LEN(x) ((x) & 0x1fff) -#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for trasmit in 1 desc */ +#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for transmit in 1 desc */ struct ast_udc_request { struct usb_request req; @@ -1009,6 +1009,8 @@ static void ast_udc_getstatus(struct ast_udc_dev *udc) break; case USB_RECIP_ENDPOINT: epnum = crq.wIndex & USB_ENDPOINT_NUMBER_MASK; + if (epnum >= AST_UDC_NUM_ENDPOINTS) + goto stall; status = udc->ep[epnum].stopped; break; default: @@ -1588,7 +1590,7 @@ MODULE_DEVICE_TABLE(of, ast_udc_of_dt_ids); static struct platform_driver ast_udc_driver = { .probe = ast_udc_probe, - .remove_new = ast_udc_remove, + .remove = ast_udc_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_udc_of_dt_ids, diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index e3bf17a98b38..aa4c61094dc6 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/ioport.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/errno.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -131,7 +132,7 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep) seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n", csr, (csr & 0x07ff0000) >> 16, - (csr & (1 << 15)) ? "enabled" : "disabled", + str_enabled_disabled(csr & (1 << 15)), (csr & (1 << 11)) ? "DATA1" : "DATA0", types[(csr & 0x700) >> 8], @@ -2002,7 +2003,7 @@ static int at91udc_resume(struct platform_device *pdev) static struct platform_driver at91_udc_driver = { .probe = at91udc_probe, - .remove_new = at91udc_remove, + .remove = at91udc_remove, .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index b76885d78e8a..0c6f2ad81d37 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -187,7 +187,6 @@ static int regs_dbg_release(struct inode *inode, struct file *file) static const struct file_operations queue_dbg_fops = { .owner = THIS_MODULE, .open = queue_dbg_open, - .llseek = no_llseek, .read = queue_dbg_read, .release = queue_dbg_release, }; @@ -2445,7 +2444,7 @@ static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); static struct platform_driver udc_driver = { .probe = usba_udc_probe, - .remove_new = usba_udc_remove, + .remove = usba_udc_remove, .driver = { .name = "atmel_usba_udc", .pm = &usba_udc_pm_ops, diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index da7011d906e0..502612a5650e 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2367,7 +2367,7 @@ static void bcm63xx_udc_remove(struct platform_device *pdev) static struct platform_driver bcm63xx_udc_driver = { .probe = bcm63xx_udc_probe, - .remove_new = bcm63xx_udc_remove, + .remove = bcm63xx_udc_remove, .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h index 8d00b1239f21..2f4abf6f8f77 100644 --- a/drivers/usb/gadget/udc/bdc/bdc.h +++ b/drivers/usb/gadget/udc/bdc/bdc.h @@ -20,7 +20,7 @@ #include <linux/debugfs.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define BRCM_BDC_NAME "bdc" #define BRCM_BDC_DESC "Broadcom USB Device Controller driver" diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 35a652807fca..5c3d8b64c0e7 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -639,6 +639,7 @@ static const struct of_device_id bdc_of_match[] = { { .compatible = "brcm,bdc" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, bdc_of_match); static struct platform_driver bdc_driver = { .driver = { @@ -647,7 +648,7 @@ static struct platform_driver bdc_driver = { .of_match_table = bdc_of_match, }, .probe = bdc_probe, - .remove_new = bdc_remove, + .remove = bdc_remove, }; module_platform_driver(bdc_driver); diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index fa88f210ecd5..f995cfa9b99e 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -30,7 +30,7 @@ #include <linux/pm.h> #include <linux/io.h> #include <linux/irq.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/platform_device.h> #include <linux/usb/composite.h> diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 53ffaf4e2e37..23826fd7a8e6 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -29,7 +29,7 @@ #include <linux/pm.h> #include <linux/io.h> #include <linux/irq.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/platform_device.h> #include "bdc.h" diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c index fa12a5d46f2e..a5a9d395fd0d 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c @@ -8,7 +8,7 @@ */ #include <linux/usb/composite.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "cdns2-gadget.h" #include "cdns2-trace.h" diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c index 0eed0e03842c..7e69944ef18a 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -29,6 +29,7 @@ #include <linux/pm_runtime.h> #include <linux/interrupt.h> #include <linux/property.h> +#include <linux/string_choices.h> #include <linux/dmapool.h> #include <linux/iopoll.h> @@ -2033,8 +2034,8 @@ static void cdns2_quiesce(struct cdns2_device *pdev) set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON); /* Disable interrupt. */ - writeb(0, &pdev->interrupt_regs->extien), - writeb(0, &pdev->interrupt_regs->usbien), + writeb(0, &pdev->interrupt_regs->extien); + writeb(0, &pdev->interrupt_regs->usbien); writew(0, &pdev->adma_regs->ep_ien); /* Clear interrupt line. */ @@ -2233,12 +2234,12 @@ static int cdns2_init_eps(struct cdns2_device *pdev) dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, " "BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n", pep->name, - (pep->endpoint.caps.type_control) ? "yes" : "no", - (pep->endpoint.caps.type_int) ? "yes" : "no", - (pep->endpoint.caps.type_bulk) ? "yes" : "no", - (pep->endpoint.caps.type_iso) ? "yes" : "no", - (pep->endpoint.caps.dir_in) ? "yes" : "no", - (pep->endpoint.caps.dir_out) ? "yes" : "no"); + str_yes_no(pep->endpoint.caps.type_control), + str_yes_no(pep->endpoint.caps.type_int), + str_yes_no(pep->endpoint.caps.type_bulk), + str_yes_no(pep->endpoint.caps.type_iso), + str_yes_no(pep->endpoint.caps.dir_in), + str_yes_no(pep->endpoint.caps.dir_out)); INIT_LIST_HEAD(&pep->pending_list); INIT_LIST_HEAD(&pep->deferred_list); @@ -2251,7 +2252,6 @@ static int cdns2_gadget_start(struct cdns2_device *pdev) { u32 max_speed; void *buf; - int val; int ret; pdev->usb_regs = pdev->regs; @@ -2261,14 +2261,9 @@ static int cdns2_gadget_start(struct cdns2_device *pdev) pdev->adma_regs = pdev->regs + CDNS2_ADMA_REGS_OFFSET; /* Reset controller. */ - set_reg_bit_8(&pdev->usb_regs->cpuctrl, CPUCTRL_SW_RST); - - ret = readl_poll_timeout_atomic(&pdev->usb_regs->cpuctrl, val, - !(val & CPUCTRL_SW_RST), 1, 10000); - if (ret) { - dev_err(pdev->dev, "Error: reset controller timeout\n"); - return -EINVAL; - } + writeb(CPUCTRL_SW_RST | CPUCTRL_UPCLK | CPUCTRL_WUEN, + &pdev->usb_regs->cpuctrl); + usleep_range(5, 10); usb_initialize_gadget(pdev->dev, &pdev->gadget, NULL); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h index 71e2f62d653a..b5d5ec12e986 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h @@ -292,8 +292,17 @@ struct cdns2_usb_regs { #define SPEEDCTRL_HSDISABLE BIT(7) /* CPUCTRL- bitmasks. */ +/* UP clock enable */ +#define CPUCTRL_UPCLK BIT(0) /* Controller reset bit. */ #define CPUCTRL_SW_RST BIT(1) +/** + * If the wuen bit is ‘1’, the upclken is automatically set to ‘1’ after + * detecting rising edge of wuintereq interrupt. If the wuen bit is ‘0’, + * the wuintereq interrupt is ignored. + */ +#define CPUCTRL_WUEN BIT(7) + /** * struct cdns2_adma_regs - ADMA controller registers. diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c index 1691541c9413..e589593b4cbf 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c @@ -15,8 +15,6 @@ #include "cdns2-gadget.h" #define PCI_DRIVER_NAME "cdns-pci-usbhs" -#define CDNS_VENDOR_ID 0x17cd -#define CDNS_DEVICE_ID 0x0120 #define PCI_BAR_DEV 0 #define PCI_DEV_FN_DEVICE 0 @@ -114,14 +112,14 @@ static const struct dev_pm_ops cdns2_pci_pm_ops = { }; static const struct pci_device_id cdns2_pci_ids[] = { - { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB), + .class = PCI_CLASS_SERIAL_USB_DEVICE }, { 0, } }; static struct pci_driver cdns2_pci_driver = { .name = "cdns2-pci", - .id_table = &cdns2_pci_ids[0], + .id_table = cdns2_pci_ids, .probe = cdns2_pci_probe, .remove = cdns2_pci_remove, .driver = { diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h index 61f241634ea5..ade1752956b1 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h +++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h @@ -64,7 +64,7 @@ DECLARE_EVENT_CLASS(cdns2_log_simple, __string(text, msg) ), TP_fast_assign( - __assign_str(text, msg); + __assign_str(text); ), TP_printk("%s", __get_str(text)) ); @@ -103,7 +103,7 @@ TRACE_EVENT(cdns2_ep_halt, __field(u8, flush) ), TP_fast_assign( - __assign_str(name, ep_priv->name); + __assign_str(name); __entry->halt = halt; __entry->flush = flush; ), @@ -119,8 +119,8 @@ TRACE_EVENT(cdns2_wa1, __string(msg, msg) ), TP_fast_assign( - __assign_str(ep_name, ep_priv->name); - __assign_str(msg, msg); + __assign_str(ep_name); + __assign_str(msg); ), TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg)) ); @@ -134,7 +134,7 @@ DECLARE_EVENT_CLASS(cdns2_log_doorbell, __field(u32, ep_trbaddr) ), TP_fast_assign( - __assign_str(name, pep->name); + __assign_str(name); __entry->ep_trbaddr = ep_trbaddr; ), TP_printk("%s, ep_trbaddr %08x", __get_str(name), @@ -196,7 +196,7 @@ DECLARE_EVENT_CLASS(cdns2_log_epx_irq, __field(u32, ep_traddr) ), TP_fast_assign( - __assign_str(ep_name, pep->name); + __assign_str(ep_name); __entry->ep_sts = readl(&pdev->adma_regs->ep_sts); __entry->ep_ists = readl(&pdev->adma_regs->ep_ists); __entry->ep_traddr = readl(&pdev->adma_regs->ep_traddr); @@ -288,7 +288,7 @@ DECLARE_EVENT_CLASS(cdns2_log_request, __field(int, end_trb) ), TP_fast_assign( - __assign_str(name, preq->pep->name); + __assign_str(name); __entry->request = &preq->request; __entry->preq = preq; __entry->buf = preq->request.buf; @@ -380,7 +380,7 @@ DECLARE_EVENT_CLASS(cdns2_log_map_request, __field(dma_addr_t, dma) ), TP_fast_assign( - __assign_str(name, priv_req->pep->name); + __assign_str(name); __entry->req = &priv_req->request; __entry->buf = priv_req->request.buf; __entry->dma = priv_req->request.dma; @@ -411,7 +411,7 @@ DECLARE_EVENT_CLASS(cdns2_log_trb, __field(u32, type) ), TP_fast_assign( - __assign_str(name, pep->name); + __assign_str(name); __entry->trb = trb; __entry->buffer = le32_to_cpu(trb->buffer); __entry->length = le32_to_cpu(trb->length); @@ -476,7 +476,7 @@ DECLARE_EVENT_CLASS(cdns2_log_ep, __field(u8, dequeue) ), TP_fast_assign( - __assign_str(name, pep->name); + __assign_str(name); __entry->maxpacket = pep->endpoint.maxpacket; __entry->maxpacket_limit = pep->endpoint.maxpacket_limit; __entry->flags = pep->ep_state; @@ -568,7 +568,7 @@ DECLARE_EVENT_CLASS(cdns2_log_epx_reg_config, __field(u32, ep_cfg_reg) ), TP_fast_assign( - __assign_str(ep_name, pep->name); + __assign_str(ep_name); __entry->burst_size = pep->trb_burst_size; __entry->maxpack_reg = pep->dir ? readw(&pdev->epx_regs->txmaxpack[pep->num - 1]) : readw(&pdev->epx_regs->rxmaxpack[pep->num - 1]); diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index d59f94464b87..4b3d5075621a 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -118,12 +118,10 @@ int usb_ep_enable(struct usb_ep *ep) goto out; /* UDC drivers can't handle endpoints with maxpacket size 0 */ - if (usb_endpoint_maxp(ep->desc) == 0) { - /* - * We should log an error message here, but we can't call - * dev_err() because there's no way to find the gadget - * given only ep. - */ + if (!ep->desc || usb_endpoint_maxp(ep->desc) == 0) { + WARN_ONCE(1, "%s: ep%d (%s) has %s\n", __func__, ep->address, ep->name, + (!ep->desc) ? "NULL descriptor" : "maxpacket 0"); + ret = -EINVAL; goto out; } @@ -292,7 +290,9 @@ int usb_ep_queue(struct usb_ep *ep, { int ret = 0; - if (WARN_ON_ONCE(!ep->enabled && ep->address)) { + if (!ep->enabled && ep->address) { + pr_debug("USB gadget: queue request to disabled ep 0x%x (%s)\n", + ep->address, ep->name); ret = -ESHUTDOWN; goto out; } @@ -903,6 +903,11 @@ int usb_gadget_map_request_by_dev(struct device *dev, if (req->length == 0) return 0; + if (req->sg_was_mapped) { + req->num_mapped_sgs = req->num_sgs; + return 0; + } + if (req->num_sgs) { int mapped; @@ -948,7 +953,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_map_request); void usb_gadget_unmap_request_by_dev(struct device *dev, struct usb_request *req, int is_in) { - if (req->length == 0) + if (req->length == 0 || req->sg_was_mapped) return; if (req->num_mapped_sgs) { @@ -1419,8 +1424,16 @@ int usb_add_gadget(struct usb_gadget *gadget) if (ret) goto err_free_id; + ret = sysfs_create_link(&udc->dev.kobj, + &gadget->dev.kobj, "gadget"); + if (ret) + goto err_del_gadget; + return 0; + err_del_gadget: + device_del(&gadget->dev); + err_free_id: ida_free(&gadget_id_numbers, gadget->id_number); @@ -1529,8 +1542,9 @@ void usb_del_gadget(struct usb_gadget *gadget) mutex_unlock(&udc_lock); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); - flush_work(&gadget->work); + sysfs_remove_link(&udc->dev.kobj, "gadget"); device_del(&gadget->dev); + flush_work(&gadget->work); ida_free(&gadget_id_numbers, gadget->id_number); cancel_work_sync(&udc->vbus_work); device_unregister(&udc->dev); @@ -1552,7 +1566,7 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc); /* ------------------------------------------------------------------------- */ -static int gadget_match_driver(struct device *dev, struct device_driver *drv) +static int gadget_match_driver(struct device *dev, const struct device_driver *drv) { struct usb_gadget *gadget = dev_to_usb_gadget(dev); struct usb_udc *udc = gadget->udc; @@ -1682,6 +1696,7 @@ int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver, driver->driver.bus = &gadget_bus_type; driver->driver.owner = owner; driver->driver.mod_name = mod_name; + driver->driver.probe_type = PROBE_FORCE_SYNCHRONOUS; ret = driver_register(&driver->driver); if (ret) { pr_warn("%s: driver registration failed: %d\n", diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 0953e1b5c030..bda08c5ba7c0 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -28,9 +28,10 @@ #include <linux/delay.h> #include <linux/ioport.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/errno.h> #include <linux/init.h> -#include <linux/timer.h> +#include <linux/hrtimer.h> #include <linux/list.h> #include <linux/interrupt.h> #include <linux/platform_device.h> @@ -42,7 +43,7 @@ #include <asm/byteorder.h> #include <linux/io.h> #include <asm/irq.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define DRIVER_DESC "USB Host+Gadget Emulator" #define DRIVER_VERSION "02 May 2005" @@ -50,6 +51,8 @@ #define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */ #define POWER_BUDGET_3 900 /* in mA */ +#define DUMMY_TIMER_INT_NSECS 125000 /* 1 microframe */ + static const char driver_name[] = "dummy_hcd"; static const char driver_desc[] = "USB Host+Gadget Emulator"; @@ -78,7 +81,7 @@ module_param_named(num, mod_data.num, uint, S_IRUGO); MODULE_PARM_DESC(num, "number of emulated controllers"); /*-------------------------------------------------------------------------*/ -/* gadget side driver data structres */ +/* gadget side driver data structures */ struct dummy_ep { struct list_head queue; unsigned long last_io; /* jiffies timestamp */ @@ -240,7 +243,7 @@ enum dummy_rh_state { struct dummy_hcd { struct dummy *dum; enum dummy_rh_state rh_state; - struct timer_list timer; + struct hrtimer timer; u32 port_status; u32 old_status; unsigned long re_timeout; @@ -252,6 +255,7 @@ struct dummy_hcd { u32 stream_en_ep; u8 num_stream[30 / 2]; + unsigned timer_pending:1; unsigned active:1; unsigned old_active:1; unsigned resuming:1; @@ -622,7 +626,7 @@ static int dummy_enable(struct usb_ep *_ep, desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", usb_ep_type_string(usb_endpoint_type(desc)), - max, ep->stream_en ? "enabled" : "disabled"); + max, str_enabled_disabled(ep->stream_en)); /* at this point real hardware should be NAKing transfers * to that endpoint, until a buffer is queued to it. @@ -1149,7 +1153,7 @@ static int dummy_udc_resume(struct platform_device *pdev) static struct platform_driver dummy_udc_driver = { .probe = dummy_udc_probe, - .remove_new = dummy_udc_remove, + .remove = dummy_udc_remove, .suspend = dummy_udc_suspend, .resume = dummy_udc_resume, .driver = { @@ -1301,8 +1305,11 @@ static int dummy_urb_enqueue( urb->error_count = 1; /* mark as a new urb */ /* kick the scheduler, it'll do the rest */ - if (!timer_pending(&dum_hcd->timer)) - mod_timer(&dum_hcd->timer, jiffies + 1); + if (!dum_hcd->timer_pending) { + dum_hcd->timer_pending = 1; + hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), + HRTIMER_MODE_REL_SOFT); + } done: spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); @@ -1321,9 +1328,10 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) spin_lock_irqsave(&dum_hcd->dum->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING && - !list_empty(&dum_hcd->urbp_list)) - mod_timer(&dum_hcd->timer, jiffies); + if (rc == 0 && !dum_hcd->timer_pending) { + dum_hcd->timer_pending = 1; + hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT); + } spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return rc; @@ -1777,7 +1785,7 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, * drivers except that the callbacks are invoked from soft interrupt * context. */ -static void dummy_timer(struct timer_list *t) +static enum hrtimer_restart dummy_timer(struct hrtimer *t) { struct dummy_hcd *dum_hcd = from_timer(dum_hcd, t, timer); struct dummy *dum = dum_hcd->dum; @@ -1808,16 +1816,15 @@ static void dummy_timer(struct timer_list *t) break; } - /* FIXME if HZ != 1000 this will probably misbehave ... */ - /* look at each urb queued by the host side driver */ spin_lock_irqsave(&dum->lock, flags); + dum_hcd->timer_pending = 0; if (!dum_hcd->udev) { dev_err(dummy_dev(dum_hcd), "timer fired with no URBs pending?\n"); spin_unlock_irqrestore(&dum->lock, flags); - return; + return HRTIMER_NORESTART; } dum_hcd->next_frame_urbp = NULL; @@ -1993,12 +2000,17 @@ return_urb: if (list_empty(&dum_hcd->urbp_list)) { usb_put_dev(dum_hcd->udev); dum_hcd->udev = NULL; - } else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { + } else if (!dum_hcd->timer_pending && + dum_hcd->rh_state == DUMMY_RH_RUNNING) { /* want a 1 msec delay here */ - mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1)); + dum_hcd->timer_pending = 1; + hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), + HRTIMER_MODE_REL_SOFT); } spin_unlock_irqrestore(&dum->lock, flags); + + return HRTIMER_NORESTART; } /*-------------------------------------------------------------------------*/ @@ -2386,8 +2398,10 @@ static int dummy_bus_resume(struct usb_hcd *hcd) } else { dum_hcd->rh_state = DUMMY_RH_RUNNING; set_link_state(dum_hcd); - if (!list_empty(&dum_hcd->urbp_list)) - mod_timer(&dum_hcd->timer, jiffies); + if (!list_empty(&dum_hcd->urbp_list)) { + dum_hcd->timer_pending = 1; + hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT); + } hcd->state = HC_STATE_RUNNING; } spin_unlock_irq(&dum_hcd->dum->lock); @@ -2465,7 +2479,8 @@ static DEVICE_ATTR_RO(urbs); static int dummy_start_ss(struct dummy_hcd *dum_hcd) { - timer_setup(&dum_hcd->timer, dummy_timer, 0); + hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + dum_hcd->timer.function = dummy_timer; dum_hcd->rh_state = DUMMY_RH_RUNNING; dum_hcd->stream_en_ep = 0; INIT_LIST_HEAD(&dum_hcd->urbp_list); @@ -2494,7 +2509,8 @@ static int dummy_start(struct usb_hcd *hcd) return dummy_start_ss(dum_hcd); spin_lock_init(&dum_hcd->dum->lock); - timer_setup(&dum_hcd->timer, dummy_timer, 0); + hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + dum_hcd->timer.function = dummy_timer; dum_hcd->rh_state = DUMMY_RH_RUNNING; INIT_LIST_HEAD(&dum_hcd->urbp_list); @@ -2513,8 +2529,12 @@ static int dummy_start(struct usb_hcd *hcd) static void dummy_stop(struct usb_hcd *hcd) { - device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs); - dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n"); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); + + hrtimer_cancel(&dum_hcd->timer); + dum_hcd->timer_pending = 0; + device_remove_file(dummy_dev(dum_hcd), &dev_attr_urbs); + dev_info(dummy_dev(dum_hcd), "stopped\n"); } /*-------------------------------------------------------------------------*/ @@ -2750,7 +2770,7 @@ static int dummy_hcd_resume(struct platform_device *pdev) static struct platform_driver dummy_hcd_driver = { .probe = dummy_hcd_probe, - .remove_new = dummy_hcd_remove, + .remove = dummy_hcd_remove, .suspend = dummy_hcd_suspend, .resume = dummy_hcd_resume, .driver = { diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index 4e88681a79b6..aacfde06387c 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -511,7 +511,7 @@ static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num) out_8(&epparam->tbmr, rtfcr); tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE); - /* MRBLR must be divisble by 4 */ + /* MRBLR must be divisible by 4 */ tmp = (u16)(((tmp >> 2) << 2) + 4); out_be16(&epparam->mrblr, tmp); @@ -1413,7 +1413,7 @@ static int ep_txframe_handle(struct qe_ep *ep) return 0; } -/* confirm the already trainsmited bd */ +/* confirm the already transmitted bd */ static int qe_ep_txconf(struct qe_ep *ep) { struct qe_bd __iomem *bd; @@ -2196,7 +2196,7 @@ static int tx_irq(struct qe_udc *udc) } -/* setup packect's rx is handle in the function too */ +/* setup packet's rx is handle in the function too */ static void rx_irq(struct qe_udc *udc) { struct qe_ep *ep; @@ -2704,7 +2704,7 @@ static struct platform_driver udc_driver = { .of_match_table = qe_udc_match, }, .probe = qe_udc_probe, - .remove_new = qe_udc_remove, + .remove = qe_udc_remove, #ifdef CONFIG_PM .suspend = qe_udc_suspend, .resume = qe_udc_resume, diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index e8042c158f6d..4dea8bc30cf6 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -13,7 +13,7 @@ * code from Dave Liu and Shlomi Gridish. */ -#undef VERBOSE +#define pr_fmt(x) "udc: " x #include <linux/module.h> #include <linux/kernel.h> @@ -22,6 +22,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/init.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -39,7 +40,7 @@ #include <asm/byteorder.h> #include <asm/io.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <asm/dma.h> #include "fsl_usb2_udc.h" @@ -183,9 +184,9 @@ __acquires(ep->udc->lock) usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); if (status && (status != -ESHUTDOWN)) - VDBG("complete %s req %p stat %d len %u/%u", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); + dev_vdbg(&udc->gadget.dev, "complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); ep->stopped = 1; @@ -285,7 +286,7 @@ static int dr_controller_setup(struct fsl_udc *udc) timeout = jiffies + FSL_UDC_RESET_TIMEOUT; while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) { if (time_after(jiffies, timeout)) { - ERR("udc reset timeout!\n"); + dev_err(&udc->gadget.dev, "udc reset timeout!\n"); return -ETIMEDOUT; } cpu_relax(); @@ -308,9 +309,10 @@ static int dr_controller_setup(struct fsl_udc *udc) tmp &= USB_EP_LIST_ADDRESS_MASK; fsl_writel(tmp, &dr_regs->endpointlistaddr); - VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x", - udc->ep_qh, (int)tmp, - fsl_readl(&dr_regs->endpointlistaddr)); + dev_vdbg(&udc->gadget.dev, + "vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x\n", + udc->ep_qh, (int)tmp, + fsl_readl(&dr_regs->endpointlistaddr)); max_no_of_ep = (0x0000001F & fsl_readl(&dr_regs->dccparams)); for (ep_num = 1; ep_num < max_no_of_ep; ep_num++) { @@ -498,7 +500,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; break; default: - VDBG("error ep type is %d", ep_type); + dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type); return; } if (zlt) @@ -611,10 +613,10 @@ static int fsl_ep_enable(struct usb_ep *_ep, spin_unlock_irqrestore(&udc->lock, flags); retval = 0; - VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name, - ep->ep.desc->bEndpointAddress & 0x0f, - (desc->bEndpointAddress & USB_DIR_IN) - ? "in" : "out", max); + dev_vdbg(&udc->gadget.dev, "enabled %s (ep%d%s) maxpacket %d\n", + ep->ep.name, ep->ep.desc->bEndpointAddress & 0x0f, + (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", + max); en_done: return retval; } @@ -633,7 +635,10 @@ static int fsl_ep_disable(struct usb_ep *_ep) ep = container_of(_ep, struct fsl_ep, ep); if (!_ep || !ep->ep.desc) { - VDBG("%s not enabled", _ep ? ep->ep.name : NULL); + /* + * dev_vdbg(&udc->gadget.dev, "%s not enabled\n", + * _ep ? ep->ep.name : NULL); + */ return -EINVAL; } @@ -659,7 +664,7 @@ static int fsl_ep_disable(struct usb_ep *_ep) ep->stopped = 1; spin_unlock_irqrestore(&udc->lock, flags); - VDBG("disabled %s OK", _ep->name); + dev_vdbg(&udc->gadget.dev, "disabled %s OK\n", _ep->name); return 0; } @@ -719,8 +724,8 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) { u32 temp, bitmask, tmp_stat; - /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr); - VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */ + /* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr); + dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */ bitmask = ep_is_in(ep) ? (1 << (ep_index(ep) + 16)) @@ -808,7 +813,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, *is_last = 0; if ((*is_last) == 0) - VDBG("multi-dtd request!"); + dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n"); /* Fill in the transfer size; set active bit */ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); @@ -820,7 +825,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, mb(); - VDBG("length = %d address= 0x%x", *length, (int)*dma); + dev_vdbg(&udc_controller->gadget.dev, "length = %d address= 0x%x\n", *length, (int)*dma); return dtd; } @@ -864,18 +869,18 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep); struct fsl_req *req = container_of(_req, struct fsl_req, req); - struct fsl_udc *udc; + struct fsl_udc *udc = ep->udc; unsigned long flags; int ret; /* catch various bogus parameters */ if (!_req || !req->req.complete || !req->req.buf || !list_empty(&req->queue)) { - VDBG("%s, bad params", __func__); + dev_vdbg(&udc->gadget.dev, "%s, bad params\n", __func__); return -EINVAL; } - if (unlikely(!_ep || !ep->ep.desc)) { - VDBG("%s, bad ep", __func__); + if (unlikely(!ep->ep.desc)) { + dev_vdbg(&udc->gadget.dev, "%s, bad ep\n", __func__); return -EINVAL; } if (usb_endpoint_xfer_isoc(ep->ep.desc)) { @@ -883,7 +888,6 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return -EMSGSIZE; } - udc = ep->udc; if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; @@ -1036,8 +1040,8 @@ static int fsl_ep_set_halt(struct usb_ep *_ep, int value) udc->ep0_dir = 0; } out: - VDBG(" %s %s halt stat %d", ep->ep.name, - value ? "set" : "clear", status); + dev_vdbg(&udc->gadget.dev, "%s %s halt stat %d\n", ep->ep.name, + value ? "set" : "clear", status); return status; } @@ -1105,7 +1109,8 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep) /* Wait until flush complete */ while (fsl_readl(&dr_regs->endptflush)) { if (time_after(jiffies, timeout)) { - ERR("ep flush timeout\n"); + dev_err(&udc_controller->gadget.dev, + "ep flush timeout\n"); return; } cpu_relax(); @@ -1177,7 +1182,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active) udc = container_of(gadget, struct fsl_udc, gadget); spin_lock_irqsave(&udc->lock, flags); - VDBG("VBUS %s", is_active ? "on" : "off"); + dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active)); udc->vbus_active = (is_active != 0); if (can_pullup(udc)) fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), @@ -1543,7 +1548,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, udc->ep0_state = WAIT_FOR_SETUP; break; case WAIT_FOR_SETUP: - ERR("Unexpected ep0 packets\n"); + dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n"); break; default: ep0stall(udc); @@ -1612,7 +1617,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, errors = hc32_to_cpu(curr_td->size_ioc_sts); if (errors & DTD_ERROR_MASK) { if (errors & DTD_STATUS_HALTED) { - ERR("dTD error %08x QH=%d\n", errors, pipe); + dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */ tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts); tmp &= ~errors; @@ -1623,32 +1628,35 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, break; } if (errors & DTD_STATUS_DATA_BUFF_ERR) { - VDBG("Transfer overflow"); + dev_vdbg(&udc->gadget.dev, "Transfer overflow\n"); status = -EPROTO; break; } else if (errors & DTD_STATUS_TRANSACTION_ERR) { - VDBG("ISO error"); + dev_vdbg(&udc->gadget.dev, "ISO error\n"); status = -EILSEQ; break; } else - ERR("Unknown error has occurred (0x%x)!\n", + dev_err(&udc->gadget.dev, + "Unknown error has occurred (0x%x)!\n", errors); } else if (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_STATUS_ACTIVE) { - VDBG("Request not complete"); + dev_vdbg(&udc->gadget.dev, "Request not complete\n"); status = REQ_UNCOMPLETE; return status; } else if (remaining_length) { if (direction) { - VDBG("Transmit dTD remaining length not zero"); + dev_vdbg(&udc->gadget.dev, + "Transmit dTD remaining length not zero\n"); status = -EPROTO; break; } else { break; } } else { - VDBG("dTD transmitted successful"); + dev_vdbg(&udc->gadget.dev, + "dTD transmitted successful\n"); } if (j != curr_req->dtd_count - 1) @@ -1691,7 +1699,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) /* If the ep is configured */ if (!curr_ep->ep.name) { - WARNING("Invalid EP?"); + dev_warn(&udc->gadget.dev, "Invalid EP?\n"); continue; } @@ -1700,8 +1708,9 @@ static void dtd_complete_irq(struct fsl_udc *udc) queue) { status = process_ep_req(udc, i, curr_req); - VDBG("status of process_ep_req= %d, ep = %d", - status, ep_num); + dev_vdbg(&udc->gadget.dev, + "status of process_ep_req= %d, ep = %d\n", + status, ep_num); if (status == REQ_UNCOMPLETE) break; /* write back status to req */ @@ -1820,7 +1829,7 @@ static void reset_irq(struct fsl_udc *udc) while (fsl_readl(&dr_regs->endpointprime)) { /* Wait until all endptprime bits cleared */ if (time_after(jiffies, timeout)) { - ERR("Timeout for reset\n"); + dev_err(&udc->gadget.dev, "Timeout for reset\n"); break; } cpu_relax(); @@ -1830,7 +1839,7 @@ static void reset_irq(struct fsl_udc *udc) fsl_writel(0xffffffff, &dr_regs->endptflush); if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { - VDBG("Bus reset"); + dev_vdbg(&udc->gadget.dev, "Bus reset\n"); /* Bus is reseting */ udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue @@ -1838,7 +1847,7 @@ static void reset_irq(struct fsl_udc *udc) reset_queues(udc, true); udc->usb_state = USB_STATE_DEFAULT; } else { - VDBG("Controller reset"); + dev_vdbg(&udc->gadget.dev, "Controller reset\n"); /* initialize usb hw reg except for regs for EP, not * touch usbintr reg */ dr_controller_setup(udc); @@ -1872,7 +1881,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Clear notification bits */ fsl_writel(irq_src, &dr_regs->usbsts); - /* VDBG("irq_src [0x%8x]", irq_src); */ + /* dev_vdbg(&udc->gadget.dev, "irq_src [0x%8x]", irq_src); */ /* Need to resume? */ if (udc->usb_state == USB_STATE_SUSPENDED) @@ -1881,7 +1890,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* USB Interrupt */ if (irq_src & USB_STS_INT) { - VDBG("Packet int"); + dev_vdbg(&udc->gadget.dev, "Packet int\n"); /* Setup package, we only support ep0 as control ep */ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) { tripwire_handler(udc, 0, @@ -1910,7 +1919,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Reset Received */ if (irq_src & USB_STS_RESET) { - VDBG("reset int"); + dev_vdbg(&udc->gadget.dev, "reset int\n"); reset_irq(udc); status = IRQ_HANDLED; } @@ -1922,7 +1931,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) } if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { - VDBG("Error IRQ %x", irq_src); + dev_vdbg(&udc->gadget.dev, "Error IRQ %x\n", irq_src); } spin_unlock_irqrestore(&udc->lock, flags); @@ -1958,7 +1967,7 @@ static int fsl_udc_start(struct usb_gadget *g, udc_controller->transceiver->otg, &udc_controller->gadget); if (retval < 0) { - ERR("can't bind to transceiver\n"); + dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n"); udc_controller->driver = NULL; return retval; } @@ -2243,7 +2252,7 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL); if (!udc->eps) { - ERR("kmalloc udc endpoint status failed\n"); + dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n"); goto eps_alloc_failed; } @@ -2258,7 +2267,7 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->ep_qh = dma_alloc_coherent(&pdev->dev, size, &udc->ep_qh_dma, GFP_KERNEL); if (!udc->ep_qh) { - ERR("malloc QHs for udc failed\n"); + dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n"); goto ep_queue_alloc_failed; } @@ -2269,14 +2278,14 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL), struct fsl_req, req); if (!udc->status_req) { - ERR("kzalloc for udc status request failed\n"); + dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n"); goto udc_status_alloc_failed; } /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); if (!udc->status_req->req.buf) { - ERR("kzalloc for udc request buffer failed\n"); + dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n"); goto udc_req_buf_alloc_failed; } @@ -2373,7 +2382,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_OTG) { udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(udc_controller->transceiver)) { - ERR("Can't find OTG driver!\n"); + dev_err(&udc_controller->gadget.dev, "Can't find OTG driver!\n"); ret = -ENODEV; goto err_kfree; } @@ -2389,7 +2398,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_DEVICE) { if (!request_mem_region(res->start, resource_size(res), driver_name)) { - ERR("request mem region for %s failed\n", pdev->name); + dev_err(&udc_controller->gadget.dev, "request mem region for %s failed\n", pdev->name); ret = -EBUSY; goto err_kfree; } @@ -2420,7 +2429,7 @@ static int fsl_udc_probe(struct platform_device *pdev) /* Read Device Controller Capability Parameters register */ dccparams = fsl_readl(&dr_regs->dccparams); if (!(dccparams & DCCPARAMS_DC)) { - ERR("This SOC doesn't support device role\n"); + dev_err(&udc_controller->gadget.dev, "This SOC doesn't support device role\n"); ret = -ENODEV; goto err_exit; } @@ -2438,14 +2447,14 @@ static int fsl_udc_probe(struct platform_device *pdev) ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED, driver_name, udc_controller); if (ret != 0) { - ERR("cannot request irq %d err %d\n", + dev_err(&udc_controller->gadget.dev, "cannot request irq %d err %d\n", udc_controller->irq, ret); goto err_exit; } /* Initialize the udc structure including QH member and other member */ if (struct_udc_setup(udc_controller, pdev)) { - ERR("Can't initialize udc data structure\n"); + dev_err(&udc_controller->gadget.dev, "Can't initialize udc data structure\n"); ret = -ENOMEM; goto err_free_irq; } @@ -2486,7 +2495,7 @@ static int fsl_udc_probe(struct platform_device *pdev) /* setup the udc->eps[] for non-control endpoints and link * to gadget.ep_list */ for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) { - char name[14]; + char name[16]; sprintf(name, "ep%dout", i); struct_ep_setup(udc_controller, i * 2, name, 1); @@ -2666,15 +2675,25 @@ static const struct platform_device_id fsl_udc_devtype[] = { } }; MODULE_DEVICE_TABLE(platform, fsl_udc_devtype); + +static const struct of_device_id fsl_udc_dt_ids[] = { + { .compatible = "fsl-usb2-dr" }, + { .compatible = "fsl-usb2-mph" }, + { .compatible = "fsl,mpc5121-usb2-dr" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_udc_dt_ids); + static struct platform_driver udc_driver = { .probe = fsl_udc_probe, - .remove_new = fsl_udc_remove, + .remove = fsl_udc_remove, .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ .suspend = fsl_udc_suspend, .resume = fsl_udc_resume, .driver = { .name = driver_name, + .of_match_table = fsl_udc_dt_ids, /* udc suspend/resume called from OTG driver */ .suspend = fsl_udc_otg_suspend, .resume = fsl_udc_otg_resume, diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h index 2efc5a930b48..cc1756f3e89d 100644 --- a/drivers/usb/gadget/udc/fsl_usb2_udc.h +++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h @@ -508,53 +508,6 @@ struct fsl_udc { /*-------------------------------------------------------------------------*/ -#ifdef DEBUG -#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \ - __func__, ## args) -#else -#define DBG(fmt, args...) do{}while(0) -#endif - -#if 0 -static void dump_msg(const char *label, const u8 * buf, unsigned int length) -{ - unsigned int start, num, i; - char line[52], *p; - - if (length >= 512) - return; - DBG("%s, length %u:\n", label, length); - start = 0; - while (length > 0) { - num = min(length, 16u); - p = line; - for (i = 0; i < num; ++i) { - if (i == 8) - *p++ = ' '; - sprintf(p, " %02x", buf[i]); - p += 3; - } - *p = 0; - printk(KERN_DEBUG "%6x: %s\n", start, line); - buf += num; - start += num; - length -= num; - } -} -#endif - -#ifdef VERBOSE -#define VDBG DBG -#else -#define VDBG(stuff...) do{}while(0) -#endif - -#define ERR(stuff...) pr_err("udc: " stuff) -#define WARNING(stuff...) pr_warn("udc: " stuff) -#define INFO(stuff...) pr_info("udc: " stuff) - -/*-------------------------------------------------------------------------*/ - /* ### Add board specific defines here */ diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 873265634ccc..5e94a99b3e53 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1297,7 +1297,7 @@ static void init_controller(struct fusb300 *fusb300) reg |= val; iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR); - /*set u1 u2 timmer*/ + /*set u1 u2 timer*/ fusb300_set_u2_timeout(fusb300, 0xff); fusb300_set_u1_timeout(fusb300, 0xff); @@ -1507,7 +1507,7 @@ clean_up: static struct platform_driver fusb300_driver = { .probe = fusb300_probe, - .remove_new = fusb300_remove, + .remove = fusb300_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 5ffb3d5c635b..b860c2e76449 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -40,7 +40,7 @@ #include <asm/byteorder.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "goku_udc.h" diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index fb901be5dac1..bf5b3c964c18 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2249,7 +2249,7 @@ static struct platform_driver gr_driver = { .of_match_table = gr_match, }, .probe = gr_probe, - .remove_new = gr_remove, + .remove = gr_remove, }; module_platform_driver(gr_driver); diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index d5f29f8fe481..89d6daf2bda7 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1487,31 +1487,29 @@ static int udc_ep0_out_req(struct lpc32xx_udc *udc) req = list_entry(ep0->queue.next, struct lpc32xx_request, queue); - if (req) { - if (req->req.length == 0) { - /* Just dequeue request */ - done(ep0, req, 0); - udc->ep0state = WAIT_FOR_SETUP; - return 1; - } + if (req->req.length == 0) { + /* Just dequeue request */ + done(ep0, req, 0); + udc->ep0state = WAIT_FOR_SETUP; + return 1; + } - /* Get data from FIFO */ - bufferspace = req->req.length - req->req.actual; - if (bufferspace > ep0->ep.maxpacket) - bufferspace = ep0->ep.maxpacket; + /* Get data from FIFO */ + bufferspace = req->req.length - req->req.actual; + if (bufferspace > ep0->ep.maxpacket) + bufferspace = ep0->ep.maxpacket; - /* Copy data to buffer */ - prefetchw(req->req.buf + req->req.actual); - tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual, - bufferspace); - req->req.actual += bufferspace; + /* Copy data to buffer */ + prefetchw(req->req.buf + req->req.actual); + tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual, + bufferspace); + req->req.actual += bufferspace; - if (tr < ep0->ep.maxpacket) { - /* This is the last packet */ - done(ep0, req, 0); - udc->ep0state = WAIT_FOR_SETUP; - return 1; - } + if (tr < ep0->ep.maxpacket) { + /* This is the last packet */ + done(ep0, req, 0); + udc->ep0state = WAIT_FOR_SETUP; + return 1; } return 0; @@ -1962,18 +1960,17 @@ static void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) /* If there isn't a request waiting, something went wrong */ req = list_entry(ep->queue.next, struct lpc32xx_request, queue); - if (req) { - done(ep, req, 0); - /* Start another request if ready */ - if (!list_empty(&ep->queue)) { - if (ep->is_in) - udc_ep_in_req_dma(udc, ep); - else - udc_ep_out_req_dma(udc, ep); - } else - ep->req_pending = 0; - } + done(ep, req, 0); + + /* Start another request if ready */ + if (!list_empty(&ep->queue)) { + if (ep->is_in) + udc_ep_in_req_dma(udc, ep); + else + udc_ep_out_req_dma(udc, ep); + } else + ep->req_pending = 0; } @@ -1989,10 +1986,6 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) #endif req = list_entry(ep->queue.next, struct lpc32xx_request, queue); - if (!req) { - ep_err(ep, "DMA interrupt on no req!\n"); - return; - } dd = req->dd_desc_ptr; /* DMA descriptor should always be retired for this call */ @@ -3256,7 +3249,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); static struct platform_driver lpc32xx_udc_driver = { .probe = lpc32xx_udc_probe, - .remove_new = lpc32xx_udc_remove, + .remove = lpc32xx_udc_remove, .shutdown = lpc32xx_udc_shutdown, .suspend = lpc32xx_udc_suspend, .resume = lpc32xx_udc_resume, diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index bfaa5291e6c8..a938b2af0944 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1688,7 +1688,7 @@ clean_up: /*-------------------------------------------------------------------------*/ static struct platform_driver m66592_driver = { .probe = m66592_probe, - .remove_new = m66592_remove, + .remove = m66592_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c index 89e8cf2a2a7d..7349ea774adf 100644 --- a/drivers/usb/gadget/udc/max3420_udc.c +++ b/drivers/usb/gadget/udc/max3420_udc.c @@ -1201,7 +1201,7 @@ static int max3420_probe(struct spi_device *spi) int err, irq; u8 reg[8]; - if (spi->master->flags & SPI_CONTROLLER_HALF_DUPLEX) { + if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) { dev_err(&spi->dev, "UDC needs full duplex to work\n"); return -EINVAL; } diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 2a421f0ff931..062f43e146aa 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -1307,7 +1307,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d) /* initialize ep0, ep0 in/out use eps[1] */ ep = &u3d->eps[1]; ep->u3d = u3d; - strncpy(ep->name, "ep0", sizeof(ep->name)); + strscpy(ep->name, "ep0"); ep->ep.name = ep->name; ep->ep.ops = &mv_u3d_ep_ops; ep->wedge = 0; @@ -1337,7 +1337,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d) ep->ep.caps.dir_out = true; } ep->u3d = u3d; - strncpy(ep->name, name, sizeof(ep->name)); + strscpy(ep->name, name); ep->ep.name = ep->name; ep->ep.caps.type_iso = true; @@ -2047,7 +2047,7 @@ static void mv_u3d_shutdown(struct platform_device *dev) static struct platform_driver mv_u3d_driver = { .probe = mv_u3d_probe, - .remove_new = mv_u3d_remove, + .remove = mv_u3d_remove, .shutdown = mv_u3d_shutdown, .driver = { .name = "mv-u3d", diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 78308b64955d..ff103e6b3048 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -30,7 +30,7 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/platform_data/mv_usb.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "mv_udc.h" @@ -2409,7 +2409,7 @@ static void mv_udc_shutdown(struct platform_device *pdev) static struct platform_driver udc_driver = { .probe = mv_udc_probe, - .remove_new = mv_udc_remove, + .remove = mv_udc_remove, .shutdown = mv_udc_shutdown, .driver = { .name = "mv-udc", diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 12e76bb62c20..7ecddbf5c90d 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -28,7 +28,7 @@ #include <linux/usb/gadget.h> #include <asm/byteorder.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "net2272.h" @@ -2097,7 +2097,7 @@ static irqreturn_t net2272_irq(int irq, void *_dev) } /* check dma interrupts */ #endif - /* Platform/devcice interrupt handler */ + /* Platform/device interrupt handler */ #if !defined(PLX_PCI_RDK) net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); @@ -2650,7 +2650,7 @@ net2272_plat_probe(struct platform_device *pdev) goto err_req; } - ret = net2272_probe_fin(dev, IRQF_TRIGGER_LOW); + ret = net2272_probe_fin(dev, irqflags); if (ret) goto err_io; @@ -2685,7 +2685,7 @@ net2272_plat_remove(struct platform_device *pdev) static struct platform_driver net2272_plat_driver = { .probe = net2272_plat_probe, - .remove_new = net2272_plat_remove, + .remove = net2272_plat_remove, .driver = { .name = driver_name, }, diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 1b929c519cd7..b2903e4bbf54 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -56,7 +56,7 @@ #include <asm/byteorder.h> #include <asm/irq.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller" #define DRIVER_VERSION "2005 Sept 27/v3.0" diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index f90eeecf27de..8902abe3ca76 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -18,6 +18,7 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -36,7 +37,7 @@ #include <asm/byteorder.h> #include <asm/irq.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <asm/mach-types.h> #include <linux/omap-dma.h> @@ -56,7 +57,6 @@ /* ISO too */ #define USE_ISO -#define DRIVER_DESC "OMAP UDC driver" #define DRIVER_VERSION "4 October 2004" #define OMAP_DMA_USB_W2FC_TX0 29 @@ -110,7 +110,6 @@ MODULE_PARM_DESC(use_dma, "enable/disable DMA"); static const char driver_name[] = "omap_udc"; -static const char driver_desc[] = DRIVER_DESC; /*-------------------------------------------------------------------------*/ @@ -578,13 +577,13 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) static void next_out_dma(struct omap_ep *ep, struct omap_req *req) { - unsigned packets = req->req.length - req->req.actual; + unsigned int packets = req->req.length - req->req.actual; int dma_trigger = 0; u16 w; /* set up this DMA transfer, enable the fifo, start */ packets /= ep->ep.maxpacket; - packets = min(packets, (unsigned)UDC_RXN_TC + 1); + packets = min_t(unsigned int, packets, UDC_RXN_TC + 1); req->dma_bytes = packets * ep->ep.maxpacket; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, ep->ep.maxpacket >> 1, packets, @@ -1254,7 +1253,7 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) udc = container_of(gadget, struct omap_udc, gadget); spin_lock_irqsave(&udc->lock, flags); - VDBG("VBUS %s\n", is_active ? "on" : "off"); + VDBG("VBUS %s\n", str_on_off(is_active)); udc->vbus_active = (is_active != 0); if (cpu_is_omap15xx()) { /* "software" detect, ignored if !VBUS_MODE_1510 */ @@ -2299,13 +2298,11 @@ static int proc_udc_show(struct seq_file *s, void *_) spin_lock_irqsave(&udc->lock, flags); - seq_printf(s, "%s, version: " DRIVER_VERSION + seq_printf(s, "OMAP UDC driver, version: " DRIVER_VERSION #ifdef USE_ISO " (iso)" #endif - "%s\n", - driver_desc, - use_dma ? " (dma)" : ""); + "%s\n", use_dma ? " (dma)" : ""); tmp = omap_readw(UDC_REV) & 0xff; seq_printf(s, @@ -2984,7 +2981,7 @@ static int omap_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .probe = omap_udc_probe, - .remove_new = omap_udc_remove, + .remove = omap_udc_remove, .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { @@ -2994,6 +2991,6 @@ static struct platform_driver udc_driver = { module_platform_driver(udc_driver); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_DESCRIPTION("OMAP UDC driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:omap_udc"); diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 1ac26cb49ecf..1e9998024aaa 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -38,7 +38,7 @@ #include <asm/byteorder.h> #include <asm/dma.h> #include <asm/mach-types.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -2474,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .shutdown = pxa25x_udc_shutdown, .probe = pxa25x_udc_probe, - .remove_new = pxa25x_udc_remove, + .remove = pxa25x_udc_remove, .suspend = pxa25x_udc_suspend, .resume = pxa25x_udc_resume, .driver = { diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 61424cfd2e1c..897f53601b5b 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -20,11 +20,11 @@ #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/prefetch.h> #include <linux/byteorder/generic.h> #include <linux/platform_data/pxa2xx_udc.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/usb.h> #include <linux/usb/ch9.h> @@ -1084,7 +1084,7 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, is_first_req = list_empty(&ep->queue); ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n", - _req, is_first_req ? "yes" : "no", + _req, str_yes_no(is_first_req), _req->length, _req->buf); if (!ep->enabled) { @@ -2356,18 +2356,19 @@ static int pxa_udc_probe(struct platform_device *pdev) struct pxa_udc *udc = &memory; int retval = 0, gpio; struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev); - unsigned long gpio_flags; if (mach) { - gpio_flags = mach->gpio_pullup_inverted ? GPIOF_ACTIVE_LOW : 0; gpio = mach->gpio_pullup; if (gpio_is_valid(gpio)) { retval = devm_gpio_request_one(&pdev->dev, gpio, - gpio_flags, + GPIOF_OUT_INIT_LOW, "USB D+ pullup"); if (retval) return retval; udc->gpiod = gpio_to_desc(mach->gpio_pullup); + + if (mach->gpio_pullup_inverted ^ gpiod_is_active_low(udc->gpiod)) + gpiod_toggle_active_low(udc->gpiod); } udc->udc_command = mach->udc_command; } else { @@ -2539,7 +2540,7 @@ static struct platform_driver udc_driver = { .of_match_table = of_match_ptr(udc_pxa_dt_ids), }, .probe = pxa_udc_probe, - .remove_new = pxa_udc_remove, + .remove = pxa_udc_remove, .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM .suspend = pxa_udc_suspend, diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index db4a10a979f9..ff6f846b1d41 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1965,7 +1965,7 @@ clean_up2: /*-------------------------------------------------------------------------*/ static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove_new = r8a66597_remove, + .remove = r8a66597_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 3b01734ce1b7..89b304cf6d03 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -310,7 +310,7 @@ struct renesas_usb3_request { struct list_head queue; }; -#define USB3_EP_NAME_SIZE 8 +#define USB3_EP_NAME_SIZE 16 struct renesas_usb3_ep { struct usb_ep ep; struct renesas_usb3 *usb3; @@ -3013,7 +3013,7 @@ static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, - .remove_new = renesas_usb3_remove, + .remove = renesas_usb3_remove, .driver = { .name = udc_name, .pm = &renesas_usb3_pm_ops, diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 657f265ac7cc..14f4b2cf05a4 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -2482,7 +2482,7 @@ static int usbf_handle_ep0_setup(struct usbf_ep *ep0) ep0->delayed_status = 0; if ((crq.ctrlreq.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) { - /* This is not a USB standard request -> delelate */ + /* This is not a USB standard request -> delegate */ goto delegate; } @@ -3381,7 +3381,7 @@ static struct platform_driver udc_driver = { .of_match_table = usbf_match, }, .probe = usbf_probe, - .remove_new = usbf_remove, + .remove = usbf_remove, }; module_platform_driver(udc_driver); diff --git a/drivers/usb/gadget/udc/rzv2m_usb3drd.c b/drivers/usb/gadget/udc/rzv2m_usb3drd.c index 36f4ff00d22f..4692eae89f44 100644 --- a/drivers/usb/gadget/udc/rzv2m_usb3drd.c +++ b/drivers/usb/gadget/udc/rzv2m_usb3drd.c @@ -127,7 +127,7 @@ static struct platform_driver rzv2m_usb3drd_driver = { .of_match_table = rzv2m_usb3drd_of_match, }, .probe = rzv2m_usb3drd_probe, - .remove_new = rzv2m_usb3drd_remove, + .remove = rzv2m_usb3drd_remove, }; module_platform_driver(rzv2m_usb3drd_driver); diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 2fc5d4d277bc..1f8a99d2a643 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -33,7 +33,7 @@ #include <linux/prefetch.h> #include <linux/moduleparam.h> #include <asm/byteorder.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "amd5536udc.h" static void udc_setup_endpoints(struct udc *dev); @@ -2707,7 +2707,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev) /* write fifo */ udc_txfifo_write(ep, &req->req); - /* lengh bytes transferred */ + /* length bytes transferred */ len = req->req.length - req->req.actual; if (len > ep->ep.maxpacket) len = ep->ep.maxpacket; diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index 547af2ed9e5e..db842a6de643 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -8,7 +8,6 @@ #include <linux/extcon.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> #include <linux/module.h> @@ -310,7 +309,7 @@ MODULE_DEVICE_TABLE(of, of_udc_match); static struct platform_driver udc_plat_driver = { .probe = udc_plat_probe, - .remove_new = udc_plat_remove, + .remove = udc_plat_remove, .driver = { .name = "snps-udc-plat", .of_match_table = of_udc_match, diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index cb85168fd00c..c7fdbc55fb0b 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -3491,8 +3491,8 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc) static int tegra_xudc_phy_get(struct tegra_xudc *xudc) { - int err = 0, usb3; - unsigned int i; + int err = 0, usb3_companion_port; + unsigned int i, j; xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys, sizeof(*xudc->utmi_phy), GFP_KERNEL); @@ -3520,7 +3520,7 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc) if (IS_ERR(xudc->utmi_phy[i])) { err = PTR_ERR(xudc->utmi_phy[i]); dev_err_probe(xudc->dev, err, - "failed to get usb2-%d PHY\n", i); + "failed to get PHY for phy-name usb2-%d\n", i); goto clean_up; } else if (xudc->utmi_phy[i]) { /* Get usb-phy, if utmi phy is available */ @@ -3539,19 +3539,30 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc) } /* Get USB3 phy */ - usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i); - if (usb3 < 0) + usb3_companion_port = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i); + if (usb3_companion_port < 0) continue; - snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3); - xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name); - if (IS_ERR(xudc->usb3_phy[i])) { - err = PTR_ERR(xudc->usb3_phy[i]); - dev_err_probe(xudc->dev, err, - "failed to get usb3-%d PHY\n", usb3); - goto clean_up; - } else if (xudc->usb3_phy[i]) - dev_dbg(xudc->dev, "usb3-%d PHY registered", usb3); + for (j = 0; j < xudc->soc->num_phys; j++) { + snprintf(phy_name, sizeof(phy_name), "usb3-%d", j); + xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name); + if (IS_ERR(xudc->usb3_phy[i])) { + err = PTR_ERR(xudc->usb3_phy[i]); + dev_err_probe(xudc->dev, err, + "failed to get PHY for phy-name usb3-%d\n", j); + goto clean_up; + } else if (xudc->usb3_phy[i]) { + int usb2_port = + tegra_xusb_padctl_get_port_number(xudc->utmi_phy[i]); + int usb3_port = + tegra_xusb_padctl_get_port_number(xudc->usb3_phy[i]); + if (usb3_port == usb3_companion_port) { + dev_dbg(xudc->dev, "USB2 port %d is paired with USB3 port %d for device mode port %d\n", + usb2_port, usb3_port, i); + break; + } + } + } } return err; @@ -4060,7 +4071,7 @@ static const struct dev_pm_ops tegra_xudc_pm_ops = { static struct platform_driver tegra_xudc_driver = { .probe = tegra_xudc_probe, - .remove_new = tegra_xudc_remove, + .remove = tegra_xudc_remove, .driver = { .name = "tegra-xudc", .pm = &tegra_xudc_pm_ops, diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h index a5ed26fbc2da..4e334298b0e8 100644 --- a/drivers/usb/gadget/udc/trace.h +++ b/drivers/usb/gadget/udc/trace.h @@ -157,7 +157,7 @@ DECLARE_EVENT_CLASS(udc_log_ep, __field(int, ret) ), TP_fast_assign( - __assign_str(name, ep->name); + __assign_str(name); __entry->maxpacket = ep->maxpacket; __entry->maxpacket_limit = ep->maxpacket_limit; __entry->max_streams = ep->max_streams; @@ -233,7 +233,7 @@ DECLARE_EVENT_CLASS(udc_log_req, __field(struct usb_request *, req) ), TP_fast_assign( - __assign_str(name, ep->name); + __assign_str(name); __entry->length = req->length; __entry->actual = req->actual; __entry->num_sgs = req->num_sgs; diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 74590f93ea61..ae2aeb271897 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -947,7 +947,7 @@ static int xudc_ep_disable(struct usb_ep *_ep) ep->desc = NULL; ep->ep_usb.desc = NULL; - dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber); + dev_dbg(udc->dev, "USB Ep %d disable\n", ep->epnumber); /* Disable the endpoint.*/ epcfg = udc->read_fn(udc->addr + ep->offset); epcfg &= ~XUSB_EP_CFG_VALID_MASK; @@ -2258,7 +2258,7 @@ static struct platform_driver xudc_driver = { .pm = &xudc_pm_ops, }, .probe = xudc_probe, - .remove_new = xudc_remove, + .remove = xudc_remove, }; module_platform_driver(xudc_driver); diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 75f6f99f8173..37a2f1b61cba 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -55,7 +55,7 @@ usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf) return -EINVAL; /* string descriptors have length, tag, then UTF16-LE text */ - len = min((size_t)USB_MAX_STRING_LEN, strlen(s->s)); + len = min_t(size_t, USB_MAX_STRING_LEN, strlen(s->s)); len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN, (wchar_t *) &buf[2], USB_MAX_STRING_LEN); if (len < 0) |