162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_phonet.c -- USB CDC Phonet function 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Rémi Denis-Courmont 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/if_ether.h> 1862306a36Sopenharmony_ci#include <linux/if_phonet.h> 1962306a36Sopenharmony_ci#include <linux/if_arp.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/usb/ch9.h> 2262306a36Sopenharmony_ci#include <linux/usb/cdc.h> 2362306a36Sopenharmony_ci#include <linux/usb/composite.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "u_phonet.h" 2662306a36Sopenharmony_ci#include "u_ether.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define PN_MEDIA_USB 0x1B 2962306a36Sopenharmony_ci#define MAXPACKET 512 3062306a36Sopenharmony_ci#if (PAGE_SIZE % MAXPACKET) 3162306a36Sopenharmony_ci#error MAXPACKET must divide PAGE_SIZE! 3262306a36Sopenharmony_ci#endif 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct phonet_port { 3762306a36Sopenharmony_ci struct f_phonet *usb; 3862306a36Sopenharmony_ci spinlock_t lock; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct f_phonet { 4262306a36Sopenharmony_ci struct usb_function function; 4362306a36Sopenharmony_ci struct { 4462306a36Sopenharmony_ci struct sk_buff *skb; 4562306a36Sopenharmony_ci spinlock_t lock; 4662306a36Sopenharmony_ci } rx; 4762306a36Sopenharmony_ci struct net_device *dev; 4862306a36Sopenharmony_ci struct usb_ep *in_ep, *out_ep; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci struct usb_request *in_req; 5162306a36Sopenharmony_ci struct usb_request *out_reqv[]; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int phonet_rxq_size = 17; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic inline struct f_phonet *func_to_pn(struct usb_function *f) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return container_of(f, struct f_phonet, function); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define USB_CDC_SUBCLASS_PHONET 0xfe 6462306a36Sopenharmony_ci#define USB_CDC_PHONET_TYPE 0xab 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct usb_interface_descriptor 6762306a36Sopenharmony_cipn_control_intf_desc = { 6862306a36Sopenharmony_ci .bLength = sizeof pn_control_intf_desc, 6962306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC, */ 7262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 7362306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_PHONET, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic const struct usb_cdc_header_desc 7762306a36Sopenharmony_cipn_header_desc = { 7862306a36Sopenharmony_ci .bLength = sizeof pn_header_desc, 7962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 8062306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_HEADER_TYPE, 8162306a36Sopenharmony_ci .bcdCDC = cpu_to_le16(0x0110), 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic const struct usb_cdc_header_desc 8562306a36Sopenharmony_cipn_phonet_desc = { 8662306a36Sopenharmony_ci .bLength = sizeof pn_phonet_desc, 8762306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 8862306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_PHONET_TYPE, 8962306a36Sopenharmony_ci .bcdCDC = cpu_to_le16(0x1505), /* ??? */ 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic struct usb_cdc_union_desc 9362306a36Sopenharmony_cipn_union_desc = { 9462306a36Sopenharmony_ci .bLength = sizeof pn_union_desc, 9562306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 9662306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_UNION_TYPE, 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* .bMasterInterface0 = DYNAMIC, */ 9962306a36Sopenharmony_ci /* .bSlaveInterface0 = DYNAMIC, */ 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic struct usb_interface_descriptor 10362306a36Sopenharmony_cipn_data_nop_intf_desc = { 10462306a36Sopenharmony_ci .bLength = sizeof pn_data_nop_intf_desc, 10562306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC, */ 10862306a36Sopenharmony_ci .bAlternateSetting = 0, 10962306a36Sopenharmony_ci .bNumEndpoints = 0, 11062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic struct usb_interface_descriptor 11462306a36Sopenharmony_cipn_data_intf_desc = { 11562306a36Sopenharmony_ci .bLength = sizeof pn_data_intf_desc, 11662306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC, */ 11962306a36Sopenharmony_ci .bAlternateSetting = 1, 12062306a36Sopenharmony_ci .bNumEndpoints = 2, 12162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor 12562306a36Sopenharmony_cipn_fs_sink_desc = { 12662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 12762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 13062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor 13462306a36Sopenharmony_cipn_hs_sink_desc = { 13562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 13662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 13962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 14062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(MAXPACKET), 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor 14462306a36Sopenharmony_cipn_fs_source_desc = { 14562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 14662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 14962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor 15362306a36Sopenharmony_cipn_hs_source_desc = { 15462306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 15562306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 15862306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 15962306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic struct usb_descriptor_header *fs_pn_function[] = { 16362306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_control_intf_desc, 16462306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_header_desc, 16562306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_phonet_desc, 16662306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_union_desc, 16762306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_data_nop_intf_desc, 16862306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_data_intf_desc, 16962306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_fs_sink_desc, 17062306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_fs_source_desc, 17162306a36Sopenharmony_ci NULL, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct usb_descriptor_header *hs_pn_function[] = { 17562306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_control_intf_desc, 17662306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_header_desc, 17762306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_phonet_desc, 17862306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_union_desc, 17962306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_data_nop_intf_desc, 18062306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_data_intf_desc, 18162306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_hs_sink_desc, 18262306a36Sopenharmony_ci (struct usb_descriptor_header *) &pn_hs_source_desc, 18362306a36Sopenharmony_ci NULL, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int pn_net_open(struct net_device *dev) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci netif_wake_queue(dev); 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int pn_net_close(struct net_device *dev) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci netif_stop_queue(dev); 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void pn_tx_complete(struct usb_ep *ep, struct usb_request *req) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct f_phonet *fp = ep->driver_data; 20362306a36Sopenharmony_ci struct net_device *dev = fp->dev; 20462306a36Sopenharmony_ci struct sk_buff *skb = req->context; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci switch (req->status) { 20762306a36Sopenharmony_ci case 0: 20862306a36Sopenharmony_ci dev->stats.tx_packets++; 20962306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci case -ESHUTDOWN: /* disconnected */ 21362306a36Sopenharmony_ci case -ECONNRESET: /* disabled */ 21462306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 21562306a36Sopenharmony_ci fallthrough; 21662306a36Sopenharmony_ci default: 21762306a36Sopenharmony_ci dev->stats.tx_errors++; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 22162306a36Sopenharmony_ci netif_wake_queue(dev); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic netdev_tx_t pn_net_xmit(struct sk_buff *skb, struct net_device *dev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct phonet_port *port = netdev_priv(dev); 22762306a36Sopenharmony_ci struct f_phonet *fp; 22862306a36Sopenharmony_ci struct usb_request *req; 22962306a36Sopenharmony_ci unsigned long flags; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (skb->protocol != htons(ETH_P_PHONET)) 23262306a36Sopenharmony_ci goto out; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 23562306a36Sopenharmony_ci fp = port->usb; 23662306a36Sopenharmony_ci if (unlikely(!fp)) /* race with carrier loss */ 23762306a36Sopenharmony_ci goto out_unlock; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci req = fp->in_req; 24062306a36Sopenharmony_ci req->buf = skb->data; 24162306a36Sopenharmony_ci req->length = skb->len; 24262306a36Sopenharmony_ci req->complete = pn_tx_complete; 24362306a36Sopenharmony_ci req->zero = 1; 24462306a36Sopenharmony_ci req->context = skb; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC))) 24762306a36Sopenharmony_ci goto out_unlock; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci netif_stop_queue(dev); 25062306a36Sopenharmony_ci skb = NULL; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciout_unlock: 25362306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 25462306a36Sopenharmony_ciout: 25562306a36Sopenharmony_ci if (unlikely(skb)) { 25662306a36Sopenharmony_ci dev_kfree_skb(skb); 25762306a36Sopenharmony_ci dev->stats.tx_dropped++; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci return NETDEV_TX_OK; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic const struct net_device_ops pn_netdev_ops = { 26362306a36Sopenharmony_ci .ndo_open = pn_net_open, 26462306a36Sopenharmony_ci .ndo_stop = pn_net_close, 26562306a36Sopenharmony_ci .ndo_start_xmit = pn_net_xmit, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void pn_net_setup(struct net_device *dev) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci const u8 addr = PN_MEDIA_USB; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci dev->features = 0; 27362306a36Sopenharmony_ci dev->type = ARPHRD_PHONET; 27462306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 27562306a36Sopenharmony_ci dev->mtu = PHONET_DEV_MTU; 27662306a36Sopenharmony_ci dev->min_mtu = PHONET_MIN_MTU; 27762306a36Sopenharmony_ci dev->max_mtu = PHONET_MAX_MTU; 27862306a36Sopenharmony_ci dev->hard_header_len = 1; 27962306a36Sopenharmony_ci dev->addr_len = 1; 28062306a36Sopenharmony_ci dev_addr_set(dev, &addr); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci dev->tx_queue_len = 1; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci dev->netdev_ops = &pn_netdev_ops; 28562306a36Sopenharmony_ci dev->needs_free_netdev = true; 28662306a36Sopenharmony_ci dev->header_ops = &phonet_header_ops; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* 29262306a36Sopenharmony_ci * Queue buffer for data from the host 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_cistatic int 29562306a36Sopenharmony_cipn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct page *page; 29862306a36Sopenharmony_ci int err; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC); 30162306a36Sopenharmony_ci if (!page) 30262306a36Sopenharmony_ci return -ENOMEM; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci req->buf = page_address(page); 30562306a36Sopenharmony_ci req->length = PAGE_SIZE; 30662306a36Sopenharmony_ci req->context = page; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci err = usb_ep_queue(fp->out_ep, req, gfp_flags); 30962306a36Sopenharmony_ci if (unlikely(err)) 31062306a36Sopenharmony_ci put_page(page); 31162306a36Sopenharmony_ci return err; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct f_phonet *fp = ep->driver_data; 31762306a36Sopenharmony_ci struct net_device *dev = fp->dev; 31862306a36Sopenharmony_ci struct page *page = req->context; 31962306a36Sopenharmony_ci struct sk_buff *skb; 32062306a36Sopenharmony_ci unsigned long flags; 32162306a36Sopenharmony_ci int status = req->status; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci switch (status) { 32462306a36Sopenharmony_ci case 0: 32562306a36Sopenharmony_ci spin_lock_irqsave(&fp->rx.lock, flags); 32662306a36Sopenharmony_ci skb = fp->rx.skb; 32762306a36Sopenharmony_ci if (!skb) 32862306a36Sopenharmony_ci skb = fp->rx.skb = netdev_alloc_skb(dev, 12); 32962306a36Sopenharmony_ci if (req->actual < req->length) /* Last fragment */ 33062306a36Sopenharmony_ci fp->rx.skb = NULL; 33162306a36Sopenharmony_ci spin_unlock_irqrestore(&fp->rx.lock, flags); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (unlikely(!skb)) 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (skb->len == 0) { /* First fragment */ 33762306a36Sopenharmony_ci skb->protocol = htons(ETH_P_PHONET); 33862306a36Sopenharmony_ci skb_reset_mac_header(skb); 33962306a36Sopenharmony_ci /* Can't use pskb_pull() on page in IRQ */ 34062306a36Sopenharmony_ci skb_put_data(skb, page_address(page), 1); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 34462306a36Sopenharmony_ci skb->len <= 1, req->actual, PAGE_SIZE); 34562306a36Sopenharmony_ci page = NULL; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (req->actual < req->length) { /* Last fragment */ 34862306a36Sopenharmony_ci skb->dev = dev; 34962306a36Sopenharmony_ci dev->stats.rx_packets++; 35062306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci netif_rx(skb); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Do not resubmit in these cases: */ 35762306a36Sopenharmony_ci case -ESHUTDOWN: /* disconnect */ 35862306a36Sopenharmony_ci case -ECONNABORTED: /* hw reset */ 35962306a36Sopenharmony_ci case -ECONNRESET: /* dequeued (unlink or netif down) */ 36062306a36Sopenharmony_ci req = NULL; 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Do resubmit in these cases: */ 36462306a36Sopenharmony_ci case -EOVERFLOW: /* request buffer overflow */ 36562306a36Sopenharmony_ci dev->stats.rx_over_errors++; 36662306a36Sopenharmony_ci fallthrough; 36762306a36Sopenharmony_ci default: 36862306a36Sopenharmony_ci dev->stats.rx_errors++; 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (page) 37362306a36Sopenharmony_ci put_page(page); 37462306a36Sopenharmony_ci if (req) 37562306a36Sopenharmony_ci pn_rx_submit(fp, req, GFP_ATOMIC); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void __pn_reset(struct usb_function *f) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct f_phonet *fp = func_to_pn(f); 38362306a36Sopenharmony_ci struct net_device *dev = fp->dev; 38462306a36Sopenharmony_ci struct phonet_port *port = netdev_priv(dev); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci netif_carrier_off(dev); 38762306a36Sopenharmony_ci port->usb = NULL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci usb_ep_disable(fp->out_ep); 39062306a36Sopenharmony_ci usb_ep_disable(fp->in_ep); 39162306a36Sopenharmony_ci if (fp->rx.skb) { 39262306a36Sopenharmony_ci dev_kfree_skb_irq(fp->rx.skb); 39362306a36Sopenharmony_ci fp->rx.skb = NULL; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct f_phonet *fp = func_to_pn(f); 40062306a36Sopenharmony_ci struct usb_gadget *gadget = fp->function.config->cdev->gadget; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (intf == pn_control_intf_desc.bInterfaceNumber) 40362306a36Sopenharmony_ci /* control interface, no altsetting */ 40462306a36Sopenharmony_ci return (alt > 0) ? -EINVAL : 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (intf == pn_data_intf_desc.bInterfaceNumber) { 40762306a36Sopenharmony_ci struct net_device *dev = fp->dev; 40862306a36Sopenharmony_ci struct phonet_port *port = netdev_priv(dev); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* data intf (0: inactive, 1: active) */ 41162306a36Sopenharmony_ci if (alt > 1) 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci spin_lock(&port->lock); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (fp->in_ep->enabled) 41762306a36Sopenharmony_ci __pn_reset(f); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (alt == 1) { 42062306a36Sopenharmony_ci int i; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (config_ep_by_speed(gadget, f, fp->in_ep) || 42362306a36Sopenharmony_ci config_ep_by_speed(gadget, f, fp->out_ep)) { 42462306a36Sopenharmony_ci fp->in_ep->desc = NULL; 42562306a36Sopenharmony_ci fp->out_ep->desc = NULL; 42662306a36Sopenharmony_ci spin_unlock(&port->lock); 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci usb_ep_enable(fp->out_ep); 43062306a36Sopenharmony_ci usb_ep_enable(fp->in_ep); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci port->usb = fp; 43362306a36Sopenharmony_ci fp->out_ep->driver_data = fp; 43462306a36Sopenharmony_ci fp->in_ep->driver_data = fp; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci netif_carrier_on(dev); 43762306a36Sopenharmony_ci for (i = 0; i < phonet_rxq_size; i++) 43862306a36Sopenharmony_ci pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci spin_unlock(&port->lock); 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int pn_get_alt(struct usb_function *f, unsigned intf) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct f_phonet *fp = func_to_pn(f); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (intf == pn_control_intf_desc.bInterfaceNumber) 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (intf == pn_data_intf_desc.bInterfaceNumber) { 45562306a36Sopenharmony_ci struct phonet_port *port = netdev_priv(fp->dev); 45662306a36Sopenharmony_ci u8 alt; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci spin_lock(&port->lock); 45962306a36Sopenharmony_ci alt = port->usb != NULL; 46062306a36Sopenharmony_ci spin_unlock(&port->lock); 46162306a36Sopenharmony_ci return alt; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void pn_disconnect(struct usb_function *f) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct f_phonet *fp = func_to_pn(f); 47062306a36Sopenharmony_ci struct phonet_port *port = netdev_priv(fp->dev); 47162306a36Sopenharmony_ci unsigned long flags; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* remain disabled until set_alt */ 47462306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 47562306a36Sopenharmony_ci __pn_reset(f); 47662306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int pn_bind(struct usb_configuration *c, struct usb_function *f) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 48462306a36Sopenharmony_ci struct usb_gadget *gadget = cdev->gadget; 48562306a36Sopenharmony_ci struct f_phonet *fp = func_to_pn(f); 48662306a36Sopenharmony_ci struct usb_ep *ep; 48762306a36Sopenharmony_ci int status, i; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci struct f_phonet_opts *phonet_opts; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* 49462306a36Sopenharmony_ci * in drivers/usb/gadget/configfs.c:configfs_composite_bind() 49562306a36Sopenharmony_ci * configurations are bound in sequence with list_for_each_entry, 49662306a36Sopenharmony_ci * in each configuration its functions are bound in sequence 49762306a36Sopenharmony_ci * with list_for_each_entry, so we assume no race condition 49862306a36Sopenharmony_ci * with regard to phonet_opts->bound access 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci if (!phonet_opts->bound) { 50162306a36Sopenharmony_ci gphonet_set_gadget(phonet_opts->net, gadget); 50262306a36Sopenharmony_ci status = gphonet_register_netdev(phonet_opts->net); 50362306a36Sopenharmony_ci if (status) 50462306a36Sopenharmony_ci return status; 50562306a36Sopenharmony_ci phonet_opts->bound = true; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Reserve interface IDs */ 50962306a36Sopenharmony_ci status = usb_interface_id(c, f); 51062306a36Sopenharmony_ci if (status < 0) 51162306a36Sopenharmony_ci goto err; 51262306a36Sopenharmony_ci pn_control_intf_desc.bInterfaceNumber = status; 51362306a36Sopenharmony_ci pn_union_desc.bMasterInterface0 = status; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci status = usb_interface_id(c, f); 51662306a36Sopenharmony_ci if (status < 0) 51762306a36Sopenharmony_ci goto err; 51862306a36Sopenharmony_ci pn_data_nop_intf_desc.bInterfaceNumber = status; 51962306a36Sopenharmony_ci pn_data_intf_desc.bInterfaceNumber = status; 52062306a36Sopenharmony_ci pn_union_desc.bSlaveInterface0 = status; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Reserve endpoints */ 52362306a36Sopenharmony_ci status = -ENODEV; 52462306a36Sopenharmony_ci ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc); 52562306a36Sopenharmony_ci if (!ep) 52662306a36Sopenharmony_ci goto err; 52762306a36Sopenharmony_ci fp->out_ep = ep; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc); 53062306a36Sopenharmony_ci if (!ep) 53162306a36Sopenharmony_ci goto err; 53262306a36Sopenharmony_ci fp->in_ep = ep; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress; 53562306a36Sopenharmony_ci pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Do not try to bind Phonet twice... */ 53862306a36Sopenharmony_ci status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function, 53962306a36Sopenharmony_ci NULL, NULL); 54062306a36Sopenharmony_ci if (status) 54162306a36Sopenharmony_ci goto err; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Incoming USB requests */ 54462306a36Sopenharmony_ci status = -ENOMEM; 54562306a36Sopenharmony_ci for (i = 0; i < phonet_rxq_size; i++) { 54662306a36Sopenharmony_ci struct usb_request *req; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL); 54962306a36Sopenharmony_ci if (!req) 55062306a36Sopenharmony_ci goto err_req; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci req->complete = pn_rx_complete; 55362306a36Sopenharmony_ci fp->out_reqv[i] = req; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* Outgoing USB requests */ 55762306a36Sopenharmony_ci fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL); 55862306a36Sopenharmony_ci if (!fp->in_req) 55962306a36Sopenharmony_ci goto err_req; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci INFO(cdev, "USB CDC Phonet function\n"); 56262306a36Sopenharmony_ci INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name, 56362306a36Sopenharmony_ci fp->out_ep->name, fp->in_ep->name); 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cierr_req: 56762306a36Sopenharmony_ci for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++) 56862306a36Sopenharmony_ci usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); 56962306a36Sopenharmony_ci usb_free_all_descriptors(f); 57062306a36Sopenharmony_cierr: 57162306a36Sopenharmony_ci ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n"); 57262306a36Sopenharmony_ci return status; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_phonet_opts, 57862306a36Sopenharmony_ci func_inst.group); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void phonet_attr_release(struct config_item *item) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct f_phonet_opts *opts = to_f_phonet_opts(item); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci usb_put_function_instance(&opts->func_inst); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic struct configfs_item_operations phonet_item_ops = { 58962306a36Sopenharmony_ci .release = phonet_attr_release, 59062306a36Sopenharmony_ci}; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic ssize_t f_phonet_ifname_show(struct config_item *item, char *page) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci return gether_get_ifname(to_f_phonet_opts(item)->net, page, PAGE_SIZE); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ciCONFIGFS_ATTR_RO(f_phonet_, ifname); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic struct configfs_attribute *phonet_attrs[] = { 60062306a36Sopenharmony_ci &f_phonet_attr_ifname, 60162306a36Sopenharmony_ci NULL, 60262306a36Sopenharmony_ci}; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic const struct config_item_type phonet_func_type = { 60562306a36Sopenharmony_ci .ct_item_ops = &phonet_item_ops, 60662306a36Sopenharmony_ci .ct_attrs = phonet_attrs, 60762306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 60862306a36Sopenharmony_ci}; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void phonet_free_inst(struct usb_function_instance *f) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct f_phonet_opts *opts; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci opts = container_of(f, struct f_phonet_opts, func_inst); 61562306a36Sopenharmony_ci if (opts->bound) 61662306a36Sopenharmony_ci gphonet_cleanup(opts->net); 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci free_netdev(opts->net); 61962306a36Sopenharmony_ci kfree(opts); 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic struct usb_function_instance *phonet_alloc_inst(void) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct f_phonet_opts *opts; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 62762306a36Sopenharmony_ci if (!opts) 62862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci opts->func_inst.free_func_inst = phonet_free_inst; 63162306a36Sopenharmony_ci opts->net = gphonet_setup_default(); 63262306a36Sopenharmony_ci if (IS_ERR(opts->net)) { 63362306a36Sopenharmony_ci struct net_device *net = opts->net; 63462306a36Sopenharmony_ci kfree(opts); 63562306a36Sopenharmony_ci return ERR_CAST(net); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", 63962306a36Sopenharmony_ci &phonet_func_type); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return &opts->func_inst; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic void phonet_free(struct usb_function *f) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct f_phonet *phonet; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci phonet = func_to_pn(f); 64962306a36Sopenharmony_ci kfree(phonet); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic void pn_unbind(struct usb_configuration *c, struct usb_function *f) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct f_phonet *fp = func_to_pn(f); 65562306a36Sopenharmony_ci int i; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* We are already disconnected */ 65862306a36Sopenharmony_ci if (fp->in_req) 65962306a36Sopenharmony_ci usb_ep_free_request(fp->in_ep, fp->in_req); 66062306a36Sopenharmony_ci for (i = 0; i < phonet_rxq_size; i++) 66162306a36Sopenharmony_ci if (fp->out_reqv[i]) 66262306a36Sopenharmony_ci usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci usb_free_all_descriptors(f); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic struct usb_function *phonet_alloc(struct usb_function_instance *fi) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct f_phonet *fp; 67062306a36Sopenharmony_ci struct f_phonet_opts *opts; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL); 67362306a36Sopenharmony_ci if (!fp) 67462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci opts = container_of(fi, struct f_phonet_opts, func_inst); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci fp->dev = opts->net; 67962306a36Sopenharmony_ci fp->function.name = "phonet"; 68062306a36Sopenharmony_ci fp->function.bind = pn_bind; 68162306a36Sopenharmony_ci fp->function.unbind = pn_unbind; 68262306a36Sopenharmony_ci fp->function.set_alt = pn_set_alt; 68362306a36Sopenharmony_ci fp->function.get_alt = pn_get_alt; 68462306a36Sopenharmony_ci fp->function.disable = pn_disconnect; 68562306a36Sopenharmony_ci fp->function.free_func = phonet_free; 68662306a36Sopenharmony_ci spin_lock_init(&fp->rx.lock); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return &fp->function; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistruct net_device *gphonet_setup_default(void) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct net_device *dev; 69462306a36Sopenharmony_ci struct phonet_port *port; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Create net device */ 69762306a36Sopenharmony_ci dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN, 69862306a36Sopenharmony_ci pn_net_setup); 69962306a36Sopenharmony_ci if (!dev) 70062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci port = netdev_priv(dev); 70362306a36Sopenharmony_ci spin_lock_init(&port->lock); 70462306a36Sopenharmony_ci netif_carrier_off(dev); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return dev; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_civoid gphonet_set_gadget(struct net_device *net, struct usb_gadget *g) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci SET_NETDEV_DEV(net, &g->dev); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciint gphonet_register_netdev(struct net_device *net) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci int status; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci status = register_netdev(net); 71962306a36Sopenharmony_ci if (status) 72062306a36Sopenharmony_ci free_netdev(net); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return status; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_civoid gphonet_cleanup(struct net_device *dev) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci unregister_netdev(dev); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc); 73162306a36Sopenharmony_ciMODULE_AUTHOR("Rémi Denis-Courmont"); 73262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 733