On Fri, Dec 20 2013, Manu Gautam wrote: > 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: Manu Gautam <mgautam@xxxxxxxxxxxxxx> > --- > drivers/usb/gadget/f_fs.c | 165 ++++++++++++++++++++++++++++-------- > drivers/usb/gadget/u_fs.h | 8 +- > include/uapi/linux/usb/functionfs.h | 5 ++ > 3 files changed, 140 insertions(+), 38 deletions(-) > > diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c > index 306a2b5..65bc861 100644 > --- a/drivers/usb/gadget/f_fs.c > +++ b/drivers/usb/gadget/f_fs.c > @@ -122,8 +122,8 @@ struct ffs_ep { > struct usb_ep *ep; /* P: ffs->eps_lock */ > struct usb_request *req; /* P: epfile->mutex */ > > - /* [0]: full speed, [1]: high speed */ > - struct usb_endpoint_descriptor *descs[2]; > + /* [0]: full speed, [1]: high speed, [2]: super speed */ > + struct usb_endpoint_descriptor *descs[3]; > > u8 num; > > @@ -1184,9 +1184,12 @@ static void ffs_data_reset(struct ffs_data *ffs) > ffs->stringtabs = NULL; > > ffs->raw_descs_length = 0; > - ffs->raw_fs_descs_length = 0; > + ffs->raw_fs_hs_descs_length = 0; > + ffs->raw_ss_descs_offset = 0; > + ffs->raw_ss_descs_length = 0; > ffs->fs_descs_count = 0; > ffs->hs_descs_count = 0; > + ffs->ss_descs_count = 0; > > ffs->strings_count = 0; > ffs->interfaces_count = 0; > @@ -1329,7 +1332,20 @@ static int ffs_func_eps_enable(struct ffs_function *func) > spin_lock_irqsave(&func->ffs->eps_lock, flags); > do { > 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; > + > + ds = ep->descs[desc_idx]; > + if (!ds) { > + ret = -EINVAL; > + break; > + } I don't like this. Why are we failing if descriptors for given speed are missing? If they are, we should fall back to lower speed. do { ds = ep->descs[desc_idx]; } while (!ds && --desc_idx >= 0); if (desc_idx < 0) { ret = -EINVAL; break; } Or something similar. Point is, why aren't we failing dawn to high/low speed if ep->descs[2] is NULL? > > ep->ep->driver_data = ep; > ep->ep->desc = ds; > @@ -1464,6 +1480,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len, > } > 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_INTERFACE_POWER: > case USB_DT_DEBUG: > @@ -1574,8 +1596,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, > static int __ffs_data_got_descs(struct ffs_data *ffs, > char *const _data, size_t len) > { > - unsigned fs_count, hs_count; > - int fs_len, ret = -EINVAL; > + unsigned fs_count, hs_count, ss_count = 0; > + int fs_len, hs_len, ss_len, ss_magic, ret = -EINVAL; > char *data = _data; > > ENTER(); > @@ -1586,9 +1608,6 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, > fs_count = get_unaligned_le32(data + 8); > hs_count = get_unaligned_le32(data + 12); > > - if (!fs_count && !hs_count) > - goto einval; > - > data += 16; > len -= 16; > > @@ -1607,22 +1626,59 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, > } > > 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); > - if (unlikely(ret < 0)) > + if (unlikely(hs_len < 0)) { > + ret = hs_len; > + goto error; > + } data += hs_len; len -= hs_len; > + } else { > + hs_len = 0; > + } > + > + if ((len >= hs_len + 8)) { With the above len -= hs_len, this just becomes “len >= 8”. Nit: too many parenthesise. Having “((…))” makes me think there's assignment inside which there's no. > + /* Check SS_MAGIC for presence of ss_descs and get SS_COUNT */ > + ss_magic = get_unaligned_le32(data + hs_len); > + if (ss_magic != FUNCTIONFS_SS_DESC_MAGIC) > + goto einval; The temporary variable doesn't really serve any purpose here, and with the above “data += hs_len” this becomes: if (get_unaligned_le32(data) != FUNCTIONFS_SS_DESC_MAGIC) goto einval; > + > + ss_count = get_unaligned_le32(data + hs_len + 4); > + data += hs_len + 8; > + len -= hs_len + 8; > + } else { > + data += hs_len; > + len -= hs_len; > + } > + > + 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; > } > > if (unlikely(len != ret)) > goto einval; > > - ffs->raw_fs_descs_length = fs_len; > - ffs->raw_descs_length = fs_len + ret; > - ffs->raw_descs = _data; > - ffs->fs_descs_count = fs_count; > - ffs->hs_descs_count = hs_count; > + ffs->raw_fs_hs_descs_length = fs_len + hs_len; > + ffs->raw_ss_descs_length = ss_len; > + ffs->raw_descs_length = ffs->raw_fs_hs_descs_length + ss_len; Nit: I would keep this consistent in the way that I'd just reference local variables: ffs->raw_descs_length = fs_len + hs_len + ss_len; > + ffs->raw_descs = _data; > + ffs->fs_descs_count = fs_count; > + ffs->hs_descs_count = hs_count; > + ffs->ss_descs_count = ss_count; > + /* ss_descs? present @ header + hs_fs_descs + ss_magic + ss_count */ > + if (ffs->ss_descs_count) > + ffs->raw_ss_descs_offset = 16 + ffs->raw_fs_hs_descs_length + 8; > > return 0; > > @@ -1850,16 +1906,23 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, > * If hs_descriptors is not NULL then we are reading hs > * descriptors now > */ > - const int isHS = func->function.hs_descriptors != NULL; > - unsigned idx; > + const int is_hs = func->function.hs_descriptors != NULL; > + const int is_ss = func->function.ss_descriptors != NULL; > + unsigned ep_desc_id, idx; > > if (type != FFS_DESCRIPTOR) > return 0; > > - if (isHS) > + if (is_ss) { > + func->function.ss_descriptors[(long)valuep] = desc; > + ep_desc_id = 2; > + } else if (is_hs) { > func->function.hs_descriptors[(long)valuep] = desc; > - else > + ep_desc_id = 1; > + } else { > func->function.fs_descriptors[(long)valuep] = desc; > + ep_desc_id = 0; > + } > > if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) > return 0; > @@ -1867,13 +1930,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, > idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; > 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", > - isHS ? "high" : "full", > + is_ss ? "super" : "high/full", is_ss ? "super" : (is_hs "high" : "full"), > ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); > return -EINVAL; > } > - ffs_ep->descs[isHS] = ds; > + ffs_ep->descs[ep_desc_id] = ds; > > ffs_dump_mem(": Original ep desc", ds, ds->bLength); > if (ffs_ep->ep) { > @@ -2017,8 +2080,10 @@ static int _ffs_func_bind(struct usb_configuration *c, > const int full = !!func->ffs->fs_descs_count; > const int high = gadget_is_dualspeed(func->gadget) && > 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 */ > vla_group(d); > @@ -2027,15 +2092,16 @@ static int _ffs_func_bind(struct usb_configuration *c, > full ? ffs->fs_descs_count + 1 : 0); > vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs, > 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, char, raw_descs, > - high ? ffs->raw_descs_length : ffs->raw_fs_descs_length); > + vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); > char *vlabuf; > > ENTER(); > > - /* Only high speed but not supported by gadget? */ > - if (unlikely(!(full | high))) > + /* Only high/super speed but not supported by gadget? */ The comment cloud be improved, e.g.: /* Has descriptions only for speeds gadgets does not support. */ > + if (unlikely(!(full | high | super))) > return -ENOTSUPP; > > /* Allocate a single chunk, less management later on */ > @@ -2045,8 +2111,16 @@ static int _ffs_func_bind(struct usb_configuration *c, > > /* Zero */ > 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, > - d_raw_descs__sz); > + ffs->raw_fs_hs_descs_length); > + /* Copy SS descriptors */ > + if (func->ffs->ss_descs_count) > + memcpy(vla_ptr(vlabuf, d, raw_descs) + > + ffs->raw_fs_hs_descs_length, > + ffs->raw_descs + ffs->raw_ss_descs_offset, > + ffs->raw_ss_descs_length); > + > memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz); > for (ret = ffs->eps_count; ret; --ret) { > struct ffs_ep *ptr; > @@ -2068,33 +2142,51 @@ static int _ffs_func_bind(struct usb_configuration *c, > */ > if (likely(full)) { > 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), > d_raw_descs__sz, > __ffs_func_bind_do_descs, func); Nit: indention of the arguments. > - if (unlikely(ret < 0)) > + if (unlikely(fs_len < 0)) { > + ret = fs_len; > goto error; > + } > } else { > - ret = 0; > + fs_len = 0; > } > > if (likely(high)) { > func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs); > - ret = ffs_do_descs(ffs->hs_descs_count, > - vla_ptr(vlabuf, d, raw_descs) + ret, > - d_raw_descs__sz - ret, > + hs_len = ffs_do_descs(ffs->hs_descs_count, > + vla_ptr(vlabuf, d, raw_descs) + fs_len, > + 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); > if (unlikely(ret < 0)) > goto error; > } > > + > /* > * Now handle interface numbers allocation and interface and > * endpoint numbers rewriting. We can do that in one go > * now. > */ > 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, > __ffs_func_bind_do_nums, func); > if (unlikely(ret < 0)) > @@ -2441,6 +2533,7 @@ static void ffs_func_unbind(struct usb_configuration *c, > */ > func->function.fs_descriptors = NULL; > func->function.hs_descriptors = NULL; > + func->function.ss_descriptors = NULL; > func->interfaces_nums = NULL; > > ffs_event_add(ffs, FUNCTIONFS_UNBIND); > diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h > index bc2d371..1580194 100644 > --- a/drivers/usb/gadget/u_fs.h > +++ b/drivers/usb/gadget/u_fs.h > @@ -213,13 +213,17 @@ struct ffs_data { > * Real descriptors are 16 bytes after raw_descs (so you need > * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the > * first full speed descriptor). raw_descs_length and > - * raw_fs_descs_length do not have those 16 bytes added. > + * raw_fs_hs_descs_length do not have those 16 bytes added. > + * ss_desc are 8 bytes (ss_magic + count) pass the hs_descs > */ > const void *raw_descs; > unsigned raw_descs_length; > - unsigned raw_fs_descs_length; > + unsigned raw_fs_hs_descs_length; > + unsigned raw_ss_descs_offset; > + unsigned raw_ss_descs_length; > unsigned fs_descs_count; > unsigned hs_descs_count; > + unsigned ss_descs_count; > > unsigned short strings_count; > unsigned short interfaces_count; > diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h > index d6b0128..1ab79a2 100644 > --- a/include/uapi/linux/usb/functionfs.h > +++ b/include/uapi/linux/usb/functionfs.h > @@ -13,6 +13,7 @@ enum { > FUNCTIONFS_STRINGS_MAGIC = 2 > }; > > +#define FUNCTIONFS_SS_DESC_MAGIC 0x0055DE5C > > #ifndef __KERNEL__ > > @@ -50,7 +51,11 @@ struct usb_functionfs_descs_head { > * | 12 | hs_count | LE32 | number of high-speed descriptors | > * | 16 | fs_descrs | Descriptor[] | list of full-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: > * > * | off | name | type | description | > -- > The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, > hosted by The Linux Foundation -- Best regards, _ _ .o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o ..o | Computer Science, Michał “mina86” Nazarewicz (o o) ooo +--<mpn@xxxxxxxxxx>--<xmpp:mina86@xxxxxxxxxx>--ooO--(_)--Ooo--
Attachment:
signature.asc
Description: PGP signature