/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/*
 * Provides HCI transport over sockets. Either over
 * TCP sockets, or Linux bluetooth socket.
 *
 * E.g. to connect to TCP target, start in another window
 * socat -x PIPE:/dev/ttyACM1 TCP4-LISTEN:14433,fork,reuseaddr
 * When building this package, set BLE_SOCK_USE_TCP=1 and
 * BLE_SOCK_TCP_PORT controls the target port this transport
 * connects to.
 *
 * To use Linux Bluetooth sockets, create an interface:
 * sudo hciattach -r -n /dev/ttyUSB0 any 57600
 * And build this package with BLE_SOCK_USE_LINUX_BLUE=1,
 * BLE_SOCK_LINUX_DEV=<interface index of the target interface>
 *
 */
#include "syscfg/syscfg.h"

#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <sys/socket.h>

#if MYNEWT_VAL(BLE_SOCK_USE_TCP)
#include <sys/errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
#include <sys/errno.h>
#define BTPROTO_HCI       1
#define HCI_CHANNEL_RAW	  0
#define HCI_CHANNEL_USER  1
#define HCIDEVUP          _IOW('H', 201, int)
#define HCIDEVDOWN        _IOW('H', 202, int)
#define HCIDEVRESET       _IOW('H', 203, int)
#define HCIGETDEVLIST     _IOR('H', 210, int)

struct sockaddr_hci {
    sa_family_t hci_family;
    unsigned short hci_dev;
    unsigned short hci_channel;
};
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
#include <errno.h>
#include <netpacket/bluetooth.h>
#endif

#include <fcntl.h>
#include <sys/ioctl.h>

#include "sysinit/sysinit.h"
#include "os/os.h"
#include "mem/mem.h"

#include "stats/stats.h"

/* BLE */
#include "nimble/ble.h"
#include "nimble/nimble_opt.h"
#include "nimble/hci_common.h"
#include "nimble/nimble_npl.h"
#include "nimble/ble_hci_trans.h"
#include "socket/ble_hci_socket.h"

/***
 * NOTES:
 * The UART HCI transport doesn't use event buffer priorities.  All incoming
 * and outgoing events use buffers from the same pool.
 *
 * The "skip" definitions are here so that when buffers cannot be allocated,
 * the command or acl packets are simply skipped so that the HCI interface
 * does not lose synchronization and resets dont (necessarily) occur.
 */

STATS_SECT_START(hci_sock_stats)
    STATS_SECT_ENTRY(imsg)
    STATS_SECT_ENTRY(icmd)
    STATS_SECT_ENTRY(ievt)
    STATS_SECT_ENTRY(iacl)
    STATS_SECT_ENTRY(ibytes)
    STATS_SECT_ENTRY(ierr)
    STATS_SECT_ENTRY(imem)
    STATS_SECT_ENTRY(omsg)
    STATS_SECT_ENTRY(oacl)
    STATS_SECT_ENTRY(ocmd)
    STATS_SECT_ENTRY(oevt)
    STATS_SECT_ENTRY(obytes)
    STATS_SECT_ENTRY(oerr)
STATS_SECT_END

STATS_SECT_DECL(hci_sock_stats) hci_sock_stats;
STATS_NAME_START(hci_sock_stats)
    STATS_NAME(hci_sock_stats, imsg)
    STATS_NAME(hci_sock_stats, icmd)
    STATS_NAME(hci_sock_stats, ievt)
    STATS_NAME(hci_sock_stats, iacl)
    STATS_NAME(hci_sock_stats, ibytes)
    STATS_NAME(hci_sock_stats, ierr)
    STATS_NAME(hci_sock_stats, imem)
    STATS_NAME(hci_sock_stats, omsg)
    STATS_NAME(hci_sock_stats, oacl)
    STATS_NAME(hci_sock_stats, ocmd)
    STATS_NAME(hci_sock_stats, oevt)
    STATS_NAME(hci_sock_stats, obytes)
    STATS_NAME(hci_sock_stats, oerr)
STATS_NAME_END(hci_sock_stats)

/***
 * NOTES:
 * The "skip" definitions are here so that when buffers cannot be allocated,
 * the command or acl packets are simply skipped so that the HCI interface
 * does not lose synchronization and resets dont (necessarily) occur.
 */
#define BLE_HCI_UART_H4_NONE        0x00
#define BLE_HCI_UART_H4_CMD         0x01
#define BLE_HCI_UART_H4_ACL         0x02
#define BLE_HCI_UART_H4_SCO         0x03
#define BLE_HCI_UART_H4_EVT         0x04
#define BLE_HCI_UART_H4_SYNC_LOSS   0x80
#define BLE_HCI_UART_H4_SKIP_CMD    0x81
#define BLE_HCI_UART_H4_SKIP_ACL    0x82

#if MYNEWT

#define BLE_SOCK_STACK_SIZE         \
    OS_STACK_ALIGN(MYNEWT_VAL(BLE_SOCK_STACK_SIZE))

struct os_task ble_sock_task;

#endif

static struct os_mempool ble_hci_sock_evt_hi_pool;
static os_membuf_t ble_hci_sock_evt_hi_buf[
        OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT),
                        MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE))
];

static struct os_mempool ble_hci_sock_evt_lo_pool;
static os_membuf_t ble_hci_sock_evt_lo_buf[
        OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT),
                        MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE))
];

static struct os_mempool ble_hci_sock_cmd_pool;
static os_membuf_t ble_hci_sock_cmd_buf[
        OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ)
];

static struct os_mempool ble_hci_sock_acl_pool;
static struct os_mbuf_pool ble_hci_sock_acl_mbuf_pool;

#define ACL_BLOCK_SIZE  OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \
                                 + BLE_MBUF_MEMBLOCK_OVERHEAD \
                                 + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT)
/*
 * The MBUF payload size must accommodate the HCI data header size plus the
 * maximum ACL data packet length. The ACL block size is the size of the
 * mbufs we will allocate.
 */

static os_membuf_t ble_hci_sock_acl_buf[
        OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT),
                        ACL_BLOCK_SIZE)
];

static ble_hci_trans_rx_cmd_fn *ble_hci_sock_rx_cmd_cb;
static void *ble_hci_sock_rx_cmd_arg;
static ble_hci_trans_rx_acl_fn *ble_hci_sock_rx_acl_cb;
static void *ble_hci_sock_rx_acl_arg;

static struct ble_hci_sock_state {
    int sock;
    struct ble_npl_eventq evq;
    struct ble_npl_event ev;
    struct ble_npl_callout timer;

    uint16_t rx_off;
    uint8_t rx_data[512];
} ble_hci_sock_state;

#if MYNEWT_VAL(BLE_SOCK_USE_TCP)
static int s_ble_hci_device = MYNEWT_VAL(BLE_SOCK_TCP_PORT);
#elif MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
static int s_ble_hci_device = MYNEWT_VAL(BLE_SOCK_LINUX_DEV);
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
static int s_ble_hci_device = 0;
#endif

/**
 * Allocates a buffer (mbuf) for ACL operation.
 *
 * @return                      The allocated buffer on success;
 *                              NULL on buffer exhaustion.
 */
static struct os_mbuf *
ble_hci_trans_acl_buf_alloc(void)
{
    struct os_mbuf *m;

    /*
     * XXX: note that for host only there would be no need to allocate
     * a user header. Address this later.
     */
    m = os_mbuf_get_pkthdr(&ble_hci_sock_acl_mbuf_pool,
                           sizeof(struct ble_mbuf_hdr));
    return m;
}

#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
static int
ble_hci_sock_acl_tx(struct os_mbuf *om)
{
    struct msghdr msg;
    struct iovec iov[8];
    int i;
    struct os_mbuf *m;
    uint8_t ch;

    memset(&msg, 0, sizeof(msg));
    memset(iov, 0, sizeof(iov));

    msg.msg_iov = iov;

    ch = BLE_HCI_UART_H4_ACL;
    iov[0].iov_len = 1;
    iov[0].iov_base = &ch;
    i = 1;
    for (m = om; m; m = SLIST_NEXT(m, om_next)) {
        iov[i].iov_base = m->om_data;
        iov[i].iov_len = m->om_len;
        i++;
    }
    msg.msg_iovlen = i;

    STATS_INC(hci_sock_stats, omsg);
    STATS_INC(hci_sock_stats, oacl);
    STATS_INCN(hci_sock_stats, obytes, OS_MBUF_PKTLEN(om) + 1);
    i = sendmsg(ble_hci_sock_state.sock, &msg, 0);
    os_mbuf_free_chain(om);
    if (i != OS_MBUF_PKTLEN(om) + 1) {
        if (i < 0) {
            dprintf(1, "sendmsg() failed : %d\n", errno);
        } else {
            dprintf(1, "sendmsg() partial write: %d\n", i);
        }
        STATS_INC(hci_sock_stats, oerr);
        return BLE_ERR_MEM_CAPACITY;
    }
    return 0;
}
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
static int
ble_hci_sock_acl_tx(struct os_mbuf *om)
{
    size_t len;
    uint8_t *buf;
    int i;
    struct os_mbuf *m;
    struct sockaddr_hci addr;

    addr.hci_family = AF_BLUETOOTH;
    addr.hci_channel = HCI_CHANNEL_RAW;
    addr.hci_dev = 0;

    memcpy(&addr, &addr, sizeof(struct sockaddr_hci));

    len = 1;

    for (m = om; m; m = SLIST_NEXT(m, om_next)) {
        len += m->om_len;
    }

    buf = (uint8_t *)malloc(len);

    buf[0] = BLE_HCI_UART_H4_ACL;

    i = 1;
    for (m = om; m; m = SLIST_NEXT(m, om_next)) {
        memcpy(&buf[i], m->om_data, m->om_len);
        i += m->om_len;
    }

    STATS_INC(hci_sock_stats, omsg);
    STATS_INC(hci_sock_stats, oacl);
    STATS_INCN(hci_sock_stats, obytes, OS_MBUF_PKTLEN(om) + 1);

    i = sendto(ble_hci_sock_state.sock, buf, len, 0,
               (struct sockaddr *)&addr, sizeof(struct sockaddr_hci));

    free(buf);

    os_mbuf_free_chain(om);
    if (i != OS_MBUF_PKTLEN(om) + 1) {
        if (i < 0) {
            dprintf(1, "sendto() failed : %d\n", errno);
        } else {
            dprintf(1, "sendto() partial write: %d\n", i);
        }
        STATS_INC(hci_sock_stats, oerr);
        return BLE_ERR_MEM_CAPACITY;
    }
    return 0;
}
#endif

#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
static int
ble_hci_sock_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type)
{
    uint8_t btaddr[6];
    struct msghdr msg;
    struct iovec iov[8];
    int len;
    int i;
    uint8_t ch;

    memset(&msg, 0, sizeof(msg));
    memset(iov, 0, sizeof(iov));

    msg.msg_iov = iov;
    msg.msg_iovlen = 2;

    ch = h4_type;
    iov[0].iov_len = 1;
    iov[0].iov_base = &ch;
    iov[1].iov_base = hci_ev;
    if (h4_type == BLE_HCI_UART_H4_CMD) {
        len = sizeof(struct ble_hci_cmd) + hci_ev[2];
        STATS_INC(hci_sock_stats, ocmd);
    } else if (h4_type == BLE_HCI_UART_H4_EVT) {
        len = sizeof(struct ble_hci_ev) + hci_ev[1];
        STATS_INC(hci_sock_stats, oevt);
    } else {
        assert(0);
    }
    iov[1].iov_len = len;

    STATS_INC(hci_sock_stats, omsg);
    STATS_INCN(hci_sock_stats, obytes, len + 1);

    i = sendmsg(ble_hci_sock_state.sock, &msg, 0);
    ble_hci_trans_buf_free(hci_ev);
    if (i != len + 1) {
        if (i < 0) {
            dprintf(1, "sendmsg() failed : %d\n", errno);
        } else {
            dprintf(1, "sendmsg() partial write: %d\n", i);
        }
        STATS_INC(hci_sock_stats, oerr);
        return BLE_ERR_MEM_CAPACITY;
    }

    return 0;
}
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
static int
ble_hci_sock_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type)
{
    uint8_t *buf;
    size_t len;
    struct sockaddr_hci addr;
    int i;

    addr.hci_family = AF_BLUETOOTH;
    addr.hci_channel = HCI_CHANNEL_RAW;
    addr.hci_dev = 0;

    memcpy(&addr, &addr, sizeof(struct sockaddr_hci));

    if (h4_type == BLE_HCI_UART_H4_CMD) {
        len = sizeof(struct ble_hci_cmd) + hci_ev[2];
        STATS_INC(hci_sock_stats, ocmd);
    } else if (h4_type == BLE_HCI_UART_H4_EVT) {
        len = sizeof(struct ble_hci_ev) + hci_ev[1];
        STATS_INC(hci_sock_stats, oevt);
    } else {
        assert(0);
    }

    STATS_INC(hci_sock_stats, omsg);
    STATS_INCN(hci_sock_stats, obytes, len + 1);

    buf = (uint8_t *)malloc(len + 1);

    buf[0] = h4_type;
    memcpy(&buf[1], hci_ev, len);

    i = sendto(ble_hci_sock_state.sock, buf, len + 1, 0,
               (struct sockaddr *)&addr, sizeof(struct sockaddr_hci));

    free(buf);
    ble_hci_trans_buf_free(hci_ev);
    if (i != len + 1) {
        if (i < 0) {
            dprintf(1, "sendto() failed : %d\n", errno);
        } else {
            dprintf(1, "sendto() partial write: %d\n", i);
        }
        STATS_INC(hci_sock_stats, oerr);
        return BLE_ERR_MEM_CAPACITY;
    }

    return 0;
}
#endif

static int
ble_hci_sock_rx_msg(void)
{
    struct ble_hci_sock_state *bhss;
    int len;
    struct os_mbuf *m;
    uint8_t *data;
    int sr;
    int rc;

    bhss = &ble_hci_sock_state;
    if (bhss->sock < 0) {
        return -1;
    }
    len = read(bhss->sock, bhss->rx_data + bhss->rx_off,
               sizeof(bhss->rx_data) - bhss->rx_off);
    if (len < 0) {
        return -2;
    }
    if (len == 0) {
        return -1;
    }
    bhss->rx_off += len;
    STATS_INCN(hci_sock_stats, ibytes, len);

    while (bhss->rx_off > 0) {
        switch (bhss->rx_data[0]) {
#if MYNEWT_VAL(BLE_CONTROLLER)
        case BLE_HCI_UART_H4_CMD:
            if (bhss->rx_off < sizeof(struct ble_hci_cmd)) {
                return -1;
            }
            len = 1 + sizeof(struct ble_hci_cmd) + bhss->rx_data[3];
            if (bhss->rx_off < len) {
                return -1;
            }
            STATS_INC(hci_sock_stats, imsg);
            STATS_INC(hci_sock_stats, icmd);
            data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
            if (!data) {
                STATS_INC(hci_sock_stats, ierr);
                break;
            }
            memcpy(data, &bhss->rx_data[1], len - 1);
            OS_ENTER_CRITICAL(sr);
            rc = ble_hci_sock_rx_cmd_cb(data, ble_hci_sock_rx_cmd_arg);
            OS_EXIT_CRITICAL(sr);
            if (rc) {
                ble_hci_trans_buf_free(data);
                STATS_INC(hci_sock_stats, ierr);
                break;
            }
            break;
#endif
#if MYNEWT_VAL(BLE_HOST)
        case BLE_HCI_UART_H4_EVT:
            if (bhss->rx_off < sizeof(struct ble_hci_ev)) {
                return -1;
            }
            len = 1 + sizeof(struct ble_hci_ev) + bhss->rx_data[2];
            if (bhss->rx_off < len) {
                return -1;
            }
            STATS_INC(hci_sock_stats, imsg);
            STATS_INC(hci_sock_stats, ievt);
            data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
            if (!data) {
                STATS_INC(hci_sock_stats, ierr);
                break;
            }
            memcpy(data, &bhss->rx_data[1], len - 1);
            OS_ENTER_CRITICAL(sr);
            rc = ble_hci_sock_rx_cmd_cb(data, ble_hci_sock_rx_cmd_arg);
            OS_EXIT_CRITICAL(sr);
            if (rc) {
                ble_hci_trans_buf_free(data);
                STATS_INC(hci_sock_stats, ierr);
                return 0;
            }
            break;
#endif
        case BLE_HCI_UART_H4_ACL:
            if (bhss->rx_off < BLE_HCI_DATA_HDR_SZ) {
                return -1;
            }
            len = 1 + BLE_HCI_DATA_HDR_SZ + (bhss->rx_data[4] << 8) +
              bhss->rx_data[3];
            if (bhss->rx_off < len) {
                return -1;
            }
            STATS_INC(hci_sock_stats, imsg);
            STATS_INC(hci_sock_stats, iacl);
            m = ble_hci_trans_acl_buf_alloc();
            if (!m) {
                STATS_INC(hci_sock_stats, imem);
                break;
            }
            if (os_mbuf_append(m, &bhss->rx_data[1], len - 1)) {
                STATS_INC(hci_sock_stats, imem);
                os_mbuf_free_chain(m);
                break;
            }
            OS_ENTER_CRITICAL(sr);
            ble_hci_sock_rx_acl_cb(m, ble_hci_sock_rx_acl_arg);
            OS_EXIT_CRITICAL(sr);
            break;
        default:
            STATS_INC(hci_sock_stats, ierr);
            break;
        }

        memmove(bhss->rx_data, &bhss->rx_data[len], bhss->rx_off - len);
        bhss->rx_off -= len;
    }
    return 0;
}

static void
ble_hci_sock_rx_ev(struct ble_npl_event *ev)
{
    int rc;
    ble_npl_time_t timeout;

    rc = ble_hci_sock_rx_msg();
    if (rc == 0) {
        ble_npl_eventq_put(&ble_hci_sock_state.evq, &ble_hci_sock_state.ev);
    } else {
        rc = ble_npl_time_ms_to_ticks(10, &timeout);
        ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout);
    }
}

#if MYNEWT_VAL(BLE_SOCK_USE_TCP)
static int
ble_hci_sock_config(void)
{
    struct ble_hci_sock_state *bhss = &ble_hci_sock_state;
    struct sockaddr_in sin;
    ble_npl_time_t timeout;
    int s;
    int rc;

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(s_ble_hci_device);
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
#ifdef MN_OSX
    sin.sin_len = sizeof(sin);
#endif

#if 0
    if (bhss->sock >= 0) {
        close(bhss->sock);
        bhss->sock = -1;
    }
#endif
    if (bhss->sock < 0) {
        s = socket(PF_INET, SOCK_STREAM, 0);
        if (s < 0) {
            goto err;
        }

        rc = connect(s, (struct sockaddr *)&sin, sizeof(sin));
        if (rc) {
            dprintf(1, "connect() failed: %d\n", errno);
            goto err;
        }

        rc = 1;

        rc = ioctl(s, FIONBIO, (char *)&rc);
        if (rc) {
            goto err;
        }
        bhss->sock = s;
    }
    rc = ble_npl_time_ms_to_ticks(10, &timeout);
    if (rc) {
        goto err;
    }
    ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout);

    return 0;
err:
    if (s >= 0) {
        close(s);
    }
    return BLE_ERR_HW_FAIL;
}
#endif

#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
static int
ble_hci_sock_config(void)
{
    struct sockaddr_hci shci;
    int s;
    int rc;
    ble_npl_time_t timeout;

    memset(&shci, 0, sizeof(shci));
    shci.hci_family = AF_BLUETOOTH;
    shci.hci_dev = s_ble_hci_device;
    shci.hci_channel = HCI_CHANNEL_USER;

    if (ble_hci_sock_state.sock >= 0) {
        close(ble_hci_sock_state.sock);
        ble_hci_sock_state.sock = -1;
    }

    s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
    if (s < 0) {
        dprintf(1, "socket() failed %d\n", errno);
        goto err;
    }

    /* 
     * HCI User Channel requires exclusive access to the device.
     * The device has to be down at the time of binding.
     */
    ioctl(s, HCIDEVDOWN, shci.hci_dev);

    rc = bind(s, (struct sockaddr *)&shci, sizeof(shci));
    if (rc) {
        dprintf(1, "bind() failed %d hci%d\n", errno, shci.hci_dev);
        goto err;
    }

    rc = 1;

    rc = ioctl(s, FIONBIO, (char *)&rc);
    if (rc) {
        goto err;
    }
    ble_hci_sock_state.sock = s;

    rc = ble_npl_time_ms_to_ticks(10, &timeout);
    if (rc) {
        goto err;
    }
    ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout);

    return 0;
err:
    if (s >= 0) {
        close(s);
    }
    return BLE_ERR_HW_FAIL;
}
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
static int
ble_hci_sock_config(void)
{
    struct sockaddr_hci shci;
    int s;
    int rc;
    ble_npl_time_t timeout;

    memset(&shci, 0, sizeof(shci));
    shci.hci_family = AF_BLUETOOTH;
    shci.hci_dev = 0;
    shci.hci_channel = HCI_CHANNEL_RAW;

    if (ble_hci_sock_state.sock >= 0) {
        close(ble_hci_sock_state.sock);
        ble_hci_sock_state.sock = -1;
    }

    s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
    if (s < 0) {
        dprintf(1, "socket() failed %d\n", errno);
        goto err;
    }

    rc = bind(s, (struct sockaddr *)&shci, sizeof(shci));
    if (rc) {
        dprintf(1, "bind() failed %d hci%d\n", errno, shci.hci_dev);
        goto err;
    }

    ble_hci_sock_state.sock = s;

    rc = ble_npl_time_ms_to_ticks(10, &timeout);
    if (rc) {
        goto err;
    }
    ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout);

    return 0;
err:
    if (s >= 0) {
        close(s);
    }
    return BLE_ERR_HW_FAIL;
}
#endif

/**
 * Sends an HCI event from the controller to the host.
 *
 * @param cmd                   The HCI event to send.  This buffer must be
 *                                  allocated via ble_hci_trans_buf_alloc().
 *
 * @return                      0 on success;
 *                              A BLE_ERR_[...] error code on failure.
 */
int
ble_hci_trans_ll_evt_tx(uint8_t *cmd)
{
    return ble_hci_sock_cmdevt_tx(cmd, BLE_HCI_UART_H4_EVT);
}

/**
 * Sends ACL data from controller to host.
 *
 * @param om                    The ACL data packet to send.
 *
 * @return                      0 on success;
 *                              A BLE_ERR_[...] error code on failure.
 */
int
ble_hci_trans_ll_acl_tx(struct os_mbuf *om)
{
    return ble_hci_sock_acl_tx(om);
}

/**
 * Sends an HCI command from the host to the controller.
 *
 * @param cmd                   The HCI command to send.  This buffer must be
 *                                  allocated via ble_hci_trans_buf_alloc().
 *
 * @return                      0 on success;
 *                              A BLE_ERR_[...] error code on failure.
 */
int
ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
{
    return ble_hci_sock_cmdevt_tx(cmd, BLE_HCI_UART_H4_CMD);
}

/**
 * Sends ACL data from host to controller.
 *
 * @param om                    The ACL data packet to send.
 *
 * @return                      0 on success;
 *                              A BLE_ERR_[...] error code on failure.
 */
int
ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
{
    return ble_hci_sock_acl_tx(om);
}

/**
 * Configures the HCI transport to call the specified callback upon receiving
 * HCI packets from the controller.  This function should only be called by by
 * host.
 *
 * @param cmd_cb                The callback to execute upon receiving an HCI
 *                                  event.
 * @param cmd_arg               Optional argument to pass to the command
 *                                  callback.
 * @param acl_cb                The callback to execute upon receiving ACL
 *                                  data.
 * @param acl_arg               Optional argument to pass to the ACL
 *                                  callback.
 */
void
ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
                     void *cmd_arg,
                     ble_hci_trans_rx_acl_fn *acl_cb,
                     void *acl_arg)
{
    ble_hci_sock_rx_cmd_cb = cmd_cb;
    ble_hci_sock_rx_cmd_arg = cmd_arg;
    ble_hci_sock_rx_acl_cb = acl_cb;
    ble_hci_sock_rx_acl_arg = acl_arg;
}

/**
 * Configures the HCI transport to operate with a host.  The transport will
 * execute specified callbacks upon receiving HCI packets from the controller.
 *
 * @param cmd_cb                The callback to execute upon receiving an HCI
 *                                  event.
 * @param cmd_arg               Optional argument to pass to the command
 *                                  callback.
 * @param acl_cb                The callback to execute upon receiving ACL
 *                                  data.
 * @param acl_arg               Optional argument to pass to the ACL
 *                                  callback.
 */
void
ble_hci_trans_cfg_ll(ble_hci_trans_rx_cmd_fn *cmd_cb,
                     void *cmd_arg,
                     ble_hci_trans_rx_acl_fn *acl_cb,
                     void *acl_arg)
{
    ble_hci_sock_rx_cmd_cb = cmd_cb;
    ble_hci_sock_rx_cmd_arg = cmd_arg;
    ble_hci_sock_rx_acl_cb = acl_cb;
    ble_hci_sock_rx_acl_arg = acl_arg;
}

/**
 * Allocates a flat buffer of the specified type.
 *
 * @param type                  The type of buffer to allocate; one of the
 *                                  BLE_HCI_TRANS_BUF_[...] constants.
 *
 * @return                      The allocated buffer on success;
 *                              NULL on buffer exhaustion.
 */
uint8_t *
ble_hci_trans_buf_alloc(int type)
{
    uint8_t *buf;

    switch (type) {
    case BLE_HCI_TRANS_BUF_CMD:
        buf = os_memblock_get(&ble_hci_sock_cmd_pool);
        break;
    case BLE_HCI_TRANS_BUF_EVT_HI:
        buf = os_memblock_get(&ble_hci_sock_evt_hi_pool);
        if (buf == NULL) {
            /* If no high-priority event buffers remain, try to grab a
             * low-priority one.
             */
            buf = os_memblock_get(&ble_hci_sock_evt_lo_pool);
        }
        break;

    case BLE_HCI_TRANS_BUF_EVT_LO:
        buf = os_memblock_get(&ble_hci_sock_evt_lo_pool);
        break;

    default:
        assert(0);
        buf = NULL;
    }

    return buf;
}

/**
 * Frees the specified flat buffer.  The buffer must have been allocated via
 * ble_hci_trans_buf_alloc().
 *
 * @param buf                   The buffer to free.
 */
void
ble_hci_trans_buf_free(uint8_t *buf)
{
    int rc;

    /*
     * XXX: this may look a bit odd, but the controller uses the command
     * buffer to send back the command complete/status as an immediate
     * response to the command. This was done to insure that the controller
     * could always send back one of these events when a command was received.
     * Thus, we check to see which pool the buffer came from so we can free
     * it to the appropriate pool
     */
    if (os_memblock_from(&ble_hci_sock_evt_hi_pool, buf)) {
        rc = os_memblock_put(&ble_hci_sock_evt_hi_pool, buf);
        assert(rc == 0);
    } else if (os_memblock_from(&ble_hci_sock_evt_lo_pool, buf)) {
        rc = os_memblock_put(&ble_hci_sock_evt_lo_pool, buf);
        assert(rc == 0);
    } else {
        assert(os_memblock_from(&ble_hci_sock_cmd_pool, buf));
        rc = os_memblock_put(&ble_hci_sock_cmd_pool, buf);
        assert(rc == 0);
    }
}

/**
 * Resets the HCI UART transport to a clean state.  Frees all buffers and
 * reconfigures the UART.
 *
 * @return                      0 on success;
 *                              A BLE_ERR_[...] error code on failure.
 */
int
ble_hci_trans_reset(void)
{
    int rc;

    ble_npl_callout_stop(&ble_hci_sock_state.timer);

    /* Reopen the UART. */
    rc = ble_hci_sock_config();
    if (rc != 0) {
        dprintf(1, "Failure restarting socket HCI\n");
        return rc;
    }

    return 0;
}

void
ble_hci_sock_ack_handler(void *arg)
{
    struct ble_npl_event *ev;

    while (1) {
        ev = ble_npl_eventq_get(&ble_hci_sock_state.evq, BLE_NPL_TIME_FOREVER);
        ble_npl_event_run(ev);
    }
}

static void
ble_hci_sock_init_task(void)
{
    ble_npl_eventq_init(&ble_hci_sock_state.evq);
    ble_npl_callout_stop(&ble_hci_sock_state.timer);
    ble_npl_callout_init(&ble_hci_sock_state.timer, &ble_hci_sock_state.evq,
                    ble_hci_sock_rx_ev, NULL);

#if MYNEWT
    {
        os_stack_t *pstack;

        pstack = malloc(sizeof(os_stack_t)*BLE_SOCK_STACK_SIZE);
        assert(pstack);
        os_task_init(&ble_sock_task, "hci_sock", ble_hci_sock_ack_handler, NULL,
                     MYNEWT_VAL(BLE_SOCK_TASK_PRIO), BLE_NPL_TIME_FOREVER, pstack,
                     BLE_SOCK_STACK_SIZE);
    }
#else
/*
 * For non-Mynewt OS it is required that OS creates task for HCI SOCKET
 * to run ble_hci_sock_ack_handler.
 */

#endif

}

void
ble_hci_sock_set_device(int dev)
{
    s_ble_hci_device = dev;
}

/**
 * Initializes the UART HCI transport module.
 *
 * @return                      0 on success;
 *                              A BLE_ERR_[...] error code on failure.
 */
void
ble_hci_sock_init(void)
{
    int rc;

    /* Ensure this function only gets called by sysinit. */
    SYSINIT_ASSERT_ACTIVE();

    memset(&ble_hci_sock_state, 0, sizeof(ble_hci_sock_state));
    ble_hci_sock_state.sock = -1;

    ble_hci_sock_init_task();
    ble_npl_event_init(&ble_hci_sock_state.ev, ble_hci_sock_rx_ev, NULL);

    rc = os_mempool_init(&ble_hci_sock_acl_pool,
                         MYNEWT_VAL(BLE_ACL_BUF_COUNT),
                         ACL_BLOCK_SIZE,
                         ble_hci_sock_acl_buf,
                         "ble_hci_sock_acl_pool");
    SYSINIT_PANIC_ASSERT(rc == 0);

    rc = os_mbuf_pool_init(&ble_hci_sock_acl_mbuf_pool,
                           &ble_hci_sock_acl_pool,
                           ACL_BLOCK_SIZE,
                           MYNEWT_VAL(BLE_ACL_BUF_COUNT));
    SYSINIT_PANIC_ASSERT(rc == 0);

    /*
     * Create memory pool of HCI command buffers. NOTE: we currently dont
     * allow this to be configured. The controller will only allow one
     * outstanding command. We decided to keep this a pool in case we allow
     * allow the controller to handle more than one outstanding command.
     */
    rc = os_mempool_init(&ble_hci_sock_cmd_pool,
                         1,
                         BLE_HCI_TRANS_CMD_SZ,
                         &ble_hci_sock_cmd_buf,
                         "ble_hci_sock_cmd_pool");
    SYSINIT_PANIC_ASSERT(rc == 0);

    rc = os_mempool_init(&ble_hci_sock_evt_hi_pool,
                         MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT),
                         MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
                         &ble_hci_sock_evt_hi_buf,
                         "ble_hci_sock_evt_hi_pool");
    SYSINIT_PANIC_ASSERT(rc == 0);

    rc = os_mempool_init(&ble_hci_sock_evt_lo_pool,
                         MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT),
                         MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
                         ble_hci_sock_evt_lo_buf,
                         "ble_hci_sock_evt_lo_pool");
    SYSINIT_PANIC_ASSERT(rc == 0);

    rc = ble_hci_sock_config();
    SYSINIT_PANIC_ASSERT_MSG(rc == 0, "Failure configuring socket HCI");

    rc = stats_init_and_reg(STATS_HDR(hci_sock_stats),
                            STATS_SIZE_INIT_PARMS(hci_sock_stats, STATS_SIZE_32),
                            STATS_NAME_INIT_PARMS(hci_sock_stats), "hci_socket");
    SYSINIT_PANIC_ASSERT(rc == 0);
}
