| /* net_driver_bt.c - IP Bluetooth LE driver */ |
| |
| /* |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <nanokernel.h> |
| #include <toolchain.h> |
| #include <sections.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #if defined(CONFIG_STDOUT_CONSOLE) |
| #include <stdio.h> |
| #define PRINT printf |
| #else |
| #include <misc/printk.h> |
| #define PRINT printk |
| #endif |
| |
| #include <net/buf.h> |
| #include <net/ip_buf.h> |
| #include <net/net_core.h> |
| #include <net/net_ip.h> |
| #include <net/net_socket.h> |
| #include "contiki/netstack.h" |
| #include <net_driver_bt.h> |
| |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/uuid.h> |
| #include <bluetooth/l2cap.h> |
| #include <bluetooth/gatt.h> |
| |
| #define L2CAP_IPSP_PSM 0x0023 |
| #define L2CAP_IPSP_MTU IP_BUF_MAX_DATA |
| |
| static inline void memswap(void *dst, const void *src, int len) |
| { |
| int i; |
| |
| for (i = 0; i < len; i++) { |
| ((uint8_t *)dst)[i] = ((uint8_t *)src)[(len - 1) - i]; |
| } |
| } |
| |
| static void ipsp_connected(struct bt_l2cap_chan *chan) |
| { |
| struct bt_conn_info info; |
| char src[BT_ADDR_LE_STR_LEN]; |
| char dst[BT_ADDR_LE_STR_LEN]; |
| linkaddr_t addr; |
| |
| bt_conn_get_info(chan->conn, &info); |
| |
| bt_addr_le_to_str(info.le.src, src, sizeof(src)); |
| bt_addr_le_to_str(info.le.dst, dst, sizeof(dst)); |
| |
| NET_DBG("Channel %p Source %s connected to Destination %s\n", chan, |
| src, dst); |
| |
| /* Swap bytes since net_set_mac expect big endian address */ |
| memswap(addr.u8, info.le.src->a.val, sizeof(addr.u8)); |
| |
| net_set_mac(addr.u8, sizeof(addr)); |
| } |
| |
| static void ipsp_disconnected(struct bt_l2cap_chan *chan) |
| { |
| NET_DBG("Channel %p disconnected\n", chan); |
| } |
| |
| static void ipsp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) |
| { |
| struct bt_conn_info info; |
| linkaddr_t src; |
| linkaddr_t dst; |
| |
| NET_DBG("Incoming data channel %p len %u\n", chan, ip_buf_len(buf)); |
| |
| bt_conn_get_info(chan->conn, &info); |
| |
| /* Swap bytes since linkaddr_copy expect big endian address */ |
| memswap(src.u8, info.le.src->a.val, sizeof(src)); |
| memswap(dst.u8, info.le.dst->a.val, sizeof(dst)); |
| |
| /* Add MAC addresses to the buffer */ |
| linkaddr_copy(&ip_buf_ll_dest(buf), &src); |
| linkaddr_copy(&ip_buf_ll_src(buf), &dst); |
| |
| /* Initialize uip_len */ |
| uip_len(buf) = ip_buf_len(buf); |
| uip_first_frag_len(buf) = 0; |
| |
| /* Uncompress data */ |
| if (!NETSTACK_COMPRESS.uncompress(buf)) { |
| NET_ERR("uncompression failed\n"); |
| return; |
| } |
| |
| /* net_recv takes ownership of the buffer */ |
| net_buf_ref(buf); |
| |
| /* Add buffer to rx_queue */ |
| if (net_recv(buf) < 0) { |
| NET_ERR("input to IP stack failed\n"); |
| net_buf_unref(buf); |
| return; |
| } |
| } |
| |
| static struct net_buf *ipsp_alloc_buf(struct bt_l2cap_chan *chan) |
| { |
| NET_DBG("Channel %p requires buffer\n", chan); |
| |
| return ip_buf_get_reserve_rx(0); |
| } |
| |
| static struct bt_l2cap_chan_ops ipsp_ops = { |
| .alloc_buf = ipsp_alloc_buf, |
| .recv = ipsp_recv, |
| .connected = ipsp_connected, |
| .disconnected = ipsp_disconnected, |
| }; |
| |
| static struct bt_l2cap_chan ipsp_chan = { |
| .ops = &ipsp_ops, |
| .rx.mtu = L2CAP_IPSP_MTU, |
| }; |
| |
| static int ipsp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) |
| { |
| NET_DBG("Incoming conn %p\n", conn); |
| |
| if (ipsp_chan.conn) { |
| NET_ERR("No channels available"); |
| return -ENOMEM; |
| } |
| |
| *chan = &ipsp_chan; |
| |
| return 0; |
| } |
| |
| static struct bt_l2cap_server server = { |
| .psm = L2CAP_IPSP_PSM, |
| .accept = ipsp_accept, |
| }; |
| |
| static struct bt_gatt_attr attrs[] = { |
| /* IPSS Service Declaration */ |
| BT_GATT_PRIMARY_SERVICE(BT_UUID_IPSS), |
| }; |
| |
| static int net_driver_bt_open(void) |
| { |
| bt_gatt_register(attrs, ARRAY_SIZE(attrs)); |
| |
| bt_l2cap_server_register(&server); |
| |
| return 0; |
| } |
| |
| static int net_driver_bt_send(struct net_buf *buf) |
| { |
| #ifdef CONFIG_NETWORKING_WITH_LOGGING |
| int orig_len = ip_buf_len(buf); |
| #endif |
| |
| if (!NETSTACK_COMPRESS.compress(buf)) { |
| NET_DBG("compression failed\n"); |
| ip_buf_unref(buf); |
| return -EINVAL; |
| } |
| |
| NET_DBG("sending %d bytes (original len %d)\n", ip_buf_len(buf), |
| orig_len); |
| |
| return bt_l2cap_chan_send(&ipsp_chan, buf); |
| } |
| |
| static struct net_driver net_driver_bt = { |
| .head_reserve = 0, |
| .open = net_driver_bt_open, |
| .send = net_driver_bt_send, |
| }; |
| |
| int net_driver_bt_init(void) |
| { |
| NETSTACK_COMPRESS.init(); |
| |
| net_register_driver(&net_driver_bt); |
| |
| return 0; |
| } |