aboutsummaryrefslogtreecommitdiffstats
path: root/net/caif/caif_usb.c
diff options
context:
space:
mode:
authorsjur.brandeland@stericsson.com <sjur.brandeland@stericsson.com>2011-12-04 06:22:53 -0500
committerDavid S. Miller <davem@davemloft.net>2011-12-05 18:27:56 -0500
commit7ad65bf68d705b445ef10b77ab50dab22be185ee (patch)
treeba354482821d658d6b9b6a4214d3dd4ba543ee23 /net/caif/caif_usb.c
parent63afe12f4be3b08597ae41ce7c0837bfc106b0ac (diff)
caif: Add support for CAIF over CDC NCM USB interface
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 interface register 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 <sjur.brandeland@stericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/caif/caif_usb.c')
-rw-r--r--net/caif/caif_usb.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c
new file mode 100644
index 000000000000..f5db57c58081
--- /dev/null
+++ b/net/caif/caif_usb.c
@@ -0,0 +1,208 @@
1/*
2 * CAIF USB handler
3 * Copyright (C) ST-Ericsson AB 2011
4 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
5 * License terms: GNU General Public License (GPL) version 2
6 *
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
10
11#include <linux/module.h>
12#include <linux/netdevice.h>
13#include <linux/slab.h>
14#include <linux/netdevice.h>
15#include <linux/mii.h>
16#include <linux/usb.h>
17#include <linux/usb/usbnet.h>
18#include <net/netns/generic.h>
19#include <net/caif/caif_dev.h>
20#include <net/caif/caif_layer.h>
21#include <net/caif/cfpkt.h>
22#include <net/caif/cfcnfg.h>
23
24MODULE_LICENSE("GPL");
25
26#define CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */
27#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */
28#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1)
29#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */
30#define STE_USB_PID_CAIF 0x2306 /* Product id for CAIF Modems */
31
32struct cfusbl {
33 struct cflayer layer;
34 u8 tx_eth_hdr[ETH_HLEN];
35};
36
37static bool pack_added;
38
39static int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt)
40{
41 u8 hpad;
42
43 /* Remove padding. */
44 cfpkt_extr_head(pkt, &hpad, 1);
45 cfpkt_extr_head(pkt, NULL, hpad);
46 return layr->up->receive(layr->up, pkt);
47}
48
49static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt)
50{
51 struct caif_payload_info *info;
52 u8 hpad;
53 u8 zeros[CFUSB_ALIGNMENT];
54 struct sk_buff *skb;
55 struct cfusbl *usbl = container_of(layr, struct cfusbl, layer);
56
57 skb = cfpkt_tonative(pkt);
58
59 skb_reset_network_header(skb);
60 skb->protocol = htons(ETH_P_IP);
61
62 info = cfpkt_info(pkt);
63 hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1);
64
65 if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) {
66 pr_warn("Headroom to small\n");
67 kfree_skb(skb);
68 return -EIO;
69 }
70 memset(zeros, 0, hpad);
71
72 cfpkt_add_head(pkt, zeros, hpad);
73 cfpkt_add_head(pkt, &hpad, 1);
74 cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr));
75 return layr->dn->transmit(layr->dn, pkt);
76}
77
78static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
79 int phyid)
80{
81 if (layr->up && layr->up->ctrlcmd)
82 layr->up->ctrlcmd(layr->up, ctrl, layr->id);
83}
84
85struct cflayer *cfusbl_create(int phyid, u8 ethaddr[ETH_ALEN],
86 u8 braddr[ETH_ALEN])
87{
88 struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC);
89
90 if (!this) {
91 pr_warn("Out of memory\n");
92 return NULL;
93 }
94 caif_assert(offsetof(struct cfusbl, layer) == 0);
95
96 memset(this, 0, sizeof(struct cflayer));
97 this->layer.receive = cfusbl_receive;
98 this->layer.transmit = cfusbl_transmit;
99 this->layer.ctrlcmd = cfusbl_ctrlcmd;
100 snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid);
101 this->layer.id = phyid;
102
103 /*
104 * Construct TX ethernet header:
105 * 0-5 destination address
106 * 5-11 source address
107 * 12-13 protocol type
108 */
109 memcpy(&this->tx_eth_hdr[ETH_ALEN], braddr, ETH_ALEN);
110 memcpy(&this->tx_eth_hdr[ETH_ALEN], ethaddr, ETH_ALEN);
111 this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff;
112 this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff;
113 pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n",
114 this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN,
115 this->tx_eth_hdr[12], this->tx_eth_hdr[13]);
116
117 return (struct cflayer *) this;
118}
119
120static struct packet_type caif_usb_type __read_mostly = {
121 .type = cpu_to_be16(ETH_P_802_EX1),
122};
123
124static int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
125 void *arg)
126{
127 struct net_device *dev = arg;
128 struct caif_dev_common common;
129 struct cflayer *layer, *link_support;
130 struct usbnet *usbnet = netdev_priv(dev);
131 struct usb_device *usbdev = usbnet->udev;
132 struct ethtool_drvinfo drvinfo;
133
134 /*
135 * Quirks: High-jack ethtool to find if we have a NCM device,
136 * and find it's VID/PID.
137 */
138 if (dev->ethtool_ops == NULL || dev->ethtool_ops->get_drvinfo == NULL)
139 return 0;
140
141 dev->ethtool_ops->get_drvinfo(dev, &drvinfo);
142 if (strncmp(drvinfo.driver, "cdc_ncm", 7) != 0)
143 return 0;
144
145 pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n",
146 le16_to_cpu(usbdev->descriptor.idVendor),
147 le16_to_cpu(usbdev->descriptor.idProduct));
148
149 /* Check for VID/PID that supports CAIF */
150 if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID &&
151 le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF))
152 return 0;
153
154 if (what == NETDEV_UNREGISTER)
155 module_put(THIS_MODULE);
156
157 if (what != NETDEV_REGISTER)
158 return 0;
159
160 __module_get(THIS_MODULE);
161
162 memset(&common, 0, sizeof(common));
163 common.use_frag = false;
164 common.use_fcs = false;
165 common.use_stx = false;
166 common.link_select = CAIF_LINK_HIGH_BANDW;
167 common.flowctrl = NULL;
168
169 link_support = cfusbl_create(dev->ifindex, dev->dev_addr,
170 dev->broadcast);
171
172 if (!link_support)
173 return -ENOMEM;
174
175 if (dev->num_tx_queues > 1)
176 pr_warn("USB device uses more than one tx queue\n");
177
178 caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN,
179 &layer, &caif_usb_type.func);
180 if (!pack_added)
181 dev_add_pack(&caif_usb_type);
182 pack_added = 1;
183
184 strncpy(layer->name, dev->name,
185 sizeof(layer->name) - 1);
186 layer->name[sizeof(layer->name) - 1] = 0;
187
188 return 0;
189}
190
191static struct notifier_block caif_device_notifier = {
192 .notifier_call = cfusbl_device_notify,
193 .priority = 0,
194};
195
196static int __init cfusbl_init(void)
197{
198 return register_netdevice_notifier(&caif_device_notifier);
199}
200
201static void __exit cfusbl_exit(void)
202{
203 unregister_netdevice_notifier(&caif_device_notifier);
204 dev_remove_pack(&caif_usb_type);
205}
206
207module_init(cfusbl_init);
208module_exit(cfusbl_exit);