diff options
author | James Morris <james.l.morris@oracle.com> | 2014-11-19 05:32:12 -0500 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2014-11-19 05:32:12 -0500 |
commit | b10778a00d40b3d9fdaaf5891e802794781ff71c (patch) | |
tree | 6ba4cbac86eecedc3f30650e7f764ecf00c83898 /net/bluetooth/6lowpan.c | |
parent | 594081ee7145cc30a3977cb4e218f81213b63dc5 (diff) | |
parent | bfe01a5ba2490f299e1d2d5508cbbbadd897bbe9 (diff) |
Merge commit 'v3.17' into next
Diffstat (limited to 'net/bluetooth/6lowpan.c')
-rw-r--r-- | net/bluetooth/6lowpan.c | 857 |
1 files changed, 649 insertions, 208 deletions
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 8796ffa08b43..206b65ccd5b8 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | Copyright (c) 2013 Intel Corp. | 2 | Copyright (c) 2013-2014 Intel Corp. |
3 | 3 | ||
4 | This program is free software; you can redistribute it and/or modify | 4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License version 2 and | 5 | it under the terms of the GNU General Public License version 2 and |
@@ -14,6 +14,8 @@ | |||
14 | #include <linux/if_arp.h> | 14 | #include <linux/if_arp.h> |
15 | #include <linux/netdevice.h> | 15 | #include <linux/netdevice.h> |
16 | #include <linux/etherdevice.h> | 16 | #include <linux/etherdevice.h> |
17 | #include <linux/module.h> | ||
18 | #include <linux/debugfs.h> | ||
17 | 19 | ||
18 | #include <net/ipv6.h> | 20 | #include <net/ipv6.h> |
19 | #include <net/ip6_route.h> | 21 | #include <net/ip6_route.h> |
@@ -25,16 +27,20 @@ | |||
25 | #include <net/bluetooth/hci_core.h> | 27 | #include <net/bluetooth/hci_core.h> |
26 | #include <net/bluetooth/l2cap.h> | 28 | #include <net/bluetooth/l2cap.h> |
27 | 29 | ||
28 | #include "6lowpan.h" | ||
29 | |||
30 | #include <net/6lowpan.h> /* for the compression support */ | 30 | #include <net/6lowpan.h> /* for the compression support */ |
31 | 31 | ||
32 | #define VERSION "0.1" | ||
33 | |||
34 | static struct dentry *lowpan_psm_debugfs; | ||
35 | static struct dentry *lowpan_control_debugfs; | ||
36 | |||
32 | #define IFACE_NAME_TEMPLATE "bt%d" | 37 | #define IFACE_NAME_TEMPLATE "bt%d" |
33 | #define EUI64_ADDR_LEN 8 | 38 | #define EUI64_ADDR_LEN 8 |
34 | 39 | ||
35 | struct skb_cb { | 40 | struct skb_cb { |
36 | struct in6_addr addr; | 41 | struct in6_addr addr; |
37 | struct l2cap_conn *conn; | 42 | struct l2cap_chan *chan; |
43 | int status; | ||
38 | }; | 44 | }; |
39 | #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb)) | 45 | #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb)) |
40 | 46 | ||
@@ -48,9 +54,19 @@ struct skb_cb { | |||
48 | static LIST_HEAD(bt_6lowpan_devices); | 54 | static LIST_HEAD(bt_6lowpan_devices); |
49 | static DEFINE_RWLOCK(devices_lock); | 55 | static DEFINE_RWLOCK(devices_lock); |
50 | 56 | ||
57 | /* If psm is set to 0 (default value), then 6lowpan is disabled. | ||
58 | * Other values are used to indicate a Protocol Service Multiplexer | ||
59 | * value for 6lowpan. | ||
60 | */ | ||
61 | static u16 psm_6lowpan; | ||
62 | |||
63 | /* We are listening incoming connections via this channel | ||
64 | */ | ||
65 | static struct l2cap_chan *listen_chan; | ||
66 | |||
51 | struct lowpan_peer { | 67 | struct lowpan_peer { |
52 | struct list_head list; | 68 | struct list_head list; |
53 | struct l2cap_conn *conn; | 69 | struct l2cap_chan *chan; |
54 | 70 | ||
55 | /* peer addresses in various formats */ | 71 | /* peer addresses in various formats */ |
56 | unsigned char eui64_addr[EUI64_ADDR_LEN]; | 72 | unsigned char eui64_addr[EUI64_ADDR_LEN]; |
@@ -84,6 +100,8 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer) | |||
84 | { | 100 | { |
85 | list_del(&peer->list); | 101 | list_del(&peer->list); |
86 | 102 | ||
103 | module_put(THIS_MODULE); | ||
104 | |||
87 | if (atomic_dec_and_test(&dev->peer_count)) { | 105 | if (atomic_dec_and_test(&dev->peer_count)) { |
88 | BT_DBG("last peer"); | 106 | BT_DBG("last peer"); |
89 | return true; | 107 | return true; |
@@ -101,13 +119,26 @@ static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev, | |||
101 | ba, type); | 119 | ba, type); |
102 | 120 | ||
103 | list_for_each_entry_safe(peer, tmp, &dev->peers, list) { | 121 | list_for_each_entry_safe(peer, tmp, &dev->peers, list) { |
104 | BT_DBG("addr %pMR type %d", | 122 | BT_DBG("dst addr %pMR dst type %d", |
105 | &peer->conn->hcon->dst, peer->conn->hcon->dst_type); | 123 | &peer->chan->dst, peer->chan->dst_type); |
106 | 124 | ||
107 | if (bacmp(&peer->conn->hcon->dst, ba)) | 125 | if (bacmp(&peer->chan->dst, ba)) |
108 | continue; | 126 | continue; |
109 | 127 | ||
110 | if (type == peer->conn->hcon->dst_type) | 128 | if (type == peer->chan->dst_type) |
129 | return peer; | ||
130 | } | ||
131 | |||
132 | return NULL; | ||
133 | } | ||
134 | |||
135 | static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev, | ||
136 | struct l2cap_chan *chan) | ||
137 | { | ||
138 | struct lowpan_peer *peer, *tmp; | ||
139 | |||
140 | list_for_each_entry_safe(peer, tmp, &dev->peers, list) { | ||
141 | if (peer->chan == chan) | ||
111 | return peer; | 142 | return peer; |
112 | } | 143 | } |
113 | 144 | ||
@@ -120,7 +151,7 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev, | |||
120 | struct lowpan_peer *peer, *tmp; | 151 | struct lowpan_peer *peer, *tmp; |
121 | 152 | ||
122 | list_for_each_entry_safe(peer, tmp, &dev->peers, list) { | 153 | list_for_each_entry_safe(peer, tmp, &dev->peers, list) { |
123 | if (peer->conn == conn) | 154 | if (peer->chan->conn == conn) |
124 | return peer; | 155 | return peer; |
125 | } | 156 | } |
126 | 157 | ||
@@ -176,16 +207,16 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) | |||
176 | return -ENOMEM; | 207 | return -ENOMEM; |
177 | 208 | ||
178 | ret = netif_rx(skb_cp); | 209 | ret = netif_rx(skb_cp); |
179 | 210 | if (ret < 0) { | |
180 | BT_DBG("receive skb %d", ret); | 211 | BT_DBG("receive skb %d", ret); |
181 | if (ret < 0) | ||
182 | return NET_RX_DROP; | 212 | return NET_RX_DROP; |
213 | } | ||
183 | 214 | ||
184 | return ret; | 215 | return ret; |
185 | } | 216 | } |
186 | 217 | ||
187 | static int process_data(struct sk_buff *skb, struct net_device *netdev, | 218 | static int process_data(struct sk_buff *skb, struct net_device *netdev, |
188 | struct l2cap_conn *conn) | 219 | struct l2cap_chan *chan) |
189 | { | 220 | { |
190 | const u8 *saddr, *daddr; | 221 | const u8 *saddr, *daddr; |
191 | u8 iphc0, iphc1; | 222 | u8 iphc0, iphc1; |
@@ -196,7 +227,7 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev, | |||
196 | dev = lowpan_dev(netdev); | 227 | dev = lowpan_dev(netdev); |
197 | 228 | ||
198 | read_lock_irqsave(&devices_lock, flags); | 229 | read_lock_irqsave(&devices_lock, flags); |
199 | peer = peer_lookup_conn(dev, conn); | 230 | peer = peer_lookup_chan(dev, chan); |
200 | read_unlock_irqrestore(&devices_lock, flags); | 231 | read_unlock_irqrestore(&devices_lock, flags); |
201 | if (!peer) | 232 | if (!peer) |
202 | goto drop; | 233 | goto drop; |
@@ -225,7 +256,7 @@ drop: | |||
225 | } | 256 | } |
226 | 257 | ||
227 | static int recv_pkt(struct sk_buff *skb, struct net_device *dev, | 258 | static int recv_pkt(struct sk_buff *skb, struct net_device *dev, |
228 | struct l2cap_conn *conn) | 259 | struct l2cap_chan *chan) |
229 | { | 260 | { |
230 | struct sk_buff *local_skb; | 261 | struct sk_buff *local_skb; |
231 | int ret; | 262 | int ret; |
@@ -269,7 +300,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, | |||
269 | if (!local_skb) | 300 | if (!local_skb) |
270 | goto drop; | 301 | goto drop; |
271 | 302 | ||
272 | ret = process_data(local_skb, dev, conn); | 303 | ret = process_data(local_skb, dev, chan); |
273 | if (ret != NET_RX_SUCCESS) | 304 | if (ret != NET_RX_SUCCESS) |
274 | goto drop; | 305 | goto drop; |
275 | 306 | ||
@@ -286,147 +317,39 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, | |||
286 | return NET_RX_SUCCESS; | 317 | return NET_RX_SUCCESS; |
287 | 318 | ||
288 | drop: | 319 | drop: |
320 | dev->stats.rx_dropped++; | ||
289 | kfree_skb(skb); | 321 | kfree_skb(skb); |
290 | return NET_RX_DROP; | 322 | return NET_RX_DROP; |
291 | } | 323 | } |
292 | 324 | ||
293 | /* Packet from BT LE device */ | 325 | /* Packet from BT LE device */ |
294 | int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb) | 326 | static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) |
295 | { | 327 | { |
296 | struct lowpan_dev *dev; | 328 | struct lowpan_dev *dev; |
297 | struct lowpan_peer *peer; | 329 | struct lowpan_peer *peer; |
298 | int err; | 330 | int err; |
299 | 331 | ||
300 | peer = lookup_peer(conn); | 332 | peer = lookup_peer(chan->conn); |
301 | if (!peer) | 333 | if (!peer) |
302 | return -ENOENT; | 334 | return -ENOENT; |
303 | 335 | ||
304 | dev = lookup_dev(conn); | 336 | dev = lookup_dev(chan->conn); |
305 | if (!dev || !dev->netdev) | 337 | if (!dev || !dev->netdev) |
306 | return -ENOENT; | 338 | return -ENOENT; |
307 | 339 | ||
308 | err = recv_pkt(skb, dev->netdev, conn); | 340 | err = recv_pkt(skb, dev->netdev, chan); |
309 | BT_DBG("recv pkt %d", err); | 341 | if (err) { |
310 | 342 | BT_DBG("recv pkt %d", err); | |
311 | return err; | 343 | err = -EAGAIN; |
312 | } | ||
313 | |||
314 | static inline int skbuff_copy(void *msg, int len, int count, int mtu, | ||
315 | struct sk_buff *skb, struct net_device *dev) | ||
316 | { | ||
317 | struct sk_buff **frag; | ||
318 | int sent = 0; | ||
319 | |||
320 | memcpy(skb_put(skb, count), msg, count); | ||
321 | |||
322 | sent += count; | ||
323 | msg += count; | ||
324 | len -= count; | ||
325 | |||
326 | dev->stats.tx_bytes += count; | ||
327 | dev->stats.tx_packets++; | ||
328 | |||
329 | raw_dump_table(__func__, "Sending", skb->data, skb->len); | ||
330 | |||
331 | /* Continuation fragments (no L2CAP header) */ | ||
332 | frag = &skb_shinfo(skb)->frag_list; | ||
333 | while (len > 0) { | ||
334 | struct sk_buff *tmp; | ||
335 | |||
336 | count = min_t(unsigned int, mtu, len); | ||
337 | |||
338 | tmp = bt_skb_alloc(count, GFP_ATOMIC); | ||
339 | if (!tmp) | ||
340 | return -ENOMEM; | ||
341 | |||
342 | *frag = tmp; | ||
343 | |||
344 | memcpy(skb_put(*frag, count), msg, count); | ||
345 | |||
346 | raw_dump_table(__func__, "Sending fragment", | ||
347 | (*frag)->data, count); | ||
348 | |||
349 | (*frag)->priority = skb->priority; | ||
350 | |||
351 | sent += count; | ||
352 | msg += count; | ||
353 | len -= count; | ||
354 | |||
355 | skb->len += (*frag)->len; | ||
356 | skb->data_len += (*frag)->len; | ||
357 | |||
358 | frag = &(*frag)->next; | ||
359 | |||
360 | dev->stats.tx_bytes += count; | ||
361 | dev->stats.tx_packets++; | ||
362 | } | 344 | } |
363 | 345 | ||
364 | return sent; | 346 | return err; |
365 | } | ||
366 | |||
367 | static struct sk_buff *create_pdu(struct l2cap_conn *conn, void *msg, | ||
368 | size_t len, u32 priority, | ||
369 | struct net_device *dev) | ||
370 | { | ||
371 | struct sk_buff *skb; | ||
372 | int err, count; | ||
373 | struct l2cap_hdr *lh; | ||
374 | |||
375 | /* FIXME: This mtu check should be not needed and atm is only used for | ||
376 | * testing purposes | ||
377 | */ | ||
378 | if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE)) | ||
379 | conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE; | ||
380 | |||
381 | count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len); | ||
382 | |||
383 | BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count); | ||
384 | |||
385 | skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_ATOMIC); | ||
386 | if (!skb) | ||
387 | return ERR_PTR(-ENOMEM); | ||
388 | |||
389 | skb->priority = priority; | ||
390 | |||
391 | lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE); | ||
392 | lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN); | ||
393 | lh->len = cpu_to_le16(len); | ||
394 | |||
395 | err = skbuff_copy(msg, len, count, conn->mtu, skb, dev); | ||
396 | if (unlikely(err < 0)) { | ||
397 | kfree_skb(skb); | ||
398 | BT_DBG("skbuff copy %d failed", err); | ||
399 | return ERR_PTR(err); | ||
400 | } | ||
401 | |||
402 | return skb; | ||
403 | } | ||
404 | |||
405 | static int conn_send(struct l2cap_conn *conn, | ||
406 | void *msg, size_t len, u32 priority, | ||
407 | struct net_device *dev) | ||
408 | { | ||
409 | struct sk_buff *skb; | ||
410 | |||
411 | skb = create_pdu(conn, msg, len, priority, dev); | ||
412 | if (IS_ERR(skb)) | ||
413 | return -EINVAL; | ||
414 | |||
415 | BT_DBG("conn %p skb %p len %d priority %u", conn, skb, skb->len, | ||
416 | skb->priority); | ||
417 | |||
418 | hci_send_acl(conn->hchan, skb, ACL_START); | ||
419 | |||
420 | return 0; | ||
421 | } | 347 | } |
422 | 348 | ||
423 | static u8 get_addr_type_from_eui64(u8 byte) | 349 | static u8 get_addr_type_from_eui64(u8 byte) |
424 | { | 350 | { |
425 | /* Is universal(0) or local(1) bit, */ | 351 | /* Is universal(0) or local(1) bit */ |
426 | if (byte & 0x02) | 352 | return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC); |
427 | return ADDR_LE_DEV_RANDOM; | ||
428 | |||
429 | return ADDR_LE_DEV_PUBLIC; | ||
430 | } | 353 | } |
431 | 354 | ||
432 | static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr) | 355 | static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr) |
@@ -475,7 +398,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, | |||
475 | if (ipv6_addr_is_multicast(&hdr->daddr)) { | 398 | if (ipv6_addr_is_multicast(&hdr->daddr)) { |
476 | memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, | 399 | memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, |
477 | sizeof(struct in6_addr)); | 400 | sizeof(struct in6_addr)); |
478 | lowpan_cb(skb)->conn = NULL; | 401 | lowpan_cb(skb)->chan = NULL; |
479 | } else { | 402 | } else { |
480 | unsigned long flags; | 403 | unsigned long flags; |
481 | 404 | ||
@@ -484,9 +407,8 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, | |||
484 | */ | 407 | */ |
485 | convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type); | 408 | convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type); |
486 | 409 | ||
487 | BT_DBG("dest addr %pMR type %s IP %pI6c", &addr, | 410 | BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, |
488 | addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM", | 411 | addr_type, &hdr->daddr); |
489 | &hdr->daddr); | ||
490 | 412 | ||
491 | read_lock_irqsave(&devices_lock, flags); | 413 | read_lock_irqsave(&devices_lock, flags); |
492 | peer = peer_lookup_ba(dev, &addr, addr_type); | 414 | peer = peer_lookup_ba(dev, &addr, addr_type); |
@@ -501,7 +423,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, | |||
501 | 423 | ||
502 | memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, | 424 | memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, |
503 | sizeof(struct in6_addr)); | 425 | sizeof(struct in6_addr)); |
504 | lowpan_cb(skb)->conn = peer->conn; | 426 | lowpan_cb(skb)->chan = peer->chan; |
505 | } | 427 | } |
506 | 428 | ||
507 | saddr = dev->netdev->dev_addr; | 429 | saddr = dev->netdev->dev_addr; |
@@ -510,14 +432,42 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, | |||
510 | } | 432 | } |
511 | 433 | ||
512 | /* Packet to BT LE device */ | 434 | /* Packet to BT LE device */ |
513 | static int send_pkt(struct l2cap_conn *conn, const void *saddr, | 435 | static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, |
514 | const void *daddr, struct sk_buff *skb, | ||
515 | struct net_device *netdev) | 436 | struct net_device *netdev) |
516 | { | 437 | { |
517 | raw_dump_table(__func__, "raw skb data dump before fragmentation", | 438 | struct msghdr msg; |
518 | skb->data, skb->len); | 439 | struct kvec iv; |
440 | int err; | ||
441 | |||
442 | /* Remember the skb so that we can send EAGAIN to the caller if | ||
443 | * we run out of credits. | ||
444 | */ | ||
445 | chan->data = skb; | ||
446 | |||
447 | memset(&msg, 0, sizeof(msg)); | ||
448 | msg.msg_iov = (struct iovec *) &iv; | ||
449 | msg.msg_iovlen = 1; | ||
450 | iv.iov_base = skb->data; | ||
451 | iv.iov_len = skb->len; | ||
452 | |||
453 | err = l2cap_chan_send(chan, &msg, skb->len); | ||
454 | if (err > 0) { | ||
455 | netdev->stats.tx_bytes += err; | ||
456 | netdev->stats.tx_packets++; | ||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | if (!err) | ||
461 | err = lowpan_cb(skb)->status; | ||
519 | 462 | ||
520 | return conn_send(conn, skb->data, skb->len, 0, netdev); | 463 | if (err < 0) { |
464 | if (err == -EAGAIN) | ||
465 | netdev->stats.tx_dropped++; | ||
466 | else | ||
467 | netdev->stats.tx_errors++; | ||
468 | } | ||
469 | |||
470 | return err; | ||
521 | } | 471 | } |
522 | 472 | ||
523 | static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) | 473 | static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) |
@@ -540,8 +490,7 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) | |||
540 | list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) { | 490 | list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) { |
541 | local_skb = skb_clone(skb, GFP_ATOMIC); | 491 | local_skb = skb_clone(skb, GFP_ATOMIC); |
542 | 492 | ||
543 | send_pkt(pentry->conn, netdev->dev_addr, | 493 | send_pkt(pentry->chan, local_skb, netdev); |
544 | pentry->eui64_addr, local_skb, netdev); | ||
545 | 494 | ||
546 | kfree_skb(local_skb); | 495 | kfree_skb(local_skb); |
547 | } | 496 | } |
@@ -553,7 +502,6 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) | |||
553 | static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) | 502 | static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) |
554 | { | 503 | { |
555 | int err = 0; | 504 | int err = 0; |
556 | unsigned char *eui64_addr; | ||
557 | struct lowpan_dev *dev; | 505 | struct lowpan_dev *dev; |
558 | struct lowpan_peer *peer; | 506 | struct lowpan_peer *peer; |
559 | bdaddr_t addr; | 507 | bdaddr_t addr; |
@@ -568,21 +516,20 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) | |||
568 | unsigned long flags; | 516 | unsigned long flags; |
569 | 517 | ||
570 | convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type); | 518 | convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type); |
571 | eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8; | ||
572 | dev = lowpan_dev(netdev); | 519 | dev = lowpan_dev(netdev); |
573 | 520 | ||
574 | read_lock_irqsave(&devices_lock, flags); | 521 | read_lock_irqsave(&devices_lock, flags); |
575 | peer = peer_lookup_ba(dev, &addr, addr_type); | 522 | peer = peer_lookup_ba(dev, &addr, addr_type); |
576 | read_unlock_irqrestore(&devices_lock, flags); | 523 | read_unlock_irqrestore(&devices_lock, flags); |
577 | 524 | ||
578 | BT_DBG("xmit %s to %pMR type %s IP %pI6c peer %p", | 525 | BT_DBG("xmit %s to %pMR type %d IP %pI6c peer %p", |
579 | netdev->name, &addr, | 526 | netdev->name, &addr, addr_type, |
580 | addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM", | ||
581 | &lowpan_cb(skb)->addr, peer); | 527 | &lowpan_cb(skb)->addr, peer); |
582 | 528 | ||
583 | if (peer && peer->conn) | 529 | if (peer && peer->chan) |
584 | err = send_pkt(peer->conn, netdev->dev_addr, | 530 | err = send_pkt(peer->chan, skb, netdev); |
585 | eui64_addr, skb, netdev); | 531 | else |
532 | err = -ENOENT; | ||
586 | } | 533 | } |
587 | dev_kfree_skb(skb); | 534 | dev_kfree_skb(skb); |
588 | 535 | ||
@@ -634,7 +581,7 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type) | |||
634 | eui[7] = addr[0]; | 581 | eui[7] = addr[0]; |
635 | 582 | ||
636 | /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */ | 583 | /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */ |
637 | if (addr_type == ADDR_LE_DEV_PUBLIC) | 584 | if (addr_type == BDADDR_LE_PUBLIC) |
638 | eui[0] &= ~0x02; | 585 | eui[0] &= ~0x02; |
639 | else | 586 | else |
640 | eui[0] |= 0x02; | 587 | eui[0] |= 0x02; |
@@ -660,6 +607,17 @@ static void ifup(struct net_device *netdev) | |||
660 | rtnl_unlock(); | 607 | rtnl_unlock(); |
661 | } | 608 | } |
662 | 609 | ||
610 | static void ifdown(struct net_device *netdev) | ||
611 | { | ||
612 | int err; | ||
613 | |||
614 | rtnl_lock(); | ||
615 | err = dev_close(netdev); | ||
616 | if (err < 0) | ||
617 | BT_INFO("iface %s cannot be closed (%d)", netdev->name, err); | ||
618 | rtnl_unlock(); | ||
619 | } | ||
620 | |||
663 | static void do_notify_peers(struct work_struct *work) | 621 | static void do_notify_peers(struct work_struct *work) |
664 | { | 622 | { |
665 | struct lowpan_dev *dev = container_of(work, struct lowpan_dev, | 623 | struct lowpan_dev *dev = container_of(work, struct lowpan_dev, |
@@ -673,26 +631,64 @@ static bool is_bt_6lowpan(struct hci_conn *hcon) | |||
673 | if (hcon->type != LE_LINK) | 631 | if (hcon->type != LE_LINK) |
674 | return false; | 632 | return false; |
675 | 633 | ||
676 | return test_bit(HCI_CONN_6LOWPAN, &hcon->flags); | 634 | if (!psm_6lowpan) |
635 | return false; | ||
636 | |||
637 | return true; | ||
638 | } | ||
639 | |||
640 | static struct l2cap_chan *chan_create(void) | ||
641 | { | ||
642 | struct l2cap_chan *chan; | ||
643 | |||
644 | chan = l2cap_chan_create(); | ||
645 | if (!chan) | ||
646 | return NULL; | ||
647 | |||
648 | l2cap_chan_set_defaults(chan); | ||
649 | |||
650 | chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; | ||
651 | chan->mode = L2CAP_MODE_LE_FLOWCTL; | ||
652 | chan->omtu = 65535; | ||
653 | chan->imtu = chan->omtu; | ||
654 | |||
655 | return chan; | ||
677 | } | 656 | } |
678 | 657 | ||
679 | static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev) | 658 | static struct l2cap_chan *chan_open(struct l2cap_chan *pchan) |
659 | { | ||
660 | struct l2cap_chan *chan; | ||
661 | |||
662 | chan = chan_create(); | ||
663 | if (!chan) | ||
664 | return NULL; | ||
665 | |||
666 | chan->remote_mps = chan->omtu; | ||
667 | chan->mps = chan->omtu; | ||
668 | |||
669 | chan->state = BT_CONNECTED; | ||
670 | |||
671 | return chan; | ||
672 | } | ||
673 | |||
674 | static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, | ||
675 | struct lowpan_dev *dev) | ||
680 | { | 676 | { |
681 | struct lowpan_peer *peer; | 677 | struct lowpan_peer *peer; |
682 | unsigned long flags; | 678 | unsigned long flags; |
683 | 679 | ||
684 | peer = kzalloc(sizeof(*peer), GFP_ATOMIC); | 680 | peer = kzalloc(sizeof(*peer), GFP_ATOMIC); |
685 | if (!peer) | 681 | if (!peer) |
686 | return -ENOMEM; | 682 | return NULL; |
687 | 683 | ||
688 | peer->conn = conn; | 684 | peer->chan = chan; |
689 | memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); | 685 | memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); |
690 | 686 | ||
691 | /* RFC 2464 ch. 5 */ | 687 | /* RFC 2464 ch. 5 */ |
692 | peer->peer_addr.s6_addr[0] = 0xFE; | 688 | peer->peer_addr.s6_addr[0] = 0xFE; |
693 | peer->peer_addr.s6_addr[1] = 0x80; | 689 | peer->peer_addr.s6_addr[1] = 0x80; |
694 | set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b, | 690 | set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b, |
695 | conn->hcon->dst_type); | 691 | chan->dst_type); |
696 | 692 | ||
697 | memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8, | 693 | memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8, |
698 | EUI64_ADDR_LEN); | 694 | EUI64_ADDR_LEN); |
@@ -706,40 +702,24 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev) | |||
706 | INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); | 702 | INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); |
707 | schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); | 703 | schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); |
708 | 704 | ||
709 | return 0; | 705 | return peer->chan; |
710 | } | 706 | } |
711 | 707 | ||
712 | /* This gets called when BT LE 6LoWPAN device is connected. We then | 708 | static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) |
713 | * create network device that acts as a proxy between BT LE device | ||
714 | * and kernel network stack. | ||
715 | */ | ||
716 | int bt_6lowpan_add_conn(struct l2cap_conn *conn) | ||
717 | { | 709 | { |
718 | struct lowpan_peer *peer = NULL; | ||
719 | struct lowpan_dev *dev; | ||
720 | struct net_device *netdev; | 710 | struct net_device *netdev; |
721 | int err = 0; | 711 | int err = 0; |
722 | unsigned long flags; | 712 | unsigned long flags; |
723 | 713 | ||
724 | if (!is_bt_6lowpan(conn->hcon)) | 714 | netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE, |
725 | return 0; | 715 | NET_NAME_UNKNOWN, netdev_setup); |
726 | |||
727 | peer = lookup_peer(conn); | ||
728 | if (peer) | ||
729 | return -EEXIST; | ||
730 | |||
731 | dev = lookup_dev(conn); | ||
732 | if (dev) | ||
733 | return add_peer_conn(conn, dev); | ||
734 | |||
735 | netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup); | ||
736 | if (!netdev) | 716 | if (!netdev) |
737 | return -ENOMEM; | 717 | return -ENOMEM; |
738 | 718 | ||
739 | set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type); | 719 | set_dev_addr(netdev, &chan->src, chan->src_type); |
740 | 720 | ||
741 | netdev->netdev_ops = &netdev_ops; | 721 | netdev->netdev_ops = &netdev_ops; |
742 | SET_NETDEV_DEV(netdev, &conn->hcon->dev); | 722 | SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev); |
743 | SET_NETDEV_DEVTYPE(netdev, &bt_type); | 723 | SET_NETDEV_DEVTYPE(netdev, &bt_type); |
744 | 724 | ||
745 | err = register_netdev(netdev); | 725 | err = register_netdev(netdev); |
@@ -749,28 +729,61 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn) | |||
749 | goto out; | 729 | goto out; |
750 | } | 730 | } |
751 | 731 | ||
752 | BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR", | 732 | BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d", |
753 | netdev->ifindex, &conn->hcon->dst, &conn->hcon->src); | 733 | netdev->ifindex, &chan->dst, chan->dst_type, |
734 | &chan->src, chan->src_type); | ||
754 | set_bit(__LINK_STATE_PRESENT, &netdev->state); | 735 | set_bit(__LINK_STATE_PRESENT, &netdev->state); |
755 | 736 | ||
756 | dev = netdev_priv(netdev); | 737 | *dev = netdev_priv(netdev); |
757 | dev->netdev = netdev; | 738 | (*dev)->netdev = netdev; |
758 | dev->hdev = conn->hcon->hdev; | 739 | (*dev)->hdev = chan->conn->hcon->hdev; |
759 | INIT_LIST_HEAD(&dev->peers); | 740 | INIT_LIST_HEAD(&(*dev)->peers); |
760 | 741 | ||
761 | write_lock_irqsave(&devices_lock, flags); | 742 | write_lock_irqsave(&devices_lock, flags); |
762 | INIT_LIST_HEAD(&dev->list); | 743 | INIT_LIST_HEAD(&(*dev)->list); |
763 | list_add(&dev->list, &bt_6lowpan_devices); | 744 | list_add(&(*dev)->list, &bt_6lowpan_devices); |
764 | write_unlock_irqrestore(&devices_lock, flags); | 745 | write_unlock_irqrestore(&devices_lock, flags); |
765 | 746 | ||
766 | ifup(netdev); | 747 | return 0; |
767 | |||
768 | return add_peer_conn(conn, dev); | ||
769 | 748 | ||
770 | out: | 749 | out: |
771 | return err; | 750 | return err; |
772 | } | 751 | } |
773 | 752 | ||
753 | static inline void chan_ready_cb(struct l2cap_chan *chan) | ||
754 | { | ||
755 | struct lowpan_dev *dev; | ||
756 | |||
757 | dev = lookup_dev(chan->conn); | ||
758 | |||
759 | BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev); | ||
760 | |||
761 | if (!dev) { | ||
762 | if (setup_netdev(chan, &dev) < 0) { | ||
763 | l2cap_chan_del(chan, -ENOENT); | ||
764 | return; | ||
765 | } | ||
766 | } | ||
767 | |||
768 | if (!try_module_get(THIS_MODULE)) | ||
769 | return; | ||
770 | |||
771 | add_peer_chan(chan, dev); | ||
772 | ifup(dev->netdev); | ||
773 | } | ||
774 | |||
775 | static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *chan) | ||
776 | { | ||
777 | struct l2cap_chan *pchan; | ||
778 | |||
779 | pchan = chan_open(chan); | ||
780 | pchan->ops = chan->ops; | ||
781 | |||
782 | BT_DBG("chan %p pchan %p", chan, pchan); | ||
783 | |||
784 | return pchan; | ||
785 | } | ||
786 | |||
774 | static void delete_netdev(struct work_struct *work) | 787 | static void delete_netdev(struct work_struct *work) |
775 | { | 788 | { |
776 | struct lowpan_dev *entry = container_of(work, struct lowpan_dev, | 789 | struct lowpan_dev *entry = container_of(work, struct lowpan_dev, |
@@ -781,26 +794,43 @@ static void delete_netdev(struct work_struct *work) | |||
781 | /* The entry pointer is deleted in device_event() */ | 794 | /* The entry pointer is deleted in device_event() */ |
782 | } | 795 | } |
783 | 796 | ||
784 | int bt_6lowpan_del_conn(struct l2cap_conn *conn) | 797 | static void chan_close_cb(struct l2cap_chan *chan) |
785 | { | 798 | { |
786 | struct lowpan_dev *entry, *tmp; | 799 | struct lowpan_dev *entry, *tmp; |
787 | struct lowpan_dev *dev = NULL; | 800 | struct lowpan_dev *dev = NULL; |
788 | struct lowpan_peer *peer; | 801 | struct lowpan_peer *peer; |
789 | int err = -ENOENT; | 802 | int err = -ENOENT; |
790 | unsigned long flags; | 803 | unsigned long flags; |
791 | bool last = false; | 804 | bool last = false, removed = true; |
792 | 805 | ||
793 | if (!conn || !is_bt_6lowpan(conn->hcon)) | 806 | BT_DBG("chan %p conn %p", chan, chan->conn); |
794 | return 0; | 807 | |
808 | if (chan->conn && chan->conn->hcon) { | ||
809 | if (!is_bt_6lowpan(chan->conn->hcon)) | ||
810 | return; | ||
811 | |||
812 | /* If conn is set, then the netdev is also there and we should | ||
813 | * not remove it. | ||
814 | */ | ||
815 | removed = false; | ||
816 | } | ||
795 | 817 | ||
796 | write_lock_irqsave(&devices_lock, flags); | 818 | write_lock_irqsave(&devices_lock, flags); |
797 | 819 | ||
798 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { | 820 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { |
799 | dev = lowpan_dev(entry->netdev); | 821 | dev = lowpan_dev(entry->netdev); |
800 | peer = peer_lookup_conn(dev, conn); | 822 | peer = peer_lookup_chan(dev, chan); |
801 | if (peer) { | 823 | if (peer) { |
802 | last = peer_del(dev, peer); | 824 | last = peer_del(dev, peer); |
803 | err = 0; | 825 | err = 0; |
826 | |||
827 | BT_DBG("dev %p removing %speer %p", dev, | ||
828 | last ? "last " : "1 ", peer); | ||
829 | BT_DBG("chan %p orig refcnt %d", chan, | ||
830 | atomic_read(&chan->kref.refcount)); | ||
831 | |||
832 | l2cap_chan_put(chan); | ||
833 | kfree(peer); | ||
804 | break; | 834 | break; |
805 | } | 835 | } |
806 | } | 836 | } |
@@ -810,18 +840,402 @@ int bt_6lowpan_del_conn(struct l2cap_conn *conn) | |||
810 | 840 | ||
811 | cancel_delayed_work_sync(&dev->notify_peers); | 841 | cancel_delayed_work_sync(&dev->notify_peers); |
812 | 842 | ||
813 | /* bt_6lowpan_del_conn() is called with hci dev lock held which | 843 | ifdown(dev->netdev); |
814 | * means that we must delete the netdevice in worker thread. | 844 | |
815 | */ | 845 | if (!removed) { |
816 | INIT_WORK(&entry->delete_netdev, delete_netdev); | 846 | INIT_WORK(&entry->delete_netdev, delete_netdev); |
817 | schedule_work(&entry->delete_netdev); | 847 | schedule_work(&entry->delete_netdev); |
848 | } | ||
818 | } else { | 849 | } else { |
819 | write_unlock_irqrestore(&devices_lock, flags); | 850 | write_unlock_irqrestore(&devices_lock, flags); |
820 | } | 851 | } |
821 | 852 | ||
853 | return; | ||
854 | } | ||
855 | |||
856 | static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err) | ||
857 | { | ||
858 | BT_DBG("chan %p conn %p state %s err %d", chan, chan->conn, | ||
859 | state_to_string(state), err); | ||
860 | } | ||
861 | |||
862 | static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan, | ||
863 | unsigned long hdr_len, | ||
864 | unsigned long len, int nb) | ||
865 | { | ||
866 | /* Note that we must allocate using GFP_ATOMIC here as | ||
867 | * this function is called originally from netdev hard xmit | ||
868 | * function in atomic context. | ||
869 | */ | ||
870 | return bt_skb_alloc(hdr_len + len, GFP_ATOMIC); | ||
871 | } | ||
872 | |||
873 | static void chan_suspend_cb(struct l2cap_chan *chan) | ||
874 | { | ||
875 | struct sk_buff *skb = chan->data; | ||
876 | |||
877 | BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); | ||
878 | |||
879 | lowpan_cb(skb)->status = -EAGAIN; | ||
880 | } | ||
881 | |||
882 | static void chan_resume_cb(struct l2cap_chan *chan) | ||
883 | { | ||
884 | struct sk_buff *skb = chan->data; | ||
885 | |||
886 | BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); | ||
887 | |||
888 | lowpan_cb(skb)->status = 0; | ||
889 | } | ||
890 | |||
891 | static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) | ||
892 | { | ||
893 | return msecs_to_jiffies(1000); | ||
894 | } | ||
895 | |||
896 | static const struct l2cap_ops bt_6lowpan_chan_ops = { | ||
897 | .name = "L2CAP 6LoWPAN channel", | ||
898 | .new_connection = chan_new_conn_cb, | ||
899 | .recv = chan_recv_cb, | ||
900 | .close = chan_close_cb, | ||
901 | .state_change = chan_state_change_cb, | ||
902 | .ready = chan_ready_cb, | ||
903 | .resume = chan_resume_cb, | ||
904 | .suspend = chan_suspend_cb, | ||
905 | .get_sndtimeo = chan_get_sndtimeo_cb, | ||
906 | .alloc_skb = chan_alloc_skb_cb, | ||
907 | .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, | ||
908 | |||
909 | .teardown = l2cap_chan_no_teardown, | ||
910 | .defer = l2cap_chan_no_defer, | ||
911 | .set_shutdown = l2cap_chan_no_set_shutdown, | ||
912 | }; | ||
913 | |||
914 | static inline __u8 bdaddr_type(__u8 type) | ||
915 | { | ||
916 | if (type == ADDR_LE_DEV_PUBLIC) | ||
917 | return BDADDR_LE_PUBLIC; | ||
918 | else | ||
919 | return BDADDR_LE_RANDOM; | ||
920 | } | ||
921 | |||
922 | static struct l2cap_chan *chan_get(void) | ||
923 | { | ||
924 | struct l2cap_chan *pchan; | ||
925 | |||
926 | pchan = chan_create(); | ||
927 | if (!pchan) | ||
928 | return NULL; | ||
929 | |||
930 | pchan->ops = &bt_6lowpan_chan_ops; | ||
931 | |||
932 | return pchan; | ||
933 | } | ||
934 | |||
935 | static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type) | ||
936 | { | ||
937 | struct l2cap_chan *pchan; | ||
938 | int err; | ||
939 | |||
940 | pchan = chan_get(); | ||
941 | if (!pchan) | ||
942 | return -EINVAL; | ||
943 | |||
944 | err = l2cap_chan_connect(pchan, cpu_to_le16(psm_6lowpan), 0, | ||
945 | addr, dst_type); | ||
946 | |||
947 | BT_DBG("chan %p err %d", pchan, err); | ||
948 | if (err < 0) | ||
949 | l2cap_chan_put(pchan); | ||
950 | |||
822 | return err; | 951 | return err; |
823 | } | 952 | } |
824 | 953 | ||
954 | static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type) | ||
955 | { | ||
956 | struct lowpan_peer *peer; | ||
957 | |||
958 | BT_DBG("conn %p dst type %d", conn, dst_type); | ||
959 | |||
960 | peer = lookup_peer(conn); | ||
961 | if (!peer) | ||
962 | return -ENOENT; | ||
963 | |||
964 | BT_DBG("peer %p chan %p", peer, peer->chan); | ||
965 | |||
966 | l2cap_chan_close(peer->chan, ENOENT); | ||
967 | |||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | static struct l2cap_chan *bt_6lowpan_listen(void) | ||
972 | { | ||
973 | bdaddr_t *addr = BDADDR_ANY; | ||
974 | struct l2cap_chan *pchan; | ||
975 | int err; | ||
976 | |||
977 | if (psm_6lowpan == 0) | ||
978 | return NULL; | ||
979 | |||
980 | pchan = chan_get(); | ||
981 | if (!pchan) | ||
982 | return NULL; | ||
983 | |||
984 | pchan->state = BT_LISTEN; | ||
985 | pchan->src_type = BDADDR_LE_PUBLIC; | ||
986 | |||
987 | BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan, | ||
988 | pchan->src_type); | ||
989 | |||
990 | err = l2cap_add_psm(pchan, addr, cpu_to_le16(psm_6lowpan)); | ||
991 | if (err) { | ||
992 | l2cap_chan_put(pchan); | ||
993 | BT_ERR("psm cannot be added err %d", err); | ||
994 | return NULL; | ||
995 | } | ||
996 | |||
997 | return pchan; | ||
998 | } | ||
999 | |||
1000 | static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, | ||
1001 | struct l2cap_conn **conn) | ||
1002 | { | ||
1003 | struct hci_conn *hcon; | ||
1004 | struct hci_dev *hdev; | ||
1005 | bdaddr_t *src = BDADDR_ANY; | ||
1006 | int n; | ||
1007 | |||
1008 | n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu", | ||
1009 | &addr->b[5], &addr->b[4], &addr->b[3], | ||
1010 | &addr->b[2], &addr->b[1], &addr->b[0], | ||
1011 | addr_type); | ||
1012 | |||
1013 | if (n < 7) | ||
1014 | return -EINVAL; | ||
1015 | |||
1016 | hdev = hci_get_route(addr, src); | ||
1017 | if (!hdev) | ||
1018 | return -ENOENT; | ||
1019 | |||
1020 | hci_dev_lock(hdev); | ||
1021 | hcon = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); | ||
1022 | hci_dev_unlock(hdev); | ||
1023 | |||
1024 | if (!hcon) | ||
1025 | return -ENOENT; | ||
1026 | |||
1027 | *conn = (struct l2cap_conn *)hcon->l2cap_data; | ||
1028 | |||
1029 | BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type); | ||
1030 | |||
1031 | return 0; | ||
1032 | } | ||
1033 | |||
1034 | static void disconnect_all_peers(void) | ||
1035 | { | ||
1036 | struct lowpan_dev *entry, *tmp_dev; | ||
1037 | struct lowpan_peer *peer, *tmp_peer, *new_peer; | ||
1038 | struct list_head peers; | ||
1039 | unsigned long flags; | ||
1040 | |||
1041 | INIT_LIST_HEAD(&peers); | ||
1042 | |||
1043 | /* We make a separate list of peers as the close_cb() will | ||
1044 | * modify the device peers list so it is better not to mess | ||
1045 | * with the same list at the same time. | ||
1046 | */ | ||
1047 | |||
1048 | read_lock_irqsave(&devices_lock, flags); | ||
1049 | |||
1050 | list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { | ||
1051 | list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) { | ||
1052 | new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC); | ||
1053 | if (!new_peer) | ||
1054 | break; | ||
1055 | |||
1056 | new_peer->chan = peer->chan; | ||
1057 | INIT_LIST_HEAD(&new_peer->list); | ||
1058 | |||
1059 | list_add(&new_peer->list, &peers); | ||
1060 | } | ||
1061 | } | ||
1062 | |||
1063 | read_unlock_irqrestore(&devices_lock, flags); | ||
1064 | |||
1065 | list_for_each_entry_safe(peer, tmp_peer, &peers, list) { | ||
1066 | l2cap_chan_close(peer->chan, ENOENT); | ||
1067 | kfree(peer); | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | static int lowpan_psm_set(void *data, u64 val) | ||
1072 | { | ||
1073 | u16 psm; | ||
1074 | |||
1075 | psm = val; | ||
1076 | if (psm == 0 || psm_6lowpan != psm) | ||
1077 | /* Disconnect existing connections if 6lowpan is | ||
1078 | * disabled (psm = 0), or if psm changes. | ||
1079 | */ | ||
1080 | disconnect_all_peers(); | ||
1081 | |||
1082 | psm_6lowpan = psm; | ||
1083 | |||
1084 | if (listen_chan) { | ||
1085 | l2cap_chan_close(listen_chan, 0); | ||
1086 | l2cap_chan_put(listen_chan); | ||
1087 | } | ||
1088 | |||
1089 | listen_chan = bt_6lowpan_listen(); | ||
1090 | |||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | static int lowpan_psm_get(void *data, u64 *val) | ||
1095 | { | ||
1096 | *val = psm_6lowpan; | ||
1097 | return 0; | ||
1098 | } | ||
1099 | |||
1100 | DEFINE_SIMPLE_ATTRIBUTE(lowpan_psm_fops, lowpan_psm_get, | ||
1101 | lowpan_psm_set, "%llu\n"); | ||
1102 | |||
1103 | static ssize_t lowpan_control_write(struct file *fp, | ||
1104 | const char __user *user_buffer, | ||
1105 | size_t count, | ||
1106 | loff_t *position) | ||
1107 | { | ||
1108 | char buf[32]; | ||
1109 | size_t buf_size = min(count, sizeof(buf) - 1); | ||
1110 | int ret; | ||
1111 | bdaddr_t addr; | ||
1112 | u8 addr_type; | ||
1113 | struct l2cap_conn *conn = NULL; | ||
1114 | |||
1115 | if (copy_from_user(buf, user_buffer, buf_size)) | ||
1116 | return -EFAULT; | ||
1117 | |||
1118 | buf[buf_size] = '\0'; | ||
1119 | |||
1120 | if (memcmp(buf, "connect ", 8) == 0) { | ||
1121 | ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn); | ||
1122 | if (ret == -EINVAL) | ||
1123 | return ret; | ||
1124 | |||
1125 | if (listen_chan) { | ||
1126 | l2cap_chan_close(listen_chan, 0); | ||
1127 | l2cap_chan_put(listen_chan); | ||
1128 | listen_chan = NULL; | ||
1129 | } | ||
1130 | |||
1131 | if (conn) { | ||
1132 | struct lowpan_peer *peer; | ||
1133 | |||
1134 | if (!is_bt_6lowpan(conn->hcon)) | ||
1135 | return -EINVAL; | ||
1136 | |||
1137 | peer = lookup_peer(conn); | ||
1138 | if (peer) { | ||
1139 | BT_DBG("6LoWPAN connection already exists"); | ||
1140 | return -EALREADY; | ||
1141 | } | ||
1142 | |||
1143 | BT_DBG("conn %p dst %pMR type %d user %d", conn, | ||
1144 | &conn->hcon->dst, conn->hcon->dst_type, | ||
1145 | addr_type); | ||
1146 | } | ||
1147 | |||
1148 | ret = bt_6lowpan_connect(&addr, addr_type); | ||
1149 | if (ret < 0) | ||
1150 | return ret; | ||
1151 | |||
1152 | return count; | ||
1153 | } | ||
1154 | |||
1155 | if (memcmp(buf, "disconnect ", 11) == 0) { | ||
1156 | ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn); | ||
1157 | if (ret < 0) | ||
1158 | return ret; | ||
1159 | |||
1160 | ret = bt_6lowpan_disconnect(conn, addr_type); | ||
1161 | if (ret < 0) | ||
1162 | return ret; | ||
1163 | |||
1164 | return count; | ||
1165 | } | ||
1166 | |||
1167 | return count; | ||
1168 | } | ||
1169 | |||
1170 | static int lowpan_control_show(struct seq_file *f, void *ptr) | ||
1171 | { | ||
1172 | struct lowpan_dev *entry, *tmp_dev; | ||
1173 | struct lowpan_peer *peer, *tmp_peer; | ||
1174 | unsigned long flags; | ||
1175 | |||
1176 | read_lock_irqsave(&devices_lock, flags); | ||
1177 | |||
1178 | list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { | ||
1179 | list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) | ||
1180 | seq_printf(f, "%pMR (type %u)\n", | ||
1181 | &peer->chan->dst, peer->chan->dst_type); | ||
1182 | } | ||
1183 | |||
1184 | read_unlock_irqrestore(&devices_lock, flags); | ||
1185 | |||
1186 | return 0; | ||
1187 | } | ||
1188 | |||
1189 | static int lowpan_control_open(struct inode *inode, struct file *file) | ||
1190 | { | ||
1191 | return single_open(file, lowpan_control_show, inode->i_private); | ||
1192 | } | ||
1193 | |||
1194 | static const struct file_operations lowpan_control_fops = { | ||
1195 | .open = lowpan_control_open, | ||
1196 | .read = seq_read, | ||
1197 | .write = lowpan_control_write, | ||
1198 | .llseek = seq_lseek, | ||
1199 | .release = single_release, | ||
1200 | }; | ||
1201 | |||
1202 | static void disconnect_devices(void) | ||
1203 | { | ||
1204 | struct lowpan_dev *entry, *tmp, *new_dev; | ||
1205 | struct list_head devices; | ||
1206 | unsigned long flags; | ||
1207 | |||
1208 | INIT_LIST_HEAD(&devices); | ||
1209 | |||
1210 | /* We make a separate list of devices because the unregister_netdev() | ||
1211 | * will call device_event() which will also want to modify the same | ||
1212 | * devices list. | ||
1213 | */ | ||
1214 | |||
1215 | read_lock_irqsave(&devices_lock, flags); | ||
1216 | |||
1217 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { | ||
1218 | new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC); | ||
1219 | if (!new_dev) | ||
1220 | break; | ||
1221 | |||
1222 | new_dev->netdev = entry->netdev; | ||
1223 | INIT_LIST_HEAD(&new_dev->list); | ||
1224 | |||
1225 | list_add(&new_dev->list, &devices); | ||
1226 | } | ||
1227 | |||
1228 | read_unlock_irqrestore(&devices_lock, flags); | ||
1229 | |||
1230 | list_for_each_entry_safe(entry, tmp, &devices, list) { | ||
1231 | ifdown(entry->netdev); | ||
1232 | BT_DBG("Unregistering netdev %s %p", | ||
1233 | entry->netdev->name, entry->netdev); | ||
1234 | unregister_netdev(entry->netdev); | ||
1235 | kfree(entry); | ||
1236 | } | ||
1237 | } | ||
1238 | |||
825 | static int device_event(struct notifier_block *unused, | 1239 | static int device_event(struct notifier_block *unused, |
826 | unsigned long event, void *ptr) | 1240 | unsigned long event, void *ptr) |
827 | { | 1241 | { |
@@ -838,6 +1252,8 @@ static int device_event(struct notifier_block *unused, | |||
838 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, | 1252 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, |
839 | list) { | 1253 | list) { |
840 | if (entry->netdev == netdev) { | 1254 | if (entry->netdev == netdev) { |
1255 | BT_DBG("Unregistered netdev %s %p", | ||
1256 | netdev->name, netdev); | ||
841 | list_del(&entry->list); | 1257 | list_del(&entry->list); |
842 | kfree(entry); | 1258 | kfree(entry); |
843 | break; | 1259 | break; |
@@ -854,12 +1270,37 @@ static struct notifier_block bt_6lowpan_dev_notifier = { | |||
854 | .notifier_call = device_event, | 1270 | .notifier_call = device_event, |
855 | }; | 1271 | }; |
856 | 1272 | ||
857 | int bt_6lowpan_init(void) | 1273 | static int __init bt_6lowpan_init(void) |
858 | { | 1274 | { |
1275 | lowpan_psm_debugfs = debugfs_create_file("6lowpan_psm", 0644, | ||
1276 | bt_debugfs, NULL, | ||
1277 | &lowpan_psm_fops); | ||
1278 | lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644, | ||
1279 | bt_debugfs, NULL, | ||
1280 | &lowpan_control_fops); | ||
1281 | |||
859 | return register_netdevice_notifier(&bt_6lowpan_dev_notifier); | 1282 | return register_netdevice_notifier(&bt_6lowpan_dev_notifier); |
860 | } | 1283 | } |
861 | 1284 | ||
862 | void bt_6lowpan_cleanup(void) | 1285 | static void __exit bt_6lowpan_exit(void) |
863 | { | 1286 | { |
1287 | debugfs_remove(lowpan_psm_debugfs); | ||
1288 | debugfs_remove(lowpan_control_debugfs); | ||
1289 | |||
1290 | if (listen_chan) { | ||
1291 | l2cap_chan_close(listen_chan, 0); | ||
1292 | l2cap_chan_put(listen_chan); | ||
1293 | } | ||
1294 | |||
1295 | disconnect_devices(); | ||
1296 | |||
864 | unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); | 1297 | unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); |
865 | } | 1298 | } |
1299 | |||
1300 | module_init(bt_6lowpan_init); | ||
1301 | module_exit(bt_6lowpan_exit); | ||
1302 | |||
1303 | MODULE_AUTHOR("Jukka Rissanen <jukka.rissanen@linux.intel.com>"); | ||
1304 | MODULE_DESCRIPTION("Bluetooth 6LoWPAN"); | ||
1305 | MODULE_VERSION(VERSION); | ||
1306 | MODULE_LICENSE("GPL"); | ||