18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CAIF USB handler 48c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2011 58c2ecf20Sopenharmony_ci * Author: Sjur Brendeland 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/mii.h> 148c2ecf20Sopenharmony_ci#include <linux/usb.h> 158c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 188c2ecf20Sopenharmony_ci#include <net/caif/caif_dev.h> 198c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h> 208c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h> 218c2ecf20Sopenharmony_ci#include <net/caif/cfcnfg.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */ 268c2ecf20Sopenharmony_ci#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */ 278c2ecf20Sopenharmony_ci#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1) 288c2ecf20Sopenharmony_ci#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */ 298c2ecf20Sopenharmony_ci#define STE_USB_PID_CAIF 0x230f /* Product id for CAIF Modems */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct cfusbl { 328c2ecf20Sopenharmony_ci struct cflayer layer; 338c2ecf20Sopenharmony_ci u8 tx_eth_hdr[ETH_HLEN]; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic bool pack_added; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci u8 hpad; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Remove padding. */ 438c2ecf20Sopenharmony_ci cfpkt_extr_head(pkt, &hpad, 1); 448c2ecf20Sopenharmony_ci cfpkt_extr_head(pkt, NULL, hpad); 458c2ecf20Sopenharmony_ci return layr->up->receive(layr->up, pkt); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct caif_payload_info *info; 518c2ecf20Sopenharmony_ci u8 hpad; 528c2ecf20Sopenharmony_ci u8 zeros[CFUSB_ALIGNMENT]; 538c2ecf20Sopenharmony_ci struct sk_buff *skb; 548c2ecf20Sopenharmony_ci struct cfusbl *usbl = container_of(layr, struct cfusbl, layer); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci skb = cfpkt_tonative(pkt); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 598c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci info = cfpkt_info(pkt); 628c2ecf20Sopenharmony_ci hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) { 658c2ecf20Sopenharmony_ci pr_warn("Headroom too small\n"); 668c2ecf20Sopenharmony_ci kfree_skb(skb); 678c2ecf20Sopenharmony_ci return -EIO; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci memset(zeros, 0, hpad); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci cfpkt_add_head(pkt, zeros, hpad); 728c2ecf20Sopenharmony_ci cfpkt_add_head(pkt, &hpad, 1); 738c2ecf20Sopenharmony_ci cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr)); 748c2ecf20Sopenharmony_ci return layr->dn->transmit(layr->dn, pkt); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 788c2ecf20Sopenharmony_ci int phyid) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (layr->up && layr->up->ctrlcmd) 818c2ecf20Sopenharmony_ci layr->up->ctrlcmd(layr->up, ctrl, layr->id); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic struct cflayer *cfusbl_create(int phyid, u8 ethaddr[ETH_ALEN], 858c2ecf20Sopenharmony_ci u8 braddr[ETH_ALEN]) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!this) 908c2ecf20Sopenharmony_ci return NULL; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci caif_assert(offsetof(struct cfusbl, layer) == 0); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci memset(&this->layer, 0, sizeof(this->layer)); 958c2ecf20Sopenharmony_ci this->layer.receive = cfusbl_receive; 968c2ecf20Sopenharmony_ci this->layer.transmit = cfusbl_transmit; 978c2ecf20Sopenharmony_ci this->layer.ctrlcmd = cfusbl_ctrlcmd; 988c2ecf20Sopenharmony_ci snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid); 998c2ecf20Sopenharmony_ci this->layer.id = phyid; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * Construct TX ethernet header: 1038c2ecf20Sopenharmony_ci * 0-5 destination address 1048c2ecf20Sopenharmony_ci * 5-11 source address 1058c2ecf20Sopenharmony_ci * 12-13 protocol type 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr); 1088c2ecf20Sopenharmony_ci ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr); 1098c2ecf20Sopenharmony_ci this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff; 1108c2ecf20Sopenharmony_ci this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff; 1118c2ecf20Sopenharmony_ci pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n", 1128c2ecf20Sopenharmony_ci this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN, 1138c2ecf20Sopenharmony_ci this->tx_eth_hdr[12], this->tx_eth_hdr[13]); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return (struct cflayer *) this; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void cfusbl_release(struct cflayer *layer) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci kfree(layer); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic struct packet_type caif_usb_type __read_mostly = { 1248c2ecf20Sopenharmony_ci .type = cpu_to_be16(ETH_P_802_EX1), 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int cfusbl_device_notify(struct notifier_block *me, unsigned long what, 1288c2ecf20Sopenharmony_ci void *ptr) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1318c2ecf20Sopenharmony_ci struct caif_dev_common common; 1328c2ecf20Sopenharmony_ci struct cflayer *layer, *link_support; 1338c2ecf20Sopenharmony_ci struct usbnet *usbnet; 1348c2ecf20Sopenharmony_ci struct usb_device *usbdev; 1358c2ecf20Sopenharmony_ci int res; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED) 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Check whether we have a NCM device, and find its VID/PID. */ 1418c2ecf20Sopenharmony_ci if (!(dev->dev.parent && dev->dev.parent->driver && 1428c2ecf20Sopenharmony_ci strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0)) 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci usbnet = netdev_priv(dev); 1468c2ecf20Sopenharmony_ci usbdev = usbnet->udev; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n", 1498c2ecf20Sopenharmony_ci le16_to_cpu(usbdev->descriptor.idVendor), 1508c2ecf20Sopenharmony_ci le16_to_cpu(usbdev->descriptor.idProduct)); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Check for VID/PID that supports CAIF */ 1538c2ecf20Sopenharmony_ci if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID && 1548c2ecf20Sopenharmony_ci le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF)) 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (what == NETDEV_UNREGISTER) 1588c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (what != NETDEV_REGISTER) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci memset(&common, 0, sizeof(common)); 1668c2ecf20Sopenharmony_ci common.use_frag = false; 1678c2ecf20Sopenharmony_ci common.use_fcs = false; 1688c2ecf20Sopenharmony_ci common.use_stx = false; 1698c2ecf20Sopenharmony_ci common.link_select = CAIF_LINK_HIGH_BANDW; 1708c2ecf20Sopenharmony_ci common.flowctrl = NULL; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci link_support = cfusbl_create(dev->ifindex, dev->dev_addr, 1738c2ecf20Sopenharmony_ci dev->broadcast); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!link_support) 1768c2ecf20Sopenharmony_ci return -ENOMEM; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (dev->num_tx_queues > 1) 1798c2ecf20Sopenharmony_ci pr_warn("USB device uses more than one tx queue\n"); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci res = caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN, 1828c2ecf20Sopenharmony_ci &layer, &caif_usb_type.func); 1838c2ecf20Sopenharmony_ci if (res) 1848c2ecf20Sopenharmony_ci goto err; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!pack_added) 1878c2ecf20Sopenharmony_ci dev_add_pack(&caif_usb_type); 1888c2ecf20Sopenharmony_ci pack_added = true; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci strlcpy(layer->name, dev->name, sizeof(layer->name)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_cierr: 1948c2ecf20Sopenharmony_ci cfusbl_release(link_support); 1958c2ecf20Sopenharmony_ci return res; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct notifier_block caif_device_notifier = { 1998c2ecf20Sopenharmony_ci .notifier_call = cfusbl_device_notify, 2008c2ecf20Sopenharmony_ci .priority = 0, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int __init cfusbl_init(void) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci return register_netdevice_notifier(&caif_device_notifier); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void __exit cfusbl_exit(void) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&caif_device_notifier); 2118c2ecf20Sopenharmony_ci dev_remove_pack(&caif_usb_type); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cimodule_init(cfusbl_init); 2158c2ecf20Sopenharmony_cimodule_exit(cfusbl_exit); 216