Commit 8d4e897b authored by Manu Gautam's avatar Manu Gautam Committed by Felipe Balbi

usb: gadget: f_fs: Add support for SuperSpeed Mode

Allow userspace to pass SuperSpeed descriptors and
handle them in the driver accordingly.
This change doesn't modify existing desc_header and thereby
keeps the ABI changes backward compatible i.e. existing
userspace drivers compiled with old header (functionfs.h)
would continue to work with the updated kernel.
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
Acked-by: default avatarMichal Nazarewicz <mina86@mina86.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent d8eb6c65
...@@ -134,8 +134,8 @@ struct ffs_ep { ...@@ -134,8 +134,8 @@ struct ffs_ep {
struct usb_ep *ep; /* P: ffs->eps_lock */ struct usb_ep *ep; /* P: ffs->eps_lock */
struct usb_request *req; /* P: epfile->mutex */ struct usb_request *req; /* P: epfile->mutex */
/* [0]: full speed, [1]: high speed */ /* [0]: full speed, [1]: high speed, [2]: super speed */
struct usb_endpoint_descriptor *descs[2]; struct usb_endpoint_descriptor *descs[3];
u8 num; u8 num;
...@@ -1450,10 +1450,11 @@ static void ffs_data_reset(struct ffs_data *ffs) ...@@ -1450,10 +1450,11 @@ static void ffs_data_reset(struct ffs_data *ffs)
ffs->raw_strings = NULL; ffs->raw_strings = NULL;
ffs->stringtabs = NULL; ffs->stringtabs = NULL;
ffs->raw_descs_length = 0; ffs->raw_fs_hs_descs_length = 0;
ffs->raw_fs_descs_length = 0; ffs->raw_ss_descs_length = 0;
ffs->fs_descs_count = 0; ffs->fs_descs_count = 0;
ffs->hs_descs_count = 0; ffs->hs_descs_count = 0;
ffs->ss_descs_count = 0;
ffs->strings_count = 0; ffs->strings_count = 0;
ffs->interfaces_count = 0; ffs->interfaces_count = 0;
...@@ -1596,7 +1597,24 @@ static int ffs_func_eps_enable(struct ffs_function *func) ...@@ -1596,7 +1597,24 @@ static int ffs_func_eps_enable(struct ffs_function *func)
spin_lock_irqsave(&func->ffs->eps_lock, flags); spin_lock_irqsave(&func->ffs->eps_lock, flags);
do { do {
struct usb_endpoint_descriptor *ds; struct usb_endpoint_descriptor *ds;
ds = ep->descs[ep->descs[1] ? 1 : 0]; int desc_idx;
if (ffs->gadget->speed == USB_SPEED_SUPER)
desc_idx = 2;
else if (ffs->gadget->speed == USB_SPEED_HIGH)
desc_idx = 1;
else
desc_idx = 0;
/* fall-back to lower speed if desc missing for current speed */
do {
ds = ep->descs[desc_idx];
} while (!ds && --desc_idx >= 0);
if (!ds) {
ret = -EINVAL;
break;
}
ep->ep->driver_data = ep; ep->ep->driver_data = ep;
ep->ep->desc = ds; ep->ep->desc = ds;
...@@ -1731,6 +1749,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len, ...@@ -1731,6 +1749,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
} }
break; break;
case USB_DT_SS_ENDPOINT_COMP:
pr_vdebug("EP SS companion descriptor\n");
if (length != sizeof(struct usb_ss_ep_comp_descriptor))
goto inv_length;
break;
case USB_DT_OTHER_SPEED_CONFIG: case USB_DT_OTHER_SPEED_CONFIG:
case USB_DT_INTERFACE_POWER: case USB_DT_INTERFACE_POWER:
case USB_DT_DEBUG: case USB_DT_DEBUG:
...@@ -1841,8 +1865,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, ...@@ -1841,8 +1865,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
static int __ffs_data_got_descs(struct ffs_data *ffs, static int __ffs_data_got_descs(struct ffs_data *ffs,
char *const _data, size_t len) char *const _data, size_t len)
{ {
unsigned fs_count, hs_count; unsigned fs_count, hs_count, ss_count = 0;
int fs_len, ret = -EINVAL; int fs_len, hs_len, ss_len, ret = -EINVAL;
char *data = _data; char *data = _data;
ENTER(); ENTER();
...@@ -1853,9 +1877,6 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, ...@@ -1853,9 +1877,6 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
fs_count = get_unaligned_le32(data + 8); fs_count = get_unaligned_le32(data + 8);
hs_count = get_unaligned_le32(data + 12); hs_count = get_unaligned_le32(data + 12);
if (!fs_count && !hs_count)
goto einval;
data += 16; data += 16;
len -= 16; len -= 16;
...@@ -1874,22 +1895,54 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, ...@@ -1874,22 +1895,54 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
} }
if (likely(hs_count)) { if (likely(hs_count)) {
ret = ffs_do_descs(hs_count, data, len, hs_len = ffs_do_descs(hs_count, data, len,
__ffs_data_do_entity, ffs); __ffs_data_do_entity, ffs);
if (unlikely(ret < 0)) if (unlikely(hs_len < 0)) {
ret = hs_len;
goto error; goto error;
}
data += hs_len;
len -= hs_len;
} else { } else {
hs_len = 0;
}
if (len >= 8) {
/* Check SS_MAGIC for presence of ss_descs and get SS_COUNT */
if (get_unaligned_le32(data) != FUNCTIONFS_SS_DESC_MAGIC)
goto einval;
ss_count = get_unaligned_le32(data + 4);
data += 8;
len -= 8;
}
if (!fs_count && !hs_count && !ss_count)
goto einval;
if (ss_count) {
ss_len = ffs_do_descs(ss_count, data, len,
__ffs_data_do_entity, ffs);
if (unlikely(ss_len < 0)) {
ret = ss_len;
goto error;
}
ret = ss_len;
} else {
ss_len = 0;
ret = 0; ret = 0;
} }
if (unlikely(len != ret)) if (unlikely(len != ret))
goto einval; goto einval;
ffs->raw_fs_descs_length = fs_len; ffs->raw_fs_hs_descs_length = fs_len + hs_len;
ffs->raw_descs_length = fs_len + ret; ffs->raw_ss_descs_length = ss_len;
ffs->raw_descs = _data; ffs->raw_descs = _data;
ffs->fs_descs_count = fs_count; ffs->fs_descs_count = fs_count;
ffs->hs_descs_count = hs_count; ffs->hs_descs_count = hs_count;
ffs->ss_descs_count = ss_count;
return 0; return 0;
...@@ -2112,21 +2165,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, ...@@ -2112,21 +2165,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct usb_endpoint_descriptor *ds = (void *)desc; struct usb_endpoint_descriptor *ds = (void *)desc;
struct ffs_function *func = priv; struct ffs_function *func = priv;
struct ffs_ep *ffs_ep; struct ffs_ep *ffs_ep;
unsigned ep_desc_id, idx;
/* static const char *speed_names[] = { "full", "high", "super" };
* If hs_descriptors is not NULL then we are reading hs
* descriptors now
*/
const int isHS = func->function.hs_descriptors != NULL;
unsigned idx;
if (type != FFS_DESCRIPTOR) if (type != FFS_DESCRIPTOR)
return 0; return 0;
if (isHS) /*
* If ss_descriptors is not NULL, we are reading super speed
* descriptors; if hs_descriptors is not NULL, we are reading high
* speed descriptors; otherwise, we are reading full speed
* descriptors.
*/
if (func->function.ss_descriptors) {
ep_desc_id = 2;
func->function.ss_descriptors[(long)valuep] = desc;
} else if (func->function.hs_descriptors) {
ep_desc_id = 1;
func->function.hs_descriptors[(long)valuep] = desc; func->function.hs_descriptors[(long)valuep] = desc;
else } else {
ep_desc_id = 0;
func->function.fs_descriptors[(long)valuep] = desc; func->function.fs_descriptors[(long)valuep] = desc;
}
if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
return 0; return 0;
...@@ -2134,13 +2194,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, ...@@ -2134,13 +2194,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
ffs_ep = func->eps + idx; ffs_ep = func->eps + idx;
if (unlikely(ffs_ep->descs[isHS])) { if (unlikely(ffs_ep->descs[ep_desc_id])) {
pr_vdebug("two %sspeed descriptors for EP %d\n", pr_err("two %sspeed descriptors for EP %d\n",
isHS ? "high" : "full", speed_names[ep_desc_id],
ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
return -EINVAL; return -EINVAL;
} }
ffs_ep->descs[isHS] = ds; ffs_ep->descs[ep_desc_id] = ds;
ffs_dump_mem(": Original ep desc", ds, ds->bLength); ffs_dump_mem(": Original ep desc", ds, ds->bLength);
if (ffs_ep->ep) { if (ffs_ep->ep) {
...@@ -2284,8 +2344,10 @@ static int _ffs_func_bind(struct usb_configuration *c, ...@@ -2284,8 +2344,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
const int full = !!func->ffs->fs_descs_count; const int full = !!func->ffs->fs_descs_count;
const int high = gadget_is_dualspeed(func->gadget) && const int high = gadget_is_dualspeed(func->gadget) &&
func->ffs->hs_descs_count; func->ffs->hs_descs_count;
const int super = gadget_is_superspeed(func->gadget) &&
func->ffs->ss_descs_count;
int ret; int fs_len, hs_len, ret;
/* Make it a single chunk, less management later on */ /* Make it a single chunk, less management later on */
vla_group(d); vla_group(d);
...@@ -2294,15 +2356,17 @@ static int _ffs_func_bind(struct usb_configuration *c, ...@@ -2294,15 +2356,17 @@ static int _ffs_func_bind(struct usb_configuration *c,
full ? ffs->fs_descs_count + 1 : 0); full ? ffs->fs_descs_count + 1 : 0);
vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs, vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
high ? ffs->hs_descs_count + 1 : 0); high ? ffs->hs_descs_count + 1 : 0);
vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
super ? ffs->ss_descs_count + 1 : 0);
vla_item_with_sz(d, short, inums, ffs->interfaces_count); vla_item_with_sz(d, short, inums, ffs->interfaces_count);
vla_item_with_sz(d, char, raw_descs, vla_item_with_sz(d, char, raw_descs,
high ? ffs->raw_descs_length : ffs->raw_fs_descs_length); ffs->raw_fs_hs_descs_length + ffs->raw_ss_descs_length);
char *vlabuf; char *vlabuf;
ENTER(); ENTER();
/* Only high speed but not supported by gadget? */ /* Has descriptors only for speeds gadget does not support */
if (unlikely(!(full | high))) if (unlikely(!(full | high | super)))
return -ENOTSUPP; return -ENOTSUPP;
/* Allocate a single chunk, less management later on */ /* Allocate a single chunk, less management later on */
...@@ -2312,8 +2376,16 @@ static int _ffs_func_bind(struct usb_configuration *c, ...@@ -2312,8 +2376,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
/* Zero */ /* Zero */
memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
/* Copy only raw (hs,fs) descriptors (until ss_magic and ss_count) */
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16, memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16,
d_raw_descs__sz); ffs->raw_fs_hs_descs_length);
/* Copy SS descs present @ header + hs_fs_descs + ss_magic + ss_count */
if (func->ffs->ss_descs_count)
memcpy(vla_ptr(vlabuf, d, raw_descs) +
ffs->raw_fs_hs_descs_length,
ffs->raw_descs + 16 + ffs->raw_fs_hs_descs_length + 8,
ffs->raw_ss_descs_length);
memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz); memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
for (ret = ffs->eps_count; ret; --ret) { for (ret = ffs->eps_count; ret; --ret) {
struct ffs_ep *ptr; struct ffs_ep *ptr;
...@@ -2335,21 +2407,37 @@ static int _ffs_func_bind(struct usb_configuration *c, ...@@ -2335,21 +2407,37 @@ static int _ffs_func_bind(struct usb_configuration *c,
*/ */
if (likely(full)) { if (likely(full)) {
func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs); func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
ret = ffs_do_descs(ffs->fs_descs_count, fs_len = ffs_do_descs(ffs->fs_descs_count,
vla_ptr(vlabuf, d, raw_descs), vla_ptr(vlabuf, d, raw_descs),
d_raw_descs__sz, d_raw_descs__sz,
__ffs_func_bind_do_descs, func); __ffs_func_bind_do_descs, func);
if (unlikely(ret < 0)) if (unlikely(fs_len < 0)) {
ret = fs_len;
goto error; goto error;
}
} else { } else {
ret = 0; fs_len = 0;
} }
if (likely(high)) { if (likely(high)) {
func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs); func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
ret = ffs_do_descs(ffs->hs_descs_count, hs_len = ffs_do_descs(ffs->hs_descs_count,
vla_ptr(vlabuf, d, raw_descs) + ret, vla_ptr(vlabuf, d, raw_descs) + fs_len,
d_raw_descs__sz - ret, d_raw_descs__sz - fs_len,
__ffs_func_bind_do_descs, func);
if (unlikely(hs_len < 0)) {
ret = hs_len;
goto error;
}
} else {
hs_len = 0;
}
if (likely(super)) {
func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
ret = ffs_do_descs(ffs->ss_descs_count,
vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
d_raw_descs__sz - fs_len - hs_len,
__ffs_func_bind_do_descs, func); __ffs_func_bind_do_descs, func);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
goto error; goto error;
...@@ -2361,7 +2449,8 @@ static int _ffs_func_bind(struct usb_configuration *c, ...@@ -2361,7 +2449,8 @@ static int _ffs_func_bind(struct usb_configuration *c,
* now. * now.
*/ */
ret = ffs_do_descs(ffs->fs_descs_count + ret = ffs_do_descs(ffs->fs_descs_count +
(high ? ffs->hs_descs_count : 0), (high ? ffs->hs_descs_count : 0) +
(super ? ffs->ss_descs_count : 0),
vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz, vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
__ffs_func_bind_do_nums, func); __ffs_func_bind_do_nums, func);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
...@@ -2708,6 +2797,7 @@ static void ffs_func_unbind(struct usb_configuration *c, ...@@ -2708,6 +2797,7 @@ static void ffs_func_unbind(struct usb_configuration *c,
*/ */
func->function.fs_descriptors = NULL; func->function.fs_descriptors = NULL;
func->function.hs_descriptors = NULL; func->function.hs_descriptors = NULL;
func->function.ss_descriptors = NULL;
func->interfaces_nums = NULL; func->interfaces_nums = NULL;
ffs_event_add(ffs, FUNCTIONFS_UNBIND); ffs_event_add(ffs, FUNCTIONFS_UNBIND);
......
...@@ -208,14 +208,16 @@ struct ffs_data { ...@@ -208,14 +208,16 @@ struct ffs_data {
/* /*
* Real descriptors are 16 bytes after raw_descs (so you need * Real descriptors are 16 bytes after raw_descs (so you need
* to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
* first full speed descriptor). raw_descs_length and * first full speed descriptor).
* raw_fs_descs_length do not have those 16 bytes added. * raw_fs_hs_descs_length does not have those 16 bytes added.
* ss_descs are 8 bytes (ss_magic + count) pass the hs_descs
*/ */
const void *raw_descs; const void *raw_descs;
unsigned raw_descs_length; unsigned raw_fs_hs_descs_length;
unsigned raw_fs_descs_length; unsigned raw_ss_descs_length;
unsigned fs_descs_count; unsigned fs_descs_count;
unsigned hs_descs_count; unsigned hs_descs_count;
unsigned ss_descs_count;
unsigned short strings_count; unsigned short strings_count;
unsigned short interfaces_count; unsigned short interfaces_count;
......
...@@ -13,6 +13,7 @@ enum { ...@@ -13,6 +13,7 @@ enum {
FUNCTIONFS_STRINGS_MAGIC = 2 FUNCTIONFS_STRINGS_MAGIC = 2
}; };
#define FUNCTIONFS_SS_DESC_MAGIC 0x0055DE5C
#ifndef __KERNEL__ #ifndef __KERNEL__
...@@ -50,7 +51,11 @@ struct usb_functionfs_descs_head { ...@@ -50,7 +51,11 @@ struct usb_functionfs_descs_head {
* | 12 | hs_count | LE32 | number of high-speed descriptors | * | 12 | hs_count | LE32 | number of high-speed descriptors |
* | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors | * | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors |
* | | hs_descrs | Descriptor[] | list of high-speed descriptors | * | | hs_descrs | Descriptor[] | list of high-speed descriptors |
* | | ss_magic | LE32 | FUNCTIONFS_SS_DESC_MAGIC |
* | | ss_count | LE32 | number of super-speed descriptors |
* | | ss_descrs | Descriptor[] | list of super-speed descriptors |
* *
* ss_magic: if present then it implies that SS_DESCs are also present
* descs are just valid USB descriptors and have the following format: * descs are just valid USB descriptors and have the following format:
* *
* | off | name | type | description | * | off | name | type | description |
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment