Skip to content
Permalink
Browse files

net: bt: Add multi-link support

This adds support for having multiple connections based on BT_MAX_CONN.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
  • Loading branch information...
Vudentz authored and jukkar committed May 2, 2019
1 parent b2bfcc2 commit 95cadb7bfe27ae1a54f14f2fda4d5ee69cec1500
Showing with 101 additions and 42 deletions.
  1. +1 −0 subsys/net/ip/Kconfig.ipv6
  2. +100 −42 subsys/net/l2/bluetooth/bluetooth.c
@@ -17,6 +17,7 @@ if NET_IPV6

config NET_IF_MAX_IPV6_COUNT
int "Max number of IPv6 network interfaces in the system"
default BT_MAX_CONN if NET_L2_BT
default NET_VLAN_COUNT if NET_VLAN
default 1
help
@@ -37,7 +37,7 @@ LOG_MODULE_REGISTER(net_bt, CONFIG_NET_L2_BT_LOG_LEVEL);
#define L2CAP_IPSP_PSM 0x0023
#define L2CAP_IPSP_MTU 1280

#define CHAN_CTXT(_ch) CONTAINER_OF(_ch, struct bt_context, ipsp_chan.chan)
#define CHAN_CONN(_conn) CONTAINER_OF(_conn, struct bt_if_conn, ipsp_chan.chan)

#if defined(CONFIG_NET_L2_BT_MGMT)
static struct bt_conn *default_conn;
@@ -49,13 +49,17 @@ extern int net_bt_shell_init(void);
#define net_bt_shell_init(...)
#endif

struct bt_context {
struct bt_if_conn {
struct net_if *iface;
struct bt_l2cap_le_chan ipsp_chan;
bt_addr_t src;
bt_addr_t dst;
};

struct bt_context {
struct bt_if_conn conns[CONFIG_BT_MAX_CONN];
};

static enum net_verdict net_bt_recv(struct net_if *iface, struct net_pkt *pkt)
{
NET_DBG("iface %p pkt %p len %zu", iface, pkt, net_pkt_get_len(pkt));
@@ -68,9 +72,23 @@ static enum net_verdict net_bt_recv(struct net_if *iface, struct net_pkt *pkt)
return NET_CONTINUE;
}

static int net_bt_send(struct net_if *iface, struct net_pkt *pkt)
static struct bt_if_conn *net_bt_get_conn(struct net_if *iface)
{
struct bt_context *ctxt = net_if_get_device(iface)->driver_data;
int i;

for (i = 0; i < CONFIG_BT_MAX_CONN; i++) {
if (ctxt->conns[i].iface == iface) {
return &ctxt->conns[i];
}
}

return NULL;
}

static int net_bt_send(struct net_if *iface, struct net_pkt *pkt)
{
struct bt_if_conn *conn = net_bt_get_conn(iface);
struct net_buf *buffer;
int length;
int ret;
@@ -94,7 +112,7 @@ static int net_bt_send(struct net_if *iface, struct net_pkt *pkt)
buffer = pkt->buffer;
pkt->buffer = NULL;

ret = bt_l2cap_chan_send(&ctxt->ipsp_chan.chan, buffer);
ret = bt_l2cap_chan_send(&conn->ipsp_chan.chan, buffer);
if (ret < 0) {
return ret;
}
@@ -106,11 +124,11 @@ static int net_bt_send(struct net_if *iface, struct net_pkt *pkt)

static int net_bt_enable(struct net_if *iface, bool state)
{
struct bt_context *ctxt = net_if_get_device(iface)->driver_data;
struct bt_if_conn *conn = net_bt_get_conn(iface);

NET_DBG("iface %p %s", iface, state ? "up" : "down");

if (state && ctxt->ipsp_chan.chan.state != BT_L2CAP_CONNECTED) {
if (state && conn->ipsp_chan.chan.state != BT_L2CAP_CONNECTED) {
return -ENETDOWN;
}

@@ -127,7 +145,7 @@ NET_L2_INIT(BLUETOOTH_L2, net_bt_recv, net_bt_send,

static void ipsp_connected(struct bt_l2cap_chan *chan)
{
struct bt_context *ctxt = CHAN_CTXT(chan);
struct bt_if_conn *conn = CHAN_CONN(chan);
struct bt_conn_info info;
struct net_linkaddr ll;
struct in6_addr in6;
@@ -150,36 +168,36 @@ static void ipsp_connected(struct bt_l2cap_chan *chan)
}

/* Swap bytes since net APIs expect big endian address */
sys_memcpy_swap(ctxt->src.val, info.le.src->a.val, sizeof(ctxt->src));
sys_memcpy_swap(ctxt->dst.val, info.le.dst->a.val, sizeof(ctxt->dst));
sys_memcpy_swap(conn->src.val, info.le.src->a.val, sizeof(conn->src));
sys_memcpy_swap(conn->dst.val, info.le.dst->a.val, sizeof(conn->dst));

net_if_set_link_addr(ctxt->iface, ctxt->src.val, sizeof(ctxt->src.val),
net_if_set_link_addr(conn->iface, conn->src.val, sizeof(conn->src.val),
NET_LINK_BLUETOOTH);

ll.addr = ctxt->dst.val;
ll.len = sizeof(ctxt->dst.val);
ll.addr = conn->dst.val;
ll.len = sizeof(conn->dst.val);
ll.type = NET_LINK_BLUETOOTH;

/* Add remote link-local address to the nbr cache to avoid sending ns:
* https://tools.ietf.org/html/rfc7668#section-3.2.3
* A Bluetooth LE 6LN MUST NOT register its link-local address.
*/
net_ipv6_addr_create_iid(&in6, &ll);
net_ipv6_nbr_add(ctxt->iface, &in6, &ll, false,
net_ipv6_nbr_add(conn->iface, &in6, &ll, false,
NET_IPV6_NBR_STATE_STATIC);

/* Set iface up */
net_if_up(ctxt->iface);
net_if_up(conn->iface);
}

static void ipsp_disconnected(struct bt_l2cap_chan *chan)
{
struct bt_context *ctxt = CHAN_CTXT(chan);
struct bt_if_conn *conn = CHAN_CONN(chan);

NET_DBG("Channel %p disconnected", chan);

/* Set iface down */
net_if_carrier_down(ctxt->iface);
net_if_carrier_down(conn->iface);

#if defined(CONFIG_NET_L2_BT_MGMT)
if (chan->conn != default_conn) {
@@ -193,34 +211,34 @@ static void ipsp_disconnected(struct bt_l2cap_chan *chan)

static int ipsp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
struct bt_context *ctxt = CHAN_CTXT(chan);
struct bt_if_conn *conn = CHAN_CONN(chan);
struct net_pkt *pkt;

NET_DBG("Incoming data channel %p len %zu", chan,
net_buf_frags_len(buf));

/* Get packet for bearer / protocol related data */
pkt = net_pkt_rx_alloc_on_iface(ctxt->iface, BUF_TIMEOUT);
pkt = net_pkt_rx_alloc_on_iface(conn->iface, BUF_TIMEOUT);
if (!pkt) {
return -ENOMEM;
}

/* Set destination address */
net_pkt_lladdr_dst(pkt)->addr = ctxt->src.val;
net_pkt_lladdr_dst(pkt)->len = sizeof(ctxt->src);
net_pkt_lladdr_dst(pkt)->addr = conn->src.val;
net_pkt_lladdr_dst(pkt)->len = sizeof(conn->src);
net_pkt_lladdr_dst(pkt)->type = NET_LINK_BLUETOOTH;

/* Set source address */
net_pkt_lladdr_src(pkt)->addr = ctxt->dst.val;
net_pkt_lladdr_src(pkt)->len = sizeof(ctxt->dst);
net_pkt_lladdr_src(pkt)->addr = conn->dst.val;
net_pkt_lladdr_src(pkt)->len = sizeof(conn->dst);
net_pkt_lladdr_src(pkt)->type = NET_LINK_BLUETOOTH;

/* Add data buffer as fragment of RX buffer, take a reference while
* doing so since L2CAP will unref the buffer after return.
*/
net_pkt_append_buffer(pkt, net_buf_ref(buf));

if (net_recv_data(ctxt->iface, pkt) < 0) {
if (net_recv_data(conn->iface, pkt) < 0) {
NET_DBG("Packet dropped by NET stack");
net_pkt_unref(pkt);
}
@@ -243,18 +261,36 @@ static struct bt_l2cap_chan_ops ipsp_ops = {
};

static struct bt_context bt_context_data = {
.iface = NULL,
.ipsp_chan.chan.ops = &ipsp_ops,
.ipsp_chan.rx.mtu = L2CAP_IPSP_MTU,
.conns[0 ... (CONFIG_BT_MAX_CONN - 1)] = {
.iface = NULL,
.ipsp_chan.chan.ops = &ipsp_ops,
.ipsp_chan.rx.mtu = L2CAP_IPSP_MTU,
}
};

static void bt_iface_init(struct net_if *iface)
{
struct bt_context *ctxt = net_if_get_device(iface)->driver_data;
struct bt_if_conn *conn = NULL;
int i;

NET_DBG("iface %p", iface);

ctxt->iface = iface;
/* Find unused slot to store the iface */
for (i = 0; i < CONFIG_BT_MAX_CONN; i++) {
if (!ctxt->conns[i].iface) {
conn = &ctxt->conns[i];
NET_DBG("[%d] alloc ctxt %p iface %p", i, ctxt, iface);
break;
}
}

if (!conn) {
NET_ERR("Unable to allocate iface");
return;
}

conn->iface = iface;

#if defined(CONFIG_NET_L2_BT_ZEP1656)
/* Workaround Linux bug, see:
@@ -270,14 +306,26 @@ static struct net_if_api bt_if_api = {

static int ipsp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
{
struct bt_if_conn *if_conn = NULL;
int i;

NET_DBG("Incoming conn %p", conn);

if (bt_context_data.ipsp_chan.chan.conn) {
/* Find unused slot to store the iface */
for (i = 0; i < CONFIG_BT_MAX_CONN; i++) {
if (bt_context_data.conns[i].iface &&
!bt_context_data.conns[i].ipsp_chan.chan.conn) {
if_conn = &bt_context_data.conns[i];
break;
}
}

if (!if_conn) {
NET_ERR("No channels available");
return -ENOMEM;
}

*chan = &bt_context_data.ipsp_chan.chan;
*chan = &if_conn->ipsp_chan.chan;

return 0;
}
@@ -321,22 +369,22 @@ static int bt_advertise(u32_t mgmt_request, struct net_if *iface, void *data,
static int bt_connect(u32_t mgmt_request, struct net_if *iface, void *data,
size_t len)
{
struct bt_context *ctxt = net_if_get_device(iface)->driver_data;
struct bt_if_conn *conn = net_bt_get_conn(iface);
bt_addr_le_t *addr = data;

if (len != sizeof(*addr)) {
NET_ERR("Invalid address");
return -EINVAL;
}

if (ctxt->ipsp_chan.chan.conn) {
if (conn->ipsp_chan.chan.conn) {
NET_ERR("No channels available");
return -ENOMEM;
}

if (default_conn) {
return bt_l2cap_chan_connect(default_conn,
&ctxt->ipsp_chan.chan,
&conn->ipsp_chan.chan,
L2CAP_IPSP_PSM);
}

@@ -379,7 +427,7 @@ static bool eir_found(u8_t type, const u8_t *data, u8_t data_len,

/* TODO: Notify device address found */
net_mgmt_event_notify(NET_EVENT_BT_SCAN_RESULT,
bt_context_data.iface);
bt_context_data.conns[0].iface);

return true;
}
@@ -476,9 +524,9 @@ static int bt_scan(u32_t mgmt_request, struct net_if *iface, void *data,
static int bt_disconnect(u32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct bt_context *ctxt = net_if_get_device(iface)->driver_data;
struct bt_if_conn *conn = net_bt_get_conn(iface);

if (!ctxt->ipsp_chan.chan.conn) {
if (!conn->ipsp_chan.chan.conn) {
NET_ERR("Not connected");
return -ENOTCONN;
}
@@ -489,11 +537,13 @@ static int bt_disconnect(u32_t mgmt_request, struct net_if *iface,
default_conn = NULL;
}

return bt_l2cap_chan_disconnect(&ctxt->ipsp_chan.chan);
return bt_l2cap_chan_disconnect(&conn->ipsp_chan.chan);
}

static void connected(struct bt_conn *conn, u8_t err)
{
int i;

if (err) {
if (CONFIG_NET_L2_BT_LOG_LEVEL >= LOG_LEVEL_DBG) {
char addr[BT_ADDR_LE_STR_LEN];
@@ -512,8 +562,15 @@ static void connected(struct bt_conn *conn, u8_t err)
return;
}

bt_l2cap_chan_connect(conn, &bt_context_data.ipsp_chan.chan,
L2CAP_IPSP_PSM);
for (i = 0; i < CONFIG_BT_MAX_CONN; i++) {
struct bt_if_conn *if_conn = &bt_context_data.conns[i];

if (if_conn->ipsp_chan.chan.conn == conn) {
bt_l2cap_chan_connect(conn, &if_conn->ipsp_chan.chan,
L2CAP_IPSP_PSM);
break;
}
}
}

static void disconnected(struct bt_conn *conn, u8_t reason)
@@ -562,7 +619,8 @@ NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_BT_SCAN, bt_scan);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_BT_DISCONNECT, bt_disconnect);
#endif

NET_DEVICE_INIT(net_bt, "net_bt", net_bt_init, &bt_context_data, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&bt_if_api, BLUETOOTH_L2,
NET_L2_GET_CTX_TYPE(BLUETOOTH_L2), L2CAP_IPSP_MTU);
DEVICE_AND_API_INIT(net_bt, "net_bt", net_bt_init, &bt_context_data, NULL,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&bt_if_api);
NET_L2_DATA_INIT(net_bt, 0, NET_L2_GET_CTX_TYPE(BLUETOOTH_L2));
NET_IF_INIT(net_bt, 0, BLUETOOTH_L2, L2CAP_IPSP_MTU, CONFIG_BT_MAX_CONN);

0 comments on commit 95cadb7

Please sign in to comment.
You can’t perform that action at this time.