162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_eem.c -- USB CDC Ethernet (EEM) link function driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003-2005,2008 David Brownell 662306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 762306a36Sopenharmony_ci * Copyright (C) 2009 EF Johnson Technologies 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/crc32.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "u_ether.h" 1862306a36Sopenharmony_ci#include "u_ether_configfs.h" 1962306a36Sopenharmony_ci#include "u_eem.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define EEM_HLEN 2 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * This function is a "CDC Ethernet Emulation Model" (CDC EEM) 2562306a36Sopenharmony_ci * Ethernet link. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct f_eem { 2962306a36Sopenharmony_ci struct gether port; 3062306a36Sopenharmony_ci u8 ctrl_id; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct in_context { 3462306a36Sopenharmony_ci struct sk_buff *skb; 3562306a36Sopenharmony_ci struct usb_ep *ep; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline struct f_eem *func_to_eem(struct usb_function *f) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return container_of(f, struct f_eem, port.func); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* interface descriptor: */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct usb_interface_descriptor eem_intf = { 4862306a36Sopenharmony_ci .bLength = sizeof eem_intf, 4962306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC */ 5262306a36Sopenharmony_ci .bNumEndpoints = 2, 5362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 5462306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_EEM, 5562306a36Sopenharmony_ci .bInterfaceProtocol = USB_CDC_PROTO_EEM, 5662306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* full speed support: */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct usb_endpoint_descriptor eem_fs_in_desc = { 6262306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 6362306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 6662306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor eem_fs_out_desc = { 7062306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 7162306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 7462306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct usb_descriptor_header *eem_fs_function[] = { 7862306a36Sopenharmony_ci /* CDC EEM control descriptors */ 7962306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_intf, 8062306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_fs_in_desc, 8162306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_fs_out_desc, 8262306a36Sopenharmony_ci NULL, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* high speed support: */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor eem_hs_in_desc = { 8862306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 8962306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 9262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 9362306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct usb_endpoint_descriptor eem_hs_out_desc = { 9762306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 9862306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 10162306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 10262306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic struct usb_descriptor_header *eem_hs_function[] = { 10662306a36Sopenharmony_ci /* CDC EEM control descriptors */ 10762306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_intf, 10862306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_hs_in_desc, 10962306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_hs_out_desc, 11062306a36Sopenharmony_ci NULL, 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* super speed support: */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor eem_ss_in_desc = { 11662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 11762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 12062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 12162306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor eem_ss_out_desc = { 12562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 12662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 12962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 13062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = { 13462306a36Sopenharmony_ci .bLength = sizeof eem_ss_bulk_comp_desc, 13562306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* the following 2 values can be tweaked if necessary */ 13862306a36Sopenharmony_ci /* .bMaxBurst = 0, */ 13962306a36Sopenharmony_ci /* .bmAttributes = 0, */ 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic struct usb_descriptor_header *eem_ss_function[] = { 14362306a36Sopenharmony_ci /* CDC EEM control descriptors */ 14462306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_intf, 14562306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_ss_in_desc, 14662306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc, 14762306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_ss_out_desc, 14862306a36Sopenharmony_ci (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc, 14962306a36Sopenharmony_ci NULL, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* string descriptors: */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic struct usb_string eem_string_defs[] = { 15562306a36Sopenharmony_ci [0].s = "CDC Ethernet Emulation Model (EEM)", 15662306a36Sopenharmony_ci { } /* end of list */ 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic struct usb_gadget_strings eem_string_table = { 16062306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 16162306a36Sopenharmony_ci .strings = eem_string_defs, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic struct usb_gadget_strings *eem_strings[] = { 16562306a36Sopenharmony_ci &eem_string_table, 16662306a36Sopenharmony_ci NULL, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int eem_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 17462306a36Sopenharmony_ci u16 w_index = le16_to_cpu(ctrl->wIndex); 17562306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 17662306a36Sopenharmony_ci u16 w_length = le16_to_cpu(ctrl->wLength); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", 17962306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 18062306a36Sopenharmony_ci w_value, w_index, w_length); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* device either stalls (value < 0) or reports success */ 18362306a36Sopenharmony_ci return -EOPNOTSUPP; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct f_eem *eem = func_to_eem(f); 19062306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 19162306a36Sopenharmony_ci struct net_device *net; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* we know alt == 0, so this is an activation or a reset */ 19462306a36Sopenharmony_ci if (alt != 0) 19562306a36Sopenharmony_ci goto fail; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (intf == eem->ctrl_id) { 19862306a36Sopenharmony_ci DBG(cdev, "reset eem\n"); 19962306a36Sopenharmony_ci gether_disconnect(&eem->port); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) { 20262306a36Sopenharmony_ci DBG(cdev, "init eem\n"); 20362306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, 20462306a36Sopenharmony_ci eem->port.in_ep) || 20562306a36Sopenharmony_ci config_ep_by_speed(cdev->gadget, f, 20662306a36Sopenharmony_ci eem->port.out_ep)) { 20762306a36Sopenharmony_ci eem->port.in_ep->desc = NULL; 20862306a36Sopenharmony_ci eem->port.out_ep->desc = NULL; 20962306a36Sopenharmony_ci goto fail; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* zlps should not occur because zero-length EEM packets 21462306a36Sopenharmony_ci * will be inserted in those cases where they would occur 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci eem->port.is_zlp_ok = 1; 21762306a36Sopenharmony_ci eem->port.cdc_filter = DEFAULT_FILTER; 21862306a36Sopenharmony_ci DBG(cdev, "activate eem\n"); 21962306a36Sopenharmony_ci net = gether_connect(&eem->port); 22062306a36Sopenharmony_ci if (IS_ERR(net)) 22162306a36Sopenharmony_ci return PTR_ERR(net); 22262306a36Sopenharmony_ci } else 22362306a36Sopenharmony_ci goto fail; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_cifail: 22762306a36Sopenharmony_ci return -EINVAL; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void eem_disable(struct usb_function *f) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct f_eem *eem = func_to_eem(f); 23362306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci DBG(cdev, "eem deactivated\n"); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (eem->port.in_ep->enabled) 23862306a36Sopenharmony_ci gether_disconnect(&eem->port); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* EEM function driver setup/binding */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int eem_bind(struct usb_configuration *c, struct usb_function *f) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 24862306a36Sopenharmony_ci struct f_eem *eem = func_to_eem(f); 24962306a36Sopenharmony_ci struct usb_string *us; 25062306a36Sopenharmony_ci int status; 25162306a36Sopenharmony_ci struct usb_ep *ep; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci struct f_eem_opts *eem_opts; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci eem_opts = container_of(f->fi, struct f_eem_opts, func_inst); 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * in drivers/usb/gadget/configfs.c:configfs_composite_bind() 25862306a36Sopenharmony_ci * configurations are bound in sequence with list_for_each_entry, 25962306a36Sopenharmony_ci * in each configuration its functions are bound in sequence 26062306a36Sopenharmony_ci * with list_for_each_entry, so we assume no race condition 26162306a36Sopenharmony_ci * with regard to eem_opts->bound access 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci if (!eem_opts->bound) { 26462306a36Sopenharmony_ci mutex_lock(&eem_opts->lock); 26562306a36Sopenharmony_ci gether_set_gadget(eem_opts->net, cdev->gadget); 26662306a36Sopenharmony_ci status = gether_register_netdev(eem_opts->net); 26762306a36Sopenharmony_ci mutex_unlock(&eem_opts->lock); 26862306a36Sopenharmony_ci if (status) 26962306a36Sopenharmony_ci return status; 27062306a36Sopenharmony_ci eem_opts->bound = true; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci us = usb_gstrings_attach(cdev, eem_strings, 27462306a36Sopenharmony_ci ARRAY_SIZE(eem_string_defs)); 27562306a36Sopenharmony_ci if (IS_ERR(us)) 27662306a36Sopenharmony_ci return PTR_ERR(us); 27762306a36Sopenharmony_ci eem_intf.iInterface = us[0].id; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* allocate instance-specific interface IDs */ 28062306a36Sopenharmony_ci status = usb_interface_id(c, f); 28162306a36Sopenharmony_ci if (status < 0) 28262306a36Sopenharmony_ci goto fail; 28362306a36Sopenharmony_ci eem->ctrl_id = status; 28462306a36Sopenharmony_ci eem_intf.bInterfaceNumber = status; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci status = -ENODEV; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 28962306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc); 29062306a36Sopenharmony_ci if (!ep) 29162306a36Sopenharmony_ci goto fail; 29262306a36Sopenharmony_ci eem->port.in_ep = ep; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc); 29562306a36Sopenharmony_ci if (!ep) 29662306a36Sopenharmony_ci goto fail; 29762306a36Sopenharmony_ci eem->port.out_ep = ep; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* support all relevant hardware speeds... we expect that when 30062306a36Sopenharmony_ci * hardware is dual speed, all bulk-capable endpoints work at 30162306a36Sopenharmony_ci * both speeds 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress; 30462306a36Sopenharmony_ci eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress; 30762306a36Sopenharmony_ci eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function, 31062306a36Sopenharmony_ci eem_ss_function, eem_ss_function); 31162306a36Sopenharmony_ci if (status) 31262306a36Sopenharmony_ci goto fail; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n", 31562306a36Sopenharmony_ci eem->port.in_ep->name, eem->port.out_ep->name); 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cifail: 31962306a36Sopenharmony_ci ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return status; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct in_context *ctx = req->context; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci dev_kfree_skb_any(ctx->skb); 32962306a36Sopenharmony_ci kfree(req->buf); 33062306a36Sopenharmony_ci usb_ep_free_request(ctx->ep, req); 33162306a36Sopenharmony_ci kfree(ctx); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/* 33562306a36Sopenharmony_ci * Add the EEM header and ethernet checksum. 33662306a36Sopenharmony_ci * We currently do not attempt to put multiple ethernet frames 33762306a36Sopenharmony_ci * into a single USB transfer 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_cistatic struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct sk_buff *skb2 = NULL; 34262306a36Sopenharmony_ci struct usb_ep *in = port->in_ep; 34362306a36Sopenharmony_ci int headroom, tailroom, padlen = 0; 34462306a36Sopenharmony_ci u16 len; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!skb) 34762306a36Sopenharmony_ci return NULL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci len = skb->len; 35062306a36Sopenharmony_ci headroom = skb_headroom(skb); 35162306a36Sopenharmony_ci tailroom = skb_tailroom(skb); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, 35462306a36Sopenharmony_ci * stick two bytes of zero-length EEM packet on the end. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) 35762306a36Sopenharmony_ci padlen += 2; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if ((tailroom >= (ETH_FCS_LEN + padlen)) && 36062306a36Sopenharmony_ci (headroom >= EEM_HLEN) && !skb_cloned(skb)) 36162306a36Sopenharmony_ci goto done; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC); 36462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 36562306a36Sopenharmony_ci skb = skb2; 36662306a36Sopenharmony_ci if (!skb) 36762306a36Sopenharmony_ci return skb; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cidone: 37062306a36Sopenharmony_ci /* use the "no CRC" option */ 37162306a36Sopenharmony_ci put_unaligned_be32(0xdeadbeef, skb_put(skb, 4)); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* EEM packet header format: 37462306a36Sopenharmony_ci * b0..13: length of ethernet frame 37562306a36Sopenharmony_ci * b14: bmCRC (0 == sentinel CRC) 37662306a36Sopenharmony_ci * b15: bmType (0 == data) 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci len = skb->len; 37962306a36Sopenharmony_ci put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2)); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* add a zero-length EEM packet, if needed */ 38262306a36Sopenharmony_ci if (padlen) 38362306a36Sopenharmony_ci put_unaligned_le16(0, skb_put(skb, 2)); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return skb; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* 38962306a36Sopenharmony_ci * Remove the EEM header. Note that there can be many EEM packets in a single 39062306a36Sopenharmony_ci * USB transfer, so we need to break them out and handle them independently. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_cistatic int eem_unwrap(struct gether *port, 39362306a36Sopenharmony_ci struct sk_buff *skb, 39462306a36Sopenharmony_ci struct sk_buff_head *list) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct usb_composite_dev *cdev = port->func.config->cdev; 39762306a36Sopenharmony_ci int status = 0; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci do { 40062306a36Sopenharmony_ci struct sk_buff *skb2; 40162306a36Sopenharmony_ci u16 header; 40262306a36Sopenharmony_ci u16 len = 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (skb->len < EEM_HLEN) { 40562306a36Sopenharmony_ci status = -EINVAL; 40662306a36Sopenharmony_ci DBG(cdev, "invalid EEM header\n"); 40762306a36Sopenharmony_ci goto error; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* remove the EEM header */ 41162306a36Sopenharmony_ci header = get_unaligned_le16(skb->data); 41262306a36Sopenharmony_ci skb_pull(skb, EEM_HLEN); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* EEM packet header format: 41562306a36Sopenharmony_ci * b0..14: EEM type dependent (data or command) 41662306a36Sopenharmony_ci * b15: bmType (0 == data, 1 == command) 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci if (header & BIT(15)) { 41962306a36Sopenharmony_ci struct usb_request *req; 42062306a36Sopenharmony_ci struct in_context *ctx; 42162306a36Sopenharmony_ci struct usb_ep *ep; 42262306a36Sopenharmony_ci u16 bmEEMCmd; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* EEM command packet format: 42562306a36Sopenharmony_ci * b0..10: bmEEMCmdParam 42662306a36Sopenharmony_ci * b11..13: bmEEMCmd 42762306a36Sopenharmony_ci * b14: reserved (must be zero) 42862306a36Sopenharmony_ci * b15: bmType (1 == command) 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci if (header & BIT(14)) 43162306a36Sopenharmony_ci continue; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci bmEEMCmd = (header >> 11) & 0x7; 43462306a36Sopenharmony_ci switch (bmEEMCmd) { 43562306a36Sopenharmony_ci case 0: /* echo */ 43662306a36Sopenharmony_ci len = header & 0x7FF; 43762306a36Sopenharmony_ci if (skb->len < len) { 43862306a36Sopenharmony_ci status = -EOVERFLOW; 43962306a36Sopenharmony_ci goto error; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci skb2 = skb_clone(skb, GFP_ATOMIC); 44362306a36Sopenharmony_ci if (unlikely(!skb2)) { 44462306a36Sopenharmony_ci DBG(cdev, "EEM echo response error\n"); 44562306a36Sopenharmony_ci goto next; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci skb_trim(skb2, len); 44862306a36Sopenharmony_ci put_unaligned_le16(BIT(15) | BIT(11) | len, 44962306a36Sopenharmony_ci skb_push(skb2, 2)); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci ep = port->in_ep; 45262306a36Sopenharmony_ci req = usb_ep_alloc_request(ep, GFP_ATOMIC); 45362306a36Sopenharmony_ci if (!req) { 45462306a36Sopenharmony_ci dev_kfree_skb_any(skb2); 45562306a36Sopenharmony_ci goto next; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci req->buf = kmalloc(skb2->len, GFP_KERNEL); 45962306a36Sopenharmony_ci if (!req->buf) { 46062306a36Sopenharmony_ci usb_ep_free_request(ep, req); 46162306a36Sopenharmony_ci dev_kfree_skb_any(skb2); 46262306a36Sopenharmony_ci goto next; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); 46662306a36Sopenharmony_ci if (!ctx) { 46762306a36Sopenharmony_ci kfree(req->buf); 46862306a36Sopenharmony_ci usb_ep_free_request(ep, req); 46962306a36Sopenharmony_ci dev_kfree_skb_any(skb2); 47062306a36Sopenharmony_ci goto next; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci ctx->skb = skb2; 47362306a36Sopenharmony_ci ctx->ep = ep; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci skb_copy_bits(skb2, 0, req->buf, skb2->len); 47662306a36Sopenharmony_ci req->length = skb2->len; 47762306a36Sopenharmony_ci req->complete = eem_cmd_complete; 47862306a36Sopenharmony_ci req->zero = 1; 47962306a36Sopenharmony_ci req->context = ctx; 48062306a36Sopenharmony_ci if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC)) 48162306a36Sopenharmony_ci DBG(cdev, "echo response queue fail\n"); 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci case 1: /* echo response */ 48562306a36Sopenharmony_ci case 2: /* suspend hint */ 48662306a36Sopenharmony_ci case 3: /* response hint */ 48762306a36Sopenharmony_ci case 4: /* response complete hint */ 48862306a36Sopenharmony_ci case 5: /* tickle */ 48962306a36Sopenharmony_ci default: /* reserved */ 49062306a36Sopenharmony_ci continue; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } else { 49362306a36Sopenharmony_ci u32 crc, crc2; 49462306a36Sopenharmony_ci struct sk_buff *skb3; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* check for zero-length EEM packet */ 49762306a36Sopenharmony_ci if (header == 0) 49862306a36Sopenharmony_ci continue; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* EEM data packet format: 50162306a36Sopenharmony_ci * b0..13: length of ethernet frame 50262306a36Sopenharmony_ci * b14: bmCRC (0 == sentinel, 1 == calculated) 50362306a36Sopenharmony_ci * b15: bmType (0 == data) 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci len = header & 0x3FFF; 50662306a36Sopenharmony_ci if ((skb->len < len) 50762306a36Sopenharmony_ci || (len < (ETH_HLEN + ETH_FCS_LEN))) { 50862306a36Sopenharmony_ci status = -EINVAL; 50962306a36Sopenharmony_ci goto error; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* validate CRC */ 51362306a36Sopenharmony_ci if (header & BIT(14)) { 51462306a36Sopenharmony_ci crc = get_unaligned_le32(skb->data + len 51562306a36Sopenharmony_ci - ETH_FCS_LEN); 51662306a36Sopenharmony_ci crc2 = ~crc32_le(~0, 51762306a36Sopenharmony_ci skb->data, len - ETH_FCS_LEN); 51862306a36Sopenharmony_ci } else { 51962306a36Sopenharmony_ci crc = get_unaligned_be32(skb->data + len 52062306a36Sopenharmony_ci - ETH_FCS_LEN); 52162306a36Sopenharmony_ci crc2 = 0xdeadbeef; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci if (crc != crc2) { 52462306a36Sopenharmony_ci DBG(cdev, "invalid EEM CRC\n"); 52562306a36Sopenharmony_ci goto next; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci skb2 = skb_clone(skb, GFP_ATOMIC); 52962306a36Sopenharmony_ci if (unlikely(!skb2)) { 53062306a36Sopenharmony_ci DBG(cdev, "unable to unframe EEM packet\n"); 53162306a36Sopenharmony_ci goto next; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci skb_trim(skb2, len - ETH_FCS_LEN); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci skb3 = skb_copy_expand(skb2, 53662306a36Sopenharmony_ci NET_IP_ALIGN, 53762306a36Sopenharmony_ci 0, 53862306a36Sopenharmony_ci GFP_ATOMIC); 53962306a36Sopenharmony_ci if (unlikely(!skb3)) { 54062306a36Sopenharmony_ci dev_kfree_skb_any(skb2); 54162306a36Sopenharmony_ci goto next; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci dev_kfree_skb_any(skb2); 54462306a36Sopenharmony_ci skb_queue_tail(list, skb3); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_cinext: 54762306a36Sopenharmony_ci skb_pull(skb, len); 54862306a36Sopenharmony_ci } while (skb->len); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cierror: 55162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 55262306a36Sopenharmony_ci return status; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic inline struct f_eem_opts *to_f_eem_opts(struct config_item *item) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_eem_opts, 55862306a36Sopenharmony_ci func_inst.group); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* f_eem_item_ops */ 56262306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM(eem); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* f_eem_opts_dev_addr */ 56562306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/* f_eem_opts_host_addr */ 56862306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* f_eem_opts_qmult */ 57162306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* f_eem_opts_ifname */ 57462306a36Sopenharmony_ciUSB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic struct configfs_attribute *eem_attrs[] = { 57762306a36Sopenharmony_ci &eem_opts_attr_dev_addr, 57862306a36Sopenharmony_ci &eem_opts_attr_host_addr, 57962306a36Sopenharmony_ci &eem_opts_attr_qmult, 58062306a36Sopenharmony_ci &eem_opts_attr_ifname, 58162306a36Sopenharmony_ci NULL, 58262306a36Sopenharmony_ci}; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic const struct config_item_type eem_func_type = { 58562306a36Sopenharmony_ci .ct_item_ops = &eem_item_ops, 58662306a36Sopenharmony_ci .ct_attrs = eem_attrs, 58762306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void eem_free_inst(struct usb_function_instance *f) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct f_eem_opts *opts; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci opts = container_of(f, struct f_eem_opts, func_inst); 59562306a36Sopenharmony_ci if (opts->bound) 59662306a36Sopenharmony_ci gether_cleanup(netdev_priv(opts->net)); 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci free_netdev(opts->net); 59962306a36Sopenharmony_ci kfree(opts); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic struct usb_function_instance *eem_alloc_inst(void) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct f_eem_opts *opts; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 60762306a36Sopenharmony_ci if (!opts) 60862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 60962306a36Sopenharmony_ci mutex_init(&opts->lock); 61062306a36Sopenharmony_ci opts->func_inst.free_func_inst = eem_free_inst; 61162306a36Sopenharmony_ci opts->net = gether_setup_default(); 61262306a36Sopenharmony_ci if (IS_ERR(opts->net)) { 61362306a36Sopenharmony_ci struct net_device *net = opts->net; 61462306a36Sopenharmony_ci kfree(opts); 61562306a36Sopenharmony_ci return ERR_CAST(net); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return &opts->func_inst; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void eem_free(struct usb_function *f) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct f_eem *eem; 62662306a36Sopenharmony_ci struct f_eem_opts *opts; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci eem = func_to_eem(f); 62962306a36Sopenharmony_ci opts = container_of(f->fi, struct f_eem_opts, func_inst); 63062306a36Sopenharmony_ci kfree(eem); 63162306a36Sopenharmony_ci mutex_lock(&opts->lock); 63262306a36Sopenharmony_ci opts->refcnt--; 63362306a36Sopenharmony_ci mutex_unlock(&opts->lock); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void eem_unbind(struct usb_configuration *c, struct usb_function *f) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci DBG(c->cdev, "eem unbind\n"); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci usb_free_all_descriptors(f); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic struct usb_function *eem_alloc(struct usb_function_instance *fi) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct f_eem *eem; 64662306a36Sopenharmony_ci struct f_eem_opts *opts; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* allocate and initialize one new instance */ 64962306a36Sopenharmony_ci eem = kzalloc(sizeof(*eem), GFP_KERNEL); 65062306a36Sopenharmony_ci if (!eem) 65162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci opts = container_of(fi, struct f_eem_opts, func_inst); 65462306a36Sopenharmony_ci mutex_lock(&opts->lock); 65562306a36Sopenharmony_ci opts->refcnt++; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci eem->port.ioport = netdev_priv(opts->net); 65862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 65962306a36Sopenharmony_ci eem->port.cdc_filter = DEFAULT_FILTER; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci eem->port.func.name = "cdc_eem"; 66262306a36Sopenharmony_ci /* descriptors are per-instance copies */ 66362306a36Sopenharmony_ci eem->port.func.bind = eem_bind; 66462306a36Sopenharmony_ci eem->port.func.unbind = eem_unbind; 66562306a36Sopenharmony_ci eem->port.func.set_alt = eem_set_alt; 66662306a36Sopenharmony_ci eem->port.func.setup = eem_setup; 66762306a36Sopenharmony_ci eem->port.func.disable = eem_disable; 66862306a36Sopenharmony_ci eem->port.func.free_func = eem_free; 66962306a36Sopenharmony_ci eem->port.wrap = eem_wrap; 67062306a36Sopenharmony_ci eem->port.unwrap = eem_unwrap; 67162306a36Sopenharmony_ci eem->port.header_len = EEM_HLEN; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci return &eem->port.func; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc); 67762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 67862306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 679