162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CAIF USB handler 462306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2011 562306a36Sopenharmony_ci * Author: Sjur Brendeland 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/mii.h> 1462306a36Sopenharmony_ci#include <linux/usb.h> 1562306a36Sopenharmony_ci#include <linux/usb/usbnet.h> 1662306a36Sopenharmony_ci#include <linux/etherdevice.h> 1762306a36Sopenharmony_ci#include <net/netns/generic.h> 1862306a36Sopenharmony_ci#include <net/caif/caif_dev.h> 1962306a36Sopenharmony_ci#include <net/caif/caif_layer.h> 2062306a36Sopenharmony_ci#include <net/caif/cfpkt.h> 2162306a36Sopenharmony_ci#include <net/caif/cfcnfg.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */ 2662306a36Sopenharmony_ci#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */ 2762306a36Sopenharmony_ci#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1) 2862306a36Sopenharmony_ci#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */ 2962306a36Sopenharmony_ci#define STE_USB_PID_CAIF 0x230f /* Product id for CAIF Modems */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct cfusbl { 3262306a36Sopenharmony_ci struct cflayer layer; 3362306a36Sopenharmony_ci u8 tx_eth_hdr[ETH_HLEN]; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic bool pack_added; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci u8 hpad; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Remove padding. */ 4362306a36Sopenharmony_ci cfpkt_extr_head(pkt, &hpad, 1); 4462306a36Sopenharmony_ci cfpkt_extr_head(pkt, NULL, hpad); 4562306a36Sopenharmony_ci return layr->up->receive(layr->up, pkt); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct caif_payload_info *info; 5162306a36Sopenharmony_ci u8 hpad; 5262306a36Sopenharmony_ci u8 zeros[CFUSB_ALIGNMENT]; 5362306a36Sopenharmony_ci struct sk_buff *skb; 5462306a36Sopenharmony_ci struct cfusbl *usbl = container_of(layr, struct cfusbl, layer); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci skb = cfpkt_tonative(pkt); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci skb_reset_network_header(skb); 5962306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci info = cfpkt_info(pkt); 6262306a36Sopenharmony_ci hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) { 6562306a36Sopenharmony_ci pr_warn("Headroom too small\n"); 6662306a36Sopenharmony_ci kfree_skb(skb); 6762306a36Sopenharmony_ci return -EIO; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci memset(zeros, 0, hpad); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci cfpkt_add_head(pkt, zeros, hpad); 7262306a36Sopenharmony_ci cfpkt_add_head(pkt, &hpad, 1); 7362306a36Sopenharmony_ci cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr)); 7462306a36Sopenharmony_ci return layr->dn->transmit(layr->dn, pkt); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 7862306a36Sopenharmony_ci int phyid) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (layr->up && layr->up->ctrlcmd) 8162306a36Sopenharmony_ci layr->up->ctrlcmd(layr->up, ctrl, layr->id); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct cflayer *cfusbl_create(int phyid, const u8 ethaddr[ETH_ALEN], 8562306a36Sopenharmony_ci u8 braddr[ETH_ALEN]) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!this) 9062306a36Sopenharmony_ci return NULL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci caif_assert(offsetof(struct cfusbl, layer) == 0); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci memset(&this->layer, 0, sizeof(this->layer)); 9562306a36Sopenharmony_ci this->layer.receive = cfusbl_receive; 9662306a36Sopenharmony_ci this->layer.transmit = cfusbl_transmit; 9762306a36Sopenharmony_ci this->layer.ctrlcmd = cfusbl_ctrlcmd; 9862306a36Sopenharmony_ci snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid); 9962306a36Sopenharmony_ci this->layer.id = phyid; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * Construct TX ethernet header: 10362306a36Sopenharmony_ci * 0-5 destination address 10462306a36Sopenharmony_ci * 5-11 source address 10562306a36Sopenharmony_ci * 12-13 protocol type 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr); 10862306a36Sopenharmony_ci ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr); 10962306a36Sopenharmony_ci this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff; 11062306a36Sopenharmony_ci this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff; 11162306a36Sopenharmony_ci pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n", 11262306a36Sopenharmony_ci this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN, 11362306a36Sopenharmony_ci this->tx_eth_hdr[12], this->tx_eth_hdr[13]); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return (struct cflayer *) this; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void cfusbl_release(struct cflayer *layer) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci kfree(layer); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic struct packet_type caif_usb_type __read_mostly = { 12462306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_802_EX1), 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int cfusbl_device_notify(struct notifier_block *me, unsigned long what, 12862306a36Sopenharmony_ci void *ptr) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 13162306a36Sopenharmony_ci struct caif_dev_common common; 13262306a36Sopenharmony_ci struct cflayer *layer, *link_support; 13362306a36Sopenharmony_ci struct usbnet *usbnet; 13462306a36Sopenharmony_ci struct usb_device *usbdev; 13562306a36Sopenharmony_ci int res; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED) 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* Check whether we have a NCM device, and find its VID/PID. */ 14162306a36Sopenharmony_ci if (!(dev->dev.parent && dev->dev.parent->driver && 14262306a36Sopenharmony_ci strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0)) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci usbnet = netdev_priv(dev); 14662306a36Sopenharmony_ci usbdev = usbnet->udev; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n", 14962306a36Sopenharmony_ci le16_to_cpu(usbdev->descriptor.idVendor), 15062306a36Sopenharmony_ci le16_to_cpu(usbdev->descriptor.idProduct)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Check for VID/PID that supports CAIF */ 15362306a36Sopenharmony_ci if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID && 15462306a36Sopenharmony_ci le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF)) 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (what == NETDEV_UNREGISTER) 15862306a36Sopenharmony_ci module_put(THIS_MODULE); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (what != NETDEV_REGISTER) 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci __module_get(THIS_MODULE); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci memset(&common, 0, sizeof(common)); 16662306a36Sopenharmony_ci common.use_frag = false; 16762306a36Sopenharmony_ci common.use_fcs = false; 16862306a36Sopenharmony_ci common.use_stx = false; 16962306a36Sopenharmony_ci common.link_select = CAIF_LINK_HIGH_BANDW; 17062306a36Sopenharmony_ci common.flowctrl = NULL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci link_support = cfusbl_create(dev->ifindex, dev->dev_addr, 17362306a36Sopenharmony_ci dev->broadcast); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (!link_support) 17662306a36Sopenharmony_ci return -ENOMEM; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (dev->num_tx_queues > 1) 17962306a36Sopenharmony_ci pr_warn("USB device uses more than one tx queue\n"); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci res = caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN, 18262306a36Sopenharmony_ci &layer, &caif_usb_type.func); 18362306a36Sopenharmony_ci if (res) 18462306a36Sopenharmony_ci goto err; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!pack_added) 18762306a36Sopenharmony_ci dev_add_pack(&caif_usb_type); 18862306a36Sopenharmony_ci pack_added = true; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci strscpy(layer->name, dev->name, sizeof(layer->name)); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_cierr: 19462306a36Sopenharmony_ci cfusbl_release(link_support); 19562306a36Sopenharmony_ci return res; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic struct notifier_block caif_device_notifier = { 19962306a36Sopenharmony_ci .notifier_call = cfusbl_device_notify, 20062306a36Sopenharmony_ci .priority = 0, 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int __init cfusbl_init(void) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci return register_netdevice_notifier(&caif_device_notifier); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void __exit cfusbl_exit(void) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci unregister_netdevice_notifier(&caif_device_notifier); 21162306a36Sopenharmony_ci dev_remove_pack(&caif_usb_type); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cimodule_init(cfusbl_init); 21562306a36Sopenharmony_cimodule_exit(cfusbl_exit); 216