// SPDX-License-Identifier: GPL-2.0-only
/*
Copyright (c) 2013-2014 Intel Corp.
*/
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/pkt_sched.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/6lowpan.h> /* for the compression support */
#define VERSION "0.1"
static struct dentry *lowpan_enable_debugfs;
static struct dentry *lowpan_control_debugfs;
#define IFACE_NAME_TEMPLATE "bt%d"
struct skb_cb {
struct in6_addr addr;
struct in6_addr gw;
struct l2cap_chan *chan;
};
#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
/* The devices list contains those devices that we are acting
* as a proxy. The BT 6LoWPAN device is a virtual device that
* connects to the Bluetooth LE device. The real connection to
* BT device is done via l2cap layer. There exists one
* virtual device / one BT 6LoWPAN network (=hciX device).
* The list contains struct lowpan_dev elements.
*/
static LIST_HEAD(bt_6lowpan_devices);
static DEFINE_SPINLOCK(devices_lock);
static bool enable_6lowpan;
/* We are listening incoming connections via this channel
*/
static struct l2cap_chan *listen_chan;
static DEFINE_MUTEX(set_lock);
struct lowpan_peer {
struct list_head list;
struct rcu_head rcu;
struct l2cap_chan *chan;
/* peer addresses in various formats */
unsigned char lladdr[ETH_ALEN];
struct in6_addr peer_addr;
};
struct lowpan_btle_dev {
struct list_head list;
struct hci_dev *hdev;
struct net_device *netdev;
struct list_head peers;
atomic_t peer_count; /* number of items in peers list */
struct work_struct delete_netdev;
struct delayed_work notify_peers;
};
static inline struct lowpan_btle_dev *
lowpan_btle_dev(const struct net_device *netdev)
{
return (struct lowpan_btle_dev *)lowpan_dev(netdev)->priv;
}
static inline void peer_add(struct lowpan_btle_dev *dev,
struct lowpan_peer *peer)
{
list_add_rcu(&peer->list, &dev->peers);
atomic_inc(&dev->peer_count);
}
static inline bool peer_del(struct lowpan_btle_dev *dev,
struct lowpan_peer *peer)
{
list_del_rcu(&peer->list);
kfree_rcu(peer, rcu);
module_put(THIS_MODULE);
if (atomic_dec_and_test(&dev->peer_count)) {
BT_DBG("last peer");
return true;
}
return false;
}
static inline struct lowpan_peer *
__peer_lookup_chan(struct lowpan_btle_dev *dev, struct l2cap_chan *chan)
{
struct lowpan_peer *peer;
list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan == chan)
return peer;
}
return NULL;
}
static inline struct lowpan_peer *
__peer_lookup_conn(struct lowpan_btle_dev *dev, struct l2cap_conn *conn)
{
struct lowpan_peer *peer;
list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan->conn == conn)
return peer;
}