usb: gadget: f_fs: make it support v2 func descp
Hans Yang [Tue, 4 Oct 2016 06:48:35 +0000 (14:48 +0800)]
To support Android N adb via super spped to work,
need to accept V2 function descriptors from user space
drivers; without this change it will crash when adb is
working as superspeed.

Bug 200235473

Change-Id: I06f74880e26dd9c71e4cee2c8034d44a0259d5a1
Signed-off-by: Hans Yang <hansy@nvidia.com>
Reviewed-on: http://git-master/r/1231235
GVS: Gerrit_Virtual_Submit
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
(cherry picked from commit 90105aa07f720c14e175499809351f240b5b6f8e)
Reviewed-on: http://git-master/r/1243574
Reviewed-by: WK Tsai <wtsai@nvidia.com>
Reviewed-by: ChihMin Cheng <ccheng@nvidia.com>
Reviewed-by: Mark Kuo <mkuo@nvidia.com>
Reviewed-by: Vinayak Pane <vpane@nvidia.com>

drivers/usb/gadget/f_fs.c
include/uapi/linux/usb/functionfs.h

index 6ff9745..24e8876 100644 (file)
@@ -2,7 +2,7 @@
  * f_fs.c -- user mode file system API for USB composite function controllers
  *
  * Copyright (C) 2010 Samsung Electronics
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * Author: Michal Nazarewicz <mina86@mina86.com>
  *
@@ -1849,25 +1849,51 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
                                char *const _data, size_t len)
 {
        unsigned fs_count, hs_count, ss_count = 0;
-       int fs_len, hs_len, ss_len, ret = -EINVAL;
+       int fs_len, hs_len, ss_len, ver, ret = -EINVAL;
        char *data = _data;
 
        ENTER();
 
-       if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
-                    get_unaligned_le32(data + 4) != len))
+       if (get_unaligned_le32(data + 4) != len)
                goto error;
-       fs_count = get_unaligned_le32(data +  8);
-       hs_count = get_unaligned_le32(data + 12);
 
-       if (!fs_count && !hs_count)
-               goto einval;
+       ver = get_unaligned_le32(data);
 
-       ffs->raw_descs_length = len;
-       ffs->raw_descs = _data;
+       switch (ver) {
+       case FUNCTIONFS_DESCRIPTORS_MAGIC:
 
-       data += 16;
-       len  -= 16;
+               fs_count = get_unaligned_le32(data +  8);
+               hs_count = get_unaligned_le32(data + 12);
+
+               if (!fs_count && !hs_count)
+                       goto einval;
+
+               ffs->raw_descs_length = len;
+               ffs->raw_descs = _data;
+
+               data += 16;
+               len  -= 16;
+
+               break;
+       case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
+
+               fs_count = get_unaligned_le32(data + 12);
+               hs_count = get_unaligned_le32(data + 16);
+               ss_count = get_unaligned_le32(data + 20);
+
+               if (!fs_count && !hs_count && !ss_count)
+                       goto einval;
+
+               ffs->raw_descs_length = len;
+               ffs->raw_descs = _data;
+
+               data += 28;
+               len  -= 28;
+
+               break;
+       default:
+               goto error;
+       }
 
        if (likely(fs_count)) {
                fs_len = ffs_do_descs(fs_count, data, len,
@@ -1903,8 +1929,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
                hs_len = 0;
        }
 
-       if (len >= 8) {
-               if (get_unaligned_le32(data) != FUNCTIONFS_SS_DESC_MAGIC)
+       if (ver == FUNCTIONFS_DESCRIPTORS_MAGIC && len >= 8) {
+               if (get_unaligned_le32(data) !=
+                               FUNCTIONFS_SS_DESC_MAGIC)
                        goto einval;
 
                ss_count = get_unaligned_le32(data + 4);
@@ -1912,7 +1939,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
                len -= 8;
        }
 
-       if (ss_count) {
+       if (likely(ss_count)) {
                ss_len = ffs_do_descs(ss_count, data, len,
                                   __ffs_data_do_entity, ffs);
                if (unlikely(ss_len < 0)) {
@@ -1928,11 +1955,6 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
                ss_len = 0;
                ret = 0;
        }
-
-
-       if (unlikely(len != ret))
-               goto einval;
-
        return 0;
 
 einval:
index 2157d71..79e089d 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
  */
 
 #ifndef _UAPI__LINUX_FUNCTIONFS_H__
@@ -15,7 +15,8 @@
 
 enum {
        FUNCTIONFS_DESCRIPTORS_MAGIC = 1,
-       FUNCTIONFS_STRINGS_MAGIC     = 2
+       FUNCTIONFS_STRINGS_MAGIC     = 2,
+       FUNCTIONFS_DESCRIPTORS_MAGIC_V2 = 3,
 };
 
 #define FUNCTIONFS_SS_DESC_MAGIC 0x0055DE5C
@@ -48,6 +49,20 @@ struct usb_functionfs_descs_head {
 /*
  * Descriptors format:
  *
+  * | off | name      | type         | description                          |
+ * |-----+-----------+--------------+--------------------------------------|
+ * |   0 | magic     | LE32         | FUNCTIONFS_DESCRIPTORS_MAGIC_V2      |
+ * |   4 | length    | LE32         | length of the whole data chunk       |
+ * |   8 | flags     | LE32         | combination of functionfs_flags      |
+ * |     | fs_count  | LE32         | number of full-speed descriptors     |
+ * |     | hs_count  | LE32         | number of high-speed descriptors     |
+ * |     | ss_count  | LE32         | number of super-speed descriptors    |
+ * |     | os_count  | LE32         | number of MS OS descriptors          |
+ * |     | fs_descrs | Descriptor[] | list of full-speed descriptors       |
+ * |     | hs_descrs | Descriptor[] | list of high-speed descriptors       |
+ * |     | ss_descrs | Descriptor[] | list of super-speed descriptors      |
+ * |     | os_descrs | OSDesc[]     | list of MS OS descriptors            |
+ *
  * | off | name      | type         | description                          |
  * |-----+-----------+--------------+--------------------------------------|
  * |   0 | magic     | LE32         | FUNCTIONFS_{FS,HS}_DESCRIPTORS_MAGIC |