diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/caif/Kconfig | 11 | ||||
-rw-r--r-- | net/caif/Makefile | 1 | ||||
-rw-r--r-- | net/caif/caif_dev.c | 1 | ||||
-rw-r--r-- | net/caif/caif_usb.c | 208 | ||||
-rw-r--r-- | net/caif/cfpkt_skbuff.c | 12 |
5 files changed, 224 insertions, 9 deletions
diff --git a/net/caif/Kconfig b/net/caif/Kconfig index 529750da9624..936361e5a2b6 100644 --- a/net/caif/Kconfig +++ b/net/caif/Kconfig | |||
@@ -40,3 +40,14 @@ config CAIF_NETDEV | |||
40 | If you select to build it as a built-in then the main CAIF device must | 40 | If you select to build it as a built-in then the main CAIF device must |
41 | also be a built-in. | 41 | also be a built-in. |
42 | If unsure say Y. | 42 | If unsure say Y. |
43 | |||
44 | config CAIF_USB | ||
45 | tristate "CAIF USB support" | ||
46 | depends on CAIF | ||
47 | default n | ||
48 | ---help--- | ||
49 | Say Y if you are using CAIF over USB CDC NCM. | ||
50 | This can be either built-in or a loadable module, | ||
51 | If you select to build it as a built-in then the main CAIF device must | ||
52 | also be a built-in. | ||
53 | If unsure say N. | ||
diff --git a/net/caif/Makefile b/net/caif/Makefile index ebcd4e7e6f47..cc2b51154d03 100644 --- a/net/caif/Makefile +++ b/net/caif/Makefile | |||
@@ -10,5 +10,6 @@ caif-y := caif_dev.o \ | |||
10 | obj-$(CONFIG_CAIF) += caif.o | 10 | obj-$(CONFIG_CAIF) += caif.o |
11 | obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o | 11 | obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o |
12 | obj-$(CONFIG_CAIF) += caif_socket.o | 12 | obj-$(CONFIG_CAIF) += caif_socket.o |
13 | obj-$(CONFIG_CAIF_USB) += caif_usb.o | ||
13 | 14 | ||
14 | export-y := caif.o | 15 | export-y := caif.o |
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index f7e8c70b343c..6acec1921e7f 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c | |||
@@ -262,6 +262,7 @@ void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, | |||
262 | if (rcv_func) | 262 | if (rcv_func) |
263 | *rcv_func = receive; | 263 | *rcv_func = receive; |
264 | } | 264 | } |
265 | EXPORT_SYMBOL(caif_enroll_dev); | ||
265 | 266 | ||
266 | /* notify Caif of device events */ | 267 | /* notify Caif of device events */ |
267 | static int caif_device_notify(struct notifier_block *me, unsigned long what, | 268 | static int caif_device_notify(struct notifier_block *me, unsigned long what, |
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 | |||
24 | MODULE_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 | |||
32 | struct cfusbl { | ||
33 | struct cflayer layer; | ||
34 | u8 tx_eth_hdr[ETH_HLEN]; | ||
35 | }; | ||
36 | |||
37 | static bool pack_added; | ||
38 | |||
39 | static 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 | |||
49 | static 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 | |||
78 | static 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 | |||
85 | struct 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 | |||
120 | static struct packet_type caif_usb_type __read_mostly = { | ||
121 | .type = cpu_to_be16(ETH_P_802_EX1), | ||
122 | }; | ||
123 | |||
124 | static 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 | |||
191 | static struct notifier_block caif_device_notifier = { | ||
192 | .notifier_call = cfusbl_device_notify, | ||
193 | .priority = 0, | ||
194 | }; | ||
195 | |||
196 | static int __init cfusbl_init(void) | ||
197 | { | ||
198 | return register_netdevice_notifier(&caif_device_notifier); | ||
199 | } | ||
200 | |||
201 | static void __exit cfusbl_exit(void) | ||
202 | { | ||
203 | unregister_netdevice_notifier(&caif_device_notifier); | ||
204 | dev_remove_pack(&caif_usb_type); | ||
205 | } | ||
206 | |||
207 | module_init(cfusbl_init); | ||
208 | module_exit(cfusbl_exit); | ||
diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c index de5390700cfe..e335ba859b97 100644 --- a/net/caif/cfpkt_skbuff.c +++ b/net/caif/cfpkt_skbuff.c | |||
@@ -63,7 +63,6 @@ static inline struct cfpkt *skb_to_pkt(struct sk_buff *skb) | |||
63 | return (struct cfpkt *) skb; | 63 | return (struct cfpkt *) skb; |
64 | } | 64 | } |
65 | 65 | ||
66 | |||
67 | struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt) | 66 | struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt) |
68 | { | 67 | { |
69 | struct cfpkt *pkt = skb_to_pkt(nativepkt); | 68 | struct cfpkt *pkt = skb_to_pkt(nativepkt); |
@@ -105,14 +104,12 @@ void cfpkt_destroy(struct cfpkt *pkt) | |||
105 | kfree_skb(skb); | 104 | kfree_skb(skb); |
106 | } | 105 | } |
107 | 106 | ||
108 | |||
109 | inline bool cfpkt_more(struct cfpkt *pkt) | 107 | inline bool cfpkt_more(struct cfpkt *pkt) |
110 | { | 108 | { |
111 | struct sk_buff *skb = pkt_to_skb(pkt); | 109 | struct sk_buff *skb = pkt_to_skb(pkt); |
112 | return skb->len > 0; | 110 | return skb->len > 0; |
113 | } | 111 | } |
114 | 112 | ||
115 | |||
116 | int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len) | 113 | int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len) |
117 | { | 114 | { |
118 | struct sk_buff *skb = pkt_to_skb(pkt); | 115 | struct sk_buff *skb = pkt_to_skb(pkt); |
@@ -148,6 +145,7 @@ int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len) | |||
148 | memcpy(data, from, len); | 145 | memcpy(data, from, len); |
149 | return 0; | 146 | return 0; |
150 | } | 147 | } |
148 | EXPORT_SYMBOL(cfpkt_extr_head); | ||
151 | 149 | ||
152 | int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) | 150 | int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) |
153 | { | 151 | { |
@@ -171,13 +169,11 @@ int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) | |||
171 | return 0; | 169 | return 0; |
172 | } | 170 | } |
173 | 171 | ||
174 | |||
175 | int cfpkt_pad_trail(struct cfpkt *pkt, u16 len) | 172 | int cfpkt_pad_trail(struct cfpkt *pkt, u16 len) |
176 | { | 173 | { |
177 | return cfpkt_add_body(pkt, NULL, len); | 174 | return cfpkt_add_body(pkt, NULL, len); |
178 | } | 175 | } |
179 | 176 | ||
180 | |||
181 | int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len) | 177 | int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len) |
182 | { | 178 | { |
183 | struct sk_buff *skb = pkt_to_skb(pkt); | 179 | struct sk_buff *skb = pkt_to_skb(pkt); |
@@ -256,21 +252,19 @@ int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len) | |||
256 | memcpy(to, data, len); | 252 | memcpy(to, data, len); |
257 | return 0; | 253 | return 0; |
258 | } | 254 | } |
259 | 255 | EXPORT_SYMBOL(cfpkt_add_head); | |
260 | 256 | ||
261 | inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len) | 257 | inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len) |
262 | { | 258 | { |
263 | return cfpkt_add_body(pkt, data, len); | 259 | return cfpkt_add_body(pkt, data, len); |
264 | } | 260 | } |
265 | 261 | ||
266 | |||
267 | inline u16 cfpkt_getlen(struct cfpkt *pkt) | 262 | inline u16 cfpkt_getlen(struct cfpkt *pkt) |
268 | { | 263 | { |
269 | struct sk_buff *skb = pkt_to_skb(pkt); | 264 | struct sk_buff *skb = pkt_to_skb(pkt); |
270 | return skb->len; | 265 | return skb->len; |
271 | } | 266 | } |
272 | 267 | ||
273 | |||
274 | inline u16 cfpkt_iterate(struct cfpkt *pkt, | 268 | inline u16 cfpkt_iterate(struct cfpkt *pkt, |
275 | u16 (*iter_func)(u16, void *, u16), | 269 | u16 (*iter_func)(u16, void *, u16), |
276 | u16 data) | 270 | u16 data) |
@@ -288,7 +282,6 @@ inline u16 cfpkt_iterate(struct cfpkt *pkt, | |||
288 | return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt)); | 282 | return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt)); |
289 | } | 283 | } |
290 | 284 | ||
291 | |||
292 | int cfpkt_setlen(struct cfpkt *pkt, u16 len) | 285 | int cfpkt_setlen(struct cfpkt *pkt, u16 len) |
293 | { | 286 | { |
294 | struct sk_buff *skb = pkt_to_skb(pkt); | 287 | struct sk_buff *skb = pkt_to_skb(pkt); |
@@ -400,3 +393,4 @@ struct caif_payload_info *cfpkt_info(struct cfpkt *pkt) | |||
400 | { | 393 | { |
401 | return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb; | 394 | return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb; |
402 | } | 395 | } |
396 | EXPORT_SYMBOL(cfpkt_info); | ||