diff options
author | Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 2014-07-09 06:20:08 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-07-10 09:36:52 -0400 |
commit | f0175ab51993d2dc2728e7b22a16ffb0c8f4cfa0 (patch) | |
tree | 03931c91c52bcccf6ea95ffb382c45a72839c281 | |
parent | 7ea4f088c810dab3ba3ab4c7a3879238f790e1fd (diff) |
usb: gadget: f_fs: OS descriptors support
Add support for OS descriptors. The new format of descriptors is used,
because the "flags" field is required for extensions. os_count gives
the number of OSDesc[] elements.
The format of descriptors is given in include/uapi/linux/usb/functionfs.h.
For extended properties descriptor the usb_ext_prop_desc structure covers
only a part of a descriptor, because the wPropertyNameLength is unknown
up front.
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r-- | drivers/usb/gadget/f_fs.c | 341 | ||||
-rw-r--r-- | drivers/usb/gadget/u_fs.h | 7 | ||||
-rw-r--r-- | include/uapi/linux/usb/functionfs.h | 81 |
3 files changed, 419 insertions, 10 deletions
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index e1b2ddd7964a..fe45060e0a7a 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c | |||
@@ -34,6 +34,7 @@ | |||
34 | 34 | ||
35 | #include "u_fs.h" | 35 | #include "u_fs.h" |
36 | #include "u_f.h" | 36 | #include "u_f.h" |
37 | #include "u_os_desc.h" | ||
37 | #include "configfs.h" | 38 | #include "configfs.h" |
38 | 39 | ||
39 | #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ | 40 | #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ |
@@ -1644,11 +1645,19 @@ enum ffs_entity_type { | |||
1644 | FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT | 1645 | FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT |
1645 | }; | 1646 | }; |
1646 | 1647 | ||
1648 | enum ffs_os_desc_type { | ||
1649 | FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP | ||
1650 | }; | ||
1651 | |||
1647 | typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, | 1652 | typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, |
1648 | u8 *valuep, | 1653 | u8 *valuep, |
1649 | struct usb_descriptor_header *desc, | 1654 | struct usb_descriptor_header *desc, |
1650 | void *priv); | 1655 | void *priv); |
1651 | 1656 | ||
1657 | typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, | ||
1658 | struct usb_os_desc_header *h, void *data, | ||
1659 | unsigned len, void *priv); | ||
1660 | |||
1652 | static int __must_check ffs_do_single_desc(char *data, unsigned len, | 1661 | static int __must_check ffs_do_single_desc(char *data, unsigned len, |
1653 | ffs_entity_callback entity, | 1662 | ffs_entity_callback entity, |
1654 | void *priv) | 1663 | void *priv) |
@@ -1856,11 +1865,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, | |||
1856 | return 0; | 1865 | return 0; |
1857 | } | 1866 | } |
1858 | 1867 | ||
1868 | static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type, | ||
1869 | struct usb_os_desc_header *desc) | ||
1870 | { | ||
1871 | u16 bcd_version = le16_to_cpu(desc->bcdVersion); | ||
1872 | u16 w_index = le16_to_cpu(desc->wIndex); | ||
1873 | |||
1874 | if (bcd_version != 1) { | ||
1875 | pr_vdebug("unsupported os descriptors version: %d", | ||
1876 | bcd_version); | ||
1877 | return -EINVAL; | ||
1878 | } | ||
1879 | switch (w_index) { | ||
1880 | case 0x4: | ||
1881 | *next_type = FFS_OS_DESC_EXT_COMPAT; | ||
1882 | break; | ||
1883 | case 0x5: | ||
1884 | *next_type = FFS_OS_DESC_EXT_PROP; | ||
1885 | break; | ||
1886 | default: | ||
1887 | pr_vdebug("unsupported os descriptor type: %d", w_index); | ||
1888 | return -EINVAL; | ||
1889 | } | ||
1890 | |||
1891 | return sizeof(*desc); | ||
1892 | } | ||
1893 | |||
1894 | /* | ||
1895 | * Process all extended compatibility/extended property descriptors | ||
1896 | * of a feature descriptor | ||
1897 | */ | ||
1898 | static int __must_check ffs_do_single_os_desc(char *data, unsigned len, | ||
1899 | enum ffs_os_desc_type type, | ||
1900 | u16 feature_count, | ||
1901 | ffs_os_desc_callback entity, | ||
1902 | void *priv, | ||
1903 | struct usb_os_desc_header *h) | ||
1904 | { | ||
1905 | int ret; | ||
1906 | const unsigned _len = len; | ||
1907 | |||
1908 | ENTER(); | ||
1909 | |||
1910 | /* loop over all ext compat/ext prop descriptors */ | ||
1911 | while (feature_count--) { | ||
1912 | ret = entity(type, h, data, len, priv); | ||
1913 | if (unlikely(ret < 0)) { | ||
1914 | pr_debug("bad OS descriptor, type: %d\n", type); | ||
1915 | return ret; | ||
1916 | } | ||
1917 | data += ret; | ||
1918 | len -= ret; | ||
1919 | } | ||
1920 | return _len - len; | ||
1921 | } | ||
1922 | |||
1923 | /* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */ | ||
1924 | static int __must_check ffs_do_os_descs(unsigned count, | ||
1925 | char *data, unsigned len, | ||
1926 | ffs_os_desc_callback entity, void *priv) | ||
1927 | { | ||
1928 | const unsigned _len = len; | ||
1929 | unsigned long num = 0; | ||
1930 | |||
1931 | ENTER(); | ||
1932 | |||
1933 | for (num = 0; num < count; ++num) { | ||
1934 | int ret; | ||
1935 | enum ffs_os_desc_type type; | ||
1936 | u16 feature_count; | ||
1937 | struct usb_os_desc_header *desc = (void *)data; | ||
1938 | |||
1939 | if (len < sizeof(*desc)) | ||
1940 | return -EINVAL; | ||
1941 | |||
1942 | /* | ||
1943 | * Record "descriptor" entity. | ||
1944 | * Process dwLength, bcdVersion, wIndex, get b/wCount. | ||
1945 | * Move the data pointer to the beginning of extended | ||
1946 | * compatibilities proper or extended properties proper | ||
1947 | * portions of the data | ||
1948 | */ | ||
1949 | if (le32_to_cpu(desc->dwLength) > len) | ||
1950 | return -EINVAL; | ||
1951 | |||
1952 | ret = __ffs_do_os_desc_header(&type, desc); | ||
1953 | if (unlikely(ret < 0)) { | ||
1954 | pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n", | ||
1955 | num, ret); | ||
1956 | return ret; | ||
1957 | } | ||
1958 | /* | ||
1959 | * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??" | ||
1960 | */ | ||
1961 | feature_count = le16_to_cpu(desc->wCount); | ||
1962 | if (type == FFS_OS_DESC_EXT_COMPAT && | ||
1963 | (feature_count > 255 || desc->Reserved)) | ||
1964 | return -EINVAL; | ||
1965 | len -= ret; | ||
1966 | data += ret; | ||
1967 | |||
1968 | /* | ||
1969 | * Process all function/property descriptors | ||
1970 | * of this Feature Descriptor | ||
1971 | */ | ||
1972 | ret = ffs_do_single_os_desc(data, len, type, | ||
1973 | feature_count, entity, priv, desc); | ||
1974 | if (unlikely(ret < 0)) { | ||
1975 | pr_debug("%s returns %d\n", __func__, ret); | ||
1976 | return ret; | ||
1977 | } | ||
1978 | |||
1979 | len -= ret; | ||
1980 | data += ret; | ||
1981 | } | ||
1982 | return _len - len; | ||
1983 | } | ||
1984 | |||
1985 | /** | ||
1986 | * Validate contents of the buffer from userspace related to OS descriptors. | ||
1987 | */ | ||
1988 | static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, | ||
1989 | struct usb_os_desc_header *h, void *data, | ||
1990 | unsigned len, void *priv) | ||
1991 | { | ||
1992 | struct ffs_data *ffs = priv; | ||
1993 | u8 length; | ||
1994 | |||
1995 | ENTER(); | ||
1996 | |||
1997 | switch (type) { | ||
1998 | case FFS_OS_DESC_EXT_COMPAT: { | ||
1999 | struct usb_ext_compat_desc *d = data; | ||
2000 | int i; | ||
2001 | |||
2002 | if (len < sizeof(*d) || | ||
2003 | d->bFirstInterfaceNumber >= ffs->interfaces_count || | ||
2004 | d->Reserved1) | ||
2005 | return -EINVAL; | ||
2006 | for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i) | ||
2007 | if (d->Reserved2[i]) | ||
2008 | return -EINVAL; | ||
2009 | |||
2010 | length = sizeof(struct usb_ext_compat_desc); | ||
2011 | } | ||
2012 | break; | ||
2013 | case FFS_OS_DESC_EXT_PROP: { | ||
2014 | struct usb_ext_prop_desc *d = data; | ||
2015 | u32 type, pdl; | ||
2016 | u16 pnl; | ||
2017 | |||
2018 | if (len < sizeof(*d) || h->interface >= ffs->interfaces_count) | ||
2019 | return -EINVAL; | ||
2020 | length = le32_to_cpu(d->dwSize); | ||
2021 | type = le32_to_cpu(d->dwPropertyDataType); | ||
2022 | if (type < USB_EXT_PROP_UNICODE || | ||
2023 | type > USB_EXT_PROP_UNICODE_MULTI) { | ||
2024 | pr_vdebug("unsupported os descriptor property type: %d", | ||
2025 | type); | ||
2026 | return -EINVAL; | ||
2027 | } | ||
2028 | pnl = le16_to_cpu(d->wPropertyNameLength); | ||
2029 | pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); | ||
2030 | if (length != 14 + pnl + pdl) { | ||
2031 | pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", | ||
2032 | length, pnl, pdl, type); | ||
2033 | return -EINVAL; | ||
2034 | } | ||
2035 | ++ffs->ms_os_descs_ext_prop_count; | ||
2036 | /* property name reported to the host as "WCHAR"s */ | ||
2037 | ffs->ms_os_descs_ext_prop_name_len += pnl * 2; | ||
2038 | ffs->ms_os_descs_ext_prop_data_len += pdl; | ||
2039 | } | ||
2040 | break; | ||
2041 | default: | ||
2042 | pr_vdebug("unknown descriptor: %d\n", type); | ||
2043 | return -EINVAL; | ||
2044 | } | ||
2045 | return length; | ||
2046 | } | ||
2047 | |||
1859 | static int __ffs_data_got_descs(struct ffs_data *ffs, | 2048 | static int __ffs_data_got_descs(struct ffs_data *ffs, |
1860 | char *const _data, size_t len) | 2049 | char *const _data, size_t len) |
1861 | { | 2050 | { |
1862 | char *data = _data, *raw_descs; | 2051 | char *data = _data, *raw_descs; |
1863 | unsigned counts[3], flags; | 2052 | unsigned os_descs_count = 0, counts[3], flags; |
1864 | int ret = -EINVAL, i; | 2053 | int ret = -EINVAL, i; |
1865 | 2054 | ||
1866 | ENTER(); | 2055 | ENTER(); |
@@ -1878,7 +2067,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, | |||
1878 | flags = get_unaligned_le32(data + 8); | 2067 | flags = get_unaligned_le32(data + 8); |
1879 | if (flags & ~(FUNCTIONFS_HAS_FS_DESC | | 2068 | if (flags & ~(FUNCTIONFS_HAS_FS_DESC | |
1880 | FUNCTIONFS_HAS_HS_DESC | | 2069 | FUNCTIONFS_HAS_HS_DESC | |
1881 | FUNCTIONFS_HAS_SS_DESC)) { | 2070 | FUNCTIONFS_HAS_SS_DESC | |
2071 | FUNCTIONFS_HAS_MS_OS_DESC)) { | ||
1882 | ret = -ENOSYS; | 2072 | ret = -ENOSYS; |
1883 | goto error; | 2073 | goto error; |
1884 | } | 2074 | } |
@@ -1901,6 +2091,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, | |||
1901 | len -= 4; | 2091 | len -= 4; |
1902 | } | 2092 | } |
1903 | } | 2093 | } |
2094 | if (flags & (1 << i)) { | ||
2095 | os_descs_count = get_unaligned_le32(data); | ||
2096 | data += 4; | ||
2097 | len -= 4; | ||
2098 | }; | ||
1904 | 2099 | ||
1905 | /* Read descriptors */ | 2100 | /* Read descriptors */ |
1906 | raw_descs = data; | 2101 | raw_descs = data; |
@@ -1914,6 +2109,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, | |||
1914 | data += ret; | 2109 | data += ret; |
1915 | len -= ret; | 2110 | len -= ret; |
1916 | } | 2111 | } |
2112 | if (os_descs_count) { | ||
2113 | ret = ffs_do_os_descs(os_descs_count, data, len, | ||
2114 | __ffs_data_do_os_desc, ffs); | ||
2115 | if (ret < 0) | ||
2116 | goto error; | ||
2117 | data += ret; | ||
2118 | len -= ret; | ||
2119 | } | ||
1917 | 2120 | ||
1918 | if (raw_descs == data || len) { | 2121 | if (raw_descs == data || len) { |
1919 | ret = -EINVAL; | 2122 | ret = -EINVAL; |
@@ -1926,6 +2129,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, | |||
1926 | ffs->fs_descs_count = counts[0]; | 2129 | ffs->fs_descs_count = counts[0]; |
1927 | ffs->hs_descs_count = counts[1]; | 2130 | ffs->hs_descs_count = counts[1]; |
1928 | ffs->ss_descs_count = counts[2]; | 2131 | ffs->ss_descs_count = counts[2]; |
2132 | ffs->ms_os_descs_count = os_descs_count; | ||
1929 | 2133 | ||
1930 | return 0; | 2134 | return 0; |
1931 | 2135 | ||
@@ -2267,6 +2471,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, | |||
2267 | return 0; | 2471 | return 0; |
2268 | } | 2472 | } |
2269 | 2473 | ||
2474 | static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, | ||
2475 | struct usb_os_desc_header *h, void *data, | ||
2476 | unsigned len, void *priv) | ||
2477 | { | ||
2478 | struct ffs_function *func = priv; | ||
2479 | u8 length = 0; | ||
2480 | |||
2481 | switch (type) { | ||
2482 | case FFS_OS_DESC_EXT_COMPAT: { | ||
2483 | struct usb_ext_compat_desc *desc = data; | ||
2484 | struct usb_os_desc_table *t; | ||
2485 | |||
2486 | t = &func->function.os_desc_table[desc->bFirstInterfaceNumber]; | ||
2487 | t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber]; | ||
2488 | memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID, | ||
2489 | ARRAY_SIZE(desc->CompatibleID) + | ||
2490 | ARRAY_SIZE(desc->SubCompatibleID)); | ||
2491 | length = sizeof(*desc); | ||
2492 | } | ||
2493 | break; | ||
2494 | case FFS_OS_DESC_EXT_PROP: { | ||
2495 | struct usb_ext_prop_desc *desc = data; | ||
2496 | struct usb_os_desc_table *t; | ||
2497 | struct usb_os_desc_ext_prop *ext_prop; | ||
2498 | char *ext_prop_name; | ||
2499 | char *ext_prop_data; | ||
2500 | |||
2501 | t = &func->function.os_desc_table[h->interface]; | ||
2502 | t->if_id = func->interfaces_nums[h->interface]; | ||
2503 | |||
2504 | ext_prop = func->ffs->ms_os_descs_ext_prop_avail; | ||
2505 | func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop); | ||
2506 | |||
2507 | ext_prop->type = le32_to_cpu(desc->dwPropertyDataType); | ||
2508 | ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength); | ||
2509 | ext_prop->data_len = le32_to_cpu(*(u32 *) | ||
2510 | usb_ext_prop_data_len_ptr(data, ext_prop->name_len)); | ||
2511 | length = ext_prop->name_len + ext_prop->data_len + 14; | ||
2512 | |||
2513 | ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail; | ||
2514 | func->ffs->ms_os_descs_ext_prop_name_avail += | ||
2515 | ext_prop->name_len; | ||
2516 | |||
2517 | ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail; | ||
2518 | func->ffs->ms_os_descs_ext_prop_data_avail += | ||
2519 | ext_prop->data_len; | ||
2520 | memcpy(ext_prop_data, | ||
2521 | usb_ext_prop_data_ptr(data, ext_prop->name_len), | ||
2522 | ext_prop->data_len); | ||
2523 | /* unicode data reported to the host as "WCHAR"s */ | ||
2524 | switch (ext_prop->type) { | ||
2525 | case USB_EXT_PROP_UNICODE: | ||
2526 | case USB_EXT_PROP_UNICODE_ENV: | ||
2527 | case USB_EXT_PROP_UNICODE_LINK: | ||
2528 | case USB_EXT_PROP_UNICODE_MULTI: | ||
2529 | ext_prop->data_len *= 2; | ||
2530 | break; | ||
2531 | } | ||
2532 | ext_prop->data = ext_prop_data; | ||
2533 | |||
2534 | memcpy(ext_prop_name, usb_ext_prop_name_ptr(data), | ||
2535 | ext_prop->name_len); | ||
2536 | /* property name reported to the host as "WCHAR"s */ | ||
2537 | ext_prop->name_len *= 2; | ||
2538 | ext_prop->name = ext_prop_name; | ||
2539 | |||
2540 | t->os_desc->ext_prop_len += | ||
2541 | ext_prop->name_len + ext_prop->data_len + 14; | ||
2542 | ++t->os_desc->ext_prop_count; | ||
2543 | list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop); | ||
2544 | } | ||
2545 | break; | ||
2546 | default: | ||
2547 | pr_vdebug("unknown descriptor: %d\n", type); | ||
2548 | } | ||
2549 | |||
2550 | return length; | ||
2551 | } | ||
2552 | |||
2270 | static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, | 2553 | static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, |
2271 | struct usb_configuration *c) | 2554 | struct usb_configuration *c) |
2272 | { | 2555 | { |
@@ -2328,7 +2611,7 @@ static int _ffs_func_bind(struct usb_configuration *c, | |||
2328 | const int super = gadget_is_superspeed(func->gadget) && | 2611 | const int super = gadget_is_superspeed(func->gadget) && |
2329 | func->ffs->ss_descs_count; | 2612 | func->ffs->ss_descs_count; |
2330 | 2613 | ||
2331 | int fs_len, hs_len, ret; | 2614 | int fs_len, hs_len, ss_len, ret, i; |
2332 | 2615 | ||
2333 | /* Make it a single chunk, less management later on */ | 2616 | /* Make it a single chunk, less management later on */ |
2334 | vla_group(d); | 2617 | vla_group(d); |
@@ -2340,6 +2623,18 @@ static int _ffs_func_bind(struct usb_configuration *c, | |||
2340 | vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, | 2623 | vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, |
2341 | super ? ffs->ss_descs_count + 1 : 0); | 2624 | super ? ffs->ss_descs_count + 1 : 0); |
2342 | vla_item_with_sz(d, short, inums, ffs->interfaces_count); | 2625 | vla_item_with_sz(d, short, inums, ffs->interfaces_count); |
2626 | vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table, | ||
2627 | c->cdev->use_os_string ? ffs->interfaces_count : 0); | ||
2628 | vla_item_with_sz(d, char[16], ext_compat, | ||
2629 | c->cdev->use_os_string ? ffs->interfaces_count : 0); | ||
2630 | vla_item_with_sz(d, struct usb_os_desc, os_desc, | ||
2631 | c->cdev->use_os_string ? ffs->interfaces_count : 0); | ||
2632 | vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop, | ||
2633 | ffs->ms_os_descs_ext_prop_count); | ||
2634 | vla_item_with_sz(d, char, ext_prop_name, | ||
2635 | ffs->ms_os_descs_ext_prop_name_len); | ||
2636 | vla_item_with_sz(d, char, ext_prop_data, | ||
2637 | ffs->ms_os_descs_ext_prop_data_len); | ||
2343 | vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); | 2638 | vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); |
2344 | char *vlabuf; | 2639 | char *vlabuf; |
2345 | 2640 | ||
@@ -2350,12 +2645,16 @@ static int _ffs_func_bind(struct usb_configuration *c, | |||
2350 | return -ENOTSUPP; | 2645 | return -ENOTSUPP; |
2351 | 2646 | ||
2352 | /* Allocate a single chunk, less management later on */ | 2647 | /* Allocate a single chunk, less management later on */ |
2353 | vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); | 2648 | vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL); |
2354 | if (unlikely(!vlabuf)) | 2649 | if (unlikely(!vlabuf)) |
2355 | return -ENOMEM; | 2650 | return -ENOMEM; |
2356 | 2651 | ||
2357 | /* Zero */ | 2652 | ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop); |
2358 | memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); | 2653 | ffs->ms_os_descs_ext_prop_name_avail = |
2654 | vla_ptr(vlabuf, d, ext_prop_name); | ||
2655 | ffs->ms_os_descs_ext_prop_data_avail = | ||
2656 | vla_ptr(vlabuf, d, ext_prop_data); | ||
2657 | |||
2359 | /* Copy descriptors */ | 2658 | /* Copy descriptors */ |
2360 | memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, | 2659 | memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, |
2361 | ffs->raw_descs_length); | 2660 | ffs->raw_descs_length); |
@@ -2409,12 +2708,16 @@ static int _ffs_func_bind(struct usb_configuration *c, | |||
2409 | 2708 | ||
2410 | if (likely(super)) { | 2709 | if (likely(super)) { |
2411 | func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); | 2710 | func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); |
2412 | ret = ffs_do_descs(ffs->ss_descs_count, | 2711 | ss_len = ffs_do_descs(ffs->ss_descs_count, |
2413 | vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, | 2712 | vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, |
2414 | d_raw_descs__sz - fs_len - hs_len, | 2713 | d_raw_descs__sz - fs_len - hs_len, |
2415 | __ffs_func_bind_do_descs, func); | 2714 | __ffs_func_bind_do_descs, func); |
2416 | if (unlikely(ret < 0)) | 2715 | if (unlikely(ss_len < 0)) { |
2716 | ret = ss_len; | ||
2417 | goto error; | 2717 | goto error; |
2718 | } | ||
2719 | } else { | ||
2720 | ss_len = 0; | ||
2418 | } | 2721 | } |
2419 | 2722 | ||
2420 | /* | 2723 | /* |
@@ -2430,6 +2733,28 @@ static int _ffs_func_bind(struct usb_configuration *c, | |||
2430 | if (unlikely(ret < 0)) | 2733 | if (unlikely(ret < 0)) |
2431 | goto error; | 2734 | goto error; |
2432 | 2735 | ||
2736 | func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table); | ||
2737 | if (c->cdev->use_os_string) | ||
2738 | for (i = 0; i < ffs->interfaces_count; ++i) { | ||
2739 | struct usb_os_desc *desc; | ||
2740 | |||
2741 | desc = func->function.os_desc_table[i].os_desc = | ||
2742 | vla_ptr(vlabuf, d, os_desc) + | ||
2743 | i * sizeof(struct usb_os_desc); | ||
2744 | desc->ext_compat_id = | ||
2745 | vla_ptr(vlabuf, d, ext_compat) + i * 16; | ||
2746 | INIT_LIST_HEAD(&desc->ext_prop); | ||
2747 | } | ||
2748 | ret = ffs_do_os_descs(ffs->ms_os_descs_count, | ||
2749 | vla_ptr(vlabuf, d, raw_descs) + | ||
2750 | fs_len + hs_len + ss_len, | ||
2751 | d_raw_descs__sz - fs_len - hs_len - ss_len, | ||
2752 | __ffs_func_bind_do_os_desc, func); | ||
2753 | if (unlikely(ret < 0)) | ||
2754 | goto error; | ||
2755 | func->function.os_desc_n = | ||
2756 | c->cdev->use_os_string ? ffs->interfaces_count : 0; | ||
2757 | |||
2433 | /* And we're done */ | 2758 | /* And we're done */ |
2434 | ffs_event_add(ffs, FUNCTIONFS_BIND); | 2759 | ffs_event_add(ffs, FUNCTIONFS_BIND); |
2435 | return 0; | 2760 | return 0; |
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index bf0ba375d459..63d6e71569c1 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h | |||
@@ -216,6 +216,13 @@ struct ffs_data { | |||
216 | unsigned fs_descs_count; | 216 | unsigned fs_descs_count; |
217 | unsigned hs_descs_count; | 217 | unsigned hs_descs_count; |
218 | unsigned ss_descs_count; | 218 | unsigned ss_descs_count; |
219 | unsigned ms_os_descs_count; | ||
220 | unsigned ms_os_descs_ext_prop_count; | ||
221 | unsigned ms_os_descs_ext_prop_name_len; | ||
222 | unsigned ms_os_descs_ext_prop_data_len; | ||
223 | void *ms_os_descs_ext_prop_avail; | ||
224 | void *ms_os_descs_ext_prop_name_avail; | ||
225 | void *ms_os_descs_ext_prop_data_avail; | ||
219 | 226 | ||
220 | unsigned short strings_count; | 227 | unsigned short strings_count; |
221 | unsigned short interfaces_count; | 228 | unsigned short interfaces_count; |
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 2a4b4a72a4f9..b66fae77c08c 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h | |||
@@ -18,10 +18,9 @@ enum functionfs_flags { | |||
18 | FUNCTIONFS_HAS_FS_DESC = 1, | 18 | FUNCTIONFS_HAS_FS_DESC = 1, |
19 | FUNCTIONFS_HAS_HS_DESC = 2, | 19 | FUNCTIONFS_HAS_HS_DESC = 2, |
20 | FUNCTIONFS_HAS_SS_DESC = 4, | 20 | FUNCTIONFS_HAS_SS_DESC = 4, |
21 | FUNCTIONFS_HAS_MS_OS_DESC = 8, | ||
21 | }; | 22 | }; |
22 | 23 | ||
23 | #ifndef __KERNEL__ | ||
24 | |||
25 | /* Descriptor of an non-audio endpoint */ | 24 | /* Descriptor of an non-audio endpoint */ |
26 | struct usb_endpoint_descriptor_no_audio { | 25 | struct usb_endpoint_descriptor_no_audio { |
27 | __u8 bLength; | 26 | __u8 bLength; |
@@ -33,6 +32,36 @@ struct usb_endpoint_descriptor_no_audio { | |||
33 | __u8 bInterval; | 32 | __u8 bInterval; |
34 | } __attribute__((packed)); | 33 | } __attribute__((packed)); |
35 | 34 | ||
35 | /* MS OS Descriptor header */ | ||
36 | struct usb_os_desc_header { | ||
37 | __u8 interface; | ||
38 | __le32 dwLength; | ||
39 | __le16 bcdVersion; | ||
40 | __le16 wIndex; | ||
41 | union { | ||
42 | struct { | ||
43 | __u8 bCount; | ||
44 | __u8 Reserved; | ||
45 | }; | ||
46 | __le16 wCount; | ||
47 | }; | ||
48 | } __attribute__((packed)); | ||
49 | |||
50 | struct usb_ext_compat_desc { | ||
51 | __u8 bFirstInterfaceNumber; | ||
52 | __u8 Reserved1; | ||
53 | __u8 CompatibleID[8]; | ||
54 | __u8 SubCompatibleID[8]; | ||
55 | __u8 Reserved2[6]; | ||
56 | }; | ||
57 | |||
58 | struct usb_ext_prop_desc { | ||
59 | __le32 dwSize; | ||
60 | __le32 dwPropertyDataType; | ||
61 | __le16 wPropertyNameLength; | ||
62 | } __attribute__((packed)); | ||
63 | |||
64 | #ifndef __KERNEL__ | ||
36 | 65 | ||
37 | /* | 66 | /* |
38 | * Descriptors format: | 67 | * Descriptors format: |
@@ -45,9 +74,11 @@ struct usb_endpoint_descriptor_no_audio { | |||
45 | * | | fs_count | LE32 | number of full-speed descriptors | | 74 | * | | fs_count | LE32 | number of full-speed descriptors | |
46 | * | | hs_count | LE32 | number of high-speed descriptors | | 75 | * | | hs_count | LE32 | number of high-speed descriptors | |
47 | * | | ss_count | LE32 | number of super-speed descriptors | | 76 | * | | ss_count | LE32 | number of super-speed descriptors | |
77 | * | | os_count | LE32 | number of MS OS descriptors | | ||
48 | * | | fs_descrs | Descriptor[] | list of full-speed descriptors | | 78 | * | | fs_descrs | Descriptor[] | list of full-speed descriptors | |
49 | * | | hs_descrs | Descriptor[] | list of high-speed descriptors | | 79 | * | | hs_descrs | Descriptor[] | list of high-speed descriptors | |
50 | * | | ss_descrs | Descriptor[] | list of super-speed descriptors | | 80 | * | | ss_descrs | Descriptor[] | list of super-speed descriptors | |
81 | * | | os_descrs | OSDesc[] | list of MS OS descriptors | | ||
51 | * | 82 | * |
52 | * Depending on which flags are set, various fields may be missing in the | 83 | * Depending on which flags are set, various fields may be missing in the |
53 | * structure. Any flags that are not recognised cause the whole block to be | 84 | * structure. Any flags that are not recognised cause the whole block to be |
@@ -74,6 +105,52 @@ struct usb_endpoint_descriptor_no_audio { | |||
74 | * | 0 | bLength | U8 | length of the descriptor | | 105 | * | 0 | bLength | U8 | length of the descriptor | |
75 | * | 1 | bDescriptorType | U8 | descriptor type | | 106 | * | 1 | bDescriptorType | U8 | descriptor type | |
76 | * | 2 | payload | | descriptor's payload | | 107 | * | 2 | payload | | descriptor's payload | |
108 | * | ||
109 | * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of | ||
110 | * the following formats: | ||
111 | * | ||
112 | * | off | name | type | description | | ||
113 | * |-----+-----------------+------+--------------------------| | ||
114 | * | 0 | inteface | U8 | related interface number | | ||
115 | * | 1 | dwLength | U32 | length of the descriptor | | ||
116 | * | 5 | bcdVersion | U16 | currently supported: 1 | | ||
117 | * | 7 | wIndex | U16 | currently supported: 4 | | ||
118 | * | 9 | bCount | U8 | number of ext. compat. | | ||
119 | * | 10 | Reserved | U8 | 0 | | ||
120 | * | 11 | ExtCompat[] | | list of ext. compat. d. | | ||
121 | * | ||
122 | * | off | name | type | description | | ||
123 | * |-----+-----------------+------+--------------------------| | ||
124 | * | 0 | inteface | U8 | related interface number | | ||
125 | * | 1 | dwLength | U32 | length of the descriptor | | ||
126 | * | 5 | bcdVersion | U16 | currently supported: 1 | | ||
127 | * | 7 | wIndex | U16 | currently supported: 5 | | ||
128 | * | 9 | wCount | U16 | number of ext. compat. | | ||
129 | * | 11 | ExtProp[] | | list of ext. prop. d. | | ||
130 | * | ||
131 | * ExtCompat[] is an array of valid Extended Compatiblity descriptors | ||
132 | * which have the following format: | ||
133 | * | ||
134 | * | off | name | type | description | | ||
135 | * |-----+-----------------------+------+-------------------------------------| | ||
136 | * | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st| | ||
137 | * | | | | interface in an IAD group | | ||
138 | * | 1 | Reserved | U8 | 0 | | ||
139 | * | 2 | CompatibleID | U8[8]| compatible ID string | | ||
140 | * | 10 | SubCompatibleID | U8[8]| subcompatible ID string | | ||
141 | * | 18 | Reserved | U8[6]| 0 | | ||
142 | * | ||
143 | * ExtProp[] is an array of valid Extended Properties descriptors | ||
144 | * which have the following format: | ||
145 | * | ||
146 | * | off | name | type | description | | ||
147 | * |-----+-----------------------+------+-------------------------------------| | ||
148 | * | 0 | dwSize | U32 | length of the descriptor | | ||
149 | * | 4 | dwPropertyDataType | U32 | 1..7 | | ||
150 | * | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) | | ||
151 | * | 10 | bPropertyName |U8[NL]| name of this property | | ||
152 | * |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) | | ||
153 | * |14+NL| bProperty |U8[DL]| payload of this property | | ||
77 | */ | 154 | */ |
78 | 155 | ||
79 | struct usb_functionfs_strings_head { | 156 | struct usb_functionfs_strings_head { |