blob: 6d1d86be187bc87344bf217f4621dde45ec2c064 [file] [log] [blame]
Sjur Braendelandc72dfae2010-03-30 13:56:25 +00001/*
2 * CAIF Interface registration.
3 * Copyright (C) ST-Ericsson AB 2010
4 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
5 * License terms: GNU General Public License (GPL) version 2
6 *
7 * Borrowed heavily from file: pn_dev.c. Thanks to
8 * Remi Denis-Courmont <remi.denis-courmont@nokia.com>
9 * and Sakari Ailus <sakari.ailus@nokia.com>
10 */
11
Joe Perchesb31fa5b2010-09-05 21:31:11 +000012#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
13
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000014#include <linux/version.h>
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000015#include <linux/kernel.h>
16#include <linux/if_arp.h>
17#include <linux/net.h>
18#include <linux/netdevice.h>
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000019#include <linux/mutex.h>
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000020#include <net/netns/generic.h>
21#include <net/net_namespace.h>
22#include <net/pkt_sched.h>
23#include <net/caif/caif_device.h>
24#include <net/caif/caif_dev.h>
25#include <net/caif/caif_layer.h>
26#include <net/caif/cfpkt.h>
27#include <net/caif/cfcnfg.h>
28
29MODULE_LICENSE("GPL");
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000030
31/* Used for local tracking of the CAIF net devices */
32struct caif_device_entry {
33 struct cflayer layer;
34 struct list_head list;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000035 struct net_device *netdev;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000036 int __percpu *pcpu_refcnt;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000037};
38
39struct caif_device_entry_list {
40 struct list_head list;
41 /* Protects simulanous deletes in list */
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000042 struct mutex lock;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000043};
44
45struct caif_net {
46 struct caif_device_entry_list caifdevs;
47};
48
49static int caif_net_id;
50static struct cfcnfg *cfg;
51
52static struct caif_device_entry_list *caif_device_list(struct net *net)
53{
54 struct caif_net *caifn;
55 BUG_ON(!net);
56 caifn = net_generic(net, caif_net_id);
57 BUG_ON(!caifn);
58 return &caifn->caifdevs;
59}
60
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000061static void caifd_put(struct caif_device_entry *e)
62{
63 irqsafe_cpu_dec(*e->pcpu_refcnt);
64}
65
66static void caifd_hold(struct caif_device_entry *e)
67{
68 irqsafe_cpu_inc(*e->pcpu_refcnt);
69}
70
71static int caifd_refcnt_read(struct caif_device_entry *e)
72{
73 int i, refcnt = 0;
74 for_each_possible_cpu(i)
75 refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
76 return refcnt;
77}
78
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000079/* Allocate new CAIF device. */
80static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
81{
82 struct caif_device_entry_list *caifdevs;
83 struct caif_device_entry *caifd;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000084
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000085 caifdevs = caif_device_list(dev_net(dev));
86 BUG_ON(!caifdevs);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000087
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000088 caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC);
89 if (!caifd)
90 return NULL;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000091 caifd->pcpu_refcnt = alloc_percpu(int);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000092 caifd->netdev = dev;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000093 dev_hold(dev);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000094 return caifd;
95}
96
97static struct caif_device_entry *caif_get(struct net_device *dev)
98{
99 struct caif_device_entry_list *caifdevs =
100 caif_device_list(dev_net(dev));
101 struct caif_device_entry *caifd;
102 BUG_ON(!caifdevs);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000103 list_for_each_entry_rcu(caifd, &caifdevs->list, list) {
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000104 if (caifd->netdev == dev)
105 return caifd;
106 }
107 return NULL;
108}
109
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000110static int transmit(struct cflayer *layer, struct cfpkt *pkt)
111{
112 struct caif_device_entry *caifd =
113 container_of(layer, struct caif_device_entry, layer);
Sjur Brændeland4dd820c2011-04-11 10:43:51 +0000114 struct sk_buff *skb;
115
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000116 skb = cfpkt_tonative(pkt);
117 skb->dev = caifd->netdev;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000118
Sjur Brændeland4dd820c2011-04-11 10:43:51 +0000119 dev_queue_xmit(skb);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000120
121 return 0;
122}
123
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000124/*
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000125 * Stuff received packets into the CAIF stack.
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000126 * On error, returns non-zero and releases the skb.
127 */
128static int receive(struct sk_buff *skb, struct net_device *dev,
129 struct packet_type *pkttype, struct net_device *orig_dev)
130{
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000131 struct cfpkt *pkt;
132 struct caif_device_entry *caifd;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000133
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000134 pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000135
136 rcu_read_lock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000137 caifd = caif_get(dev);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000138
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000139 if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
140 !netif_oper_up(caifd->netdev)) {
141 rcu_read_unlock();
142 kfree_skb(skb);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000143 return NET_RX_DROP;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000144 }
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000145
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000146 /* Hold reference to netdevice while using CAIF stack */
147 caifd_hold(caifd);
148 rcu_read_unlock();
149
150 caifd->layer.up->receive(caifd->layer.up, pkt);
151
152 /* Release reference to stack upwards */
153 caifd_put(caifd);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000154 return 0;
155}
156
157static struct packet_type caif_packet_type __read_mostly = {
158 .type = cpu_to_be16(ETH_P_CAIF),
159 .func = receive,
160};
161
162static void dev_flowctrl(struct net_device *dev, int on)
163{
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000164 struct caif_device_entry *caifd;
165
166 rcu_read_lock();
167
168 caifd = caif_get(dev);
169 if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
170 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000171 return;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000172 }
173
174 caifd_hold(caifd);
175 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000176
177 caifd->layer.up->ctrlcmd(caifd->layer.up,
178 on ?
179 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
180 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
181 caifd->layer.id);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000182 caifd_put(caifd);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000183}
184
185/* notify Caif of device events */
186static int caif_device_notify(struct notifier_block *me, unsigned long what,
187 void *arg)
188{
189 struct net_device *dev = arg;
190 struct caif_device_entry *caifd = NULL;
191 struct caif_dev_common *caifdev;
192 enum cfcnfg_phy_preference pref;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000193 enum cfcnfg_phy_type phy_type;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000194 struct caif_device_entry_list *caifdevs =
195 caif_device_list(dev_net(dev));
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000196
197 if (dev->type != ARPHRD_CAIF)
198 return 0;
199
200 switch (what) {
201 case NETDEV_REGISTER:
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000202 caifd = caif_device_alloc(dev);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000203 if (!caifd)
204 return 0;
205
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000206 caifdev = netdev_priv(dev);
207 caifdev->flowctrl = dev_flowctrl;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000208
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000209 caifd->layer.transmit = transmit;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000210
211 if (caifdev->use_frag)
212 phy_type = CFPHYTYPE_FRAG;
213 else
214 phy_type = CFPHYTYPE_CAIF;
215
216 switch (caifdev->link_select) {
217 case CAIF_LINK_HIGH_BANDW:
Sjur Braendeland2c485202010-04-28 08:54:40 +0000218 pref = CFPHYPREF_HIGH_BW;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000219 break;
220 case CAIF_LINK_LOW_LATENCY:
Sjur Braendeland2c485202010-04-28 08:54:40 +0000221 pref = CFPHYPREF_LOW_LAT;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000222 break;
223 default:
224 pref = CFPHYPREF_HIGH_BW;
225 break;
226 }
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000227 strncpy(caifd->layer.name, dev->name,
228 sizeof(caifd->layer.name) - 1);
229 caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
230
231 mutex_lock(&caifdevs->lock);
232 list_add_rcu(&caifd->list, &caifdevs->list);
233
Stephen Hemminger73d6ac62011-04-11 10:43:50 +0000234 cfcnfg_add_phy_layer(cfg,
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000235 phy_type,
236 dev,
237 &caifd->layer,
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000238 0,
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000239 pref,
240 caifdev->use_fcs,
241 caifdev->use_stx);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000242 mutex_unlock(&caifdevs->lock);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000243 break;
244
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000245 case NETDEV_UP:
246 rcu_read_lock();
247
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000248 caifd = caif_get(dev);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000249 if (caifd == NULL) {
250 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000251 break;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000252 }
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000253
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000254 cfcnfg_set_phy_state(cfg, &caifd->layer, true);
255 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000256
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000257 break;
258
259 case NETDEV_DOWN:
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000260 rcu_read_lock();
261
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000262 caifd = caif_get(dev);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000263 if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
264 rcu_read_unlock();
265 return -EINVAL;
266 }
267
268 cfcnfg_set_phy_state(cfg, &caifd->layer, false);
269 caifd_hold(caifd);
270 rcu_read_unlock();
271
272 caifd->layer.up->ctrlcmd(caifd->layer.up,
273 _CAIF_CTRLCMD_PHYIF_DOWN_IND,
274 caifd->layer.id);
275 caifd_put(caifd);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000276 break;
277
278 case NETDEV_UNREGISTER:
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000279 mutex_lock(&caifdevs->lock);
280
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000281 caifd = caif_get(dev);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000282 if (caifd == NULL) {
283 mutex_unlock(&caifdevs->lock);
André Carvalho de Matosf2527ec2010-11-01 11:52:47 +0000284 break;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000285 }
286 list_del_rcu(&caifd->list);
287
288 /*
289 * NETDEV_UNREGISTER is called repeatedly until all reference
290 * counts for the net-device are released. If references to
291 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
292 * the next call to NETDEV_UNREGISTER.
293 *
294 * If any packets are in flight down the CAIF Stack,
295 * cfcnfg_del_phy_layer will return nonzero.
296 * If no packets are in flight, the CAIF Stack associated
297 * with the net-device un-registering is freed.
298 */
299
300 if (caifd_refcnt_read(caifd) != 0 ||
301 cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
302
303 pr_info("Wait for device inuse\n");
304 /* Enrole device if CAIF Stack is still in use */
305 list_add_rcu(&caifd->list, &caifdevs->list);
306 mutex_unlock(&caifdevs->lock);
307 break;
308 }
309
310 synchronize_rcu();
311 dev_put(caifd->netdev);
312 free_percpu(caifd->pcpu_refcnt);
313 kfree(caifd);
314
315 mutex_unlock(&caifdevs->lock);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000316 break;
317 }
318 return 0;
319}
320
321static struct notifier_block caif_device_notifier = {
322 .notifier_call = caif_device_notify,
323 .priority = 0,
324};
325
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000326int caif_connect_client(struct caif_connect_request *conn_req,
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000327 struct cflayer *client_layer, int *ifindex,
328 int *headroom, int *tailroom)
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000329{
330 struct cfctrl_link_param param;
Sjur Braendelande539d832010-04-28 08:54:35 +0000331 int ret;
Stephen Hemminger73d6ac62011-04-11 10:43:50 +0000332
333 ret = caif_connect_req_to_link_param(cfg, conn_req, &param);
Sjur Braendelande539d832010-04-28 08:54:35 +0000334 if (ret)
335 return ret;
336 /* Hook up the adaptation layer. */
Stephen Hemminger73d6ac62011-04-11 10:43:50 +0000337 return cfcnfg_add_adaptation_layer(cfg, &param,
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000338 client_layer, ifindex,
339 headroom, tailroom);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000340}
341EXPORT_SYMBOL(caif_connect_client);
342
343int caif_disconnect_client(struct cflayer *adap_layer)
344{
Stephen Hemminger73d6ac62011-04-11 10:43:50 +0000345 return cfcnfg_disconn_adapt_layer(cfg, adap_layer);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000346}
347EXPORT_SYMBOL(caif_disconnect_client);
348
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000349/* Per-namespace Caif devices handling */
350static int caif_init_net(struct net *net)
351{
352 struct caif_net *caifn = net_generic(net, caif_net_id);
353 INIT_LIST_HEAD(&caifn->caifdevs.list);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000354 mutex_init(&caifn->caifdevs.lock);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000355 return 0;
356}
357
358static void caif_exit_net(struct net *net)
359{
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000360 struct caif_device_entry *caifd, *tmp;
361 struct caif_device_entry_list *caifdevs =
362 caif_device_list(net);
363
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000364 rtnl_lock();
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000365 mutex_lock(&caifdevs->lock);
366
367 list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
368 int i = 0;
369 list_del_rcu(&caifd->list);
370 cfcnfg_set_phy_state(cfg, &caifd->layer, false);
371
372 while (i < 10 &&
373 (caifd_refcnt_read(caifd) != 0 ||
374 cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
375
376 pr_info("Wait for device inuse\n");
377 msleep(250);
378 i++;
379 }
380 synchronize_rcu();
381 dev_put(caifd->netdev);
382 free_percpu(caifd->pcpu_refcnt);
383 kfree(caifd);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000384 }
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000385
386
387 mutex_unlock(&caifdevs->lock);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000388 rtnl_unlock();
389}
390
391static struct pernet_operations caif_net_ops = {
392 .init = caif_init_net,
393 .exit = caif_exit_net,
394 .id = &caif_net_id,
395 .size = sizeof(struct caif_net),
396};
397
398/* Initialize Caif devices list */
399static int __init caif_device_init(void)
400{
401 int result;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000402
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000403 cfg = cfcnfg_create();
404 if (!cfg) {
Joe Perchesb31fa5b2010-09-05 21:31:11 +0000405 pr_warn("can't create cfcnfg\n");
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000406 goto err_cfcnfg_create_failed;
407 }
408 result = register_pernet_device(&caif_net_ops);
409
410 if (result) {
411 kfree(cfg);
412 cfg = NULL;
413 return result;
414 }
415 dev_add_pack(&caif_packet_type);
416 register_netdevice_notifier(&caif_device_notifier);
417
418 return result;
419err_cfcnfg_create_failed:
420 return -ENODEV;
421}
422
423static void __exit caif_device_exit(void)
424{
425 dev_remove_pack(&caif_packet_type);
426 unregister_pernet_device(&caif_net_ops);
427 unregister_netdevice_notifier(&caif_device_notifier);
428 cfcnfg_remove(cfg);
429}
430
431module_init(caif_device_init);
432module_exit(caif_device_exit);