summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-14 21:37:50 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-14 21:37:50 +0300
commit81522637485dd6ec9de4279c9714d58f884b6091 (patch)
tree74657b5881c08b1e6a7042482e593897e678afe2 /drivers/usb/gadget/function
parent54a2ec67f1db62a763f57b7f8f2e82874f5f358b (diff)
parente6be244a83211f3a9daaf5e29ee97fe0bf1efe5a (diff)
downloadlinux-81522637485dd6ec9de4279c9714d58f884b6091.tar.xz
Merge tag 'usb-for-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes: usb: patches for v4.9 merge window This time around we have 92 non-merge commits. Most of the changes are in drivers/usb/gadget (40.3%) with drivers/usb/gadget/function being the most active directory (27.2%). As for UDC drivers, only dwc3 (26.5%) and dwc2 (12.7%) have really been active. The most important changes for dwc3 are better support for scatterlist and, again, throughput improvements. While on dwc2 got some minor stability fixes related to soft reset and FIFO usage. Felipe Tonello has done some good work fixing up our f_midi gadget and Tal Shorer has implemented a nice API change for our ULPI bus. Apart from these, we have our usual set of non-critical fixes, spelling fixes, build warning fixes, etc.
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r--drivers/usb/gadget/function/f_fs.c45
-rw-r--r--drivers/usb/gadget/function/f_hid.c28
-rw-r--r--drivers/usb/gadget/function/f_loopback.c14
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c28
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h1
-rw-r--r--drivers/usb/gadget/function/f_midi.c234
-rw-r--r--drivers/usb/gadget/function/f_ncm.c84
-rw-r--r--drivers/usb/gadget/function/f_printer.c6
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c6
-rw-r--r--drivers/usb/gadget/function/f_uvc.c7
-rw-r--r--drivers/usb/gadget/function/storage_common.c24
-rw-r--r--drivers/usb/gadget/function/storage_common.h10
-rw-r--r--drivers/usb/gadget/function/u_ether.c15
-rw-r--r--drivers/usb/gadget/function/u_ether.h1
14 files changed, 370 insertions, 133 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 5c8429f23a89..0aeed85bb5cb 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -98,6 +98,9 @@ static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *,
const struct usb_ctrlrequest *);
+static bool ffs_func_req_match(struct usb_function *,
+ const struct usb_ctrlrequest *,
+ bool config0);
static void ffs_func_suspend(struct usb_function *);
static void ffs_func_resume(struct usb_function *);
@@ -2243,7 +2246,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
FUNCTIONFS_HAS_SS_DESC |
FUNCTIONFS_HAS_MS_OS_DESC |
FUNCTIONFS_VIRTUAL_ADDR |
- FUNCTIONFS_EVENTFD)) {
+ FUNCTIONFS_EVENTFD |
+ FUNCTIONFS_ALL_CTRL_RECIP |
+ FUNCTIONFS_CONFIG0_SETUP)) {
ret = -ENOSYS;
goto error;
}
@@ -3094,8 +3099,9 @@ static int ffs_func_setup(struct usb_function *f,
* handle them. All other either handled by composite or
* passed to usb_configuration->setup() (if one is set). No
* matter, we will handle requests directed to endpoint here
- * as well (as it's straightforward) but what to do with any
- * other request?
+ * as well (as it's straightforward). Other request recipient
+ * types are only handled when the user flag FUNCTIONFS_ALL_CTRL_RECIP
+ * is being used.
*/
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
@@ -3116,7 +3122,10 @@ static int ffs_func_setup(struct usb_function *f,
break;
default:
- return -EOPNOTSUPP;
+ if (func->ffs->user_flags & FUNCTIONFS_ALL_CTRL_RECIP)
+ ret = le16_to_cpu(creq->wIndex);
+ else
+ return -EOPNOTSUPP;
}
spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
@@ -3128,6 +3137,28 @@ static int ffs_func_setup(struct usb_function *f,
return 0;
}
+static bool ffs_func_req_match(struct usb_function *f,
+ const struct usb_ctrlrequest *creq,
+ bool config0)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+
+ if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP))
+ return false;
+
+ switch (creq->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ return ffs_func_revmap_intf(func,
+ le16_to_cpu(creq->wIndex) >= 0);
+ case USB_RECIP_ENDPOINT:
+ return ffs_func_revmap_ep(func,
+ le16_to_cpu(creq->wIndex) >= 0);
+ default:
+ return (bool) (func->ffs->user_flags &
+ FUNCTIONFS_ALL_CTRL_RECIP);
+ }
+}
+
static void ffs_func_suspend(struct usb_function *f)
{
ENTER();
@@ -3378,6 +3409,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
func->function.set_alt = ffs_func_set_alt;
func->function.disable = ffs_func_disable;
func->function.setup = ffs_func_setup;
+ func->function.req_match = ffs_func_req_match;
func->function.suspend = ffs_func_suspend;
func->function.resume = ffs_func_resume;
func->function.free_func = ffs_free;
@@ -3470,6 +3502,11 @@ static void _ffs_free_dev(struct ffs_dev *dev)
list_del(&dev->entry);
if (dev->name_allocated)
kfree(dev->name);
+
+ /* Clear the private_data pointer to stop incorrect dev access */
+ if (dev->ffs_data)
+ dev->ffs_data->private_data = NULL;
+
kfree(dev);
if (list_empty(&ffs_devices))
functionfs_cleanup();
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 51980c50546d..e2966f87c860 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -365,7 +365,7 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
unsigned length)
{
- return alloc_ep_req(ep, length, length);
+ return alloc_ep_req(ep, length);
}
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
@@ -617,14 +617,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
/* preallocate request and buffer */
status = -ENOMEM;
- hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
+ hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length);
if (!hidg->req)
goto fail;
- hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
- if (!hidg->req->buf)
- goto fail;
-
/* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
@@ -677,11 +673,8 @@ fail_free_descs:
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
- if (hidg->req != NULL) {
- kfree(hidg->req->buf);
- if (hidg->in_ep != NULL)
- usb_ep_free_request(hidg->in_ep, hidg->req);
- }
+ if (hidg->req != NULL)
+ free_ep_req(hidg->in_ep, hidg->req);
return status;
}
@@ -809,11 +802,21 @@ end:
CONFIGFS_ATTR(f_hid_opts_, report_desc);
+static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page)
+{
+ struct f_hid_opts *opts = to_f_hid_opts(item);
+
+ return sprintf(page, "%d:%d\n", major, opts->minor);
+}
+
+CONFIGFS_ATTR_RO(f_hid_opts_, dev);
+
static struct configfs_attribute *hid_attrs[] = {
&f_hid_opts_attr_subclass,
&f_hid_opts_attr_protocol,
&f_hid_opts_attr_report_length,
&f_hid_opts_attr_report_desc,
+ &f_hid_opts_attr_dev,
NULL,
};
@@ -910,8 +913,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
/* disable/free request and end point */
usb_ep_disable(hidg->in_ep);
- kfree(hidg->req->buf);
- usb_ep_free_request(hidg->in_ep, hidg->req);
+ free_ep_req(hidg->in_ep, hidg->req);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 3a9f8f9c77bd..e70093835e14 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -308,9 +308,7 @@ static void disable_loopback(struct f_loopback *loop)
static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
{
- struct f_loopback *loop = ep->driver_data;
-
- return alloc_ep_req(ep, len, loop->buflen);
+ return alloc_ep_req(ep, len);
}
static int alloc_requests(struct usb_composite_dev *cdev,
@@ -333,7 +331,7 @@ static int alloc_requests(struct usb_composite_dev *cdev,
if (!in_req)
goto fail;
- out_req = lb_alloc_ep_req(loop->out_ep, 0);
+ out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen);
if (!out_req)
goto fail_in;
@@ -593,13 +591,9 @@ DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
int __init lb_modinit(void)
{
- int ret;
-
- ret = usb_function_register(&Loopbackusb_func);
- if (ret)
- return ret;
- return ret;
+ return usb_function_register(&Loopbackusb_func);
}
+
void __exit lb_modexit(void)
{
usb_function_unregister(&Loopbackusb_func);
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 2505117e88e8..8f3659b65f53 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -311,11 +311,7 @@ struct fsg_common {
/* Gadget's private data. */
void *private_data;
- /*
- * Vendor (8 chars), product (16 chars), release (4
- * hexadecimal digits) and NUL byte
- */
- char inquiry_string[8 + 16 + 4 + 1];
+ char inquiry_string[INQUIRY_STRING_LEN];
struct kref ref;
};
@@ -1107,7 +1103,12 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
buf[5] = 0; /* No special options */
buf[6] = 0;
buf[7] = 0;
- memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string);
+ if (curlun->inquiry_string[0])
+ memcpy(buf + 8, curlun->inquiry_string,
+ sizeof(curlun->inquiry_string));
+ else
+ memcpy(buf + 8, common->inquiry_string,
+ sizeof(common->inquiry_string));
return 36;
}
@@ -3209,12 +3210,27 @@ static ssize_t fsg_lun_opts_nofua_store(struct config_item *item,
CONFIGFS_ATTR(fsg_lun_opts_, nofua);
+static ssize_t fsg_lun_opts_inquiry_string_show(struct config_item *item,
+ char *page)
+{
+ return fsg_show_inquiry_string(to_fsg_lun_opts(item)->lun, page);
+}
+
+static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ return fsg_store_inquiry_string(to_fsg_lun_opts(item)->lun, page, len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string);
+
static struct configfs_attribute *fsg_lun_attrs[] = {
&fsg_lun_opts_attr_file,
&fsg_lun_opts_attr_ro,
&fsg_lun_opts_attr_removable,
&fsg_lun_opts_attr_cdrom,
&fsg_lun_opts_attr_nofua,
+ &fsg_lun_opts_attr_inquiry_string,
NULL,
};
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index b6a9918eaefb..d3902313b8ac 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -100,6 +100,7 @@ struct fsg_lun_config {
char removable;
char cdrom;
char nofua;
+ char inquiry_string[INQUIRY_STRING_LEN];
};
struct fsg_config {
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 58fc199a18ec..a5719f271bf0 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -51,6 +51,19 @@ static const char f_midi_longname[] = "MIDI Gadget";
*/
#define MAX_PORTS 16
+/* MIDI message states */
+enum {
+ STATE_INITIAL = 0, /* pseudo state */
+ STATE_1PARAM,
+ STATE_2PARAM_1,
+ STATE_2PARAM_2,
+ STATE_SYSEX_0,
+ STATE_SYSEX_1,
+ STATE_SYSEX_2,
+ STATE_REAL_TIME,
+ STATE_FINISHED, /* pseudo state */
+};
+
/*
* This is a gadget, and the IN/OUT naming is from the host's perspective.
* USB -> OUT endpoint -> rawmidi
@@ -61,13 +74,6 @@ struct gmidi_in_port {
int active;
uint8_t cable;
uint8_t state;
-#define STATE_UNKNOWN 0
-#define STATE_1PARAM 1
-#define STATE_2PARAM_1 2
-#define STATE_2PARAM_2 3
-#define STATE_SYSEX_0 4
-#define STATE_SYSEX_1 5
-#define STATE_SYSEX_2 6
uint8_t data[2];
};
@@ -205,7 +211,7 @@ static struct usb_gadget_strings *midi_strings[] = {
static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
unsigned length)
{
- return alloc_ep_req(ep, length, length);
+ return alloc_ep_req(ep, length);
}
static const uint8_t f_midi_cin_length[] = {
@@ -299,6 +305,19 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
}
}
+static void f_midi_drop_out_substreams(struct f_midi *midi)
+{
+ unsigned int i;
+
+ for (i = 0; i < midi->in_ports; i++) {
+ struct gmidi_in_port *port = midi->in_ports_array + i;
+ struct snd_rawmidi_substream *substream = port->substream;
+
+ if (port->active && substream)
+ snd_rawmidi_drop_output(substream);
+ }
+}
+
static int f_midi_start_ep(struct f_midi *midi,
struct usb_function *f,
struct usb_ep *ep)
@@ -360,9 +379,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
/* allocate a bunch of read buffers and queue them all at once. */
for (i = 0; i < midi->qlen && err == 0; i++) {
struct usb_request *req =
- midi_alloc_ep_req(midi->out_ep,
- max_t(unsigned, midi->buflen,
- bulk_out_desc.wMaxPacketSize));
+ midi_alloc_ep_req(midi->out_ep, midi->buflen);
+
if (req == NULL)
return -ENOMEM;
@@ -397,6 +415,8 @@ static void f_midi_disable(struct usb_function *f)
/* release IN requests */
while (kfifo_get(&midi->in_req_fifo, &req))
free_ep_req(midi->in_ep, req);
+
+ f_midi_drop_out_substreams(midi);
}
static int f_midi_snd_free(struct snd_device *device)
@@ -404,130 +424,166 @@ static int f_midi_snd_free(struct snd_device *device)
return 0;
}
-static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0,
- uint8_t p1, uint8_t p2, uint8_t p3)
-{
- unsigned length = req->length;
- u8 *buf = (u8 *)req->buf + length;
-
- buf[0] = p0;
- buf[1] = p1;
- buf[2] = p2;
- buf[3] = p3;
- req->length = length + 4;
-}
-
/*
* Converts MIDI commands to USB MIDI packets.
*/
static void f_midi_transmit_byte(struct usb_request *req,
struct gmidi_in_port *port, uint8_t b)
{
- uint8_t p0 = port->cable << 4;
+ uint8_t p[4] = { port->cable << 4, 0, 0, 0 };
+ uint8_t next_state = STATE_INITIAL;
+
+ switch (b) {
+ case 0xf8 ... 0xff:
+ /* System Real-Time Messages */
+ p[0] |= 0x0f;
+ p[1] = b;
+ next_state = port->state;
+ port->state = STATE_REAL_TIME;
+ break;
- if (b >= 0xf8) {
- f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
- } else if (b >= 0xf0) {
+ case 0xf7:
+ /* End of SysEx */
+ switch (port->state) {
+ case STATE_SYSEX_0:
+ p[0] |= 0x05;
+ p[1] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ case STATE_SYSEX_1:
+ p[0] |= 0x06;
+ p[1] = port->data[0];
+ p[2] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ case STATE_SYSEX_2:
+ p[0] |= 0x07;
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ default:
+ /* Ignore byte */
+ next_state = port->state;
+ port->state = STATE_INITIAL;
+ }
+ break;
+
+ case 0xf0 ... 0xf6:
+ /* System Common Messages */
+ port->data[0] = port->data[1] = 0;
+ port->state = STATE_INITIAL;
switch (b) {
case 0xf0:
port->data[0] = b;
- port->state = STATE_SYSEX_1;
+ port->data[1] = 0;
+ next_state = STATE_SYSEX_1;
break;
case 0xf1:
case 0xf3:
port->data[0] = b;
- port->state = STATE_1PARAM;
+ next_state = STATE_1PARAM;
break;
case 0xf2:
port->data[0] = b;
- port->state = STATE_2PARAM_1;
+ next_state = STATE_2PARAM_1;
break;
case 0xf4:
case 0xf5:
- port->state = STATE_UNKNOWN;
+ next_state = STATE_INITIAL;
break;
case 0xf6:
- f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
- port->state = STATE_UNKNOWN;
- break;
- case 0xf7:
- switch (port->state) {
- case STATE_SYSEX_0:
- f_midi_transmit_packet(req,
- p0 | 0x05, 0xf7, 0, 0);
- break;
- case STATE_SYSEX_1:
- f_midi_transmit_packet(req,
- p0 | 0x06, port->data[0], 0xf7, 0);
- break;
- case STATE_SYSEX_2:
- f_midi_transmit_packet(req,
- p0 | 0x07, port->data[0],
- port->data[1], 0xf7);
- break;
- }
- port->state = STATE_UNKNOWN;
+ p[0] |= 0x05;
+ p[1] = 0xf6;
+ next_state = STATE_FINISHED;
break;
}
- } else if (b >= 0x80) {
+ break;
+
+ case 0x80 ... 0xef:
+ /*
+ * Channel Voice Messages, Channel Mode Messages
+ * and Control Change Messages.
+ */
port->data[0] = b;
+ port->data[1] = 0;
+ port->state = STATE_INITIAL;
if (b >= 0xc0 && b <= 0xdf)
- port->state = STATE_1PARAM;
+ next_state = STATE_1PARAM;
else
- port->state = STATE_2PARAM_1;
- } else { /* b < 0x80 */
+ next_state = STATE_2PARAM_1;
+ break;
+
+ case 0x00 ... 0x7f:
+ /* Message parameters */
switch (port->state) {
case STATE_1PARAM:
- if (port->data[0] < 0xf0) {
- p0 |= port->data[0] >> 4;
- } else {
- p0 |= 0x02;
- port->state = STATE_UNKNOWN;
- }
- f_midi_transmit_packet(req, p0, port->data[0], b, 0);
+ if (port->data[0] < 0xf0)
+ p[0] |= port->data[0] >> 4;
+ else
+ p[0] |= 0x02;
+
+ p[1] = port->data[0];
+ p[2] = b;
+ /* This is to allow Running State Messages */
+ next_state = STATE_1PARAM;
break;
case STATE_2PARAM_1:
port->data[1] = b;
- port->state = STATE_2PARAM_2;
+ next_state = STATE_2PARAM_2;
break;
case STATE_2PARAM_2:
- if (port->data[0] < 0xf0) {
- p0 |= port->data[0] >> 4;
- port->state = STATE_2PARAM_1;
- } else {
- p0 |= 0x03;
- port->state = STATE_UNKNOWN;
- }
- f_midi_transmit_packet(req,
- p0, port->data[0], port->data[1], b);
+ if (port->data[0] < 0xf0)
+ p[0] |= port->data[0] >> 4;
+ else
+ p[0] |= 0x03;
+
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = b;
+ /* This is to allow Running State Messages */
+ next_state = STATE_2PARAM_1;
break;
case STATE_SYSEX_0:
port->data[0] = b;
- port->state = STATE_SYSEX_1;
+ next_state = STATE_SYSEX_1;
break;
case STATE_SYSEX_1:
port->data[1] = b;
- port->state = STATE_SYSEX_2;
+ next_state = STATE_SYSEX_2;
break;
case STATE_SYSEX_2:
- f_midi_transmit_packet(req,
- p0 | 0x04, port->data[0], port->data[1], b);
- port->state = STATE_SYSEX_0;
+ p[0] |= 0x04;
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = b;
+ next_state = STATE_SYSEX_0;
break;
}
+ break;
}
-}
-static void f_midi_drop_out_substreams(struct f_midi *midi)
-{
- unsigned int i;
+ /* States where we have to write into the USB request */
+ if (next_state == STATE_FINISHED ||
+ port->state == STATE_SYSEX_2 ||
+ port->state == STATE_1PARAM ||
+ port->state == STATE_2PARAM_2 ||
+ port->state == STATE_REAL_TIME) {
- for (i = 0; i < midi->in_ports; i++) {
- struct gmidi_in_port *port = midi->in_ports_array + i;
- struct snd_rawmidi_substream *substream = port->substream;
- if (port->active && substream)
- snd_rawmidi_drop_output(substream);
+ unsigned int length = req->length;
+ u8 *buf = (u8 *)req->buf + length;
+
+ memcpy(buf, p, sizeof(p));
+ req->length = length + sizeof(p);
+
+ if (next_state == STATE_FINISHED) {
+ next_state = STATE_INITIAL;
+ port->data[0] = port->data[1] = 0;
+ }
}
+
+ port->state = next_state;
}
static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep)
@@ -642,7 +698,7 @@ static int f_midi_in_open(struct snd_rawmidi_substream *substream)
VDBG(midi, "%s()\n", __func__);
port = midi->in_ports_array + substream->number;
port->substream = substream;
- port->state = STATE_UNKNOWN;
+ port->state = STATE_INITIAL;
return 0;
}
@@ -1123,7 +1179,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1;
- opts->buflen = 256;
+ opts->buflen = 512;
opts->qlen = 32;
opts->in_ports = 1;
opts->out_ports = 1;
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 97f0a9bc84df..639603722709 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -90,7 +90,9 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ncm_bitrate(struct usb_gadget *g)
{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return 13 * 1024 * 8 * 1000 * 8;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
return 19 * 64 * 1 * 1000 * 8;
@@ -333,6 +335,76 @@ static struct usb_descriptor_header *ncm_hs_function[] = {
NULL,
};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
+ .bInterval = USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
+ .bLength = sizeof(ss_ncm_notify_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(NCM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_ncm_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ncm_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {
+ .bLength = sizeof(ss_ncm_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *ncm_ss_function[] = {
+ (struct usb_descriptor_header *) &ncm_iad_desc,
+ /* CDC NCM control descriptors */
+ (struct usb_descriptor_header *) &ncm_control_intf,
+ (struct usb_descriptor_header *) &ncm_header_desc,
+ (struct usb_descriptor_header *) &ncm_union_desc,
+ (struct usb_descriptor_header *) &ecm_desc,
+ (struct usb_descriptor_header *) &ncm_desc,
+ (struct usb_descriptor_header *) &ss_ncm_notify_desc,
+ (struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,
+ /* data interface, altsettings 0 and 1 */
+ (struct usb_descriptor_header *) &ncm_data_nop_intf,
+ (struct usb_descriptor_header *) &ncm_data_intf,
+ (struct usb_descriptor_header *) &ss_ncm_in_desc,
+ (struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
+ (struct usb_descriptor_header *) &ss_ncm_out_desc,
+ (struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
+ NULL,
+};
+
/* string descriptors: */
#define STRING_CTRL_IDX 0
@@ -852,6 +924,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
*/
ncm->port.is_zlp_ok =
gadget_is_zlp_supported(cdev->gadget);
+ ncm->port.no_skb_reserve =
+ gadget_avoids_skb_reserve(cdev->gadget);
ncm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ncm\n");
net = gether_connect(&ncm->port);
@@ -1431,8 +1505,13 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
hs_ncm_notify_desc.bEndpointAddress =
fs_ncm_notify_desc.bEndpointAddress;
+ ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
+ ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
+ ss_ncm_notify_desc.bEndpointAddress =
+ fs_ncm_notify_desc.bEndpointAddress;
+
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
- NULL, NULL);
+ ncm_ss_function, NULL);
if (status)
goto fail;
@@ -1450,6 +1529,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->task_timer.function = ncm_tx_timeout;
DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ncm->port.in_ep->name, ncm->port.out_ep->name,
ncm->notify->name);
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 64706a789580..0de36cda6e41 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -889,13 +889,17 @@ static void printer_soft_reset(struct printer_dev *dev)
/*-------------------------------------------------------------------------*/
static bool gprinter_req_match(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
+ const struct usb_ctrlrequest *ctrl,
+ bool config0)
{
struct printer_dev *dev = func_to_printer(f);
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ if (config0)
+ return false;
+
if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
(ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
return false;
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index df0189ddfdd5..8784fa12ea2c 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -293,9 +293,7 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
{
- struct f_sourcesink *ss = ep->driver_data;
-
- return alloc_ep_req(ep, len, ss->buflen);
+ return alloc_ep_req(ep, len);
}
static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
@@ -606,7 +604,7 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
} else {
ep = is_in ? ss->in_ep : ss->out_ep;
qlen = ss->bulk_qlen;
- size = 0;
+ size = ss->buflen;
}
for (i = 0; i < qlen; i++) {
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 29b41b5dee04..27ed51b5082f 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -258,6 +258,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
v4l2_event_queue(&uvc->vdev, &v4l2_event);
+ /* Pass additional setup data to userspace */
+ if (uvc->event_setup_out && uvc->event_length) {
+ uvc->control_req->length = uvc->event_length;
+ return usb_ep_queue(uvc->func.config->cdev->gadget->ep0,
+ uvc->control_req, GFP_ATOMIC);
+ }
+
return 0;
}
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 990df221c629..8fbf6861690d 100644
--- a/drivers/usb/gadget/function/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
@@ -369,6 +369,12 @@ ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
}
EXPORT_SYMBOL_GPL(fsg_show_removable);
+ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf)
+{
+ return sprintf(buf, "%s\n", curlun->inquiry_string);
+}
+EXPORT_SYMBOL_GPL(fsg_show_inquiry_string);
+
/*
* The caller must hold fsg->filesem for reading when calling this function.
*/
@@ -499,4 +505,22 @@ ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
}
EXPORT_SYMBOL_GPL(fsg_store_removable);
+ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
+ size_t count)
+{
+ const size_t len = min(count, sizeof(curlun->inquiry_string));
+
+ if (len == 0 || buf[0] == '\n') {
+ curlun->inquiry_string[0] = 0;
+ } else {
+ snprintf(curlun->inquiry_string,
+ sizeof(curlun->inquiry_string), "%-28s", buf);
+ if (curlun->inquiry_string[len-1] == '\n')
+ curlun->inquiry_string[len-1] = ' ';
+ }
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(fsg_store_inquiry_string);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index c3544e61da66..e69848994cb4 100644
--- a/drivers/usb/gadget/function/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -88,6 +88,12 @@ do { \
#define ASC(x) ((u8) ((x) >> 8))
#define ASCQ(x) ((u8) (x))
+/*
+ * Vendor (8 chars), product (16 chars), release (4 hexadecimal digits) and NUL
+ * byte
+ */
+#define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1))
+
struct fsg_lun {
struct file *filp;
loff_t file_length;
@@ -112,6 +118,7 @@ struct fsg_lun {
struct device dev;
const char *name; /* "lun.name" */
const char **name_pfx; /* "function.name" */
+ char inquiry_string[INQUIRY_STRING_LEN];
};
static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
@@ -210,6 +217,7 @@ ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
char *buf);
+ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
@@ -221,5 +229,7 @@ ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
size_t count);
+ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
+ size_t count);
#endif /* USB_STORAGE_COMMON_H */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 5f562c1ec795..8cb08033b7c1 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -82,6 +82,7 @@ struct eth_dev {
#define WORK_RX_MEMORY 0
bool zlp;
+ bool no_skb_reserve;
u8 host_mac[ETH_ALEN];
u8 dev_mac[ETH_ALEN];
};
@@ -233,7 +234,8 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
* but on at least one, checksumming fails otherwise. Note:
* RNDIS headers involve variable numbers of LE32 values.
*/
- skb_reserve(skb, NET_IP_ALIGN);
+ if (likely(!dev->no_skb_reserve))
+ skb_reserve(skb, NET_IP_ALIGN);
req->buf = skb->data;
req->length = size;
@@ -551,14 +553,16 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb)
skb = dev->wrap(dev->port_usb, skb);
- spin_unlock_irqrestore(&dev->lock, flags);
if (!skb) {
/* Multi frame CDC protocols may store the frame for
* later which is not a dropped frame.
*/
if (dev->port_usb &&
- dev->port_usb->supports_multi_frame)
+ dev->port_usb->supports_multi_frame) {
+ spin_unlock_irqrestore(&dev->lock, flags);
goto multiframe;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
goto drop;
}
}
@@ -569,12 +573,14 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
req->complete = tx_complete;
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
- if (dev->port_usb->is_fixed &&
+ if (dev->port_usb &&
+ dev->port_usb->is_fixed &&
length == dev->port_usb->fixed_in_len &&
(length % in->maxpacket) == 0)
req->zero = 0;
else
req->zero = 1;
+ spin_unlock_irqrestore(&dev->lock, flags);
/* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding.
@@ -1063,6 +1069,7 @@ struct net_device *gether_connect(struct gether *link)
if (result == 0) {
dev->zlp = link->is_zlp_ok;
+ dev->no_skb_reserve = link->no_skb_reserve;
DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
dev->header_len = link->header_len;
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index c77145bd6b5b..81d94a7ae4b4 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -64,6 +64,7 @@ struct gether {
struct usb_ep *out_ep;
bool is_zlp_ok;
+ bool no_skb_reserve;
u16 cdc_filter;