| .. | .. |
|---|
| 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 */ |
|---|
| .. | .. |
|---|
| 379 | 378 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, |
|---|
| 380 | 379 | |
|---|
| 381 | 380 | /* the following 2 values can be tweaked if necessary */ |
|---|
| 382 | | - /* .bMaxBurst = 0, */ |
|---|
| 381 | + .bMaxBurst = 15, |
|---|
| 383 | 382 | /* .bmAttributes = 0, */ |
|---|
| 384 | 383 | }; |
|---|
| 385 | 384 | |
|---|
| .. | .. |
|---|
| 529 | 528 | { |
|---|
| 530 | 529 | ncm->parser_opts = &ndp16_opts; |
|---|
| 531 | 530 | ncm->is_crc = false; |
|---|
| 531 | + ncm->ndp_sign = ncm->parser_opts->ndp_sign; |
|---|
| 532 | 532 | ncm->port.cdc_filter = DEFAULT_FILTER; |
|---|
| 533 | 533 | |
|---|
| 534 | 534 | /* doesn't make sense for ncm, fixed size used */ |
|---|
| .. | .. |
|---|
| 811 | 811 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) |
|---|
| 812 | 812 | | USB_CDC_SET_CRC_MODE: |
|---|
| 813 | 813 | { |
|---|
| 814 | | - int ndp_hdr_crc = 0; |
|---|
| 815 | | - |
|---|
| 816 | 814 | if (w_length != 0 || w_index != ncm->ctrl_id) |
|---|
| 817 | 815 | goto invalid; |
|---|
| 818 | 816 | switch (w_value) { |
|---|
| 819 | 817 | case 0x0000: |
|---|
| 820 | 818 | ncm->is_crc = false; |
|---|
| 821 | | - ndp_hdr_crc = NCM_NDP_HDR_NOCRC; |
|---|
| 822 | 819 | DBG(cdev, "non-CRC mode selected\n"); |
|---|
| 823 | 820 | break; |
|---|
| 824 | 821 | case 0x0001: |
|---|
| 825 | 822 | ncm->is_crc = true; |
|---|
| 826 | | - ndp_hdr_crc = NCM_NDP_HDR_CRC; |
|---|
| 827 | 823 | DBG(cdev, "CRC mode selected\n"); |
|---|
| 828 | 824 | break; |
|---|
| 829 | 825 | default: |
|---|
| 830 | 826 | goto invalid; |
|---|
| 831 | 827 | } |
|---|
| 832 | | - ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc; |
|---|
| 833 | 828 | value = 0; |
|---|
| 834 | 829 | break; |
|---|
| 835 | 830 | } |
|---|
| .. | .. |
|---|
| 846 | 841 | ctrl->bRequestType, ctrl->bRequest, |
|---|
| 847 | 842 | w_value, w_index, w_length); |
|---|
| 848 | 843 | } |
|---|
| 844 | + ncm->ndp_sign = ncm->parser_opts->ndp_sign | |
|---|
| 845 | + (ncm->is_crc ? NCM_NDP_HDR_CRC : 0); |
|---|
| 849 | 846 | |
|---|
| 850 | 847 | /* respond with data transfer or status phase? */ |
|---|
| 851 | 848 | if (value >= 0) { |
|---|
| .. | .. |
|---|
| 1432 | 1429 | return -EINVAL; |
|---|
| 1433 | 1430 | |
|---|
| 1434 | 1431 | ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); |
|---|
| 1432 | + |
|---|
| 1433 | + if (cdev->use_os_string) { |
|---|
| 1434 | + f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), |
|---|
| 1435 | + GFP_KERNEL); |
|---|
| 1436 | + if (!f->os_desc_table) |
|---|
| 1437 | + return -ENOMEM; |
|---|
| 1438 | + f->os_desc_n = 1; |
|---|
| 1439 | + f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; |
|---|
| 1440 | + } |
|---|
| 1441 | + |
|---|
| 1435 | 1442 | /* |
|---|
| 1436 | 1443 | * in drivers/usb/gadget/configfs.c:configfs_composite_bind() |
|---|
| 1437 | 1444 | * configurations are bound in sequence with list_for_each_entry, |
|---|
| .. | .. |
|---|
| 1445 | 1452 | status = gether_register_netdev(ncm_opts->net); |
|---|
| 1446 | 1453 | mutex_unlock(&ncm_opts->lock); |
|---|
| 1447 | 1454 | if (status) |
|---|
| 1448 | | - return status; |
|---|
| 1455 | + goto fail; |
|---|
| 1449 | 1456 | ncm_opts->bound = true; |
|---|
| 1450 | 1457 | } |
|---|
| 1451 | 1458 | us = usb_gstrings_attach(cdev, ncm_strings, |
|---|
| 1452 | 1459 | ARRAY_SIZE(ncm_string_defs)); |
|---|
| 1453 | | - if (IS_ERR(us)) |
|---|
| 1454 | | - return PTR_ERR(us); |
|---|
| 1460 | + if (IS_ERR(us)) { |
|---|
| 1461 | + status = PTR_ERR(us); |
|---|
| 1462 | + goto fail; |
|---|
| 1463 | + } |
|---|
| 1455 | 1464 | ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; |
|---|
| 1456 | 1465 | ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; |
|---|
| 1457 | 1466 | ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; |
|---|
| .. | .. |
|---|
| 1467 | 1476 | |
|---|
| 1468 | 1477 | ncm_control_intf.bInterfaceNumber = status; |
|---|
| 1469 | 1478 | ncm_union_desc.bMasterInterface0 = status; |
|---|
| 1479 | + |
|---|
| 1480 | + if (cdev->use_os_string) |
|---|
| 1481 | + f->os_desc_table[0].if_id = |
|---|
| 1482 | + ncm_iad_desc.bFirstInterface; |
|---|
| 1470 | 1483 | |
|---|
| 1471 | 1484 | status = usb_interface_id(c, f); |
|---|
| 1472 | 1485 | if (status < 0) |
|---|
| .. | .. |
|---|
| 1547 | 1560 | return 0; |
|---|
| 1548 | 1561 | |
|---|
| 1549 | 1562 | fail: |
|---|
| 1563 | + kfree(f->os_desc_table); |
|---|
| 1564 | + f->os_desc_n = 0; |
|---|
| 1565 | + |
|---|
| 1550 | 1566 | if (ncm->notify_req) { |
|---|
| 1551 | 1567 | kfree(ncm->notify_req->buf); |
|---|
| 1552 | 1568 | usb_ep_free_request(ncm->notify, ncm->notify_req); |
|---|
| .. | .. |
|---|
| 1601 | 1617 | gether_cleanup(netdev_priv(opts->net)); |
|---|
| 1602 | 1618 | else |
|---|
| 1603 | 1619 | free_netdev(opts->net); |
|---|
| 1620 | + kfree(opts->ncm_interf_group); |
|---|
| 1604 | 1621 | kfree(opts); |
|---|
| 1605 | 1622 | } |
|---|
| 1606 | 1623 | |
|---|
| 1607 | 1624 | static struct usb_function_instance *ncm_alloc_inst(void) |
|---|
| 1608 | 1625 | { |
|---|
| 1609 | 1626 | struct f_ncm_opts *opts; |
|---|
| 1627 | + struct usb_os_desc *descs[1]; |
|---|
| 1628 | + char *names[1]; |
|---|
| 1629 | + struct config_group *ncm_interf_group; |
|---|
| 1610 | 1630 | |
|---|
| 1611 | 1631 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); |
|---|
| 1612 | 1632 | if (!opts) |
|---|
| 1613 | 1633 | return ERR_PTR(-ENOMEM); |
|---|
| 1634 | + opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; |
|---|
| 1635 | + |
|---|
| 1614 | 1636 | mutex_init(&opts->lock); |
|---|
| 1615 | 1637 | opts->func_inst.free_func_inst = ncm_free_inst; |
|---|
| 1616 | 1638 | opts->net = gether_setup_default(); |
|---|
| .. | .. |
|---|
| 1619 | 1641 | kfree(opts); |
|---|
| 1620 | 1642 | return ERR_CAST(net); |
|---|
| 1621 | 1643 | } |
|---|
| 1644 | + INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); |
|---|
| 1645 | + |
|---|
| 1646 | + descs[0] = &opts->ncm_os_desc; |
|---|
| 1647 | + names[0] = "ncm"; |
|---|
| 1622 | 1648 | |
|---|
| 1623 | 1649 | config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); |
|---|
| 1650 | + ncm_interf_group = |
|---|
| 1651 | + usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, |
|---|
| 1652 | + names, THIS_MODULE); |
|---|
| 1653 | + if (IS_ERR(ncm_interf_group)) { |
|---|
| 1654 | + ncm_free_inst(&opts->func_inst); |
|---|
| 1655 | + return ERR_CAST(ncm_interf_group); |
|---|
| 1656 | + } |
|---|
| 1657 | + opts->ncm_interf_group = ncm_interf_group; |
|---|
| 1624 | 1658 | |
|---|
| 1625 | 1659 | return &opts->func_inst; |
|---|
| 1626 | 1660 | } |
|---|
| .. | .. |
|---|
| 1646 | 1680 | |
|---|
| 1647 | 1681 | hrtimer_cancel(&ncm->task_timer); |
|---|
| 1648 | 1682 | |
|---|
| 1683 | + kfree(f->os_desc_table); |
|---|
| 1684 | + f->os_desc_n = 0; |
|---|
| 1685 | + |
|---|
| 1649 | 1686 | ncm_string_defs[0].id = 0; |
|---|
| 1650 | 1687 | usb_free_all_descriptors(f); |
|---|
| 1651 | 1688 | |
|---|