From patchwork Fri Dec 2 14:06:17 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: sjur.brandeland@stericsson.com X-Patchwork-Id: 128867 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 69BE7B6F75 for ; Sat, 3 Dec 2011 01:06:43 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756748Ab1LBOGh (ORCPT ); Fri, 2 Dec 2011 09:06:37 -0500 Received: from mail-ww0-f44.google.com ([74.125.82.44]:59155 "EHLO mail-ww0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756695Ab1LBOGf (ORCPT ); Fri, 2 Dec 2011 09:06:35 -0500 Received: by wgbdr13 with SMTP id dr13so1270960wgb.1 for ; Fri, 02 Dec 2011 06:06:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:mime-version:content-type:content-transfer-encoding; bh=BApkU8GiNGphIw6cWXcKf3mJ5gYrAHMj+T14F5NKIzI=; b=UzKexIsA/IKQdN9SEAR7uC8vvoPZXj7ntU7adLNWnOvoItUBtEXZu3SY9R0x9lNoAt mY52/jICMiQfsIrb9TCZMUbFSz9+E3RObEJ+I54Ip6kHyn1o5BZszUMFWrtMnM31fIsM GBgPSo1arF/rMHOt5mXFpfL63YQNLTglkunWo= Received: by 10.227.207.205 with SMTP id fz13mr6131647wbb.0.1322834790961; Fri, 02 Dec 2011 06:06:30 -0800 (PST) Received: from localhost.localdomain ([212.4.57.94]) by mx.google.com with ESMTPS id u5sm3981183wbm.2.2011.12.02.06.06.29 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 02 Dec 2011 06:06:30 -0800 (PST) From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= To: netdev@vger.kernel.org Cc: David Miller , Alexey Orishko , =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Subject: [PATCH 3/3] caif: Add support for CAIF over CDC NCM USB interface Date: Fri, 2 Dec 2011 15:06:17 +0100 Message-Id: <1322834777-2486-4-git-send-email-sjur.brandeland@stericsson.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1322834777-2486-1-git-send-email-sjur.brandeland@stericsson.com> References: <1322834777-2486-1-git-send-email-sjur.brandeland@stericsson.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org NCM 1.0 does not support anything but Ethernet framing, hence CAIF payload will be put into Ethernet frames. Discovery is based on fixed USB vendor 0x04cc (ST-Ericsson), product-id 0x230f (NCM). In this variant only CAIF payload is sent over the NCM interface. The CAIF stack (cfusbl.c) will when USB registers first check if we got a CDC NCM USB interface with the right VID, PID. It will then read the device's Ethernet address and create a 'template' Ethernet TX header, using a broadcast address as the destination address, and EthType 0x88b5 (802.1 Local Experimental - vendor specific). A protocol handler for 0x88b5 is setup for reception of CAIF frames from the CDC NCM USB interface. Signed-off-by: Sjur Brændeland --- include/net/caif/cfusbl.h | 13 +++ net/caif/Kconfig | 11 +++ net/caif/Makefile | 4 +- net/caif/caif_dev.c | 7 ++ net/caif/cfusbl.c | 199 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 233 insertions(+), 1 deletions(-) create mode 100644 include/net/caif/cfusbl.h create mode 100644 net/caif/cfusbl.c diff --git a/include/net/caif/cfusbl.h b/include/net/caif/cfusbl.h new file mode 100644 index 0000000..7ef2499 --- /dev/null +++ b/include/net/caif/cfusbl.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland/sjur.brandeland@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef CFUSBL_H_ +#define CFUSBL_H_ + +void cfusbl_init(void); +void cfusbl_exit(void); + +#endif /* CFUSBL_H_ */ diff --git a/net/caif/Kconfig b/net/caif/Kconfig index 529750d..43cd844 100644 --- a/net/caif/Kconfig +++ b/net/caif/Kconfig @@ -40,3 +40,14 @@ config CAIF_NETDEV If you select to build it as a built-in then the main CAIF device must also be a built-in. If unsure say Y. + +config CAIF_USB + bool "CAIF USB support" + depends on CAIF + default CAIF + ---help--- + Say Y if you are using CAIF over USB CDC NCM. + This can be either built-in or a loadable module, + If you select to build it as a built-in then the main CAIF device must + also be a built-in. + If unsure say N. diff --git a/net/caif/Makefile b/net/caif/Makefile index ebcd4e7..3965f73 100644 --- a/net/caif/Makefile +++ b/net/caif/Makefile @@ -1,4 +1,5 @@ -ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG +ccflags-$(CONFIG_CAIF_DEBUG) += -DDEBUG +ccflags-$(CONFIG_CAIF_USB) += -DCAIF_USB caif-y := caif_dev.o \ cfcnfg.o cfmuxl.o cfctrl.o \ @@ -6,6 +7,7 @@ caif-y := caif_dev.o \ cfserl.o cfdgml.o \ cfrfml.o cfvidl.o cfutill.o \ cfsrvl.o cfpkt_skbuff.o +caif-$(CONFIG_CAIF_USB) += cfusbl.o obj-$(CONFIG_CAIF) += caif.o obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 415353e..6ef0e90 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -25,6 +25,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); @@ -506,6 +507,9 @@ static int __init caif_device_init(void) if (result) return result; +#ifdef CAIF_USB + cfusbl_init(); +#endif register_netdevice_notifier(&caif_device_notifier); dev_add_pack(&caif_packet_type); @@ -517,6 +521,9 @@ static void __exit caif_device_exit(void) unregister_pernet_device(&caif_net_ops); unregister_netdevice_notifier(&caif_device_notifier); dev_remove_pack(&caif_packet_type); +#ifdef CAIF_USB + cfusbl_exit(); +#endif } module_init(caif_device_init); diff --git a/net/caif/cfusbl.c b/net/caif/cfusbl.c new file mode 100644 index 0000000..8353a55 --- /dev/null +++ b/net/caif/cfusbl.c @@ -0,0 +1,199 @@ +/* + * CAIF USB handler + * Copyright (C) ST-Ericsson AB 2011 + * Author: Sjur Brendeland/sjur.brandeland@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +#define CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */ +#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */ +#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1) +#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */ +#define STE_USB_PID_CAIF 0x2306 /* Product id for CAIF Modems */ + +struct cfusbl { + struct cflayer layer; + u8 tx_eth_hdr[ETH_HLEN]; +}; + +static bool pack_added; + +static int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt) +{ + u8 hpad; + + /* Remove padding. */ + cfpkt_extr_head(pkt, &hpad, 1); + cfpkt_extr_head(pkt, NULL, hpad); + return layr->up->receive(layr->up, pkt); +} + +static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt) +{ + struct caif_payload_info *info; + u8 hpad; + u8 zeros[CFUSB_ALIGNMENT]; + struct sk_buff *skb; + struct cfusbl *usbl = container_of(layr, struct cfusbl, layer); + + skb = cfpkt_tonative(pkt); + + skb_reset_network_header(skb); + skb->protocol = htons(ETH_P_IP); + + info = cfpkt_info(pkt); + hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1); + + if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) { + pr_warn("Headroom to small\n"); + kfree_skb(skb); + return -EIO; + } + memset(zeros, 0, hpad); + + cfpkt_add_head(pkt, zeros, hpad); + cfpkt_add_head(pkt, &hpad, 1); + cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr)); + return layr->dn->transmit(layr->dn, pkt); +} + +static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, + int phyid) +{ + if (layr->up && layr->up->ctrlcmd) + layr->up->ctrlcmd(layr->up, ctrl, layr->id); +} + +struct cflayer *cfusbl_create(int phyid, u8 ethaddr[ETH_ALEN], + u8 braddr[ETH_ALEN]) +{ + struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC); + + if (!this) { + pr_warn("Out of memory\n"); + return NULL; + } + caif_assert(offsetof(struct cfusbl, layer) == 0); + + memset(this, 0, sizeof(struct cflayer)); + this->layer.receive = cfusbl_receive; + this->layer.transmit = cfusbl_transmit; + this->layer.ctrlcmd = cfusbl_ctrlcmd; + snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid); + this->layer.id = phyid; + + /* + * Construct TX ethernet header: + * 0-5 destination address + * 5-11 source address + * 12-13 protocol type + */ + memcpy(&this->tx_eth_hdr[ETH_ALEN], braddr, ETH_ALEN); + memcpy(&this->tx_eth_hdr[ETH_ALEN], ethaddr, ETH_ALEN); + this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff; + this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff; + pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n", + this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN, + this->tx_eth_hdr[12], this->tx_eth_hdr[13]); + + return (struct cflayer *) this; +} + +static struct packet_type caif_usb_type __read_mostly = { + .type = cpu_to_be16(ETH_P_802_EX1), +}; + +static int cfusbl_device_notify(struct notifier_block *me, unsigned long what, + void *arg) +{ + struct net_device *dev = arg; + struct caif_dev_common common; + struct cflayer *layer, *link_support; + struct usbnet *usbnet = netdev_priv(dev); + struct usb_device *usbdev = usbnet->udev; + struct ethtool_drvinfo drvinfo; + + if (what != NETDEV_REGISTER) + return 0; + /* + * Quirks: High-jack ethtool to find if we have a NCM device, + * and find it's VID/PID. + */ + if (dev->ethtool_ops == NULL || dev->ethtool_ops->get_drvinfo == NULL) + return 0; + + dev->ethtool_ops->get_drvinfo(dev, &drvinfo); + if (strncmp(drvinfo.driver, "cdc_ncm", 7) != 0) + return 0; + + pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n", + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct)); + + /* Check for VID/PID that supports CAIF */ + if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID && + le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF)) + return 0; + + memset(&common, 0, sizeof(common)); + common.use_frag = false; + common.use_fcs = false; + common.use_stx = false; + common.link_select = CAIF_LINK_HIGH_BANDW; + common.flowctrl = NULL; + + link_support = cfusbl_create(dev->ifindex, dev->dev_addr, + dev->broadcast); + if (!link_support) { + pr_warn("Out of memory\n"); + return -ENOMEM; + } + + if (dev->num_tx_queues > 1) + pr_warn("USB device uses more than one tx queue\n"); + + caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN, + &layer, &caif_usb_type.func); + if (!pack_added) + dev_add_pack(&caif_usb_type); + pack_added = 1; + + strncpy(layer->name, dev->name, + sizeof(layer->name) - 1); + layer->name[sizeof(layer->name) - 1] = 0; + + return 0; +} + +static struct notifier_block caif_device_notifier = { + .notifier_call = cfusbl_device_notify, + .priority = 0, +}; + +void cfusbl_init(void) +{ + register_netdevice_notifier(&caif_device_notifier); +} + +void cfusbl_exit(void) +{ + unregister_netdevice_notifier(&caif_device_notifier); + dev_remove_pack(&caif_usb_type); +}