Skip to content

Commit 37a3a53

Browse files
Andrzej PietrasiewiczFelipe Balbi
authored andcommitted
usb: gadget: OS Feature Descriptors support
There is a custom (non-USB IF) extension to the USB standard: http://msdn.microsoft.com/library/windows/hardware/gg463182 They grant permission to use the specification - there is "Microsoft OS Descriptor Specification License Agreement" under the link mentioned above, and its Section 2 "Grant of License", letter (b) reads: "Patent license. Microsoft hereby grants to You a nonexclusive, royalty-free, nontransferable, worldwide license under Microsoft’s patents embodied solely within the Specification and that are owned or licensable by Microsoft to make, use, import, offer to sell, sell and distribute directly or indirectly to Your Licensees Your Implementation. You may sublicense this patent license to Your Licensees under the same terms and conditions." The said extension is maintained by Microsoft for Microsoft. Yet it is fairly common for various devices to use it, and a popular proprietary operating system expects devices to provide "OS descriptors", so Linux-based USB gadgets whishing to be able to talk to a variety of operating systems should be able to provide the "OS descriptors". This patch adds optional support for gadgets whishing to expose the so called "OS Feature Descriptors", that is "Extended Compatibility ID" and "Extended Properties". Hosts which do request "OS descriptors" from gadgets do so during the enumeration phase and before the configuration is set with SET_CONFIGURATION. What is more, those hosts never ask for configurations at indices other than 0. Therefore, gadgets whishing to provide "OS descriptors" must designate one configuration to be used with this kind of hosts - this is what os_desc_config is added for in struct usb_composite_dev. There is an additional advantage to it: if a gadget provides "OS descriptors" and designates one configuration to be used with such non-USB-compliant hosts it can invoke "usb_add_config" in any order because the designated configuration will be reported to be at index 0 anyway. This patch also adds handling vendor-specific requests addressed at device or interface and related to handling "OS descriptors". Signed-off-by: Andrzej Pietrasiewicz <[email protected]> Acked-by: Michal Nazarewicz <[email protected]> Signed-off-by: Felipe Balbi <[email protected]>
1 parent 19824d5 commit 37a3a53

File tree

3 files changed

+435
-1
lines changed

3 files changed

+435
-1
lines changed

drivers/usb/gadget/composite.c

Lines changed: 287 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <linux/usb/composite.h>
2222
#include <asm/unaligned.h>
2323

24+
#include "u_os_desc.h"
25+
2426
/**
2527
* struct usb_os_string - represents OS String to be reported by a gadget
2628
* @bLength: total length of the entire descritor, always 0x12
@@ -438,6 +440,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
438440
{
439441
struct usb_gadget *gadget = cdev->gadget;
440442
struct usb_configuration *c;
443+
struct list_head *pos;
441444
u8 type = w_value >> 8;
442445
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
443446

@@ -456,7 +459,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
456459

457460
/* This is a lookup by config *INDEX* */
458461
w_value &= 0xff;
459-
list_for_each_entry(c, &cdev->configs, list) {
462+
463+
pos = &cdev->configs;
464+
c = cdev->os_desc_config;
465+
if (c)
466+
goto check_config;
467+
468+
while ((pos = pos->next) != &cdev->configs) {
469+
c = list_entry(pos, typeof(*c), list);
470+
471+
/* skip OS Descriptors config which is handled separately */
472+
if (c == cdev->os_desc_config)
473+
continue;
474+
475+
check_config:
460476
/* ignore configs that won't work at this speed */
461477
switch (speed) {
462478
case USB_SPEED_SUPER:
@@ -1236,6 +1252,158 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
12361252
req->status, req->actual, req->length);
12371253
}
12381254

1255+
static int count_ext_compat(struct usb_configuration *c)
1256+
{
1257+
int i, res;
1258+
1259+
res = 0;
1260+
for (i = 0; i < c->next_interface_id; ++i) {
1261+
struct usb_function *f;
1262+
int j;
1263+
1264+
f = c->interface[i];
1265+
for (j = 0; j < f->os_desc_n; ++j) {
1266+
struct usb_os_desc *d;
1267+
1268+
if (i != f->os_desc_table[j].if_id)
1269+
continue;
1270+
d = f->os_desc_table[j].os_desc;
1271+
if (d && d->ext_compat_id)
1272+
++res;
1273+
}
1274+
}
1275+
BUG_ON(res > 255);
1276+
return res;
1277+
}
1278+
1279+
static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
1280+
{
1281+
int i, count;
1282+
1283+
count = 16;
1284+
for (i = 0; i < c->next_interface_id; ++i) {
1285+
struct usb_function *f;
1286+
int j;
1287+
1288+
f = c->interface[i];
1289+
for (j = 0; j < f->os_desc_n; ++j) {
1290+
struct usb_os_desc *d;
1291+
1292+
if (i != f->os_desc_table[j].if_id)
1293+
continue;
1294+
d = f->os_desc_table[j].os_desc;
1295+
if (d && d->ext_compat_id) {
1296+
*buf++ = i;
1297+
*buf++ = 0x01;
1298+
memcpy(buf, d->ext_compat_id, 16);
1299+
buf += 22;
1300+
} else {
1301+
++buf;
1302+
*buf = 0x01;
1303+
buf += 23;
1304+
}
1305+
count += 24;
1306+
if (count >= 4096)
1307+
return;
1308+
}
1309+
}
1310+
}
1311+
1312+
static int count_ext_prop(struct usb_configuration *c, int interface)
1313+
{
1314+
struct usb_function *f;
1315+
int j, res;
1316+
1317+
res = 0;
1318+
1319+
f = c->interface[interface];
1320+
for (j = 0; j < f->os_desc_n; ++j) {
1321+
struct usb_os_desc *d;
1322+
1323+
if (interface != f->os_desc_table[j].if_id)
1324+
continue;
1325+
d = f->os_desc_table[j].os_desc;
1326+
if (d && d->ext_compat_id)
1327+
return d->ext_prop_count;
1328+
}
1329+
return res;
1330+
}
1331+
1332+
static int len_ext_prop(struct usb_configuration *c, int interface)
1333+
{
1334+
struct usb_function *f;
1335+
struct usb_os_desc *d;
1336+
int j, res;
1337+
1338+
res = 10; /* header length */
1339+
f = c->interface[interface];
1340+
for (j = 0; j < f->os_desc_n; ++j) {
1341+
if (interface != f->os_desc_table[j].if_id)
1342+
continue;
1343+
d = f->os_desc_table[j].os_desc;
1344+
if (d)
1345+
return min(res + d->ext_prop_len, 4096);
1346+
}
1347+
return res;
1348+
}
1349+
1350+
static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
1351+
{
1352+
struct usb_function *f;
1353+
struct usb_os_desc *d;
1354+
struct usb_os_desc_ext_prop *ext_prop;
1355+
int j, count, n, ret;
1356+
u8 *start = buf;
1357+
1358+
f = c->interface[interface];
1359+
for (j = 0; j < f->os_desc_n; ++j) {
1360+
if (interface != f->os_desc_table[j].if_id)
1361+
continue;
1362+
d = f->os_desc_table[j].os_desc;
1363+
if (d)
1364+
list_for_each_entry(ext_prop, &d->ext_prop, entry) {
1365+
/* 4kB minus header length */
1366+
n = buf - start;
1367+
if (n >= 4086)
1368+
return 0;
1369+
1370+
count = ext_prop->data_len +
1371+
ext_prop->name_len + 14;
1372+
if (count > 4086 - n)
1373+
return -EINVAL;
1374+
usb_ext_prop_put_size(buf, count);
1375+
usb_ext_prop_put_type(buf, ext_prop->type);
1376+
ret = usb_ext_prop_put_name(buf, ext_prop->name,
1377+
ext_prop->name_len);
1378+
if (ret < 0)
1379+
return ret;
1380+
switch (ext_prop->type) {
1381+
case USB_EXT_PROP_UNICODE:
1382+
case USB_EXT_PROP_UNICODE_ENV:
1383+
case USB_EXT_PROP_UNICODE_LINK:
1384+
usb_ext_prop_put_unicode(buf, ret,
1385+
ext_prop->data,
1386+
ext_prop->data_len);
1387+
break;
1388+
case USB_EXT_PROP_BINARY:
1389+
usb_ext_prop_put_binary(buf, ret,
1390+
ext_prop->data,
1391+
ext_prop->data_len);
1392+
break;
1393+
case USB_EXT_PROP_LE32:
1394+
/* not implemented */
1395+
case USB_EXT_PROP_BE32:
1396+
/* not implemented */
1397+
default:
1398+
return -EINVAL;
1399+
}
1400+
buf += count;
1401+
}
1402+
}
1403+
1404+
return 0;
1405+
}
1406+
12391407
/*
12401408
* The setup() callback implements all the ep0 functionality that's
12411409
* not handled lower down, in hardware or the hardware driver(like
@@ -1445,6 +1613,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
14451613
break;
14461614
default:
14471615
unknown:
1616+
/*
1617+
* OS descriptors handling
1618+
*/
1619+
if (cdev->use_os_string && cdev->os_desc_config &&
1620+
(ctrl->bRequest & USB_TYPE_VENDOR) &&
1621+
ctrl->bRequest == cdev->b_vendor_code) {
1622+
struct usb_request *req;
1623+
struct usb_configuration *os_desc_cfg;
1624+
u8 *buf;
1625+
int interface;
1626+
int count = 0;
1627+
1628+
req = cdev->os_desc_req;
1629+
req->complete = composite_setup_complete;
1630+
buf = req->buf;
1631+
os_desc_cfg = cdev->os_desc_config;
1632+
memset(buf, 0, w_length);
1633+
buf[5] = 0x01;
1634+
switch (ctrl->bRequestType & USB_RECIP_MASK) {
1635+
case USB_RECIP_DEVICE:
1636+
if (w_index != 0x4 || (w_value >> 8))
1637+
break;
1638+
buf[6] = w_index;
1639+
if (w_length == 0x10) {
1640+
/* Number of ext compat interfaces */
1641+
count = count_ext_compat(os_desc_cfg);
1642+
buf[8] = count;
1643+
count *= 24; /* 24 B/ext compat desc */
1644+
count += 16; /* header */
1645+
put_unaligned_le32(count, buf);
1646+
value = w_length;
1647+
} else {
1648+
/* "extended compatibility ID"s */
1649+
count = count_ext_compat(os_desc_cfg);
1650+
buf[8] = count;
1651+
count *= 24; /* 24 B/ext compat desc */
1652+
count += 16; /* header */
1653+
put_unaligned_le32(count, buf);
1654+
buf += 16;
1655+
fill_ext_compat(os_desc_cfg, buf);
1656+
value = w_length;
1657+
}
1658+
break;
1659+
case USB_RECIP_INTERFACE:
1660+
if (w_index != 0x5 || (w_value >> 8))
1661+
break;
1662+
interface = w_value & 0xFF;
1663+
buf[6] = w_index;
1664+
if (w_length == 0x0A) {
1665+
count = count_ext_prop(os_desc_cfg,
1666+
interface);
1667+
put_unaligned_le16(count, buf + 8);
1668+
count = len_ext_prop(os_desc_cfg,
1669+
interface);
1670+
put_unaligned_le32(count, buf);
1671+
1672+
value = w_length;
1673+
} else {
1674+
count = count_ext_prop(os_desc_cfg,
1675+
interface);
1676+
put_unaligned_le16(count, buf + 8);
1677+
count = len_ext_prop(os_desc_cfg,
1678+
interface);
1679+
put_unaligned_le32(count, buf);
1680+
buf += 10;
1681+
value = fill_ext_prop(os_desc_cfg,
1682+
interface, buf);
1683+
if (value < 0)
1684+
return value;
1685+
1686+
value = w_length;
1687+
}
1688+
break;
1689+
}
1690+
req->length = value;
1691+
req->zero = value < w_length;
1692+
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
1693+
if (value < 0) {
1694+
DBG(cdev, "ep_queue --> %d\n", value);
1695+
req->status = 0;
1696+
composite_setup_complete(gadget->ep0, req);
1697+
}
1698+
return value;
1699+
}
1700+
14481701
VDBG(cdev,
14491702
"non-core control req%02x.%02x v%04x i%04x l%d\n",
14501703
ctrl->bRequestType, ctrl->bRequest,
@@ -1668,6 +1921,29 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
16681921
return ret;
16691922
}
16701923

1924+
int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
1925+
struct usb_ep *ep0)
1926+
{
1927+
int ret = 0;
1928+
1929+
cdev->os_desc_req = usb_ep_alloc_request(ep0, GFP_KERNEL);
1930+
if (!cdev->os_desc_req) {
1931+
ret = PTR_ERR(cdev->os_desc_req);
1932+
goto end;
1933+
}
1934+
1935+
/* OS feature descriptor length <= 4kB */
1936+
cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
1937+
if (!cdev->os_desc_req->buf) {
1938+
ret = PTR_ERR(cdev->os_desc_req->buf);
1939+
kfree(cdev->os_desc_req);
1940+
goto end;
1941+
}
1942+
cdev->os_desc_req->complete = composite_setup_complete;
1943+
end:
1944+
return ret;
1945+
}
1946+
16711947
void composite_dev_cleanup(struct usb_composite_dev *cdev)
16721948
{
16731949
struct usb_gadget_string_container *uc, *tmp;
@@ -1676,6 +1952,10 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
16761952
list_del(&uc->list);
16771953
kfree(uc);
16781954
}
1955+
if (cdev->os_desc_req) {
1956+
kfree(cdev->os_desc_req->buf);
1957+
usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
1958+
}
16791959
if (cdev->req) {
16801960
kfree(cdev->req->buf);
16811961
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
@@ -1713,6 +1993,12 @@ static int composite_bind(struct usb_gadget *gadget,
17131993
if (status < 0)
17141994
goto fail;
17151995

1996+
if (cdev->use_os_string) {
1997+
status = composite_os_desc_req_prepare(cdev, gadget->ep0);
1998+
if (status)
1999+
goto fail;
2000+
}
2001+
17162002
update_unchanged_dev_desc(&cdev->desc, composite->dev);
17172003

17182004
/* has userspace failed to provide a serial number? */

0 commit comments

Comments
 (0)