18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci   Copyright (c) 2013-2014 Intel Corp.
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci*/
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
88c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
98c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <net/ipv6.h>
148c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
158c2ecf20Sopenharmony_ci#include <net/addrconf.h>
168c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <net/bluetooth/bluetooth.h>
198c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_core.h>
208c2ecf20Sopenharmony_ci#include <net/bluetooth/l2cap.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <net/6lowpan.h> /* for the compression support */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define VERSION "0.1"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct dentry *lowpan_enable_debugfs;
278c2ecf20Sopenharmony_cistatic struct dentry *lowpan_control_debugfs;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define IFACE_NAME_TEMPLATE "bt%d"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct skb_cb {
328c2ecf20Sopenharmony_ci	struct in6_addr addr;
338c2ecf20Sopenharmony_ci	struct in6_addr gw;
348c2ecf20Sopenharmony_ci	struct l2cap_chan *chan;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* The devices list contains those devices that we are acting
398c2ecf20Sopenharmony_ci * as a proxy. The BT 6LoWPAN device is a virtual device that
408c2ecf20Sopenharmony_ci * connects to the Bluetooth LE device. The real connection to
418c2ecf20Sopenharmony_ci * BT device is done via l2cap layer. There exists one
428c2ecf20Sopenharmony_ci * virtual device / one BT 6LoWPAN network (=hciX device).
438c2ecf20Sopenharmony_ci * The list contains struct lowpan_dev elements.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistatic LIST_HEAD(bt_6lowpan_devices);
468c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(devices_lock);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic bool enable_6lowpan;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* We are listening incoming connections via this channel
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic struct l2cap_chan *listen_chan;
538c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(set_lock);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct lowpan_peer {
568c2ecf20Sopenharmony_ci	struct list_head list;
578c2ecf20Sopenharmony_ci	struct rcu_head rcu;
588c2ecf20Sopenharmony_ci	struct l2cap_chan *chan;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/* peer addresses in various formats */
618c2ecf20Sopenharmony_ci	unsigned char lladdr[ETH_ALEN];
628c2ecf20Sopenharmony_ci	struct in6_addr peer_addr;
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct lowpan_btle_dev {
668c2ecf20Sopenharmony_ci	struct list_head list;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	struct hci_dev *hdev;
698c2ecf20Sopenharmony_ci	struct net_device *netdev;
708c2ecf20Sopenharmony_ci	struct list_head peers;
718c2ecf20Sopenharmony_ci	atomic_t peer_count; /* number of items in peers list */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	struct work_struct delete_netdev;
748c2ecf20Sopenharmony_ci	struct delayed_work notify_peers;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic inline struct lowpan_btle_dev *
788c2ecf20Sopenharmony_cilowpan_btle_dev(const struct net_device *netdev)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	return (struct lowpan_btle_dev *)lowpan_dev(netdev)->priv;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic inline void peer_add(struct lowpan_btle_dev *dev,
848c2ecf20Sopenharmony_ci			    struct lowpan_peer *peer)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	list_add_rcu(&peer->list, &dev->peers);
878c2ecf20Sopenharmony_ci	atomic_inc(&dev->peer_count);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic inline bool peer_del(struct lowpan_btle_dev *dev,
918c2ecf20Sopenharmony_ci			    struct lowpan_peer *peer)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	list_del_rcu(&peer->list);
948c2ecf20Sopenharmony_ci	kfree_rcu(peer, rcu);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&dev->peer_count)) {
998c2ecf20Sopenharmony_ci		BT_DBG("last peer");
1008c2ecf20Sopenharmony_ci		return true;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return false;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic inline struct lowpan_peer *peer_lookup_ba(struct lowpan_btle_dev *dev,
1078c2ecf20Sopenharmony_ci						 bdaddr_t *ba, __u8 type)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count),
1128c2ecf20Sopenharmony_ci	       ba, type);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	rcu_read_lock();
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(peer, &dev->peers, list) {
1178c2ecf20Sopenharmony_ci		BT_DBG("dst addr %pMR dst type %d",
1188c2ecf20Sopenharmony_ci		       &peer->chan->dst, peer->chan->dst_type);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		if (bacmp(&peer->chan->dst, ba))
1218c2ecf20Sopenharmony_ci			continue;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		if (type == peer->chan->dst_type) {
1248c2ecf20Sopenharmony_ci			rcu_read_unlock();
1258c2ecf20Sopenharmony_ci			return peer;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	rcu_read_unlock();
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return NULL;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic inline struct lowpan_peer *
1358c2ecf20Sopenharmony_ci__peer_lookup_chan(struct lowpan_btle_dev *dev, struct l2cap_chan *chan)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(peer, &dev->peers, list) {
1408c2ecf20Sopenharmony_ci		if (peer->chan == chan)
1418c2ecf20Sopenharmony_ci			return peer;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return NULL;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic inline struct lowpan_peer *
1488c2ecf20Sopenharmony_ci__peer_lookup_conn(struct lowpan_btle_dev *dev, struct l2cap_conn *conn)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(peer, &dev->peers, list) {
1538c2ecf20Sopenharmony_ci		if (peer->chan->conn == conn)
1548c2ecf20Sopenharmony_ci			return peer;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return NULL;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
1618c2ecf20Sopenharmony_ci						  struct in6_addr *daddr,
1628c2ecf20Sopenharmony_ci						  struct sk_buff *skb)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
1658c2ecf20Sopenharmony_ci	int count = atomic_read(&dev->peer_count);
1668c2ecf20Sopenharmony_ci	const struct in6_addr *nexthop;
1678c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
1688c2ecf20Sopenharmony_ci	struct neighbour *neigh;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (!rt) {
1738c2ecf20Sopenharmony_ci		if (ipv6_addr_any(&lowpan_cb(skb)->gw)) {
1748c2ecf20Sopenharmony_ci			/* There is neither route nor gateway,
1758c2ecf20Sopenharmony_ci			 * probably the destination is a direct peer.
1768c2ecf20Sopenharmony_ci			 */
1778c2ecf20Sopenharmony_ci			nexthop = daddr;
1788c2ecf20Sopenharmony_ci		} else {
1798c2ecf20Sopenharmony_ci			/* There is a known gateway
1808c2ecf20Sopenharmony_ci			 */
1818c2ecf20Sopenharmony_ci			nexthop = &lowpan_cb(skb)->gw;
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci	} else {
1848c2ecf20Sopenharmony_ci		nexthop = rt6_nexthop(rt, daddr);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		/* We need to remember the address because it is needed
1878c2ecf20Sopenharmony_ci		 * by bt_xmit() when sending the packet. In bt_xmit(), the
1888c2ecf20Sopenharmony_ci		 * destination routing info is not set.
1898c2ecf20Sopenharmony_ci		 */
1908c2ecf20Sopenharmony_ci		memcpy(&lowpan_cb(skb)->gw, nexthop, sizeof(struct in6_addr));
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	BT_DBG("gw %pI6c", nexthop);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	rcu_read_lock();
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(peer, &dev->peers, list) {
1988c2ecf20Sopenharmony_ci		BT_DBG("dst addr %pMR dst type %d ip %pI6c",
1998c2ecf20Sopenharmony_ci		       &peer->chan->dst, peer->chan->dst_type,
2008c2ecf20Sopenharmony_ci		       &peer->peer_addr);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
2038c2ecf20Sopenharmony_ci			rcu_read_unlock();
2048c2ecf20Sopenharmony_ci			return peer;
2058c2ecf20Sopenharmony_ci		}
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* use the neighbour cache for matching addresses assigned by SLAAC
2098c2ecf20Sopenharmony_ci	*/
2108c2ecf20Sopenharmony_ci	neigh = __ipv6_neigh_lookup(dev->netdev, nexthop);
2118c2ecf20Sopenharmony_ci	if (neigh) {
2128c2ecf20Sopenharmony_ci		list_for_each_entry_rcu(peer, &dev->peers, list) {
2138c2ecf20Sopenharmony_ci			if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) {
2148c2ecf20Sopenharmony_ci				neigh_release(neigh);
2158c2ecf20Sopenharmony_ci				rcu_read_unlock();
2168c2ecf20Sopenharmony_ci				return peer;
2178c2ecf20Sopenharmony_ci			}
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci		neigh_release(neigh);
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	rcu_read_unlock();
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return NULL;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry;
2308c2ecf20Sopenharmony_ci	struct lowpan_peer *peer = NULL;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	rcu_read_lock();
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
2358c2ecf20Sopenharmony_ci		peer = __peer_lookup_conn(entry, conn);
2368c2ecf20Sopenharmony_ci		if (peer)
2378c2ecf20Sopenharmony_ci			break;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	rcu_read_unlock();
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return peer;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic struct lowpan_btle_dev *lookup_dev(struct l2cap_conn *conn)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry;
2488c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev = NULL;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	rcu_read_lock();
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
2538c2ecf20Sopenharmony_ci		if (conn->hcon->hdev == entry->hdev) {
2548c2ecf20Sopenharmony_ci			dev = entry;
2558c2ecf20Sopenharmony_ci			break;
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	rcu_read_unlock();
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return dev;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct sk_buff *skb_cp;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	skb_cp = skb_copy(skb, GFP_ATOMIC);
2698c2ecf20Sopenharmony_ci	if (!skb_cp)
2708c2ecf20Sopenharmony_ci		return NET_RX_DROP;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return netif_rx_ni(skb_cp);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
2768c2ecf20Sopenharmony_ci			   struct lowpan_peer *peer)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	const u8 *saddr;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	saddr = peer->lladdr;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return lowpan_header_decompress(skb, netdev, netdev->dev_addr, saddr);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int recv_pkt(struct sk_buff *skb, struct net_device *dev,
2868c2ecf20Sopenharmony_ci		    struct lowpan_peer *peer)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct sk_buff *local_skb;
2898c2ecf20Sopenharmony_ci	int ret;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (!netif_running(dev))
2928c2ecf20Sopenharmony_ci		goto drop;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_6LOWPAN || !skb->len)
2958c2ecf20Sopenharmony_ci		goto drop;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
3008c2ecf20Sopenharmony_ci	if (!skb)
3018c2ecf20Sopenharmony_ci		goto drop;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/* check that it's our buffer */
3048c2ecf20Sopenharmony_ci	if (lowpan_is_ipv6(*skb_network_header(skb))) {
3058c2ecf20Sopenharmony_ci		/* Pull off the 1-byte of 6lowpan header. */
3068c2ecf20Sopenharmony_ci		skb_pull(skb, 1);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		/* Copy the packet so that the IPv6 header is
3098c2ecf20Sopenharmony_ci		 * properly aligned.
3108c2ecf20Sopenharmony_ci		 */
3118c2ecf20Sopenharmony_ci		local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
3128c2ecf20Sopenharmony_ci					    skb_tailroom(skb), GFP_ATOMIC);
3138c2ecf20Sopenharmony_ci		if (!local_skb)
3148c2ecf20Sopenharmony_ci			goto drop;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		local_skb->protocol = htons(ETH_P_IPV6);
3178c2ecf20Sopenharmony_ci		local_skb->pkt_type = PACKET_HOST;
3188c2ecf20Sopenharmony_ci		local_skb->dev = dev;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
3238c2ecf20Sopenharmony_ci			kfree_skb(local_skb);
3248c2ecf20Sopenharmony_ci			goto drop;
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		dev->stats.rx_bytes += skb->len;
3288c2ecf20Sopenharmony_ci		dev->stats.rx_packets++;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		consume_skb(local_skb);
3318c2ecf20Sopenharmony_ci		consume_skb(skb);
3328c2ecf20Sopenharmony_ci	} else if (lowpan_is_iphc(*skb_network_header(skb))) {
3338c2ecf20Sopenharmony_ci		local_skb = skb_clone(skb, GFP_ATOMIC);
3348c2ecf20Sopenharmony_ci		if (!local_skb)
3358c2ecf20Sopenharmony_ci			goto drop;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		local_skb->dev = dev;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		ret = iphc_decompress(local_skb, dev, peer);
3408c2ecf20Sopenharmony_ci		if (ret < 0) {
3418c2ecf20Sopenharmony_ci			BT_DBG("iphc_decompress failed: %d", ret);
3428c2ecf20Sopenharmony_ci			kfree_skb(local_skb);
3438c2ecf20Sopenharmony_ci			goto drop;
3448c2ecf20Sopenharmony_ci		}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		local_skb->protocol = htons(ETH_P_IPV6);
3478c2ecf20Sopenharmony_ci		local_skb->pkt_type = PACKET_HOST;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		if (give_skb_to_upper(local_skb, dev)
3508c2ecf20Sopenharmony_ci				!= NET_RX_SUCCESS) {
3518c2ecf20Sopenharmony_ci			kfree_skb(local_skb);
3528c2ecf20Sopenharmony_ci			goto drop;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		dev->stats.rx_bytes += skb->len;
3568c2ecf20Sopenharmony_ci		dev->stats.rx_packets++;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		consume_skb(local_skb);
3598c2ecf20Sopenharmony_ci		consume_skb(skb);
3608c2ecf20Sopenharmony_ci	} else {
3618c2ecf20Sopenharmony_ci		BT_DBG("unknown packet type");
3628c2ecf20Sopenharmony_ci		goto drop;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cidrop:
3688c2ecf20Sopenharmony_ci	dev->stats.rx_dropped++;
3698c2ecf20Sopenharmony_ci	return NET_RX_DROP;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci/* Packet from BT LE device */
3738c2ecf20Sopenharmony_cistatic int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev;
3768c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
3778c2ecf20Sopenharmony_ci	int err;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	peer = lookup_peer(chan->conn);
3808c2ecf20Sopenharmony_ci	if (!peer)
3818c2ecf20Sopenharmony_ci		return -ENOENT;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	dev = lookup_dev(chan->conn);
3848c2ecf20Sopenharmony_ci	if (!dev || !dev->netdev)
3858c2ecf20Sopenharmony_ci		return -ENOENT;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	err = recv_pkt(skb, dev->netdev, peer);
3888c2ecf20Sopenharmony_ci	if (err) {
3898c2ecf20Sopenharmony_ci		BT_DBG("recv pkt %d", err);
3908c2ecf20Sopenharmony_ci		err = -EAGAIN;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return err;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int setup_header(struct sk_buff *skb, struct net_device *netdev,
3978c2ecf20Sopenharmony_ci			bdaddr_t *peer_addr, u8 *peer_addr_type)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct in6_addr ipv6_daddr;
4008c2ecf20Sopenharmony_ci	struct ipv6hdr *hdr;
4018c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev;
4028c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
4038c2ecf20Sopenharmony_ci	u8 *daddr;
4048c2ecf20Sopenharmony_ci	int err, status = 0;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	hdr = ipv6_hdr(skb);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	dev = lowpan_btle_dev(netdev);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	memcpy(&ipv6_daddr, &hdr->daddr, sizeof(ipv6_daddr));
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (ipv6_addr_is_multicast(&ipv6_daddr)) {
4138c2ecf20Sopenharmony_ci		lowpan_cb(skb)->chan = NULL;
4148c2ecf20Sopenharmony_ci		daddr = NULL;
4158c2ecf20Sopenharmony_ci	} else {
4168c2ecf20Sopenharmony_ci		BT_DBG("dest IP %pI6c", &ipv6_daddr);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		/* The packet might be sent to 6lowpan interface
4198c2ecf20Sopenharmony_ci		 * because of routing (either via default route
4208c2ecf20Sopenharmony_ci		 * or user set route) so get peer according to
4218c2ecf20Sopenharmony_ci		 * the destination address.
4228c2ecf20Sopenharmony_ci		 */
4238c2ecf20Sopenharmony_ci		peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
4248c2ecf20Sopenharmony_ci		if (!peer) {
4258c2ecf20Sopenharmony_ci			BT_DBG("no such peer");
4268c2ecf20Sopenharmony_ci			return -ENOENT;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		daddr = peer->lladdr;
4308c2ecf20Sopenharmony_ci		*peer_addr = peer->chan->dst;
4318c2ecf20Sopenharmony_ci		*peer_addr_type = peer->chan->dst_type;
4328c2ecf20Sopenharmony_ci		lowpan_cb(skb)->chan = peer->chan;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		status = 1;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0);
4408c2ecf20Sopenharmony_ci	if (err < 0)
4418c2ecf20Sopenharmony_ci		return err;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return status;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int header_create(struct sk_buff *skb, struct net_device *netdev,
4478c2ecf20Sopenharmony_ci			 unsigned short type, const void *_daddr,
4488c2ecf20Sopenharmony_ci			 const void *_saddr, unsigned int len)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	if (type != ETH_P_IPV6)
4518c2ecf20Sopenharmony_ci		return -EINVAL;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci/* Packet to BT LE device */
4578c2ecf20Sopenharmony_cistatic int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
4588c2ecf20Sopenharmony_ci		    struct net_device *netdev)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct msghdr msg;
4618c2ecf20Sopenharmony_ci	struct kvec iv;
4628c2ecf20Sopenharmony_ci	int err;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* Remember the skb so that we can send EAGAIN to the caller if
4658c2ecf20Sopenharmony_ci	 * we run out of credits.
4668c2ecf20Sopenharmony_ci	 */
4678c2ecf20Sopenharmony_ci	chan->data = skb;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	iv.iov_base = skb->data;
4708c2ecf20Sopenharmony_ci	iv.iov_len = skb->len;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
4738c2ecf20Sopenharmony_ci	iov_iter_kvec(&msg.msg_iter, WRITE, &iv, 1, skb->len);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	err = l2cap_chan_send(chan, &msg, skb->len);
4768c2ecf20Sopenharmony_ci	if (err > 0) {
4778c2ecf20Sopenharmony_ci		netdev->stats.tx_bytes += err;
4788c2ecf20Sopenharmony_ci		netdev->stats.tx_packets++;
4798c2ecf20Sopenharmony_ci		return 0;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (err < 0)
4838c2ecf20Sopenharmony_ci		netdev->stats.tx_errors++;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	return err;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct sk_buff *local_skb;
4918c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry;
4928c2ecf20Sopenharmony_ci	int err = 0;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	rcu_read_lock();
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
4978c2ecf20Sopenharmony_ci		struct lowpan_peer *pentry;
4988c2ecf20Sopenharmony_ci		struct lowpan_btle_dev *dev;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci		if (entry->netdev != netdev)
5018c2ecf20Sopenharmony_ci			continue;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		dev = lowpan_btle_dev(entry->netdev);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		list_for_each_entry_rcu(pentry, &dev->peers, list) {
5068c2ecf20Sopenharmony_ci			int ret;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci			local_skb = skb_clone(skb, GFP_ATOMIC);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci			BT_DBG("xmit %s to %pMR type %d IP %pI6c chan %p",
5118c2ecf20Sopenharmony_ci			       netdev->name,
5128c2ecf20Sopenharmony_ci			       &pentry->chan->dst, pentry->chan->dst_type,
5138c2ecf20Sopenharmony_ci			       &pentry->peer_addr, pentry->chan);
5148c2ecf20Sopenharmony_ci			ret = send_pkt(pentry->chan, local_skb, netdev);
5158c2ecf20Sopenharmony_ci			if (ret < 0)
5168c2ecf20Sopenharmony_ci				err = ret;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci			kfree_skb(local_skb);
5198c2ecf20Sopenharmony_ci		}
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	rcu_read_unlock();
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return err;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	int err = 0;
5308c2ecf20Sopenharmony_ci	bdaddr_t addr;
5318c2ecf20Sopenharmony_ci	u8 addr_type;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* We must take a copy of the skb before we modify/replace the ipv6
5348c2ecf20Sopenharmony_ci	 * header as the header could be used elsewhere
5358c2ecf20Sopenharmony_ci	 */
5368c2ecf20Sopenharmony_ci	skb = skb_unshare(skb, GFP_ATOMIC);
5378c2ecf20Sopenharmony_ci	if (!skb)
5388c2ecf20Sopenharmony_ci		return NET_XMIT_DROP;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* Return values from setup_header()
5418c2ecf20Sopenharmony_ci	 *  <0 - error, packet is dropped
5428c2ecf20Sopenharmony_ci	 *   0 - this is a multicast packet
5438c2ecf20Sopenharmony_ci	 *   1 - this is unicast packet
5448c2ecf20Sopenharmony_ci	 */
5458c2ecf20Sopenharmony_ci	err = setup_header(skb, netdev, &addr, &addr_type);
5468c2ecf20Sopenharmony_ci	if (err < 0) {
5478c2ecf20Sopenharmony_ci		kfree_skb(skb);
5488c2ecf20Sopenharmony_ci		return NET_XMIT_DROP;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	if (err) {
5528c2ecf20Sopenharmony_ci		if (lowpan_cb(skb)->chan) {
5538c2ecf20Sopenharmony_ci			BT_DBG("xmit %s to %pMR type %d IP %pI6c chan %p",
5548c2ecf20Sopenharmony_ci			       netdev->name, &addr, addr_type,
5558c2ecf20Sopenharmony_ci			       &lowpan_cb(skb)->addr, lowpan_cb(skb)->chan);
5568c2ecf20Sopenharmony_ci			err = send_pkt(lowpan_cb(skb)->chan, skb, netdev);
5578c2ecf20Sopenharmony_ci		} else {
5588c2ecf20Sopenharmony_ci			err = -ENOENT;
5598c2ecf20Sopenharmony_ci		}
5608c2ecf20Sopenharmony_ci	} else {
5618c2ecf20Sopenharmony_ci		/* We need to send the packet to every device behind this
5628c2ecf20Sopenharmony_ci		 * interface.
5638c2ecf20Sopenharmony_ci		 */
5648c2ecf20Sopenharmony_ci		err = send_mcast_pkt(skb, netdev);
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (err)
5708c2ecf20Sopenharmony_ci		BT_DBG("ERROR: xmit failed (%d)", err);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	return err < 0 ? NET_XMIT_DROP : err;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic int bt_dev_init(struct net_device *dev)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	netdev_lockdep_set_classes(dev);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	return 0;
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
5838c2ecf20Sopenharmony_ci	.ndo_init		= bt_dev_init,
5848c2ecf20Sopenharmony_ci	.ndo_start_xmit		= bt_xmit,
5858c2ecf20Sopenharmony_ci};
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic const struct header_ops header_ops = {
5888c2ecf20Sopenharmony_ci	.create	= header_create,
5898c2ecf20Sopenharmony_ci};
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic void netdev_setup(struct net_device *dev)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	dev->hard_header_len	= 0;
5948c2ecf20Sopenharmony_ci	dev->needed_tailroom	= 0;
5958c2ecf20Sopenharmony_ci	dev->flags		= IFF_RUNNING | IFF_MULTICAST;
5968c2ecf20Sopenharmony_ci	dev->watchdog_timeo	= 0;
5978c2ecf20Sopenharmony_ci	dev->tx_queue_len	= DEFAULT_TX_QUEUE_LEN;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	dev->netdev_ops		= &netdev_ops;
6008c2ecf20Sopenharmony_ci	dev->header_ops		= &header_ops;
6018c2ecf20Sopenharmony_ci	dev->needs_free_netdev	= true;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic struct device_type bt_type = {
6058c2ecf20Sopenharmony_ci	.name	= "bluetooth",
6068c2ecf20Sopenharmony_ci};
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic void ifup(struct net_device *netdev)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	int err;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	rtnl_lock();
6138c2ecf20Sopenharmony_ci	err = dev_open(netdev, NULL);
6148c2ecf20Sopenharmony_ci	if (err < 0)
6158c2ecf20Sopenharmony_ci		BT_INFO("iface %s cannot be opened (%d)", netdev->name, err);
6168c2ecf20Sopenharmony_ci	rtnl_unlock();
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic void ifdown(struct net_device *netdev)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	rtnl_lock();
6228c2ecf20Sopenharmony_ci	dev_close(netdev);
6238c2ecf20Sopenharmony_ci	rtnl_unlock();
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic void do_notify_peers(struct work_struct *work)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev = container_of(work, struct lowpan_btle_dev,
6298c2ecf20Sopenharmony_ci						   notify_peers.work);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	netdev_notify_peers(dev->netdev); /* send neighbour adv at startup */
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic bool is_bt_6lowpan(struct hci_conn *hcon)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	if (hcon->type != LE_LINK)
6378c2ecf20Sopenharmony_ci		return false;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (!enable_6lowpan)
6408c2ecf20Sopenharmony_ci		return false;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	return true;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic struct l2cap_chan *chan_create(void)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	struct l2cap_chan *chan;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	chan = l2cap_chan_create();
6508c2ecf20Sopenharmony_ci	if (!chan)
6518c2ecf20Sopenharmony_ci		return NULL;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	l2cap_chan_set_defaults(chan);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
6568c2ecf20Sopenharmony_ci	chan->mode = L2CAP_MODE_LE_FLOWCTL;
6578c2ecf20Sopenharmony_ci	chan->imtu = 1280;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	return chan;
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cistatic struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
6638c2ecf20Sopenharmony_ci					struct lowpan_btle_dev *dev,
6648c2ecf20Sopenharmony_ci					bool new_netdev)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
6698c2ecf20Sopenharmony_ci	if (!peer)
6708c2ecf20Sopenharmony_ci		return NULL;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	peer->chan = chan;
6738c2ecf20Sopenharmony_ci	memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	baswap((void *)peer->lladdr, &chan->dst);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
6808c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&peer->list);
6818c2ecf20Sopenharmony_ci	peer_add(dev, peer);
6828c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	/* Notifying peers about us needs to be done without locks held */
6858c2ecf20Sopenharmony_ci	if (new_netdev)
6868c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
6878c2ecf20Sopenharmony_ci	schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	return peer->chan;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	struct net_device *netdev;
6958c2ecf20Sopenharmony_ci	int err = 0;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	netdev = alloc_netdev(LOWPAN_PRIV_SIZE(sizeof(struct lowpan_btle_dev)),
6988c2ecf20Sopenharmony_ci			      IFACE_NAME_TEMPLATE, NET_NAME_UNKNOWN,
6998c2ecf20Sopenharmony_ci			      netdev_setup);
7008c2ecf20Sopenharmony_ci	if (!netdev)
7018c2ecf20Sopenharmony_ci		return -ENOMEM;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	netdev->addr_assign_type = NET_ADDR_PERM;
7048c2ecf20Sopenharmony_ci	baswap((void *)netdev->dev_addr, &chan->src);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	netdev->netdev_ops = &netdev_ops;
7078c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev);
7088c2ecf20Sopenharmony_ci	SET_NETDEV_DEVTYPE(netdev, &bt_type);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	*dev = lowpan_btle_dev(netdev);
7118c2ecf20Sopenharmony_ci	(*dev)->netdev = netdev;
7128c2ecf20Sopenharmony_ci	(*dev)->hdev = chan->conn->hcon->hdev;
7138c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&(*dev)->peers);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
7168c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&(*dev)->list);
7178c2ecf20Sopenharmony_ci	list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
7188c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
7218c2ecf20Sopenharmony_ci	if (err < 0) {
7228c2ecf20Sopenharmony_ci		BT_INFO("register_netdev failed %d", err);
7238c2ecf20Sopenharmony_ci		spin_lock(&devices_lock);
7248c2ecf20Sopenharmony_ci		list_del_rcu(&(*dev)->list);
7258c2ecf20Sopenharmony_ci		spin_unlock(&devices_lock);
7268c2ecf20Sopenharmony_ci		free_netdev(netdev);
7278c2ecf20Sopenharmony_ci		goto out;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
7318c2ecf20Sopenharmony_ci	       netdev->ifindex, &chan->dst, chan->dst_type,
7328c2ecf20Sopenharmony_ci	       &chan->src, chan->src_type);
7338c2ecf20Sopenharmony_ci	set_bit(__LINK_STATE_PRESENT, &netdev->state);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	return 0;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ciout:
7388c2ecf20Sopenharmony_ci	return err;
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_cistatic inline void chan_ready_cb(struct l2cap_chan *chan)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev;
7448c2ecf20Sopenharmony_ci	bool new_netdev = false;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	dev = lookup_dev(chan->conn);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (!dev) {
7518c2ecf20Sopenharmony_ci		if (setup_netdev(chan, &dev) < 0) {
7528c2ecf20Sopenharmony_ci			l2cap_chan_del(chan, -ENOENT);
7538c2ecf20Sopenharmony_ci			return;
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci		new_netdev = true;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
7598c2ecf20Sopenharmony_ci		return;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	add_peer_chan(chan, dev, new_netdev);
7628c2ecf20Sopenharmony_ci	ifup(dev->netdev);
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	struct l2cap_chan *chan;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	chan = chan_create();
7708c2ecf20Sopenharmony_ci	if (!chan)
7718c2ecf20Sopenharmony_ci		return NULL;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	chan->ops = pchan->ops;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	BT_DBG("chan %p pchan %p", chan, pchan);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	return chan;
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic void delete_netdev(struct work_struct *work)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry = container_of(work,
7838c2ecf20Sopenharmony_ci						     struct lowpan_btle_dev,
7848c2ecf20Sopenharmony_ci						     delete_netdev);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	lowpan_unregister_netdev(entry->netdev);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	/* The entry pointer is deleted by the netdev destructor. */
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic void chan_close_cb(struct l2cap_chan *chan)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry;
7948c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev = NULL;
7958c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
7968c2ecf20Sopenharmony_ci	int err = -ENOENT;
7978c2ecf20Sopenharmony_ci	bool last = false, remove = true;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	BT_DBG("chan %p conn %p", chan, chan->conn);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	if (chan->conn && chan->conn->hcon) {
8028c2ecf20Sopenharmony_ci		if (!is_bt_6lowpan(chan->conn->hcon))
8038c2ecf20Sopenharmony_ci			return;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		/* If conn is set, then the netdev is also there and we should
8068c2ecf20Sopenharmony_ci		 * not remove it.
8078c2ecf20Sopenharmony_ci		 */
8088c2ecf20Sopenharmony_ci		remove = false;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
8148c2ecf20Sopenharmony_ci		dev = lowpan_btle_dev(entry->netdev);
8158c2ecf20Sopenharmony_ci		peer = __peer_lookup_chan(dev, chan);
8168c2ecf20Sopenharmony_ci		if (peer) {
8178c2ecf20Sopenharmony_ci			last = peer_del(dev, peer);
8188c2ecf20Sopenharmony_ci			err = 0;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci			BT_DBG("dev %p removing %speer %p", dev,
8218c2ecf20Sopenharmony_ci			       last ? "last " : "1 ", peer);
8228c2ecf20Sopenharmony_ci			BT_DBG("chan %p orig refcnt %d", chan,
8238c2ecf20Sopenharmony_ci			       kref_read(&chan->kref));
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci			l2cap_chan_put(chan);
8268c2ecf20Sopenharmony_ci			break;
8278c2ecf20Sopenharmony_ci		}
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (!err && last && dev && !atomic_read(&dev->peer_count)) {
8318c2ecf20Sopenharmony_ci		spin_unlock(&devices_lock);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&dev->notify_peers);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci		ifdown(dev->netdev);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci		if (remove) {
8388c2ecf20Sopenharmony_ci			INIT_WORK(&entry->delete_netdev, delete_netdev);
8398c2ecf20Sopenharmony_ci			schedule_work(&entry->delete_netdev);
8408c2ecf20Sopenharmony_ci		}
8418c2ecf20Sopenharmony_ci	} else {
8428c2ecf20Sopenharmony_ci		spin_unlock(&devices_lock);
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	return;
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic void chan_state_change_cb(struct l2cap_chan *chan, int state, int err)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	BT_DBG("chan %p conn %p state %s err %d", chan, chan->conn,
8518c2ecf20Sopenharmony_ci	       state_to_string(state), err);
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan,
8558c2ecf20Sopenharmony_ci					 unsigned long hdr_len,
8568c2ecf20Sopenharmony_ci					 unsigned long len, int nb)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	/* Note that we must allocate using GFP_ATOMIC here as
8598c2ecf20Sopenharmony_ci	 * this function is called originally from netdev hard xmit
8608c2ecf20Sopenharmony_ci	 * function in atomic context.
8618c2ecf20Sopenharmony_ci	 */
8628c2ecf20Sopenharmony_ci	return bt_skb_alloc(hdr_len + len, GFP_ATOMIC);
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic void chan_suspend_cb(struct l2cap_chan *chan)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	BT_DBG("chan %p suspend", chan);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	dev = lookup_dev(chan->conn);
8728c2ecf20Sopenharmony_ci	if (!dev || !dev->netdev)
8738c2ecf20Sopenharmony_ci		return;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	netif_stop_queue(dev->netdev);
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic void chan_resume_cb(struct l2cap_chan *chan)
8798c2ecf20Sopenharmony_ci{
8808c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *dev;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	BT_DBG("chan %p resume", chan);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	dev = lookup_dev(chan->conn);
8858c2ecf20Sopenharmony_ci	if (!dev || !dev->netdev)
8868c2ecf20Sopenharmony_ci		return;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	netif_wake_queue(dev->netdev);
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cistatic long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	return L2CAP_CONN_TIMEOUT;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic const struct l2cap_ops bt_6lowpan_chan_ops = {
8978c2ecf20Sopenharmony_ci	.name			= "L2CAP 6LoWPAN channel",
8988c2ecf20Sopenharmony_ci	.new_connection		= chan_new_conn_cb,
8998c2ecf20Sopenharmony_ci	.recv			= chan_recv_cb,
9008c2ecf20Sopenharmony_ci	.close			= chan_close_cb,
9018c2ecf20Sopenharmony_ci	.state_change		= chan_state_change_cb,
9028c2ecf20Sopenharmony_ci	.ready			= chan_ready_cb,
9038c2ecf20Sopenharmony_ci	.resume			= chan_resume_cb,
9048c2ecf20Sopenharmony_ci	.suspend		= chan_suspend_cb,
9058c2ecf20Sopenharmony_ci	.get_sndtimeo		= chan_get_sndtimeo_cb,
9068c2ecf20Sopenharmony_ci	.alloc_skb		= chan_alloc_skb_cb,
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	.teardown		= l2cap_chan_no_teardown,
9098c2ecf20Sopenharmony_ci	.defer			= l2cap_chan_no_defer,
9108c2ecf20Sopenharmony_ci	.set_shutdown		= l2cap_chan_no_set_shutdown,
9118c2ecf20Sopenharmony_ci};
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_cistatic inline __u8 bdaddr_type(__u8 type)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	if (type == ADDR_LE_DEV_PUBLIC)
9168c2ecf20Sopenharmony_ci		return BDADDR_LE_PUBLIC;
9178c2ecf20Sopenharmony_ci	else
9188c2ecf20Sopenharmony_ci		return BDADDR_LE_RANDOM;
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct l2cap_chan *chan;
9248c2ecf20Sopenharmony_ci	int err;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	chan = chan_create();
9278c2ecf20Sopenharmony_ci	if (!chan)
9288c2ecf20Sopenharmony_ci		return -EINVAL;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	chan->ops = &bt_6lowpan_chan_ops;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
9338c2ecf20Sopenharmony_ci				 addr, dst_type);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	BT_DBG("chan %p err %d", chan, err);
9368c2ecf20Sopenharmony_ci	if (err < 0)
9378c2ecf20Sopenharmony_ci		l2cap_chan_put(chan);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	return err;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	BT_DBG("conn %p dst type %d", conn, dst_type);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	peer = lookup_peer(conn);
9498c2ecf20Sopenharmony_ci	if (!peer)
9508c2ecf20Sopenharmony_ci		return -ENOENT;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	BT_DBG("peer %p chan %p", peer, peer->chan);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	l2cap_chan_close(peer->chan, ENOENT);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	return 0;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic struct l2cap_chan *bt_6lowpan_listen(void)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	bdaddr_t *addr = BDADDR_ANY;
9628c2ecf20Sopenharmony_ci	struct l2cap_chan *chan;
9638c2ecf20Sopenharmony_ci	int err;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	if (!enable_6lowpan)
9668c2ecf20Sopenharmony_ci		return NULL;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	chan = chan_create();
9698c2ecf20Sopenharmony_ci	if (!chan)
9708c2ecf20Sopenharmony_ci		return NULL;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	chan->ops = &bt_6lowpan_chan_ops;
9738c2ecf20Sopenharmony_ci	chan->state = BT_LISTEN;
9748c2ecf20Sopenharmony_ci	chan->src_type = BDADDR_LE_PUBLIC;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	BT_DBG("chan %p src type %d", chan, chan->src_type);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	err = l2cap_add_psm(chan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
9818c2ecf20Sopenharmony_ci	if (err) {
9828c2ecf20Sopenharmony_ci		l2cap_chan_put(chan);
9838c2ecf20Sopenharmony_ci		BT_ERR("psm cannot be added err %d", err);
9848c2ecf20Sopenharmony_ci		return NULL;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	return chan;
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
9918c2ecf20Sopenharmony_ci			  struct l2cap_conn **conn)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	struct hci_conn *hcon;
9948c2ecf20Sopenharmony_ci	struct hci_dev *hdev;
9958c2ecf20Sopenharmony_ci	int n;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
9988c2ecf20Sopenharmony_ci		   &addr->b[5], &addr->b[4], &addr->b[3],
9998c2ecf20Sopenharmony_ci		   &addr->b[2], &addr->b[1], &addr->b[0],
10008c2ecf20Sopenharmony_ci		   addr_type);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (n < 7)
10038c2ecf20Sopenharmony_ci		return -EINVAL;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	/* The LE_PUBLIC address type is ignored because of BDADDR_ANY */
10068c2ecf20Sopenharmony_ci	hdev = hci_get_route(addr, BDADDR_ANY, BDADDR_LE_PUBLIC);
10078c2ecf20Sopenharmony_ci	if (!hdev)
10088c2ecf20Sopenharmony_ci		return -ENOENT;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	hci_dev_lock(hdev);
10118c2ecf20Sopenharmony_ci	hcon = hci_conn_hash_lookup_le(hdev, addr, *addr_type);
10128c2ecf20Sopenharmony_ci	hci_dev_unlock(hdev);
10138c2ecf20Sopenharmony_ci	hci_dev_put(hdev);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (!hcon)
10168c2ecf20Sopenharmony_ci		return -ENOENT;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	*conn = (struct l2cap_conn *)hcon->l2cap_data;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	return 0;
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_cistatic void disconnect_all_peers(void)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry;
10288c2ecf20Sopenharmony_ci	struct lowpan_peer *peer, *tmp_peer, *new_peer;
10298c2ecf20Sopenharmony_ci	struct list_head peers;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&peers);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* We make a separate list of peers as the close_cb() will
10348c2ecf20Sopenharmony_ci	 * modify the device peers list so it is better not to mess
10358c2ecf20Sopenharmony_ci	 * with the same list at the same time.
10368c2ecf20Sopenharmony_ci	 */
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	rcu_read_lock();
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
10418c2ecf20Sopenharmony_ci		list_for_each_entry_rcu(peer, &entry->peers, list) {
10428c2ecf20Sopenharmony_ci			new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
10438c2ecf20Sopenharmony_ci			if (!new_peer)
10448c2ecf20Sopenharmony_ci				break;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci			new_peer->chan = peer->chan;
10478c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&new_peer->list);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci			list_add(&new_peer->list, &peers);
10508c2ecf20Sopenharmony_ci		}
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	rcu_read_unlock();
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
10568c2ecf20Sopenharmony_ci	list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
10578c2ecf20Sopenharmony_ci		l2cap_chan_close(peer->chan, ENOENT);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci		list_del_rcu(&peer->list);
10608c2ecf20Sopenharmony_ci		kfree_rcu(peer, rcu);
10618c2ecf20Sopenharmony_ci	}
10628c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistruct set_enable {
10668c2ecf20Sopenharmony_ci	struct work_struct work;
10678c2ecf20Sopenharmony_ci	bool flag;
10688c2ecf20Sopenharmony_ci};
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic void do_enable_set(struct work_struct *work)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct set_enable *set_enable = container_of(work,
10738c2ecf20Sopenharmony_ci						     struct set_enable, work);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	if (!set_enable->flag || enable_6lowpan != set_enable->flag)
10768c2ecf20Sopenharmony_ci		/* Disconnect existing connections if 6lowpan is
10778c2ecf20Sopenharmony_ci		 * disabled
10788c2ecf20Sopenharmony_ci		 */
10798c2ecf20Sopenharmony_ci		disconnect_all_peers();
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	enable_6lowpan = set_enable->flag;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	mutex_lock(&set_lock);
10848c2ecf20Sopenharmony_ci	if (listen_chan) {
10858c2ecf20Sopenharmony_ci		l2cap_chan_close(listen_chan, 0);
10868c2ecf20Sopenharmony_ci		l2cap_chan_put(listen_chan);
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	listen_chan = bt_6lowpan_listen();
10908c2ecf20Sopenharmony_ci	mutex_unlock(&set_lock);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	kfree(set_enable);
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cistatic int lowpan_enable_set(void *data, u64 val)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci	struct set_enable *set_enable;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	set_enable = kzalloc(sizeof(*set_enable), GFP_KERNEL);
11008c2ecf20Sopenharmony_ci	if (!set_enable)
11018c2ecf20Sopenharmony_ci		return -ENOMEM;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	set_enable->flag = !!val;
11048c2ecf20Sopenharmony_ci	INIT_WORK(&set_enable->work, do_enable_set);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	schedule_work(&set_enable->work);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	return 0;
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_cistatic int lowpan_enable_get(void *data, u64 *val)
11128c2ecf20Sopenharmony_ci{
11138c2ecf20Sopenharmony_ci	*val = enable_6lowpan;
11148c2ecf20Sopenharmony_ci	return 0;
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(lowpan_enable_fops, lowpan_enable_get,
11188c2ecf20Sopenharmony_ci			 lowpan_enable_set, "%llu\n");
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic ssize_t lowpan_control_write(struct file *fp,
11218c2ecf20Sopenharmony_ci				    const char __user *user_buffer,
11228c2ecf20Sopenharmony_ci				    size_t count,
11238c2ecf20Sopenharmony_ci				    loff_t *position)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	char buf[32];
11268c2ecf20Sopenharmony_ci	size_t buf_size = min(count, sizeof(buf) - 1);
11278c2ecf20Sopenharmony_ci	int ret;
11288c2ecf20Sopenharmony_ci	bdaddr_t addr;
11298c2ecf20Sopenharmony_ci	u8 addr_type;
11308c2ecf20Sopenharmony_ci	struct l2cap_conn *conn = NULL;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	if (copy_from_user(buf, user_buffer, buf_size))
11338c2ecf20Sopenharmony_ci		return -EFAULT;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	buf[buf_size] = '\0';
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	if (memcmp(buf, "connect ", 8) == 0) {
11388c2ecf20Sopenharmony_ci		ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn);
11398c2ecf20Sopenharmony_ci		if (ret == -EINVAL)
11408c2ecf20Sopenharmony_ci			return ret;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci		mutex_lock(&set_lock);
11438c2ecf20Sopenharmony_ci		if (listen_chan) {
11448c2ecf20Sopenharmony_ci			l2cap_chan_close(listen_chan, 0);
11458c2ecf20Sopenharmony_ci			l2cap_chan_put(listen_chan);
11468c2ecf20Sopenharmony_ci			listen_chan = NULL;
11478c2ecf20Sopenharmony_ci		}
11488c2ecf20Sopenharmony_ci		mutex_unlock(&set_lock);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci		if (conn) {
11518c2ecf20Sopenharmony_ci			struct lowpan_peer *peer;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci			if (!is_bt_6lowpan(conn->hcon))
11548c2ecf20Sopenharmony_ci				return -EINVAL;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci			peer = lookup_peer(conn);
11578c2ecf20Sopenharmony_ci			if (peer) {
11588c2ecf20Sopenharmony_ci				BT_DBG("6LoWPAN connection already exists");
11598c2ecf20Sopenharmony_ci				return -EALREADY;
11608c2ecf20Sopenharmony_ci			}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci			BT_DBG("conn %p dst %pMR type %d user %d", conn,
11638c2ecf20Sopenharmony_ci			       &conn->hcon->dst, conn->hcon->dst_type,
11648c2ecf20Sopenharmony_ci			       addr_type);
11658c2ecf20Sopenharmony_ci		}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci		ret = bt_6lowpan_connect(&addr, addr_type);
11688c2ecf20Sopenharmony_ci		if (ret < 0)
11698c2ecf20Sopenharmony_ci			return ret;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci		return count;
11728c2ecf20Sopenharmony_ci	}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	if (memcmp(buf, "disconnect ", 11) == 0) {
11758c2ecf20Sopenharmony_ci		ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn);
11768c2ecf20Sopenharmony_ci		if (ret < 0)
11778c2ecf20Sopenharmony_ci			return ret;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci		ret = bt_6lowpan_disconnect(conn, addr_type);
11808c2ecf20Sopenharmony_ci		if (ret < 0)
11818c2ecf20Sopenharmony_ci			return ret;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci		return count;
11848c2ecf20Sopenharmony_ci	}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	return count;
11878c2ecf20Sopenharmony_ci}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_cistatic int lowpan_control_show(struct seq_file *f, void *ptr)
11908c2ecf20Sopenharmony_ci{
11918c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry;
11928c2ecf20Sopenharmony_ci	struct lowpan_peer *peer;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	spin_lock(&devices_lock);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &bt_6lowpan_devices, list) {
11978c2ecf20Sopenharmony_ci		list_for_each_entry(peer, &entry->peers, list)
11988c2ecf20Sopenharmony_ci			seq_printf(f, "%pMR (type %u)\n",
11998c2ecf20Sopenharmony_ci				   &peer->chan->dst, peer->chan->dst_type);
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	spin_unlock(&devices_lock);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	return 0;
12058c2ecf20Sopenharmony_ci}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_cistatic int lowpan_control_open(struct inode *inode, struct file *file)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	return single_open(file, lowpan_control_show, inode->i_private);
12108c2ecf20Sopenharmony_ci}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_cistatic const struct file_operations lowpan_control_fops = {
12138c2ecf20Sopenharmony_ci	.open		= lowpan_control_open,
12148c2ecf20Sopenharmony_ci	.read		= seq_read,
12158c2ecf20Sopenharmony_ci	.write		= lowpan_control_write,
12168c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
12178c2ecf20Sopenharmony_ci	.release	= single_release,
12188c2ecf20Sopenharmony_ci};
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_cistatic void disconnect_devices(void)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry, *tmp, *new_dev;
12238c2ecf20Sopenharmony_ci	struct list_head devices;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&devices);
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	/* We make a separate list of devices because the unregister_netdev()
12288c2ecf20Sopenharmony_ci	 * will call device_event() which will also want to modify the same
12298c2ecf20Sopenharmony_ci	 * devices list.
12308c2ecf20Sopenharmony_ci	 */
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	rcu_read_lock();
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
12358c2ecf20Sopenharmony_ci		new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
12368c2ecf20Sopenharmony_ci		if (!new_dev)
12378c2ecf20Sopenharmony_ci			break;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci		new_dev->netdev = entry->netdev;
12408c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&new_dev->list);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci		list_add_rcu(&new_dev->list, &devices);
12438c2ecf20Sopenharmony_ci	}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	rcu_read_unlock();
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	list_for_each_entry_safe(entry, tmp, &devices, list) {
12488c2ecf20Sopenharmony_ci		ifdown(entry->netdev);
12498c2ecf20Sopenharmony_ci		BT_DBG("Unregistering netdev %s %p",
12508c2ecf20Sopenharmony_ci		       entry->netdev->name, entry->netdev);
12518c2ecf20Sopenharmony_ci		lowpan_unregister_netdev(entry->netdev);
12528c2ecf20Sopenharmony_ci		kfree(entry);
12538c2ecf20Sopenharmony_ci	}
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_cistatic int device_event(struct notifier_block *unused,
12578c2ecf20Sopenharmony_ci			unsigned long event, void *ptr)
12588c2ecf20Sopenharmony_ci{
12598c2ecf20Sopenharmony_ci	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
12608c2ecf20Sopenharmony_ci	struct lowpan_btle_dev *entry;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	if (netdev->type != ARPHRD_6LOWPAN)
12638c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	switch (event) {
12668c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
12678c2ecf20Sopenharmony_ci		spin_lock(&devices_lock);
12688c2ecf20Sopenharmony_ci		list_for_each_entry(entry, &bt_6lowpan_devices, list) {
12698c2ecf20Sopenharmony_ci			if (entry->netdev == netdev) {
12708c2ecf20Sopenharmony_ci				BT_DBG("Unregistered netdev %s %p",
12718c2ecf20Sopenharmony_ci				       netdev->name, netdev);
12728c2ecf20Sopenharmony_ci				list_del(&entry->list);
12738c2ecf20Sopenharmony_ci				break;
12748c2ecf20Sopenharmony_ci			}
12758c2ecf20Sopenharmony_ci		}
12768c2ecf20Sopenharmony_ci		spin_unlock(&devices_lock);
12778c2ecf20Sopenharmony_ci		break;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic struct notifier_block bt_6lowpan_dev_notifier = {
12848c2ecf20Sopenharmony_ci	.notifier_call = device_event,
12858c2ecf20Sopenharmony_ci};
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic int __init bt_6lowpan_init(void)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	lowpan_enable_debugfs = debugfs_create_file_unsafe("6lowpan_enable",
12908c2ecf20Sopenharmony_ci							   0644, bt_debugfs,
12918c2ecf20Sopenharmony_ci							   NULL,
12928c2ecf20Sopenharmony_ci							   &lowpan_enable_fops);
12938c2ecf20Sopenharmony_ci	lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644,
12948c2ecf20Sopenharmony_ci						     bt_debugfs, NULL,
12958c2ecf20Sopenharmony_ci						     &lowpan_control_fops);
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
12988c2ecf20Sopenharmony_ci}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_cistatic void __exit bt_6lowpan_exit(void)
13018c2ecf20Sopenharmony_ci{
13028c2ecf20Sopenharmony_ci	debugfs_remove(lowpan_enable_debugfs);
13038c2ecf20Sopenharmony_ci	debugfs_remove(lowpan_control_debugfs);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	if (listen_chan) {
13068c2ecf20Sopenharmony_ci		l2cap_chan_close(listen_chan, 0);
13078c2ecf20Sopenharmony_ci		l2cap_chan_put(listen_chan);
13088c2ecf20Sopenharmony_ci	}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	disconnect_devices();
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_cimodule_init(bt_6lowpan_init);
13168c2ecf20Sopenharmony_cimodule_exit(bt_6lowpan_exit);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jukka Rissanen <jukka.rissanen@linux.intel.com>");
13198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Bluetooth 6LoWPAN");
13208c2ecf20Sopenharmony_ciMODULE_VERSION(VERSION);
13218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1322