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