| .. | .. |
|---|
| 23 | 23 | #include "u_ether.h" |
|---|
| 24 | 24 | #include "u_ether_configfs.h" |
|---|
| 25 | 25 | #include "u_ncm.h" |
|---|
| 26 | +#include "configfs.h" |
|---|
| 26 | 27 | |
|---|
| 27 | 28 | /* |
|---|
| 28 | 29 | * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link. |
|---|
| .. | .. |
|---|
| 35 | 36 | |
|---|
| 36 | 37 | /* to trigger crc/non-crc ndp signature */ |
|---|
| 37 | 38 | |
|---|
| 38 | | -#define NCM_NDP_HDR_CRC_MASK 0x01000000 |
|---|
| 39 | 39 | #define NCM_NDP_HDR_CRC 0x01000000 |
|---|
| 40 | | -#define NCM_NDP_HDR_NOCRC 0x00000000 |
|---|
| 41 | 40 | |
|---|
| 42 | 41 | enum ncm_notify_state { |
|---|
| 43 | 42 | NCM_NOTIFY_NONE, /* don't notify */ |
|---|
| .. | .. |
|---|
| 86 | 85 | /* peak (theoretical) bulk transfer rate in bits-per-second */ |
|---|
| 87 | 86 | static inline unsigned ncm_bitrate(struct usb_gadget *g) |
|---|
| 88 | 87 | { |
|---|
| 89 | | - if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS) |
|---|
| 88 | + if (!g) |
|---|
| 89 | + return 0; |
|---|
| 90 | + else if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS) |
|---|
| 90 | 91 | return 4250000000U; |
|---|
| 91 | 92 | else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) |
|---|
| 92 | 93 | return 3750000000U; |
|---|
| .. | .. |
|---|
| 379 | 380 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, |
|---|
| 380 | 381 | |
|---|
| 381 | 382 | /* the following 2 values can be tweaked if necessary */ |
|---|
| 382 | | - /* .bMaxBurst = 0, */ |
|---|
| 383 | + .bMaxBurst = 15, |
|---|
| 383 | 384 | /* .bmAttributes = 0, */ |
|---|
| 384 | 385 | }; |
|---|
| 385 | 386 | |
|---|
| .. | .. |
|---|
| 529 | 530 | { |
|---|
| 530 | 531 | ncm->parser_opts = &ndp16_opts; |
|---|
| 531 | 532 | ncm->is_crc = false; |
|---|
| 533 | + ncm->ndp_sign = ncm->parser_opts->ndp_sign; |
|---|
| 532 | 534 | ncm->port.cdc_filter = DEFAULT_FILTER; |
|---|
| 533 | 535 | |
|---|
| 534 | 536 | /* doesn't make sense for ncm, fixed size used */ |
|---|
| .. | .. |
|---|
| 811 | 813 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) |
|---|
| 812 | 814 | | USB_CDC_SET_CRC_MODE: |
|---|
| 813 | 815 | { |
|---|
| 814 | | - int ndp_hdr_crc = 0; |
|---|
| 815 | | - |
|---|
| 816 | 816 | if (w_length != 0 || w_index != ncm->ctrl_id) |
|---|
| 817 | 817 | goto invalid; |
|---|
| 818 | 818 | switch (w_value) { |
|---|
| 819 | 819 | case 0x0000: |
|---|
| 820 | 820 | ncm->is_crc = false; |
|---|
| 821 | | - ndp_hdr_crc = NCM_NDP_HDR_NOCRC; |
|---|
| 822 | 821 | DBG(cdev, "non-CRC mode selected\n"); |
|---|
| 823 | 822 | break; |
|---|
| 824 | 823 | case 0x0001: |
|---|
| 825 | 824 | ncm->is_crc = true; |
|---|
| 826 | | - ndp_hdr_crc = NCM_NDP_HDR_CRC; |
|---|
| 827 | 825 | DBG(cdev, "CRC mode selected\n"); |
|---|
| 828 | 826 | break; |
|---|
| 829 | 827 | default: |
|---|
| 830 | 828 | goto invalid; |
|---|
| 831 | 829 | } |
|---|
| 832 | | - ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc; |
|---|
| 833 | 830 | value = 0; |
|---|
| 834 | 831 | break; |
|---|
| 835 | 832 | } |
|---|
| .. | .. |
|---|
| 846 | 843 | ctrl->bRequestType, ctrl->bRequest, |
|---|
| 847 | 844 | w_value, w_index, w_length); |
|---|
| 848 | 845 | } |
|---|
| 846 | + ncm->ndp_sign = ncm->parser_opts->ndp_sign | |
|---|
| 847 | + (ncm->is_crc ? NCM_NDP_HDR_CRC : 0); |
|---|
| 849 | 848 | |
|---|
| 850 | 849 | /* respond with data transfer or status phase? */ |
|---|
| 851 | 850 | if (value >= 0) { |
|---|
| .. | .. |
|---|
| 1181 | 1180 | struct sk_buff_head *list) |
|---|
| 1182 | 1181 | { |
|---|
| 1183 | 1182 | struct f_ncm *ncm = func_to_ncm(&port->func); |
|---|
| 1184 | | - __le16 *tmp = (void *) skb->data; |
|---|
| 1183 | + unsigned char *ntb_ptr = skb->data; |
|---|
| 1184 | + __le16 *tmp; |
|---|
| 1185 | 1185 | unsigned index, index2; |
|---|
| 1186 | 1186 | int ndp_index; |
|---|
| 1187 | 1187 | unsigned dg_len, dg_len2; |
|---|
| .. | .. |
|---|
| 1194 | 1194 | const struct ndp_parser_opts *opts = ncm->parser_opts; |
|---|
| 1195 | 1195 | unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; |
|---|
| 1196 | 1196 | int dgram_counter; |
|---|
| 1197 | + int to_process = skb->len; |
|---|
| 1198 | + |
|---|
| 1199 | +parse_ntb: |
|---|
| 1200 | + tmp = (__le16 *)ntb_ptr; |
|---|
| 1197 | 1201 | |
|---|
| 1198 | 1202 | /* dwSignature */ |
|---|
| 1199 | 1203 | if (get_unaligned_le32(tmp) != opts->nth_sign) { |
|---|
| .. | .. |
|---|
| 1240 | 1244 | * walk through NDP |
|---|
| 1241 | 1245 | * dwSignature |
|---|
| 1242 | 1246 | */ |
|---|
| 1243 | | - tmp = (void *)(skb->data + ndp_index); |
|---|
| 1247 | + tmp = (__le16 *)(ntb_ptr + ndp_index); |
|---|
| 1244 | 1248 | if (get_unaligned_le32(tmp) != ncm->ndp_sign) { |
|---|
| 1245 | 1249 | INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); |
|---|
| 1246 | 1250 | goto err; |
|---|
| .. | .. |
|---|
| 1297 | 1301 | if (ncm->is_crc) { |
|---|
| 1298 | 1302 | uint32_t crc, crc2; |
|---|
| 1299 | 1303 | |
|---|
| 1300 | | - crc = get_unaligned_le32(skb->data + |
|---|
| 1304 | + crc = get_unaligned_le32(ntb_ptr + |
|---|
| 1301 | 1305 | index + dg_len - |
|---|
| 1302 | 1306 | crc_len); |
|---|
| 1303 | 1307 | crc2 = ~crc32_le(~0, |
|---|
| 1304 | | - skb->data + index, |
|---|
| 1308 | + ntb_ptr + index, |
|---|
| 1305 | 1309 | dg_len - crc_len); |
|---|
| 1306 | 1310 | if (crc != crc2) { |
|---|
| 1307 | 1311 | INFO(port->func.config->cdev, |
|---|
| .. | .. |
|---|
| 1328 | 1332 | dg_len - crc_len); |
|---|
| 1329 | 1333 | if (skb2 == NULL) |
|---|
| 1330 | 1334 | goto err; |
|---|
| 1331 | | - skb_put_data(skb2, skb->data + index, |
|---|
| 1335 | + skb_put_data(skb2, ntb_ptr + index, |
|---|
| 1332 | 1336 | dg_len - crc_len); |
|---|
| 1333 | 1337 | |
|---|
| 1334 | 1338 | skb_queue_tail(list, skb2); |
|---|
| .. | .. |
|---|
| 1341 | 1345 | } while (ndp_len > 2 * (opts->dgram_item_len * 2)); |
|---|
| 1342 | 1346 | } while (ndp_index); |
|---|
| 1343 | 1347 | |
|---|
| 1344 | | - dev_consume_skb_any(skb); |
|---|
| 1345 | | - |
|---|
| 1346 | 1348 | VDBG(port->func.config->cdev, |
|---|
| 1347 | 1349 | "Parsed NTB with %d frames\n", dgram_counter); |
|---|
| 1350 | + |
|---|
| 1351 | + to_process -= block_len; |
|---|
| 1352 | + if (to_process != 0) { |
|---|
| 1353 | + ntb_ptr = (unsigned char *)(ntb_ptr + block_len); |
|---|
| 1354 | + goto parse_ntb; |
|---|
| 1355 | + } |
|---|
| 1356 | + |
|---|
| 1357 | + dev_consume_skb_any(skb); |
|---|
| 1358 | + |
|---|
| 1348 | 1359 | return 0; |
|---|
| 1349 | 1360 | err: |
|---|
| 1350 | 1361 | skb_queue_purge(list); |
|---|
| .. | .. |
|---|
| 1432 | 1443 | return -EINVAL; |
|---|
| 1433 | 1444 | |
|---|
| 1434 | 1445 | ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); |
|---|
| 1446 | + |
|---|
| 1447 | + if (cdev->use_os_string) { |
|---|
| 1448 | + f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), |
|---|
| 1449 | + GFP_KERNEL); |
|---|
| 1450 | + if (!f->os_desc_table) |
|---|
| 1451 | + return -ENOMEM; |
|---|
| 1452 | + f->os_desc_n = 1; |
|---|
| 1453 | + f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; |
|---|
| 1454 | + } |
|---|
| 1455 | + |
|---|
| 1435 | 1456 | /* |
|---|
| 1436 | 1457 | * in drivers/usb/gadget/configfs.c:configfs_composite_bind() |
|---|
| 1437 | 1458 | * configurations are bound in sequence with list_for_each_entry, |
|---|
| .. | .. |
|---|
| 1445 | 1466 | status = gether_register_netdev(ncm_opts->net); |
|---|
| 1446 | 1467 | mutex_unlock(&ncm_opts->lock); |
|---|
| 1447 | 1468 | if (status) |
|---|
| 1448 | | - return status; |
|---|
| 1469 | + goto fail; |
|---|
| 1449 | 1470 | ncm_opts->bound = true; |
|---|
| 1450 | 1471 | } |
|---|
| 1451 | 1472 | us = usb_gstrings_attach(cdev, ncm_strings, |
|---|
| 1452 | 1473 | ARRAY_SIZE(ncm_string_defs)); |
|---|
| 1453 | | - if (IS_ERR(us)) |
|---|
| 1454 | | - return PTR_ERR(us); |
|---|
| 1474 | + if (IS_ERR(us)) { |
|---|
| 1475 | + status = PTR_ERR(us); |
|---|
| 1476 | + goto fail; |
|---|
| 1477 | + } |
|---|
| 1455 | 1478 | ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; |
|---|
| 1456 | 1479 | ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; |
|---|
| 1457 | 1480 | ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; |
|---|
| .. | .. |
|---|
| 1467 | 1490 | |
|---|
| 1468 | 1491 | ncm_control_intf.bInterfaceNumber = status; |
|---|
| 1469 | 1492 | ncm_union_desc.bMasterInterface0 = status; |
|---|
| 1493 | + |
|---|
| 1494 | + if (cdev->use_os_string) |
|---|
| 1495 | + f->os_desc_table[0].if_id = |
|---|
| 1496 | + ncm_iad_desc.bFirstInterface; |
|---|
| 1470 | 1497 | |
|---|
| 1471 | 1498 | status = usb_interface_id(c, f); |
|---|
| 1472 | 1499 | if (status < 0) |
|---|
| .. | .. |
|---|
| 1547 | 1574 | return 0; |
|---|
| 1548 | 1575 | |
|---|
| 1549 | 1576 | fail: |
|---|
| 1577 | + kfree(f->os_desc_table); |
|---|
| 1578 | + f->os_desc_n = 0; |
|---|
| 1579 | + |
|---|
| 1550 | 1580 | if (ncm->notify_req) { |
|---|
| 1551 | 1581 | kfree(ncm->notify_req->buf); |
|---|
| 1552 | 1582 | usb_ep_free_request(ncm->notify, ncm->notify_req); |
|---|
| .. | .. |
|---|
| 1601 | 1631 | gether_cleanup(netdev_priv(opts->net)); |
|---|
| 1602 | 1632 | else |
|---|
| 1603 | 1633 | free_netdev(opts->net); |
|---|
| 1634 | + kfree(opts->ncm_interf_group); |
|---|
| 1604 | 1635 | kfree(opts); |
|---|
| 1605 | 1636 | } |
|---|
| 1606 | 1637 | |
|---|
| 1607 | 1638 | static struct usb_function_instance *ncm_alloc_inst(void) |
|---|
| 1608 | 1639 | { |
|---|
| 1609 | 1640 | struct f_ncm_opts *opts; |
|---|
| 1641 | + struct usb_os_desc *descs[1]; |
|---|
| 1642 | + char *names[1]; |
|---|
| 1643 | + struct config_group *ncm_interf_group; |
|---|
| 1610 | 1644 | |
|---|
| 1611 | 1645 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); |
|---|
| 1612 | 1646 | if (!opts) |
|---|
| 1613 | 1647 | return ERR_PTR(-ENOMEM); |
|---|
| 1648 | + opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; |
|---|
| 1649 | + |
|---|
| 1614 | 1650 | mutex_init(&opts->lock); |
|---|
| 1615 | 1651 | opts->func_inst.free_func_inst = ncm_free_inst; |
|---|
| 1616 | 1652 | opts->net = gether_setup_default(); |
|---|
| .. | .. |
|---|
| 1619 | 1655 | kfree(opts); |
|---|
| 1620 | 1656 | return ERR_CAST(net); |
|---|
| 1621 | 1657 | } |
|---|
| 1658 | + INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); |
|---|
| 1659 | + |
|---|
| 1660 | + descs[0] = &opts->ncm_os_desc; |
|---|
| 1661 | + names[0] = "ncm"; |
|---|
| 1622 | 1662 | |
|---|
| 1623 | 1663 | config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); |
|---|
| 1664 | + ncm_interf_group = |
|---|
| 1665 | + usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, |
|---|
| 1666 | + names, THIS_MODULE); |
|---|
| 1667 | + if (IS_ERR(ncm_interf_group)) { |
|---|
| 1668 | + ncm_free_inst(&opts->func_inst); |
|---|
| 1669 | + return ERR_CAST(ncm_interf_group); |
|---|
| 1670 | + } |
|---|
| 1671 | + opts->ncm_interf_group = ncm_interf_group; |
|---|
| 1624 | 1672 | |
|---|
| 1625 | 1673 | return &opts->func_inst; |
|---|
| 1626 | 1674 | } |
|---|
| .. | .. |
|---|
| 1646 | 1694 | |
|---|
| 1647 | 1695 | hrtimer_cancel(&ncm->task_timer); |
|---|
| 1648 | 1696 | |
|---|
| 1697 | + kfree(f->os_desc_table); |
|---|
| 1698 | + f->os_desc_n = 0; |
|---|
| 1699 | + |
|---|
| 1649 | 1700 | ncm_string_defs[0].id = 0; |
|---|
| 1650 | 1701 | usb_free_all_descriptors(f); |
|---|
| 1651 | 1702 | |
|---|