xref: /openbmc/linux/net/bluetooth/6lowpan.c (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
218722c24SJukka Rissanen /*
36b8d4a6aSJukka Rissanen    Copyright (c) 2013-2014 Intel Corp.
418722c24SJukka Rissanen 
518722c24SJukka Rissanen */
618722c24SJukka Rissanen 
718722c24SJukka Rissanen #include <linux/if_arp.h>
818722c24SJukka Rissanen #include <linux/netdevice.h>
918722c24SJukka Rissanen #include <linux/etherdevice.h>
105547e48cSJukka Rissanen #include <linux/module.h>
116b8d4a6aSJukka Rissanen #include <linux/debugfs.h>
1218722c24SJukka Rissanen 
1318722c24SJukka Rissanen #include <net/ipv6.h>
1418722c24SJukka Rissanen #include <net/ip6_route.h>
1518722c24SJukka Rissanen #include <net/addrconf.h>
16814f1b24SLuiz Augusto von Dentz #include <net/pkt_sched.h>
1718722c24SJukka Rissanen 
1818722c24SJukka Rissanen #include <net/bluetooth/bluetooth.h>
1918722c24SJukka Rissanen #include <net/bluetooth/hci_core.h>
2018722c24SJukka Rissanen #include <net/bluetooth/l2cap.h>
2118722c24SJukka Rissanen 
22cefc8c8aSAlexander Aring #include <net/6lowpan.h> /* for the compression support */
2318722c24SJukka Rissanen 
246b8d4a6aSJukka Rissanen #define VERSION "0.1"
256b8d4a6aSJukka Rissanen 
267b2ed60eSJukka Rissanen static struct dentry *lowpan_enable_debugfs;
276b8d4a6aSJukka Rissanen static struct dentry *lowpan_control_debugfs;
286b8d4a6aSJukka Rissanen 
2918722c24SJukka Rissanen #define IFACE_NAME_TEMPLATE "bt%d"
3018722c24SJukka Rissanen 
3118722c24SJukka Rissanen struct skb_cb {
3218722c24SJukka Rissanen 	struct in6_addr addr;
3339e90c77SJukka Rissanen 	struct in6_addr gw;
346b8d4a6aSJukka Rissanen 	struct l2cap_chan *chan;
3518722c24SJukka Rissanen };
3618722c24SJukka Rissanen #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
3718722c24SJukka Rissanen 
3818722c24SJukka Rissanen /* The devices list contains those devices that we are acting
3918722c24SJukka Rissanen  * as a proxy. The BT 6LoWPAN device is a virtual device that
4018722c24SJukka Rissanen  * connects to the Bluetooth LE device. The real connection to
4118722c24SJukka Rissanen  * BT device is done via l2cap layer. There exists one
4218722c24SJukka Rissanen  * virtual device / one BT 6LoWPAN network (=hciX device).
4318722c24SJukka Rissanen  * The list contains struct lowpan_dev elements.
4418722c24SJukka Rissanen  */
4518722c24SJukka Rissanen static LIST_HEAD(bt_6lowpan_devices);
4690305829SJukka Rissanen static DEFINE_SPINLOCK(devices_lock);
4718722c24SJukka Rissanen 
487b2ed60eSJukka Rissanen static bool enable_6lowpan;
496b8d4a6aSJukka Rissanen 
506b8d4a6aSJukka Rissanen /* We are listening incoming connections via this channel
516b8d4a6aSJukka Rissanen  */
526b8d4a6aSJukka Rissanen static struct l2cap_chan *listen_chan;
53f9c70bdcSLihong Kou static DEFINE_MUTEX(set_lock);
546b8d4a6aSJukka Rissanen 
5518722c24SJukka Rissanen struct lowpan_peer {
5618722c24SJukka Rissanen 	struct list_head list;
5790305829SJukka Rissanen 	struct rcu_head rcu;
586b8d4a6aSJukka Rissanen 	struct l2cap_chan *chan;
5918722c24SJukka Rissanen 
6018722c24SJukka Rissanen 	/* peer addresses in various formats */
61fa09ae66SLuiz Augusto von Dentz 	unsigned char lladdr[ETH_ALEN];
6218722c24SJukka Rissanen 	struct in6_addr peer_addr;
6318722c24SJukka Rissanen };
6418722c24SJukka Rissanen 
652e4d60cbSAlexander Aring struct lowpan_btle_dev {
6618722c24SJukka Rissanen 	struct list_head list;
6718722c24SJukka Rissanen 
6818722c24SJukka Rissanen 	struct hci_dev *hdev;
6918722c24SJukka Rissanen 	struct net_device *netdev;
7018722c24SJukka Rissanen 	struct list_head peers;
7118722c24SJukka Rissanen 	atomic_t peer_count; /* number of items in peers list */
7218722c24SJukka Rissanen 
7318722c24SJukka Rissanen 	struct work_struct delete_netdev;
7418722c24SJukka Rissanen 	struct delayed_work notify_peers;
7518722c24SJukka Rissanen };
7618722c24SJukka Rissanen 
772e4d60cbSAlexander Aring static inline struct lowpan_btle_dev *
lowpan_btle_dev(const struct net_device * netdev)782e4d60cbSAlexander Aring lowpan_btle_dev(const struct net_device *netdev)
7918722c24SJukka Rissanen {
802e4d60cbSAlexander Aring 	return (struct lowpan_btle_dev *)lowpan_dev(netdev)->priv;
8118722c24SJukka Rissanen }
8218722c24SJukka Rissanen 
peer_add(struct lowpan_btle_dev * dev,struct lowpan_peer * peer)832e4d60cbSAlexander Aring static inline void peer_add(struct lowpan_btle_dev *dev,
842e4d60cbSAlexander Aring 			    struct lowpan_peer *peer)
8518722c24SJukka Rissanen {
8690305829SJukka Rissanen 	list_add_rcu(&peer->list, &dev->peers);
8718722c24SJukka Rissanen 	atomic_inc(&dev->peer_count);
8818722c24SJukka Rissanen }
8918722c24SJukka Rissanen 
peer_del(struct lowpan_btle_dev * dev,struct lowpan_peer * peer)902e4d60cbSAlexander Aring static inline bool peer_del(struct lowpan_btle_dev *dev,
912e4d60cbSAlexander Aring 			    struct lowpan_peer *peer)
9218722c24SJukka Rissanen {
9390305829SJukka Rissanen 	list_del_rcu(&peer->list);
944e790226SJohan Hedberg 	kfree_rcu(peer, rcu);
9518722c24SJukka Rissanen 
9618d93c17SJukka Rissanen 	module_put(THIS_MODULE);
9718d93c17SJukka Rissanen 
9818722c24SJukka Rissanen 	if (atomic_dec_and_test(&dev->peer_count)) {
9918722c24SJukka Rissanen 		BT_DBG("last peer");
10018722c24SJukka Rissanen 		return true;
10118722c24SJukka Rissanen 	}
10218722c24SJukka Rissanen 
10318722c24SJukka Rissanen 	return false;
10418722c24SJukka Rissanen }
10518722c24SJukka Rissanen 
1062e4d60cbSAlexander Aring static inline struct lowpan_peer *
__peer_lookup_chan(struct lowpan_btle_dev * dev,struct l2cap_chan * chan)1072e4d60cbSAlexander Aring __peer_lookup_chan(struct lowpan_btle_dev *dev, struct l2cap_chan *chan)
1086b8d4a6aSJukka Rissanen {
10990305829SJukka Rissanen 	struct lowpan_peer *peer;
1106b8d4a6aSJukka Rissanen 
11190305829SJukka Rissanen 	list_for_each_entry_rcu(peer, &dev->peers, list) {
1126b8d4a6aSJukka Rissanen 		if (peer->chan == chan)
11318722c24SJukka Rissanen 			return peer;
11418722c24SJukka Rissanen 	}
11518722c24SJukka Rissanen 
11618722c24SJukka Rissanen 	return NULL;
11718722c24SJukka Rissanen }
11818722c24SJukka Rissanen 
1192e4d60cbSAlexander Aring static inline struct lowpan_peer *
__peer_lookup_conn(struct lowpan_btle_dev * dev,struct l2cap_conn * conn)1202e4d60cbSAlexander Aring __peer_lookup_conn(struct lowpan_btle_dev *dev, struct l2cap_conn *conn)
12118722c24SJukka Rissanen {
12290305829SJukka Rissanen 	struct lowpan_peer *peer;
12318722c24SJukka Rissanen 
12490305829SJukka Rissanen 	list_for_each_entry_rcu(peer, &dev->peers, list) {
1256b8d4a6aSJukka Rissanen 		if (peer->chan->conn == conn)
12618722c24SJukka Rissanen 			return peer;
12718722c24SJukka Rissanen 	}
12818722c24SJukka Rissanen 
12918722c24SJukka Rissanen 	return NULL;
13018722c24SJukka Rissanen }
13118722c24SJukka Rissanen 
peer_lookup_dst(struct lowpan_btle_dev * dev,struct in6_addr * daddr,struct sk_buff * skb)1322e4d60cbSAlexander Aring static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
13339e90c77SJukka Rissanen 						  struct in6_addr *daddr,
13439e90c77SJukka Rissanen 						  struct sk_buff *skb)
13539e90c77SJukka Rissanen {
136*797a4c1fSEric Dumazet 	struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
13739e90c77SJukka Rissanen 	int count = atomic_read(&dev->peer_count);
1389b1c1ef1SNicolas Dichtel 	const struct in6_addr *nexthop;
1399b1c1ef1SNicolas Dichtel 	struct lowpan_peer *peer;
1405636376cSJosua Mayer 	struct neighbour *neigh;
14139e90c77SJukka Rissanen 
14239e90c77SJukka Rissanen 	BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt);
14339e90c77SJukka Rissanen 
14439e90c77SJukka Rissanen 	if (!rt) {
145b188b032SJosua Mayer 		if (ipv6_addr_any(&lowpan_cb(skb)->gw)) {
146b188b032SJosua Mayer 			/* There is neither route nor gateway,
147b188b032SJosua Mayer 			 * probably the destination is a direct peer.
148b188b032SJosua Mayer 			 */
149b188b032SJosua Mayer 			nexthop = daddr;
150b188b032SJosua Mayer 		} else {
151b188b032SJosua Mayer 			/* There is a known gateway
152b188b032SJosua Mayer 			 */
15339e90c77SJukka Rissanen 			nexthop = &lowpan_cb(skb)->gw;
154b188b032SJosua Mayer 		}
15539e90c77SJukka Rissanen 	} else {
1562647a9b0SMartin KaFai Lau 		nexthop = rt6_nexthop(rt, daddr);
15739e90c77SJukka Rissanen 
15839e90c77SJukka Rissanen 		/* We need to remember the address because it is needed
15939e90c77SJukka Rissanen 		 * by bt_xmit() when sending the packet. In bt_xmit(), the
16039e90c77SJukka Rissanen 		 * destination routing info is not set.
16139e90c77SJukka Rissanen 		 */
16239e90c77SJukka Rissanen 		memcpy(&lowpan_cb(skb)->gw, nexthop, sizeof(struct in6_addr));
16339e90c77SJukka Rissanen 	}
16439e90c77SJukka Rissanen 
16539e90c77SJukka Rissanen 	BT_DBG("gw %pI6c", nexthop);
16639e90c77SJukka Rissanen 
16790305829SJukka Rissanen 	rcu_read_lock();
16890305829SJukka Rissanen 
16990305829SJukka Rissanen 	list_for_each_entry_rcu(peer, &dev->peers, list) {
170658d5d80SKai Ye 		BT_DBG("dst addr %pMR dst type %u ip %pI6c",
17139e90c77SJukka Rissanen 		       &peer->chan->dst, peer->chan->dst_type,
17239e90c77SJukka Rissanen 		       &peer->peer_addr);
17339e90c77SJukka Rissanen 
17490305829SJukka Rissanen 		if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
17590305829SJukka Rissanen 			rcu_read_unlock();
17639e90c77SJukka Rissanen 			return peer;
17739e90c77SJukka Rissanen 		}
17890305829SJukka Rissanen 	}
17990305829SJukka Rissanen 
180149b3f13SMeng Yu 	/* use the neighbour cache for matching addresses assigned by SLAAC */
1815636376cSJosua Mayer 	neigh = __ipv6_neigh_lookup(dev->netdev, nexthop);
1825636376cSJosua Mayer 	if (neigh) {
1835636376cSJosua Mayer 		list_for_each_entry_rcu(peer, &dev->peers, list) {
1845636376cSJosua Mayer 			if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) {
1855636376cSJosua Mayer 				neigh_release(neigh);
1865636376cSJosua Mayer 				rcu_read_unlock();
1875636376cSJosua Mayer 				return peer;
1885636376cSJosua Mayer 			}
1895636376cSJosua Mayer 		}
1905636376cSJosua Mayer 		neigh_release(neigh);
1915636376cSJosua Mayer 	}
1925636376cSJosua Mayer 
19390305829SJukka Rissanen 	rcu_read_unlock();
19439e90c77SJukka Rissanen 
19539e90c77SJukka Rissanen 	return NULL;
19639e90c77SJukka Rissanen }
19739e90c77SJukka Rissanen 
lookup_peer(struct l2cap_conn * conn)19818722c24SJukka Rissanen static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
19918722c24SJukka Rissanen {
2002e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry;
20118722c24SJukka Rissanen 	struct lowpan_peer *peer = NULL;
20218722c24SJukka Rissanen 
20390305829SJukka Rissanen 	rcu_read_lock();
20418722c24SJukka Rissanen 
20590305829SJukka Rissanen 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
20690305829SJukka Rissanen 		peer = __peer_lookup_conn(entry, conn);
20718722c24SJukka Rissanen 		if (peer)
20818722c24SJukka Rissanen 			break;
20918722c24SJukka Rissanen 	}
21018722c24SJukka Rissanen 
21190305829SJukka Rissanen 	rcu_read_unlock();
21218722c24SJukka Rissanen 
21318722c24SJukka Rissanen 	return peer;
21418722c24SJukka Rissanen }
21518722c24SJukka Rissanen 
lookup_dev(struct l2cap_conn * conn)2162e4d60cbSAlexander Aring static struct lowpan_btle_dev *lookup_dev(struct l2cap_conn *conn)
21718722c24SJukka Rissanen {
2182e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry;
2192e4d60cbSAlexander Aring 	struct lowpan_btle_dev *dev = NULL;
22018722c24SJukka Rissanen 
22190305829SJukka Rissanen 	rcu_read_lock();
22218722c24SJukka Rissanen 
22390305829SJukka Rissanen 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
22418722c24SJukka Rissanen 		if (conn->hcon->hdev == entry->hdev) {
22518722c24SJukka Rissanen 			dev = entry;
22618722c24SJukka Rissanen 			break;
22718722c24SJukka Rissanen 		}
22818722c24SJukka Rissanen 	}
22918722c24SJukka Rissanen 
23090305829SJukka Rissanen 	rcu_read_unlock();
23118722c24SJukka Rissanen 
23218722c24SJukka Rissanen 	return dev;
23318722c24SJukka Rissanen }
23418722c24SJukka Rissanen 
give_skb_to_upper(struct sk_buff * skb,struct net_device * dev)23518722c24SJukka Rissanen static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
23618722c24SJukka Rissanen {
23718722c24SJukka Rissanen 	struct sk_buff *skb_cp;
23818722c24SJukka Rissanen 
23918722c24SJukka Rissanen 	skb_cp = skb_copy(skb, GFP_ATOMIC);
24018722c24SJukka Rissanen 	if (!skb_cp)
241f8b36176SMartin Townsend 		return NET_RX_DROP;
24218722c24SJukka Rissanen 
243d33d0dc9SSebastian Andrzej Siewior 	return netif_rx(skb_cp);
24418722c24SJukka Rissanen }
24518722c24SJukka Rissanen 
iphc_decompress(struct sk_buff * skb,struct net_device * netdev,struct lowpan_peer * peer)24601141234SMartin Townsend static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
24727ce68a3SLuiz Augusto von Dentz 			   struct lowpan_peer *peer)
24818722c24SJukka Rissanen {
249c259d141SPatrik Flykt 	const u8 *saddr;
25018722c24SJukka Rissanen 
251fa09ae66SLuiz Augusto von Dentz 	saddr = peer->lladdr;
25218722c24SJukka Rissanen 
253fa09ae66SLuiz Augusto von Dentz 	return lowpan_header_decompress(skb, netdev, netdev->dev_addr, saddr);
25418722c24SJukka Rissanen }
25518722c24SJukka Rissanen 
recv_pkt(struct sk_buff * skb,struct net_device * dev,struct lowpan_peer * peer)25618722c24SJukka Rissanen static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
25727ce68a3SLuiz Augusto von Dentz 		    struct lowpan_peer *peer)
25818722c24SJukka Rissanen {
25918722c24SJukka Rissanen 	struct sk_buff *local_skb;
26018722c24SJukka Rissanen 	int ret;
26118722c24SJukka Rissanen 
26218722c24SJukka Rissanen 	if (!netif_running(dev))
26318722c24SJukka Rissanen 		goto drop;
26418722c24SJukka Rissanen 
265cefdb801SAlexander Aring 	if (dev->type != ARPHRD_6LOWPAN || !skb->len)
26618722c24SJukka Rissanen 		goto drop;
26718722c24SJukka Rissanen 
268cefdb801SAlexander Aring 	skb_reset_network_header(skb);
269cefdb801SAlexander Aring 
27011e3ff70SMartin Townsend 	skb = skb_share_check(skb, GFP_ATOMIC);
27111e3ff70SMartin Townsend 	if (!skb)
27211e3ff70SMartin Townsend 		goto drop;
27311e3ff70SMartin Townsend 
27418722c24SJukka Rissanen 	/* check that it's our buffer */
275cefdb801SAlexander Aring 	if (lowpan_is_ipv6(*skb_network_header(skb))) {
27687f5fedbSLukasz Duda 		/* Pull off the 1-byte of 6lowpan header. */
27787f5fedbSLukasz Duda 		skb_pull(skb, 1);
27887f5fedbSLukasz Duda 
27918722c24SJukka Rissanen 		/* Copy the packet so that the IPv6 header is
28018722c24SJukka Rissanen 		 * properly aligned.
28118722c24SJukka Rissanen 		 */
28218722c24SJukka Rissanen 		local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
28318722c24SJukka Rissanen 					    skb_tailroom(skb), GFP_ATOMIC);
28418722c24SJukka Rissanen 		if (!local_skb)
28518722c24SJukka Rissanen 			goto drop;
28618722c24SJukka Rissanen 
28718722c24SJukka Rissanen 		local_skb->protocol = htons(ETH_P_IPV6);
28818722c24SJukka Rissanen 		local_skb->pkt_type = PACKET_HOST;
2894c58f328SGlenn Ruben Bakke 		local_skb->dev = dev;
29018722c24SJukka Rissanen 
29118722c24SJukka Rissanen 		skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
29218722c24SJukka Rissanen 
29318722c24SJukka Rissanen 		if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
29418722c24SJukka Rissanen 			kfree_skb(local_skb);
29518722c24SJukka Rissanen 			goto drop;
29618722c24SJukka Rissanen 		}
29718722c24SJukka Rissanen 
29818722c24SJukka Rissanen 		dev->stats.rx_bytes += skb->len;
29918722c24SJukka Rissanen 		dev->stats.rx_packets++;
30018722c24SJukka Rissanen 
3013c400b84SMartin Townsend 		consume_skb(local_skb);
3023c400b84SMartin Townsend 		consume_skb(skb);
303cefdb801SAlexander Aring 	} else if (lowpan_is_iphc(*skb_network_header(skb))) {
30418722c24SJukka Rissanen 		local_skb = skb_clone(skb, GFP_ATOMIC);
30518722c24SJukka Rissanen 		if (!local_skb)
30618722c24SJukka Rissanen 			goto drop;
30718722c24SJukka Rissanen 
3084c58f328SGlenn Ruben Bakke 		local_skb->dev = dev;
3094c58f328SGlenn Ruben Bakke 
31027ce68a3SLuiz Augusto von Dentz 		ret = iphc_decompress(local_skb, dev, peer);
31156b2c3eeSMartin Townsend 		if (ret < 0) {
312da75fdc6SLuiz Augusto von Dentz 			BT_DBG("iphc_decompress failed: %d", ret);
31356b2c3eeSMartin Townsend 			kfree_skb(local_skb);
31418722c24SJukka Rissanen 			goto drop;
31556b2c3eeSMartin Townsend 		}
31618722c24SJukka Rissanen 
317f8b36176SMartin Townsend 		local_skb->protocol = htons(ETH_P_IPV6);
318f8b36176SMartin Townsend 		local_skb->pkt_type = PACKET_HOST;
319f8b36176SMartin Townsend 
320f8b36176SMartin Townsend 		if (give_skb_to_upper(local_skb, dev)
321f8b36176SMartin Townsend 				!= NET_RX_SUCCESS) {
322f8b36176SMartin Townsend 			kfree_skb(local_skb);
323f8b36176SMartin Townsend 			goto drop;
324f8b36176SMartin Townsend 		}
325f8b36176SMartin Townsend 
32618722c24SJukka Rissanen 		dev->stats.rx_bytes += skb->len;
32718722c24SJukka Rissanen 		dev->stats.rx_packets++;
32818722c24SJukka Rissanen 
3293c400b84SMartin Townsend 		consume_skb(local_skb);
3303c400b84SMartin Townsend 		consume_skb(skb);
331cefdb801SAlexander Aring 	} else {
332da75fdc6SLuiz Augusto von Dentz 		BT_DBG("unknown packet type");
333cefdb801SAlexander Aring 		goto drop;
33418722c24SJukka Rissanen 	}
33518722c24SJukka Rissanen 
33618722c24SJukka Rissanen 	return NET_RX_SUCCESS;
33718722c24SJukka Rissanen 
33818722c24SJukka Rissanen drop:
3396b8d4a6aSJukka Rissanen 	dev->stats.rx_dropped++;
34018722c24SJukka Rissanen 	return NET_RX_DROP;
34118722c24SJukka Rissanen }
34218722c24SJukka Rissanen 
34318722c24SJukka Rissanen /* Packet from BT LE device */
chan_recv_cb(struct l2cap_chan * chan,struct sk_buff * skb)3446b8d4a6aSJukka Rissanen static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
34518722c24SJukka Rissanen {
3462e4d60cbSAlexander Aring 	struct lowpan_btle_dev *dev;
34718722c24SJukka Rissanen 	struct lowpan_peer *peer;
34818722c24SJukka Rissanen 	int err;
34918722c24SJukka Rissanen 
3506b8d4a6aSJukka Rissanen 	peer = lookup_peer(chan->conn);
35118722c24SJukka Rissanen 	if (!peer)
35218722c24SJukka Rissanen 		return -ENOENT;
35318722c24SJukka Rissanen 
3546b8d4a6aSJukka Rissanen 	dev = lookup_dev(chan->conn);
35530d3db44SJohan Hedberg 	if (!dev || !dev->netdev)
35618722c24SJukka Rissanen 		return -ENOENT;
35718722c24SJukka Rissanen 
35827ce68a3SLuiz Augusto von Dentz 	err = recv_pkt(skb, dev->netdev, peer);
3596b8d4a6aSJukka Rissanen 	if (err) {
36018722c24SJukka Rissanen 		BT_DBG("recv pkt %d", err);
3616b8d4a6aSJukka Rissanen 		err = -EAGAIN;
3626b8d4a6aSJukka Rissanen 	}
36318722c24SJukka Rissanen 
36418722c24SJukka Rissanen 	return err;
36518722c24SJukka Rissanen }
36618722c24SJukka Rissanen 
setup_header(struct sk_buff * skb,struct net_device * netdev,bdaddr_t * peer_addr,u8 * peer_addr_type)36736b3dd25SJukka Rissanen static int setup_header(struct sk_buff *skb, struct net_device *netdev,
36836b3dd25SJukka Rissanen 			bdaddr_t *peer_addr, u8 *peer_addr_type)
36918722c24SJukka Rissanen {
37036b3dd25SJukka Rissanen 	struct in6_addr ipv6_daddr;
37155441070SGlenn Ruben Bakke 	struct ipv6hdr *hdr;
3722e4d60cbSAlexander Aring 	struct lowpan_btle_dev *dev;
37318722c24SJukka Rissanen 	struct lowpan_peer *peer;
3749dae2e03SLuiz Augusto von Dentz 	u8 *daddr;
37536b3dd25SJukka Rissanen 	int err, status = 0;
37618722c24SJukka Rissanen 
37755441070SGlenn Ruben Bakke 	hdr = ipv6_hdr(skb);
37855441070SGlenn Ruben Bakke 
3792e4d60cbSAlexander Aring 	dev = lowpan_btle_dev(netdev);
38018722c24SJukka Rissanen 
38155441070SGlenn Ruben Bakke 	memcpy(&ipv6_daddr, &hdr->daddr, sizeof(ipv6_daddr));
38236b3dd25SJukka Rissanen 
38336b3dd25SJukka Rissanen 	if (ipv6_addr_is_multicast(&ipv6_daddr)) {
3846b8d4a6aSJukka Rissanen 		lowpan_cb(skb)->chan = NULL;
3859dae2e03SLuiz Augusto von Dentz 		daddr = NULL;
38618722c24SJukka Rissanen 	} else {
3879dae2e03SLuiz Augusto von Dentz 		BT_DBG("dest IP %pI6c", &ipv6_daddr);
38818722c24SJukka Rissanen 
38939e90c77SJukka Rissanen 		/* The packet might be sent to 6lowpan interface
39039e90c77SJukka Rissanen 		 * because of routing (either via default route
39139e90c77SJukka Rissanen 		 * or user set route) so get peer according to
39239e90c77SJukka Rissanen 		 * the destination address.
39339e90c77SJukka Rissanen 		 */
39436b3dd25SJukka Rissanen 		peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
39539e90c77SJukka Rissanen 		if (!peer) {
3969dae2e03SLuiz Augusto von Dentz 			BT_DBG("no such peer");
39718722c24SJukka Rissanen 			return -ENOENT;
39818722c24SJukka Rissanen 		}
39918722c24SJukka Rissanen 
400fa09ae66SLuiz Augusto von Dentz 		daddr = peer->lladdr;
401fa0eaf84SColin Ian King 		*peer_addr = peer->chan->dst;
4029dae2e03SLuiz Augusto von Dentz 		*peer_addr_type = peer->chan->dst_type;
4036b8d4a6aSJukka Rissanen 		lowpan_cb(skb)->chan = peer->chan;
40436b3dd25SJukka Rissanen 
40536b3dd25SJukka Rissanen 		status = 1;
40618722c24SJukka Rissanen 	}
40718722c24SJukka Rissanen 
408a6f77389SAlexander Aring 	lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr);
40918722c24SJukka Rissanen 
41036b3dd25SJukka Rissanen 	err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0);
41136b3dd25SJukka Rissanen 	if (err < 0)
41236b3dd25SJukka Rissanen 		return err;
41336b3dd25SJukka Rissanen 
41436b3dd25SJukka Rissanen 	return status;
41536b3dd25SJukka Rissanen }
41636b3dd25SJukka Rissanen 
header_create(struct sk_buff * skb,struct net_device * netdev,unsigned short type,const void * _daddr,const void * _saddr,unsigned int len)41736b3dd25SJukka Rissanen static int header_create(struct sk_buff *skb, struct net_device *netdev,
41836b3dd25SJukka Rissanen 			 unsigned short type, const void *_daddr,
41936b3dd25SJukka Rissanen 			 const void *_saddr, unsigned int len)
42036b3dd25SJukka Rissanen {
42136b3dd25SJukka Rissanen 	if (type != ETH_P_IPV6)
42236b3dd25SJukka Rissanen 		return -EINVAL;
42336b3dd25SJukka Rissanen 
42436b3dd25SJukka Rissanen 	return 0;
42518722c24SJukka Rissanen }
42618722c24SJukka Rissanen 
42718722c24SJukka Rissanen /* Packet to BT LE device */
send_pkt(struct l2cap_chan * chan,struct sk_buff * skb,struct net_device * netdev)4286b8d4a6aSJukka Rissanen static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
429d7b6b0a5SJukka Rissanen 		    struct net_device *netdev)
43018722c24SJukka Rissanen {
4316b8d4a6aSJukka Rissanen 	struct msghdr msg;
4326b8d4a6aSJukka Rissanen 	struct kvec iv;
4336b8d4a6aSJukka Rissanen 	int err;
43418722c24SJukka Rissanen 
4356b8d4a6aSJukka Rissanen 	/* Remember the skb so that we can send EAGAIN to the caller if
436d7b6b0a5SJukka Rissanen 	 * we run out of credits.
4376b8d4a6aSJukka Rissanen 	 */
4386b8d4a6aSJukka Rissanen 	chan->data = skb;
4396b8d4a6aSJukka Rissanen 
4406b8d4a6aSJukka Rissanen 	iv.iov_base = skb->data;
4416b8d4a6aSJukka Rissanen 	iv.iov_len = skb->len;
4426b8d4a6aSJukka Rissanen 
443c0371da6SAl Viro 	memset(&msg, 0, sizeof(msg));
444de4eda9dSAl Viro 	iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &iv, 1, skb->len);
445c0371da6SAl Viro 
4466b8d4a6aSJukka Rissanen 	err = l2cap_chan_send(chan, &msg, skb->len);
4476b8d4a6aSJukka Rissanen 	if (err > 0) {
4486b8d4a6aSJukka Rissanen 		netdev->stats.tx_bytes += err;
4496b8d4a6aSJukka Rissanen 		netdev->stats.tx_packets++;
4506b8d4a6aSJukka Rissanen 		return 0;
4516b8d4a6aSJukka Rissanen 	}
4526b8d4a6aSJukka Rissanen 
453e1008f95SLuiz Augusto von Dentz 	if (err < 0)
4546b8d4a6aSJukka Rissanen 		netdev->stats.tx_errors++;
4556b8d4a6aSJukka Rissanen 
4566b8d4a6aSJukka Rissanen 	return err;
45718722c24SJukka Rissanen }
45818722c24SJukka Rissanen 
send_mcast_pkt(struct sk_buff * skb,struct net_device * netdev)4599c238ca8SJukka Rissanen static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
46018722c24SJukka Rissanen {
46118722c24SJukka Rissanen 	struct sk_buff *local_skb;
4622e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry;
4639c238ca8SJukka Rissanen 	int err = 0;
46418722c24SJukka Rissanen 
46590305829SJukka Rissanen 	rcu_read_lock();
46618722c24SJukka Rissanen 
46790305829SJukka Rissanen 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
46890305829SJukka Rissanen 		struct lowpan_peer *pentry;
4692e4d60cbSAlexander Aring 		struct lowpan_btle_dev *dev;
47018722c24SJukka Rissanen 
47118722c24SJukka Rissanen 		if (entry->netdev != netdev)
47218722c24SJukka Rissanen 			continue;
47318722c24SJukka Rissanen 
4742e4d60cbSAlexander Aring 		dev = lowpan_btle_dev(entry->netdev);
47518722c24SJukka Rissanen 
47690305829SJukka Rissanen 		list_for_each_entry_rcu(pentry, &dev->peers, list) {
4779c238ca8SJukka Rissanen 			int ret;
4789c238ca8SJukka Rissanen 
47918722c24SJukka Rissanen 			local_skb = skb_clone(skb, GFP_ATOMIC);
48018722c24SJukka Rissanen 
481658d5d80SKai Ye 			BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
48236b3dd25SJukka Rissanen 			       netdev->name,
48336b3dd25SJukka Rissanen 			       &pentry->chan->dst, pentry->chan->dst_type,
48436b3dd25SJukka Rissanen 			       &pentry->peer_addr, pentry->chan);
4859c238ca8SJukka Rissanen 			ret = send_pkt(pentry->chan, local_skb, netdev);
4869c238ca8SJukka Rissanen 			if (ret < 0)
4879c238ca8SJukka Rissanen 				err = ret;
48818722c24SJukka Rissanen 
48918722c24SJukka Rissanen 			kfree_skb(local_skb);
49018722c24SJukka Rissanen 		}
49118722c24SJukka Rissanen 	}
49218722c24SJukka Rissanen 
49390305829SJukka Rissanen 	rcu_read_unlock();
4949c238ca8SJukka Rissanen 
4959c238ca8SJukka Rissanen 	return err;
49618722c24SJukka Rissanen }
49718722c24SJukka Rissanen 
bt_xmit(struct sk_buff * skb,struct net_device * netdev)49818722c24SJukka Rissanen static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
49918722c24SJukka Rissanen {
50018722c24SJukka Rissanen 	int err = 0;
50118722c24SJukka Rissanen 	bdaddr_t addr;
50218722c24SJukka Rissanen 	u8 addr_type;
50318722c24SJukka Rissanen 
50436b3dd25SJukka Rissanen 	/* We must take a copy of the skb before we modify/replace the ipv6
50536b3dd25SJukka Rissanen 	 * header as the header could be used elsewhere
50618722c24SJukka Rissanen 	 */
507b0c42cd7SAlexander Aring 	skb = skb_unshare(skb, GFP_ATOMIC);
508b0c42cd7SAlexander Aring 	if (!skb)
50936b3dd25SJukka Rissanen 		return NET_XMIT_DROP;
51018722c24SJukka Rissanen 
51136b3dd25SJukka Rissanen 	/* Return values from setup_header()
51236b3dd25SJukka Rissanen 	 *  <0 - error, packet is dropped
51336b3dd25SJukka Rissanen 	 *   0 - this is a multicast packet
51436b3dd25SJukka Rissanen 	 *   1 - this is unicast packet
51536b3dd25SJukka Rissanen 	 */
51636b3dd25SJukka Rissanen 	err = setup_header(skb, netdev, &addr, &addr_type);
51736b3dd25SJukka Rissanen 	if (err < 0) {
51836b3dd25SJukka Rissanen 		kfree_skb(skb);
51936b3dd25SJukka Rissanen 		return NET_XMIT_DROP;
52036b3dd25SJukka Rissanen 	}
52118722c24SJukka Rissanen 
52236b3dd25SJukka Rissanen 	if (err) {
52336b3dd25SJukka Rissanen 		if (lowpan_cb(skb)->chan) {
524658d5d80SKai Ye 			BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
5256b8d4a6aSJukka Rissanen 			       netdev->name, &addr, addr_type,
52636b3dd25SJukka Rissanen 			       &lowpan_cb(skb)->addr, lowpan_cb(skb)->chan);
527d7b6b0a5SJukka Rissanen 			err = send_pkt(lowpan_cb(skb)->chan, skb, netdev);
52836b3dd25SJukka Rissanen 		} else {
5296b8d4a6aSJukka Rissanen 			err = -ENOENT;
53018722c24SJukka Rissanen 		}
53136b3dd25SJukka Rissanen 	} else {
53236b3dd25SJukka Rissanen 		/* We need to send the packet to every device behind this
53336b3dd25SJukka Rissanen 		 * interface.
53436b3dd25SJukka Rissanen 		 */
5359c238ca8SJukka Rissanen 		err = send_mcast_pkt(skb, netdev);
53636b3dd25SJukka Rissanen 	}
53718722c24SJukka Rissanen 
538fc12518aSJukka Rissanen 	dev_kfree_skb(skb);
539fc12518aSJukka Rissanen 
54018722c24SJukka Rissanen 	if (err)
54118722c24SJukka Rissanen 		BT_DBG("ERROR: xmit failed (%d)", err);
54218722c24SJukka Rissanen 
54336b3dd25SJukka Rissanen 	return err < 0 ? NET_XMIT_DROP : err;
54418722c24SJukka Rissanen }
54518722c24SJukka Rissanen 
bt_dev_init(struct net_device * dev)5461a33e10eSCong Wang static int bt_dev_init(struct net_device *dev)
5471a33e10eSCong Wang {
5481a33e10eSCong Wang 	netdev_lockdep_set_classes(dev);
5491a33e10eSCong Wang 
5501a33e10eSCong Wang 	return 0;
5511a33e10eSCong Wang }
5521a33e10eSCong Wang 
55318722c24SJukka Rissanen static const struct net_device_ops netdev_ops = {
5541a33e10eSCong Wang 	.ndo_init		= bt_dev_init,
55518722c24SJukka Rissanen 	.ndo_start_xmit		= bt_xmit,
55618722c24SJukka Rissanen };
55718722c24SJukka Rissanen 
558569428daSNishka Dasgupta static const struct header_ops header_ops = {
55918722c24SJukka Rissanen 	.create	= header_create,
56018722c24SJukka Rissanen };
56118722c24SJukka Rissanen 
netdev_setup(struct net_device * dev)56218722c24SJukka Rissanen static void netdev_setup(struct net_device *dev)
56318722c24SJukka Rissanen {
56418722c24SJukka Rissanen 	dev->hard_header_len	= 0;
56518722c24SJukka Rissanen 	dev->needed_tailroom	= 0;
56625869522SPatrik Flykt 	dev->flags		= IFF_RUNNING | IFF_MULTICAST;
56718722c24SJukka Rissanen 	dev->watchdog_timeo	= 0;
568814f1b24SLuiz Augusto von Dentz 	dev->tx_queue_len	= DEFAULT_TX_QUEUE_LEN;
56918722c24SJukka Rissanen 
57018722c24SJukka Rissanen 	dev->netdev_ops		= &netdev_ops;
57118722c24SJukka Rissanen 	dev->header_ops		= &header_ops;
572cf124db5SDavid S. Miller 	dev->needs_free_netdev	= true;
57318722c24SJukka Rissanen }
57418722c24SJukka Rissanen 
57518722c24SJukka Rissanen static struct device_type bt_type = {
57618722c24SJukka Rissanen 	.name	= "bluetooth",
57718722c24SJukka Rissanen };
57818722c24SJukka Rissanen 
ifup(struct net_device * netdev)57918722c24SJukka Rissanen static void ifup(struct net_device *netdev)
58018722c24SJukka Rissanen {
58118722c24SJukka Rissanen 	int err;
58218722c24SJukka Rissanen 
58318722c24SJukka Rissanen 	rtnl_lock();
58400f54e68SPetr Machata 	err = dev_open(netdev, NULL);
58518722c24SJukka Rissanen 	if (err < 0)
58618722c24SJukka Rissanen 		BT_INFO("iface %s cannot be opened (%d)", netdev->name, err);
58718722c24SJukka Rissanen 	rtnl_unlock();
58818722c24SJukka Rissanen }
58918722c24SJukka Rissanen 
ifdown(struct net_device * netdev)5907f118253SJukka Rissanen static void ifdown(struct net_device *netdev)
5917f118253SJukka Rissanen {
5927f118253SJukka Rissanen 	rtnl_lock();
593ddee3103Sstephen hemminger 	dev_close(netdev);
5947f118253SJukka Rissanen 	rtnl_unlock();
5957f118253SJukka Rissanen }
5967f118253SJukka Rissanen 
do_notify_peers(struct work_struct * work)59718722c24SJukka Rissanen static void do_notify_peers(struct work_struct *work)
59818722c24SJukka Rissanen {
5992e4d60cbSAlexander Aring 	struct lowpan_btle_dev *dev = container_of(work, struct lowpan_btle_dev,
60018722c24SJukka Rissanen 						   notify_peers.work);
60118722c24SJukka Rissanen 
60218722c24SJukka Rissanen 	netdev_notify_peers(dev->netdev); /* send neighbour adv at startup */
60318722c24SJukka Rissanen }
60418722c24SJukka Rissanen 
is_bt_6lowpan(struct hci_conn * hcon)60518722c24SJukka Rissanen static bool is_bt_6lowpan(struct hci_conn *hcon)
60618722c24SJukka Rissanen {
60718722c24SJukka Rissanen 	if (hcon->type != LE_LINK)
60818722c24SJukka Rissanen 		return false;
60918722c24SJukka Rissanen 
6107b2ed60eSJukka Rissanen 	if (!enable_6lowpan)
6116b8d4a6aSJukka Rissanen 		return false;
6126b8d4a6aSJukka Rissanen 
6136b8d4a6aSJukka Rissanen 	return true;
61418722c24SJukka Rissanen }
61518722c24SJukka Rissanen 
chan_create(void)6166b8d4a6aSJukka Rissanen static struct l2cap_chan *chan_create(void)
6176b8d4a6aSJukka Rissanen {
6186b8d4a6aSJukka Rissanen 	struct l2cap_chan *chan;
6196b8d4a6aSJukka Rissanen 
6206b8d4a6aSJukka Rissanen 	chan = l2cap_chan_create();
6216b8d4a6aSJukka Rissanen 	if (!chan)
6226b8d4a6aSJukka Rissanen 		return NULL;
6236b8d4a6aSJukka Rissanen 
6246b8d4a6aSJukka Rissanen 	l2cap_chan_set_defaults(chan);
6256b8d4a6aSJukka Rissanen 
6266b8d4a6aSJukka Rissanen 	chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
6276b8d4a6aSJukka Rissanen 	chan->mode = L2CAP_MODE_LE_FLOWCTL;
628301de2cbSJohan Hedberg 	chan->imtu = 1280;
6296b8d4a6aSJukka Rissanen 
6306b8d4a6aSJukka Rissanen 	return chan;
6316b8d4a6aSJukka Rissanen }
6326b8d4a6aSJukka Rissanen 
add_peer_chan(struct l2cap_chan * chan,struct lowpan_btle_dev * dev,bool new_netdev)6336b8d4a6aSJukka Rissanen static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
634d2891c4dSMichael Scott 					struct lowpan_btle_dev *dev,
635d2891c4dSMichael Scott 					bool new_netdev)
63618722c24SJukka Rissanen {
63718722c24SJukka Rissanen 	struct lowpan_peer *peer;
63818722c24SJukka Rissanen 
63918722c24SJukka Rissanen 	peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
64018722c24SJukka Rissanen 	if (!peer)
6416b8d4a6aSJukka Rissanen 		return NULL;
64218722c24SJukka Rissanen 
6436b8d4a6aSJukka Rissanen 	peer->chan = chan;
64418722c24SJukka Rissanen 
645fa09ae66SLuiz Augusto von Dentz 	baswap((void *)peer->lladdr, &chan->dst);
64618722c24SJukka Rissanen 
647fa09ae66SLuiz Augusto von Dentz 	lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr);
64818722c24SJukka Rissanen 
64990305829SJukka Rissanen 	spin_lock(&devices_lock);
65018722c24SJukka Rissanen 	INIT_LIST_HEAD(&peer->list);
65118722c24SJukka Rissanen 	peer_add(dev, peer);
65290305829SJukka Rissanen 	spin_unlock(&devices_lock);
65318722c24SJukka Rissanen 
65418722c24SJukka Rissanen 	/* Notifying peers about us needs to be done without locks held */
655d2891c4dSMichael Scott 	if (new_netdev)
65618722c24SJukka Rissanen 		INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
65718722c24SJukka Rissanen 	schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
65818722c24SJukka Rissanen 
6596b8d4a6aSJukka Rissanen 	return peer->chan;
66018722c24SJukka Rissanen }
66118722c24SJukka Rissanen 
setup_netdev(struct l2cap_chan * chan,struct lowpan_btle_dev ** dev)6622e4d60cbSAlexander Aring static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
66318722c24SJukka Rissanen {
66418722c24SJukka Rissanen 	struct net_device *netdev;
665a1916d34SJakub Kicinski 	bdaddr_t addr;
666c469c9c9SKai Ye 	int err;
66718722c24SJukka Rissanen 
6682e4d60cbSAlexander Aring 	netdev = alloc_netdev(LOWPAN_PRIV_SIZE(sizeof(struct lowpan_btle_dev)),
669b72f6f51SAlexander Aring 			      IFACE_NAME_TEMPLATE, NET_NAME_UNKNOWN,
670b72f6f51SAlexander Aring 			      netdev_setup);
67118722c24SJukka Rissanen 	if (!netdev)
67218722c24SJukka Rissanen 		return -ENOMEM;
67318722c24SJukka Rissanen 
674c259d141SPatrik Flykt 	netdev->addr_assign_type = NET_ADDR_PERM;
675a1916d34SJakub Kicinski 	baswap(&addr, &chan->src);
676a1916d34SJakub Kicinski 	__dev_addr_set(netdev, &addr, sizeof(addr));
67718722c24SJukka Rissanen 
67818722c24SJukka Rissanen 	netdev->netdev_ops = &netdev_ops;
679fc84242fSGlenn Ruben Bakke 	SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev);
68018722c24SJukka Rissanen 	SET_NETDEV_DEVTYPE(netdev, &bt_type);
68118722c24SJukka Rissanen 
6822e4d60cbSAlexander Aring 	*dev = lowpan_btle_dev(netdev);
6836b8d4a6aSJukka Rissanen 	(*dev)->netdev = netdev;
6846b8d4a6aSJukka Rissanen 	(*dev)->hdev = chan->conn->hcon->hdev;
6856b8d4a6aSJukka Rissanen 	INIT_LIST_HEAD(&(*dev)->peers);
68618722c24SJukka Rissanen 
68790305829SJukka Rissanen 	spin_lock(&devices_lock);
6886b8d4a6aSJukka Rissanen 	INIT_LIST_HEAD(&(*dev)->list);
68990305829SJukka Rissanen 	list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
69090305829SJukka Rissanen 	spin_unlock(&devices_lock);
69118722c24SJukka Rissanen 
69200f59314SAlexander Aring 	err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
6935857d1dbSAlexander Aring 	if (err < 0) {
6945857d1dbSAlexander Aring 		BT_INFO("register_netdev failed %d", err);
6955857d1dbSAlexander Aring 		spin_lock(&devices_lock);
6965857d1dbSAlexander Aring 		list_del_rcu(&(*dev)->list);
6975857d1dbSAlexander Aring 		spin_unlock(&devices_lock);
6985857d1dbSAlexander Aring 		free_netdev(netdev);
6995857d1dbSAlexander Aring 		goto out;
7005857d1dbSAlexander Aring 	}
7015857d1dbSAlexander Aring 
7025857d1dbSAlexander Aring 	BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
7035857d1dbSAlexander Aring 	       netdev->ifindex, &chan->dst, chan->dst_type,
7045857d1dbSAlexander Aring 	       &chan->src, chan->src_type);
7055857d1dbSAlexander Aring 	set_bit(__LINK_STATE_PRESENT, &netdev->state);
7065857d1dbSAlexander Aring 
7076b8d4a6aSJukka Rissanen 	return 0;
70818722c24SJukka Rissanen 
70918722c24SJukka Rissanen out:
71018722c24SJukka Rissanen 	return err;
71118722c24SJukka Rissanen }
71218722c24SJukka Rissanen 
chan_ready_cb(struct l2cap_chan * chan)7136b8d4a6aSJukka Rissanen static inline void chan_ready_cb(struct l2cap_chan *chan)
7146b8d4a6aSJukka Rissanen {
7152e4d60cbSAlexander Aring 	struct lowpan_btle_dev *dev;
716d2891c4dSMichael Scott 	bool new_netdev = false;
7176b8d4a6aSJukka Rissanen 
7186b8d4a6aSJukka Rissanen 	dev = lookup_dev(chan->conn);
7196b8d4a6aSJukka Rissanen 
7206b8d4a6aSJukka Rissanen 	BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev);
7216b8d4a6aSJukka Rissanen 
7226b8d4a6aSJukka Rissanen 	if (!dev) {
7236b8d4a6aSJukka Rissanen 		if (setup_netdev(chan, &dev) < 0) {
7246b8d4a6aSJukka Rissanen 			l2cap_chan_del(chan, -ENOENT);
7256b8d4a6aSJukka Rissanen 			return;
7266b8d4a6aSJukka Rissanen 		}
727d2891c4dSMichael Scott 		new_netdev = true;
7286b8d4a6aSJukka Rissanen 	}
7296b8d4a6aSJukka Rissanen 
73018d93c17SJukka Rissanen 	if (!try_module_get(THIS_MODULE))
73118d93c17SJukka Rissanen 		return;
73218d93c17SJukka Rissanen 
733d2891c4dSMichael Scott 	add_peer_chan(chan, dev, new_netdev);
7346b8d4a6aSJukka Rissanen 	ifup(dev->netdev);
7356b8d4a6aSJukka Rissanen }
7366b8d4a6aSJukka Rissanen 
chan_new_conn_cb(struct l2cap_chan * pchan)7372b293490SJohan Hedberg static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
7386b8d4a6aSJukka Rissanen {
7392b293490SJohan Hedberg 	struct l2cap_chan *chan;
7406b8d4a6aSJukka Rissanen 
741630ef791SJohan Hedberg 	chan = chan_create();
742630ef791SJohan Hedberg 	if (!chan)
743630ef791SJohan Hedberg 		return NULL;
744630ef791SJohan Hedberg 
7452b293490SJohan Hedberg 	chan->ops = pchan->ops;
7466b8d4a6aSJukka Rissanen 
7476b8d4a6aSJukka Rissanen 	BT_DBG("chan %p pchan %p", chan, pchan);
7486b8d4a6aSJukka Rissanen 
7492b293490SJohan Hedberg 	return chan;
7506b8d4a6aSJukka Rissanen }
7516b8d4a6aSJukka Rissanen 
delete_netdev(struct work_struct * work)75218722c24SJukka Rissanen static void delete_netdev(struct work_struct *work)
75318722c24SJukka Rissanen {
7542e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry = container_of(work,
7552e4d60cbSAlexander Aring 						     struct lowpan_btle_dev,
75618722c24SJukka Rissanen 						     delete_netdev);
75718722c24SJukka Rissanen 
75800f59314SAlexander Aring 	lowpan_unregister_netdev(entry->netdev);
75918722c24SJukka Rissanen 
7602ad88fb2SGlenn Ruben Bakke 	/* The entry pointer is deleted by the netdev destructor. */
76118722c24SJukka Rissanen }
76218722c24SJukka Rissanen 
chan_close_cb(struct l2cap_chan * chan)7636b8d4a6aSJukka Rissanen static void chan_close_cb(struct l2cap_chan *chan)
76418722c24SJukka Rissanen {
7652e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry;
7662e4d60cbSAlexander Aring 	struct lowpan_btle_dev *dev = NULL;
76718722c24SJukka Rissanen 	struct lowpan_peer *peer;
76818722c24SJukka Rissanen 	int err = -ENOENT;
769f63666d2SGlenn Ruben Bakke 	bool last = false, remove = true;
77018722c24SJukka Rissanen 
7716b8d4a6aSJukka Rissanen 	BT_DBG("chan %p conn %p", chan, chan->conn);
7726b8d4a6aSJukka Rissanen 
7736b8d4a6aSJukka Rissanen 	if (chan->conn && chan->conn->hcon) {
7746b8d4a6aSJukka Rissanen 		if (!is_bt_6lowpan(chan->conn->hcon))
7756b8d4a6aSJukka Rissanen 			return;
7766b8d4a6aSJukka Rissanen 
7776b8d4a6aSJukka Rissanen 		/* If conn is set, then the netdev is also there and we should
7786b8d4a6aSJukka Rissanen 		 * not remove it.
7796b8d4a6aSJukka Rissanen 		 */
780f63666d2SGlenn Ruben Bakke 		remove = false;
7816b8d4a6aSJukka Rissanen 	}
78218722c24SJukka Rissanen 
78390305829SJukka Rissanen 	spin_lock(&devices_lock);
78418722c24SJukka Rissanen 
78590305829SJukka Rissanen 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
7862e4d60cbSAlexander Aring 		dev = lowpan_btle_dev(entry->netdev);
78790305829SJukka Rissanen 		peer = __peer_lookup_chan(dev, chan);
78818722c24SJukka Rissanen 		if (peer) {
78918722c24SJukka Rissanen 			last = peer_del(dev, peer);
79018722c24SJukka Rissanen 			err = 0;
7916b8d4a6aSJukka Rissanen 
7926b8d4a6aSJukka Rissanen 			BT_DBG("dev %p removing %speer %p", dev,
7936b8d4a6aSJukka Rissanen 			       last ? "last " : "1 ", peer);
794658d5d80SKai Ye 			BT_DBG("chan %p orig refcnt %u", chan,
7952c935bc5SPeter Zijlstra 			       kref_read(&chan->kref));
7966b8d4a6aSJukka Rissanen 
7976b8d4a6aSJukka Rissanen 			l2cap_chan_put(chan);
79818722c24SJukka Rissanen 			break;
79918722c24SJukka Rissanen 		}
80018722c24SJukka Rissanen 	}
80118722c24SJukka Rissanen 
80218722c24SJukka Rissanen 	if (!err && last && dev && !atomic_read(&dev->peer_count)) {
80390305829SJukka Rissanen 		spin_unlock(&devices_lock);
80418722c24SJukka Rissanen 
80518722c24SJukka Rissanen 		cancel_delayed_work_sync(&dev->notify_peers);
80618722c24SJukka Rissanen 
8077f118253SJukka Rissanen 		ifdown(dev->netdev);
8087f118253SJukka Rissanen 
809f63666d2SGlenn Ruben Bakke 		if (remove) {
81018722c24SJukka Rissanen 			INIT_WORK(&entry->delete_netdev, delete_netdev);
81118722c24SJukka Rissanen 			schedule_work(&entry->delete_netdev);
8126b8d4a6aSJukka Rissanen 		}
81318722c24SJukka Rissanen 	} else {
81490305829SJukka Rissanen 		spin_unlock(&devices_lock);
81518722c24SJukka Rissanen 	}
8166b8d4a6aSJukka Rissanen }
8176b8d4a6aSJukka Rissanen 
chan_state_change_cb(struct l2cap_chan * chan,int state,int err)8186b8d4a6aSJukka Rissanen static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err)
8196b8d4a6aSJukka Rissanen {
8206b8d4a6aSJukka Rissanen 	BT_DBG("chan %p conn %p state %s err %d", chan, chan->conn,
8216b8d4a6aSJukka Rissanen 	       state_to_string(state), err);
8226b8d4a6aSJukka Rissanen }
8236b8d4a6aSJukka Rissanen 
chan_alloc_skb_cb(struct l2cap_chan * chan,unsigned long hdr_len,unsigned long len,int nb)8246b8d4a6aSJukka Rissanen static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan,
8256b8d4a6aSJukka Rissanen 					 unsigned long hdr_len,
8266b8d4a6aSJukka Rissanen 					 unsigned long len, int nb)
8276b8d4a6aSJukka Rissanen {
8286b8d4a6aSJukka Rissanen 	/* Note that we must allocate using GFP_ATOMIC here as
8296b8d4a6aSJukka Rissanen 	 * this function is called originally from netdev hard xmit
8306b8d4a6aSJukka Rissanen 	 * function in atomic context.
8316b8d4a6aSJukka Rissanen 	 */
8326b8d4a6aSJukka Rissanen 	return bt_skb_alloc(hdr_len + len, GFP_ATOMIC);
8336b8d4a6aSJukka Rissanen }
8346b8d4a6aSJukka Rissanen 
chan_suspend_cb(struct l2cap_chan * chan)8356b8d4a6aSJukka Rissanen static void chan_suspend_cb(struct l2cap_chan *chan)
8366b8d4a6aSJukka Rissanen {
837f183e52bSLuiz Augusto von Dentz 	struct lowpan_btle_dev *dev;
838f183e52bSLuiz Augusto von Dentz 
8396dea44f5SMichael Scott 	BT_DBG("chan %p suspend", chan);
840f183e52bSLuiz Augusto von Dentz 
841f183e52bSLuiz Augusto von Dentz 	dev = lookup_dev(chan->conn);
842f183e52bSLuiz Augusto von Dentz 	if (!dev || !dev->netdev)
843f183e52bSLuiz Augusto von Dentz 		return;
844f183e52bSLuiz Augusto von Dentz 
845f183e52bSLuiz Augusto von Dentz 	netif_stop_queue(dev->netdev);
8466b8d4a6aSJukka Rissanen }
8476b8d4a6aSJukka Rissanen 
chan_resume_cb(struct l2cap_chan * chan)8486b8d4a6aSJukka Rissanen static void chan_resume_cb(struct l2cap_chan *chan)
8496b8d4a6aSJukka Rissanen {
850f183e52bSLuiz Augusto von Dentz 	struct lowpan_btle_dev *dev;
851f183e52bSLuiz Augusto von Dentz 
8526dea44f5SMichael Scott 	BT_DBG("chan %p resume", chan);
853f183e52bSLuiz Augusto von Dentz 
854f183e52bSLuiz Augusto von Dentz 	dev = lookup_dev(chan->conn);
855f183e52bSLuiz Augusto von Dentz 	if (!dev || !dev->netdev)
856f183e52bSLuiz Augusto von Dentz 		return;
857f183e52bSLuiz Augusto von Dentz 
858f183e52bSLuiz Augusto von Dentz 	netif_wake_queue(dev->netdev);
8596b8d4a6aSJukka Rissanen }
8606b8d4a6aSJukka Rissanen 
chan_get_sndtimeo_cb(struct l2cap_chan * chan)8616b8d4a6aSJukka Rissanen static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
8626b8d4a6aSJukka Rissanen {
8632ae50d8dSJukka Rissanen 	return L2CAP_CONN_TIMEOUT;
8646b8d4a6aSJukka Rissanen }
8656b8d4a6aSJukka Rissanen 
8666b8d4a6aSJukka Rissanen static const struct l2cap_ops bt_6lowpan_chan_ops = {
8676b8d4a6aSJukka Rissanen 	.name			= "L2CAP 6LoWPAN channel",
8686b8d4a6aSJukka Rissanen 	.new_connection		= chan_new_conn_cb,
8696b8d4a6aSJukka Rissanen 	.recv			= chan_recv_cb,
8706b8d4a6aSJukka Rissanen 	.close			= chan_close_cb,
8716b8d4a6aSJukka Rissanen 	.state_change		= chan_state_change_cb,
8726b8d4a6aSJukka Rissanen 	.ready			= chan_ready_cb,
8736b8d4a6aSJukka Rissanen 	.resume			= chan_resume_cb,
8746b8d4a6aSJukka Rissanen 	.suspend		= chan_suspend_cb,
8756b8d4a6aSJukka Rissanen 	.get_sndtimeo		= chan_get_sndtimeo_cb,
8766b8d4a6aSJukka Rissanen 	.alloc_skb		= chan_alloc_skb_cb,
8776b8d4a6aSJukka Rissanen 
8786b8d4a6aSJukka Rissanen 	.teardown		= l2cap_chan_no_teardown,
8796b8d4a6aSJukka Rissanen 	.defer			= l2cap_chan_no_defer,
8806b8d4a6aSJukka Rissanen 	.set_shutdown		= l2cap_chan_no_set_shutdown,
8816b8d4a6aSJukka Rissanen };
8826b8d4a6aSJukka Rissanen 
bt_6lowpan_connect(bdaddr_t * addr,u8 dst_type)8836b8d4a6aSJukka Rissanen static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
8846b8d4a6aSJukka Rissanen {
8850cd088fcSJohan Hedberg 	struct l2cap_chan *chan;
8866b8d4a6aSJukka Rissanen 	int err;
8876b8d4a6aSJukka Rissanen 
88826d46dffSJohan Hedberg 	chan = chan_create();
8890cd088fcSJohan Hedberg 	if (!chan)
8906b8d4a6aSJukka Rissanen 		return -EINVAL;
8916b8d4a6aSJukka Rissanen 
89226d46dffSJohan Hedberg 	chan->ops = &bt_6lowpan_chan_ops;
89326d46dffSJohan Hedberg 
8940cd088fcSJohan Hedberg 	err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
8956b8d4a6aSJukka Rissanen 				 addr, dst_type);
8966b8d4a6aSJukka Rissanen 
8970cd088fcSJohan Hedberg 	BT_DBG("chan %p err %d", chan, err);
8986b8d4a6aSJukka Rissanen 	if (err < 0)
8990cd088fcSJohan Hedberg 		l2cap_chan_put(chan);
9006b8d4a6aSJukka Rissanen 
90118722c24SJukka Rissanen 	return err;
90218722c24SJukka Rissanen }
90318722c24SJukka Rissanen 
bt_6lowpan_disconnect(struct l2cap_conn * conn,u8 dst_type)9046b8d4a6aSJukka Rissanen static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
9056b8d4a6aSJukka Rissanen {
9066b8d4a6aSJukka Rissanen 	struct lowpan_peer *peer;
9076b8d4a6aSJukka Rissanen 
908658d5d80SKai Ye 	BT_DBG("conn %p dst type %u", conn, dst_type);
9096b8d4a6aSJukka Rissanen 
9106b8d4a6aSJukka Rissanen 	peer = lookup_peer(conn);
9116b8d4a6aSJukka Rissanen 	if (!peer)
9126b8d4a6aSJukka Rissanen 		return -ENOENT;
9136b8d4a6aSJukka Rissanen 
9146b8d4a6aSJukka Rissanen 	BT_DBG("peer %p chan %p", peer, peer->chan);
9156b8d4a6aSJukka Rissanen 
9166b8d4a6aSJukka Rissanen 	l2cap_chan_close(peer->chan, ENOENT);
9176b8d4a6aSJukka Rissanen 
9186b8d4a6aSJukka Rissanen 	return 0;
9196b8d4a6aSJukka Rissanen }
9206b8d4a6aSJukka Rissanen 
bt_6lowpan_listen(void)9216b8d4a6aSJukka Rissanen static struct l2cap_chan *bt_6lowpan_listen(void)
9226b8d4a6aSJukka Rissanen {
9236b8d4a6aSJukka Rissanen 	bdaddr_t *addr = BDADDR_ANY;
9240cd088fcSJohan Hedberg 	struct l2cap_chan *chan;
9256b8d4a6aSJukka Rissanen 	int err;
9266b8d4a6aSJukka Rissanen 
9277b2ed60eSJukka Rissanen 	if (!enable_6lowpan)
9286b8d4a6aSJukka Rissanen 		return NULL;
9296b8d4a6aSJukka Rissanen 
93026d46dffSJohan Hedberg 	chan = chan_create();
9310cd088fcSJohan Hedberg 	if (!chan)
9326b8d4a6aSJukka Rissanen 		return NULL;
9336b8d4a6aSJukka Rissanen 
93426d46dffSJohan Hedberg 	chan->ops = &bt_6lowpan_chan_ops;
9350cd088fcSJohan Hedberg 	chan->state = BT_LISTEN;
9360cd088fcSJohan Hedberg 	chan->src_type = BDADDR_LE_PUBLIC;
9376b8d4a6aSJukka Rissanen 
9380cd088fcSJohan Hedberg 	atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
9392773b024SJohan Hedberg 
940658d5d80SKai Ye 	BT_DBG("chan %p src type %u", chan, chan->src_type);
9416b8d4a6aSJukka Rissanen 
9420cd088fcSJohan Hedberg 	err = l2cap_add_psm(chan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
9436b8d4a6aSJukka Rissanen 	if (err) {
9440cd088fcSJohan Hedberg 		l2cap_chan_put(chan);
9456b8d4a6aSJukka Rissanen 		BT_ERR("psm cannot be added err %d", err);
9466b8d4a6aSJukka Rissanen 		return NULL;
9476b8d4a6aSJukka Rissanen 	}
9486b8d4a6aSJukka Rissanen 
9490cd088fcSJohan Hedberg 	return chan;
9506b8d4a6aSJukka Rissanen }
9516b8d4a6aSJukka Rissanen 
get_l2cap_conn(char * buf,bdaddr_t * addr,u8 * addr_type,struct l2cap_conn ** conn)9526b8d4a6aSJukka Rissanen static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
9536b8d4a6aSJukka Rissanen 			  struct l2cap_conn **conn)
9546b8d4a6aSJukka Rissanen {
9556b8d4a6aSJukka Rissanen 	struct hci_conn *hcon;
9566b8d4a6aSJukka Rissanen 	struct hci_dev *hdev;
9576b8d4a6aSJukka Rissanen 	int n;
9586b8d4a6aSJukka Rissanen 
9596b8d4a6aSJukka Rissanen 	n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
9606b8d4a6aSJukka Rissanen 		   &addr->b[5], &addr->b[4], &addr->b[3],
9616b8d4a6aSJukka Rissanen 		   &addr->b[2], &addr->b[1], &addr->b[0],
9626b8d4a6aSJukka Rissanen 		   addr_type);
9636b8d4a6aSJukka Rissanen 
9646b8d4a6aSJukka Rissanen 	if (n < 7)
9656b8d4a6aSJukka Rissanen 		return -EINVAL;
9666b8d4a6aSJukka Rissanen 
96739385cb5SJohan Hedberg 	/* The LE_PUBLIC address type is ignored because of BDADDR_ANY */
96839385cb5SJohan Hedberg 	hdev = hci_get_route(addr, BDADDR_ANY, BDADDR_LE_PUBLIC);
9696b8d4a6aSJukka Rissanen 	if (!hdev)
9706b8d4a6aSJukka Rissanen 		return -ENOENT;
9716b8d4a6aSJukka Rissanen 
9726b8d4a6aSJukka Rissanen 	hci_dev_lock(hdev);
973f5ad4ffcSJohan Hedberg 	hcon = hci_conn_hash_lookup_le(hdev, addr, *addr_type);
9746b8d4a6aSJukka Rissanen 	hci_dev_unlock(hdev);
975747da130SWang ShaoBo 	hci_dev_put(hdev);
9766b8d4a6aSJukka Rissanen 
9776b8d4a6aSJukka Rissanen 	if (!hcon)
9786b8d4a6aSJukka Rissanen 		return -ENOENT;
9796b8d4a6aSJukka Rissanen 
9806b8d4a6aSJukka Rissanen 	*conn = (struct l2cap_conn *)hcon->l2cap_data;
9816b8d4a6aSJukka Rissanen 
982658d5d80SKai Ye 	BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type);
9836b8d4a6aSJukka Rissanen 
9846b8d4a6aSJukka Rissanen 	return 0;
9856b8d4a6aSJukka Rissanen }
9866b8d4a6aSJukka Rissanen 
disconnect_all_peers(void)9876b8d4a6aSJukka Rissanen static void disconnect_all_peers(void)
9886b8d4a6aSJukka Rissanen {
9892e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry;
9906b8d4a6aSJukka Rissanen 	struct lowpan_peer *peer, *tmp_peer, *new_peer;
9916b8d4a6aSJukka Rissanen 	struct list_head peers;
9926b8d4a6aSJukka Rissanen 
9936b8d4a6aSJukka Rissanen 	INIT_LIST_HEAD(&peers);
9946b8d4a6aSJukka Rissanen 
9956b8d4a6aSJukka Rissanen 	/* We make a separate list of peers as the close_cb() will
9966b8d4a6aSJukka Rissanen 	 * modify the device peers list so it is better not to mess
9976b8d4a6aSJukka Rissanen 	 * with the same list at the same time.
9986b8d4a6aSJukka Rissanen 	 */
9996b8d4a6aSJukka Rissanen 
100090305829SJukka Rissanen 	rcu_read_lock();
10016b8d4a6aSJukka Rissanen 
100290305829SJukka Rissanen 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
100390305829SJukka Rissanen 		list_for_each_entry_rcu(peer, &entry->peers, list) {
10046b8d4a6aSJukka Rissanen 			new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
10056b8d4a6aSJukka Rissanen 			if (!new_peer)
10066b8d4a6aSJukka Rissanen 				break;
10076b8d4a6aSJukka Rissanen 
10086b8d4a6aSJukka Rissanen 			new_peer->chan = peer->chan;
10096b8d4a6aSJukka Rissanen 			INIT_LIST_HEAD(&new_peer->list);
10106b8d4a6aSJukka Rissanen 
10116b8d4a6aSJukka Rissanen 			list_add(&new_peer->list, &peers);
10126b8d4a6aSJukka Rissanen 		}
10136b8d4a6aSJukka Rissanen 	}
10146b8d4a6aSJukka Rissanen 
101590305829SJukka Rissanen 	rcu_read_unlock();
10166b8d4a6aSJukka Rissanen 
101790305829SJukka Rissanen 	spin_lock(&devices_lock);
10186b8d4a6aSJukka Rissanen 	list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
10196b8d4a6aSJukka Rissanen 		l2cap_chan_close(peer->chan, ENOENT);
102090305829SJukka Rissanen 
102190305829SJukka Rissanen 		list_del_rcu(&peer->list);
10224e790226SJohan Hedberg 		kfree_rcu(peer, rcu);
10236b8d4a6aSJukka Rissanen 	}
102490305829SJukka Rissanen 	spin_unlock(&devices_lock);
10256b8d4a6aSJukka Rissanen }
10266b8d4a6aSJukka Rissanen 
10277b2ed60eSJukka Rissanen struct set_enable {
102890305829SJukka Rissanen 	struct work_struct work;
10297b2ed60eSJukka Rissanen 	bool flag;
103090305829SJukka Rissanen };
10316b8d4a6aSJukka Rissanen 
do_enable_set(struct work_struct * work)10327b2ed60eSJukka Rissanen static void do_enable_set(struct work_struct *work)
103390305829SJukka Rissanen {
10347b2ed60eSJukka Rissanen 	struct set_enable *set_enable = container_of(work,
10357b2ed60eSJukka Rissanen 						     struct set_enable, work);
103690305829SJukka Rissanen 
10377b2ed60eSJukka Rissanen 	if (!set_enable->flag || enable_6lowpan != set_enable->flag)
10386b8d4a6aSJukka Rissanen 		/* Disconnect existing connections if 6lowpan is
10397b2ed60eSJukka Rissanen 		 * disabled
10406b8d4a6aSJukka Rissanen 		 */
10416b8d4a6aSJukka Rissanen 		disconnect_all_peers();
10426b8d4a6aSJukka Rissanen 
10437b2ed60eSJukka Rissanen 	enable_6lowpan = set_enable->flag;
10446b8d4a6aSJukka Rissanen 
1045f9c70bdcSLihong Kou 	mutex_lock(&set_lock);
10466b8d4a6aSJukka Rissanen 	if (listen_chan) {
10476b8d4a6aSJukka Rissanen 		l2cap_chan_close(listen_chan, 0);
10486b8d4a6aSJukka Rissanen 		l2cap_chan_put(listen_chan);
10496b8d4a6aSJukka Rissanen 	}
10506b8d4a6aSJukka Rissanen 
10516b8d4a6aSJukka Rissanen 	listen_chan = bt_6lowpan_listen();
1052f9c70bdcSLihong Kou 	mutex_unlock(&set_lock);
10536b8d4a6aSJukka Rissanen 
10547b2ed60eSJukka Rissanen 	kfree(set_enable);
105590305829SJukka Rissanen }
105690305829SJukka Rissanen 
lowpan_enable_set(void * data,u64 val)10577b2ed60eSJukka Rissanen static int lowpan_enable_set(void *data, u64 val)
105890305829SJukka Rissanen {
10597b2ed60eSJukka Rissanen 	struct set_enable *set_enable;
106090305829SJukka Rissanen 
10617b2ed60eSJukka Rissanen 	set_enable = kzalloc(sizeof(*set_enable), GFP_KERNEL);
10627b2ed60eSJukka Rissanen 	if (!set_enable)
106390305829SJukka Rissanen 		return -ENOMEM;
106490305829SJukka Rissanen 
10657b2ed60eSJukka Rissanen 	set_enable->flag = !!val;
10667b2ed60eSJukka Rissanen 	INIT_WORK(&set_enable->work, do_enable_set);
106790305829SJukka Rissanen 
10687b2ed60eSJukka Rissanen 	schedule_work(&set_enable->work);
106990305829SJukka Rissanen 
10706b8d4a6aSJukka Rissanen 	return 0;
10716b8d4a6aSJukka Rissanen }
10726b8d4a6aSJukka Rissanen 
lowpan_enable_get(void * data,u64 * val)10737b2ed60eSJukka Rissanen static int lowpan_enable_get(void *data, u64 *val)
10746b8d4a6aSJukka Rissanen {
10757b2ed60eSJukka Rissanen 	*val = enable_6lowpan;
10766b8d4a6aSJukka Rissanen 	return 0;
10776b8d4a6aSJukka Rissanen }
10786b8d4a6aSJukka Rissanen 
1079e250fab6SYueHaibing DEFINE_DEBUGFS_ATTRIBUTE(lowpan_enable_fops, lowpan_enable_get,
10807b2ed60eSJukka Rissanen 			 lowpan_enable_set, "%llu\n");
10816b8d4a6aSJukka Rissanen 
lowpan_control_write(struct file * fp,const char __user * user_buffer,size_t count,loff_t * position)10826b8d4a6aSJukka Rissanen static ssize_t lowpan_control_write(struct file *fp,
10836b8d4a6aSJukka Rissanen 				    const char __user *user_buffer,
10846b8d4a6aSJukka Rissanen 				    size_t count,
10856b8d4a6aSJukka Rissanen 				    loff_t *position)
10866b8d4a6aSJukka Rissanen {
10876b8d4a6aSJukka Rissanen 	char buf[32];
10886b8d4a6aSJukka Rissanen 	size_t buf_size = min(count, sizeof(buf) - 1);
10896b8d4a6aSJukka Rissanen 	int ret;
10906b8d4a6aSJukka Rissanen 	bdaddr_t addr;
10916b8d4a6aSJukka Rissanen 	u8 addr_type;
10926b8d4a6aSJukka Rissanen 	struct l2cap_conn *conn = NULL;
10936b8d4a6aSJukka Rissanen 
10946b8d4a6aSJukka Rissanen 	if (copy_from_user(buf, user_buffer, buf_size))
10956b8d4a6aSJukka Rissanen 		return -EFAULT;
10966b8d4a6aSJukka Rissanen 
10976b8d4a6aSJukka Rissanen 	buf[buf_size] = '\0';
10986b8d4a6aSJukka Rissanen 
10996b8d4a6aSJukka Rissanen 	if (memcmp(buf, "connect ", 8) == 0) {
11006b8d4a6aSJukka Rissanen 		ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn);
11016b8d4a6aSJukka Rissanen 		if (ret == -EINVAL)
11026b8d4a6aSJukka Rissanen 			return ret;
11036b8d4a6aSJukka Rissanen 
1104f9c70bdcSLihong Kou 		mutex_lock(&set_lock);
11056b8d4a6aSJukka Rissanen 		if (listen_chan) {
11066b8d4a6aSJukka Rissanen 			l2cap_chan_close(listen_chan, 0);
11076b8d4a6aSJukka Rissanen 			l2cap_chan_put(listen_chan);
11086b8d4a6aSJukka Rissanen 			listen_chan = NULL;
11096b8d4a6aSJukka Rissanen 		}
1110f9c70bdcSLihong Kou 		mutex_unlock(&set_lock);
11116b8d4a6aSJukka Rissanen 
11126b8d4a6aSJukka Rissanen 		if (conn) {
11136b8d4a6aSJukka Rissanen 			struct lowpan_peer *peer;
11146b8d4a6aSJukka Rissanen 
11156b8d4a6aSJukka Rissanen 			if (!is_bt_6lowpan(conn->hcon))
11166b8d4a6aSJukka Rissanen 				return -EINVAL;
11176b8d4a6aSJukka Rissanen 
11186b8d4a6aSJukka Rissanen 			peer = lookup_peer(conn);
11196b8d4a6aSJukka Rissanen 			if (peer) {
11206b8d4a6aSJukka Rissanen 				BT_DBG("6LoWPAN connection already exists");
11216b8d4a6aSJukka Rissanen 				return -EALREADY;
11226b8d4a6aSJukka Rissanen 			}
11236b8d4a6aSJukka Rissanen 
1124658d5d80SKai Ye 			BT_DBG("conn %p dst %pMR type %d user %u", conn,
11256b8d4a6aSJukka Rissanen 			       &conn->hcon->dst, conn->hcon->dst_type,
11266b8d4a6aSJukka Rissanen 			       addr_type);
11276b8d4a6aSJukka Rissanen 		}
11286b8d4a6aSJukka Rissanen 
11296b8d4a6aSJukka Rissanen 		ret = bt_6lowpan_connect(&addr, addr_type);
11306b8d4a6aSJukka Rissanen 		if (ret < 0)
11316b8d4a6aSJukka Rissanen 			return ret;
11326b8d4a6aSJukka Rissanen 
11336b8d4a6aSJukka Rissanen 		return count;
11346b8d4a6aSJukka Rissanen 	}
11356b8d4a6aSJukka Rissanen 
11366b8d4a6aSJukka Rissanen 	if (memcmp(buf, "disconnect ", 11) == 0) {
11376b8d4a6aSJukka Rissanen 		ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn);
11386b8d4a6aSJukka Rissanen 		if (ret < 0)
11396b8d4a6aSJukka Rissanen 			return ret;
11406b8d4a6aSJukka Rissanen 
11416b8d4a6aSJukka Rissanen 		ret = bt_6lowpan_disconnect(conn, addr_type);
11426b8d4a6aSJukka Rissanen 		if (ret < 0)
11436b8d4a6aSJukka Rissanen 			return ret;
11446b8d4a6aSJukka Rissanen 
11456b8d4a6aSJukka Rissanen 		return count;
11466b8d4a6aSJukka Rissanen 	}
11476b8d4a6aSJukka Rissanen 
11486b8d4a6aSJukka Rissanen 	return count;
11496b8d4a6aSJukka Rissanen }
11506b8d4a6aSJukka Rissanen 
lowpan_control_show(struct seq_file * f,void * ptr)11516b8d4a6aSJukka Rissanen static int lowpan_control_show(struct seq_file *f, void *ptr)
11526b8d4a6aSJukka Rissanen {
11532e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry;
115490305829SJukka Rissanen 	struct lowpan_peer *peer;
11556b8d4a6aSJukka Rissanen 
115690305829SJukka Rissanen 	spin_lock(&devices_lock);
11576b8d4a6aSJukka Rissanen 
115890305829SJukka Rissanen 	list_for_each_entry(entry, &bt_6lowpan_devices, list) {
115990305829SJukka Rissanen 		list_for_each_entry(peer, &entry->peers, list)
11606b8d4a6aSJukka Rissanen 			seq_printf(f, "%pMR (type %u)\n",
11616b8d4a6aSJukka Rissanen 				   &peer->chan->dst, peer->chan->dst_type);
11626b8d4a6aSJukka Rissanen 	}
11636b8d4a6aSJukka Rissanen 
116490305829SJukka Rissanen 	spin_unlock(&devices_lock);
11656b8d4a6aSJukka Rissanen 
11666b8d4a6aSJukka Rissanen 	return 0;
11676b8d4a6aSJukka Rissanen }
11686b8d4a6aSJukka Rissanen 
lowpan_control_open(struct inode * inode,struct file * file)11696b8d4a6aSJukka Rissanen static int lowpan_control_open(struct inode *inode, struct file *file)
11706b8d4a6aSJukka Rissanen {
11716b8d4a6aSJukka Rissanen 	return single_open(file, lowpan_control_show, inode->i_private);
11726b8d4a6aSJukka Rissanen }
11736b8d4a6aSJukka Rissanen 
11746b8d4a6aSJukka Rissanen static const struct file_operations lowpan_control_fops = {
11756b8d4a6aSJukka Rissanen 	.open		= lowpan_control_open,
11766b8d4a6aSJukka Rissanen 	.read		= seq_read,
11776b8d4a6aSJukka Rissanen 	.write		= lowpan_control_write,
11786b8d4a6aSJukka Rissanen 	.llseek		= seq_lseek,
11796b8d4a6aSJukka Rissanen 	.release	= single_release,
11806b8d4a6aSJukka Rissanen };
11816b8d4a6aSJukka Rissanen 
disconnect_devices(void)11827f118253SJukka Rissanen static void disconnect_devices(void)
11837f118253SJukka Rissanen {
11842e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry, *tmp, *new_dev;
11857f118253SJukka Rissanen 	struct list_head devices;
11867f118253SJukka Rissanen 
11877f118253SJukka Rissanen 	INIT_LIST_HEAD(&devices);
11887f118253SJukka Rissanen 
11897f118253SJukka Rissanen 	/* We make a separate list of devices because the unregister_netdev()
11907f118253SJukka Rissanen 	 * will call device_event() which will also want to modify the same
11917f118253SJukka Rissanen 	 * devices list.
11927f118253SJukka Rissanen 	 */
11937f118253SJukka Rissanen 
119490305829SJukka Rissanen 	rcu_read_lock();
11957f118253SJukka Rissanen 
119690305829SJukka Rissanen 	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
11977f118253SJukka Rissanen 		new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
11987f118253SJukka Rissanen 		if (!new_dev)
11997f118253SJukka Rissanen 			break;
12007f118253SJukka Rissanen 
12017f118253SJukka Rissanen 		new_dev->netdev = entry->netdev;
12027f118253SJukka Rissanen 		INIT_LIST_HEAD(&new_dev->list);
12037f118253SJukka Rissanen 
120490305829SJukka Rissanen 		list_add_rcu(&new_dev->list, &devices);
12057f118253SJukka Rissanen 	}
12067f118253SJukka Rissanen 
120790305829SJukka Rissanen 	rcu_read_unlock();
12087f118253SJukka Rissanen 
1209daac197cSDan Carpenter 	list_for_each_entry_safe(entry, tmp, &devices, list) {
12107f118253SJukka Rissanen 		ifdown(entry->netdev);
12117f118253SJukka Rissanen 		BT_DBG("Unregistering netdev %s %p",
12127f118253SJukka Rissanen 		       entry->netdev->name, entry->netdev);
121300f59314SAlexander Aring 		lowpan_unregister_netdev(entry->netdev);
12147f118253SJukka Rissanen 		kfree(entry);
12157f118253SJukka Rissanen 	}
12167f118253SJukka Rissanen }
12177f118253SJukka Rissanen 
device_event(struct notifier_block * unused,unsigned long event,void * ptr)121818722c24SJukka Rissanen static int device_event(struct notifier_block *unused,
121918722c24SJukka Rissanen 			unsigned long event, void *ptr)
122018722c24SJukka Rissanen {
122118722c24SJukka Rissanen 	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
12222e4d60cbSAlexander Aring 	struct lowpan_btle_dev *entry;
122318722c24SJukka Rissanen 
122418722c24SJukka Rissanen 	if (netdev->type != ARPHRD_6LOWPAN)
122518722c24SJukka Rissanen 		return NOTIFY_DONE;
122618722c24SJukka Rissanen 
122718722c24SJukka Rissanen 	switch (event) {
122818722c24SJukka Rissanen 	case NETDEV_UNREGISTER:
122990305829SJukka Rissanen 		spin_lock(&devices_lock);
123090305829SJukka Rissanen 		list_for_each_entry(entry, &bt_6lowpan_devices, list) {
123118722c24SJukka Rissanen 			if (entry->netdev == netdev) {
12327f118253SJukka Rissanen 				BT_DBG("Unregistered netdev %s %p",
12337f118253SJukka Rissanen 				       netdev->name, netdev);
123418722c24SJukka Rissanen 				list_del(&entry->list);
123518722c24SJukka Rissanen 				break;
123618722c24SJukka Rissanen 			}
123718722c24SJukka Rissanen 		}
123890305829SJukka Rissanen 		spin_unlock(&devices_lock);
123918722c24SJukka Rissanen 		break;
124018722c24SJukka Rissanen 	}
124118722c24SJukka Rissanen 
124218722c24SJukka Rissanen 	return NOTIFY_DONE;
124318722c24SJukka Rissanen }
124418722c24SJukka Rissanen 
124518722c24SJukka Rissanen static struct notifier_block bt_6lowpan_dev_notifier = {
124618722c24SJukka Rissanen 	.notifier_call = device_event,
124718722c24SJukka Rissanen };
124818722c24SJukka Rissanen 
bt_6lowpan_init(void)12495547e48cSJukka Rissanen static int __init bt_6lowpan_init(void)
125018722c24SJukka Rissanen {
1251e250fab6SYueHaibing 	lowpan_enable_debugfs = debugfs_create_file_unsafe("6lowpan_enable",
1252e250fab6SYueHaibing 							   0644, bt_debugfs,
1253e250fab6SYueHaibing 							   NULL,
12547b2ed60eSJukka Rissanen 							   &lowpan_enable_fops);
12556b8d4a6aSJukka Rissanen 	lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644,
12566b8d4a6aSJukka Rissanen 						     bt_debugfs, NULL,
12576b8d4a6aSJukka Rissanen 						     &lowpan_control_fops);
12586b8d4a6aSJukka Rissanen 
125918722c24SJukka Rissanen 	return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
126018722c24SJukka Rissanen }
126118722c24SJukka Rissanen 
bt_6lowpan_exit(void)12625547e48cSJukka Rissanen static void __exit bt_6lowpan_exit(void)
126318722c24SJukka Rissanen {
12647b2ed60eSJukka Rissanen 	debugfs_remove(lowpan_enable_debugfs);
12656b8d4a6aSJukka Rissanen 	debugfs_remove(lowpan_control_debugfs);
12666b8d4a6aSJukka Rissanen 
12676b8d4a6aSJukka Rissanen 	if (listen_chan) {
12686b8d4a6aSJukka Rissanen 		l2cap_chan_close(listen_chan, 0);
12696b8d4a6aSJukka Rissanen 		l2cap_chan_put(listen_chan);
12706b8d4a6aSJukka Rissanen 	}
12716b8d4a6aSJukka Rissanen 
12727f118253SJukka Rissanen 	disconnect_devices();
12737f118253SJukka Rissanen 
127418722c24SJukka Rissanen 	unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
127518722c24SJukka Rissanen }
12765547e48cSJukka Rissanen 
12775547e48cSJukka Rissanen module_init(bt_6lowpan_init);
12785547e48cSJukka Rissanen module_exit(bt_6lowpan_exit);
12795547e48cSJukka Rissanen 
12805547e48cSJukka Rissanen MODULE_AUTHOR("Jukka Rissanen <jukka.rissanen@linux.intel.com>");
12815547e48cSJukka Rissanen MODULE_DESCRIPTION("Bluetooth 6LoWPAN");
12825547e48cSJukka Rissanen MODULE_VERSION(VERSION);
12835547e48cSJukka Rissanen MODULE_LICENSE("GPL");
1284