576 lines
16 KiB
C
576 lines
16 KiB
C
|
/*
|
||
|
* Copyright 2016 DiUS Computing Pty Ltd. All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
*
|
||
|
* - Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* - Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the
|
||
|
* distribution.
|
||
|
* - Neither the name of the copyright holders nor the names of
|
||
|
* its contributors may be used to endorse or promote products derived
|
||
|
* from this software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||
|
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*
|
||
|
* @author Johny Mattsson <jmattsson@dius.com.au>
|
||
|
*/
|
||
|
|
||
|
#include "module.h"
|
||
|
#include "lauxlib.h"
|
||
|
#include "task/task.h"
|
||
|
#include "platform.h"
|
||
|
#include "bt.h"
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#define BD_ADDR_LEN 6
|
||
|
typedef uint8_t bd_addr_t[BD_ADDR_LEN];
|
||
|
|
||
|
#define st(x) do { x } while (0)
|
||
|
|
||
|
#define STREAM_U8(p, v) st( *(p)++ = (uint8_t)(v); )
|
||
|
#define STREAM_U16(p, v) \
|
||
|
st( \
|
||
|
*(p)++ = (uint8_t)(v); \
|
||
|
*(p)++ = (uint8_t)((v) >> 8); \
|
||
|
)
|
||
|
#define STREAM_BD_ADDR(p, addr) \
|
||
|
st( \
|
||
|
for (int i = 0; i < BD_ADDR_LEN; ++i) \
|
||
|
*(p)++ = (uint8_t)addr[BD_ADDR_LEN - 1 - i];\
|
||
|
)
|
||
|
#define STREAM_ARRAY(p, arr, len) \
|
||
|
st( \
|
||
|
for (int i = 0; i < len; ++i) \
|
||
|
*(p)++ = (uint8_t)arr[i]; \
|
||
|
)
|
||
|
|
||
|
|
||
|
enum {
|
||
|
H4_TYPE_COMMAND = 1,
|
||
|
H4_TYPE_ACL = 2,
|
||
|
H4_TYPE_SCO = 3,
|
||
|
H4_TYPE_EVENT = 4
|
||
|
};
|
||
|
|
||
|
#define EVENT_COMMAND_COMPLETE 0x0e
|
||
|
#define EVENT_LE_META 0x3e
|
||
|
|
||
|
// Subevent codes for LE-Meta
|
||
|
#define EVENT_LE_META_ADV_REPORT 0x02
|
||
|
|
||
|
#define ERROR_UNSPECIFIED 0x1f
|
||
|
|
||
|
#define HCI_OGF(x) (x << 10)
|
||
|
|
||
|
#define HCI_GRP_HOST_CONT_BASEBAND_CMDS HCI_OGF(0x03)
|
||
|
#define HCI_GRP_BLE_CMDS HCI_OGF(0x08)
|
||
|
|
||
|
#define HCI_RESET (HCI_GRP_HOST_CONT_BASEBAND_CMDS | 0x0003)
|
||
|
#define HCI_SET_EVENT_MASK (HCI_GRP_HOST_CONT_BASEBAND_CMDS | 0x0001)
|
||
|
#define HCI_BLE_ADV_WRI_PARAMS (HCI_GRP_BLE_CMDS | 0x0006)
|
||
|
#define HCI_BLE_ADV_WRI_DATA (HCI_GRP_BLE_CMDS | 0x0008)
|
||
|
#define HCI_BLE_ADV_WRI_ENABLE (HCI_GRP_BLE_CMDS | 0x000a)
|
||
|
#define HCI_BLE_SET_SCAN_PARAMS (HCI_GRP_BLE_CMDS | 0x000b)
|
||
|
#define HCI_BLE_SET_SCAN_ENABLE (HCI_GRP_BLE_CMDS | 0x000c)
|
||
|
|
||
|
#define SZ_HDR 4
|
||
|
|
||
|
#define SZ_HCI_RESET 4
|
||
|
#define SZ_HCI_BLE_ADV_WRI_PARAMS 19
|
||
|
#define SZ_HCI_BLE_ADV_WRI_DATA 36
|
||
|
#define SZ_HCI_BLE_ADV_WRI_ENABLE 5
|
||
|
#define SZ_HCI_BLE_SET_SCAN_ENABLE 6
|
||
|
#define SZ_HCI_BLE_SET_SCAN_PARAMS 11
|
||
|
|
||
|
#define ADV_MIN_INTERVAL 0x00a0
|
||
|
#define ADV_MAX_DATA 31
|
||
|
|
||
|
enum {
|
||
|
ADV_IND = 0x00,
|
||
|
ADV_DIRECT_IND_HI_DUTY = 0x01,
|
||
|
ADV_SCAN_IND = 0x02,
|
||
|
ADV_NONCONN_IND = 0x03,
|
||
|
ADV_DIRECT_IND_LO_DUTY = 0x04
|
||
|
};
|
||
|
enum {
|
||
|
ADV_OWN_ADDR_PUB = 0x00,
|
||
|
ADV_OWN_ADDR_RAND = 0x01,
|
||
|
ADV_OWN_ADDR_PRIV_OR_PUB = 0x02,
|
||
|
ADV_OWN_ADDR_PRIV_OR_RAND = 0x03
|
||
|
};
|
||
|
enum {
|
||
|
ADV_PEER_ADDR_PUB = 0x00,
|
||
|
ADV_PEER_ADDR_RAND = 0x01
|
||
|
};
|
||
|
#define ADV_CHAN_37 0x01
|
||
|
#define ADV_CHAN_38 0x02
|
||
|
#define ADV_CHAN_39 0x03
|
||
|
#define ADV_CHAN_ALL (ADV_CHAN_37 | ADV_CHAN_38 | ADV_CHAN_39)
|
||
|
|
||
|
#define ADV_FILTER_NONE 0x00
|
||
|
#define ADV_FILTER_SCAN_WHITELIST 0x01
|
||
|
#define ADV_FILTER_CONN_WHITELIST 0x02
|
||
|
#define ADV_FILTER_SCAN_CONN_WHITELIST \
|
||
|
(ADV_FILTER_SCAN_WHITELIST | ADV_FILTER_CONN_WHITELIST)
|
||
|
|
||
|
|
||
|
#define get_opt_field_int(idx, var, name) \
|
||
|
st( \
|
||
|
lua_getfield (L, idx, name); \
|
||
|
if (!lua_isnil (L, -1)) \
|
||
|
var = lua_tointeger (L, -1); \
|
||
|
)
|
||
|
|
||
|
|
||
|
#define MAX_CMD_Q 5
|
||
|
|
||
|
|
||
|
// --- Local state --------------------------------------------
|
||
|
|
||
|
static struct cmd_list
|
||
|
{
|
||
|
uint16_t cmd;
|
||
|
int cb_ref;
|
||
|
} __attribute__((packed)) cmd_q[MAX_CMD_Q];
|
||
|
|
||
|
static task_handle_t hci_event_task_handle;
|
||
|
static int adv_rep_cb_ref = LUA_NOREF;
|
||
|
|
||
|
|
||
|
// --- VHCI callbacks ------------------------------------------
|
||
|
|
||
|
static void on_bthci_can_send (void)
|
||
|
{
|
||
|
// Unused, we don't support queuing up commands on this level
|
||
|
}
|
||
|
|
||
|
|
||
|
static int on_bthci_receive (uint8_t *data, uint16_t len)
|
||
|
{
|
||
|
#if 0
|
||
|
printf ("BT:");
|
||
|
for (int i = 0; i < len; ++i)
|
||
|
printf (" %02x", data[i]);
|
||
|
printf ("\n");
|
||
|
#endif
|
||
|
|
||
|
if (data[0] == H4_TYPE_EVENT)
|
||
|
{
|
||
|
unsigned len = data[2];
|
||
|
char *copy = malloc (SZ_HDR + len);
|
||
|
if (copy)
|
||
|
memcpy (copy, data, SZ_HDR + len);
|
||
|
if (!copy || !task_post_high (hci_event_task_handle, (task_param_t)copy))
|
||
|
{
|
||
|
NODE_ERR("Dropped BT event due to no mem!");
|
||
|
free (copy);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const vhci_host_callback_t bthci_callbacks =
|
||
|
{
|
||
|
on_bthci_can_send,
|
||
|
on_bthci_receive
|
||
|
};
|
||
|
|
||
|
|
||
|
// --- Helper functions ---------------------------------
|
||
|
|
||
|
// Expects callback function at top of stack
|
||
|
static int send_hci_command (lua_State *L, uint8_t *data, unsigned len)
|
||
|
{
|
||
|
if (API_vhci_host_check_send_available ())
|
||
|
{
|
||
|
uint16_t cmd = (((uint16_t)data[2]) << 8) | data[1];
|
||
|
for (int i = 0; i < MAX_CMD_Q; ++i)
|
||
|
{
|
||
|
if (cmd_q[i].cb_ref == LUA_NOREF)
|
||
|
{
|
||
|
if (lua_gettop (L) > 0 && !lua_isnil (L, -1))
|
||
|
{
|
||
|
cmd_q[i].cmd = cmd;
|
||
|
luaL_checkanyfunction (L, -1);
|
||
|
lua_pushvalue (L, -1);
|
||
|
cmd_q[i].cb_ref = luaL_ref (L, LUA_REGISTRYINDEX);
|
||
|
}
|
||
|
API_vhci_host_send_packet (data, len);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Nope, couldn't send this command!
|
||
|
lua_pushinteger (L, ERROR_UNSPECIFIED);
|
||
|
lua_pushlstring (L, NULL, 0);
|
||
|
lua_call (L, 2, 0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void enable_le_meta_events (void)
|
||
|
{
|
||
|
uint8_t buf[SZ_HDR + 8];
|
||
|
uint8_t *p = buf;
|
||
|
|
||
|
STREAM_U8 (p, H4_TYPE_COMMAND);
|
||
|
STREAM_U16(p, HCI_SET_EVENT_MASK);
|
||
|
STREAM_U8 (p, sizeof (buf) - SZ_HDR);
|
||
|
|
||
|
STREAM_U8 (p, 0xff);
|
||
|
STREAM_U8 (p, 0xff);
|
||
|
STREAM_U8 (p, 0xff);
|
||
|
STREAM_U8 (p, 0xff);
|
||
|
STREAM_U8 (p, 0xff);
|
||
|
STREAM_U8 (p, 0x1f);
|
||
|
STREAM_U8 (p, 0x00);
|
||
|
STREAM_U8 (p, 0x20); // LE Meta-Event
|
||
|
|
||
|
API_vhci_host_send_packet (buf, sizeof (buf));
|
||
|
}
|
||
|
|
||
|
|
||
|
// --- Lua context handlers ------------------------------------
|
||
|
|
||
|
static void invoke_cmd_q_callback (
|
||
|
lua_State *L, unsigned idx, uint8_t code, unsigned len, const uint8_t *data)
|
||
|
{
|
||
|
if (cmd_q[idx].cb_ref != LUA_NOREF)
|
||
|
{
|
||
|
lua_rawgeti (L, LUA_REGISTRYINDEX, cmd_q[idx].cb_ref);
|
||
|
luaL_unref (L, LUA_REGISTRYINDEX, cmd_q[idx].cb_ref);
|
||
|
cmd_q[idx].cb_ref = LUA_NOREF;
|
||
|
|
||
|
if (code) // non-zero response code?
|
||
|
lua_pushinteger (L, code);
|
||
|
else
|
||
|
lua_pushnil (L); // no error
|
||
|
lua_pushlstring (L, (const char *)data, len ); // extra bytes, if any
|
||
|
lua_call (L, 2, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void handle_hci_event (task_param_t arg, task_prio_t prio)
|
||
|
{
|
||
|
(void)prio;
|
||
|
lua_State *L = lua_getstate ();
|
||
|
uint8_t *hci_event = (uint8_t *)arg;
|
||
|
|
||
|
unsigned type = hci_event[1];
|
||
|
unsigned len = hci_event[2];
|
||
|
if (type == EVENT_COMMAND_COMPLETE)
|
||
|
{
|
||
|
uint16_t cmd = (((uint16_t)hci_event[5]) << 8) | hci_event[4];
|
||
|
for (int i = 0; i < MAX_CMD_Q; ++i)
|
||
|
{
|
||
|
if (cmd_q[i].cb_ref != LUA_NOREF && cmd_q[i].cmd == cmd)
|
||
|
{
|
||
|
invoke_cmd_q_callback (L, i, hci_event[6], len - 4, &hci_event[7]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (cmd == HCI_RESET) // clear cmd_q to prevent leaking slots
|
||
|
{
|
||
|
for (int i = 0; i < MAX_CMD_Q; ++i)
|
||
|
invoke_cmd_q_callback (L, i, ERROR_UNSPECIFIED, 0, NULL);
|
||
|
|
||
|
enable_le_meta_events (); // renenable le events
|
||
|
}
|
||
|
}
|
||
|
else if (type == EVENT_LE_META)
|
||
|
{
|
||
|
unsigned subtype = hci_event[3];
|
||
|
if (subtype == EVENT_LE_META_ADV_REPORT)
|
||
|
{
|
||
|
unsigned num_reps = hci_event[4];
|
||
|
// The encoding of multiple reports is not clear in spec, and I've never
|
||
|
// seen a multiple-report event, so for now we only handle single reports
|
||
|
if (adv_rep_cb_ref != LUA_NOREF && num_reps == 1)
|
||
|
{
|
||
|
uint8_t *report = &hci_event[5];
|
||
|
lua_rawgeti (L, LUA_REGISTRYINDEX, adv_rep_cb_ref);
|
||
|
lua_pushlstring (L, (const char *)report, len - 2);
|
||
|
lua_call (L, 1, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free (hci_event);
|
||
|
}
|
||
|
|
||
|
|
||
|
// --- Lua functions ------------------------------------
|
||
|
|
||
|
static int lbthci_init (lua_State *L)
|
||
|
{
|
||
|
hci_event_task_handle = task_get_id (handle_hci_event);
|
||
|
for (int i = 0; i < MAX_CMD_Q; ++i)
|
||
|
cmd_q[i].cb_ref = LUA_NOREF;
|
||
|
|
||
|
bt_controller_init ();
|
||
|
|
||
|
API_vhci_host_register_callback (&bthci_callbacks);
|
||
|
|
||
|
enable_le_meta_events ();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lbthci_reset (lua_State *L)
|
||
|
{
|
||
|
uint8_t buf[SZ_HCI_RESET];
|
||
|
uint8_t *p = buf;
|
||
|
STREAM_U8 (p, H4_TYPE_COMMAND);
|
||
|
STREAM_U16(p, HCI_RESET);
|
||
|
STREAM_U8 (p, 0);
|
||
|
|
||
|
lua_settop (L, 1);
|
||
|
return send_hci_command (L, buf, sizeof (buf));
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lbthci_adv_enable (lua_State *L)
|
||
|
{
|
||
|
bool onoff = lua_tointeger (L, 1) > 0;
|
||
|
|
||
|
uint8_t buf[SZ_HCI_BLE_ADV_WRI_ENABLE];
|
||
|
uint8_t *p = buf;
|
||
|
|
||
|
STREAM_U8 (p, H4_TYPE_COMMAND);
|
||
|
STREAM_U16(p, HCI_BLE_ADV_WRI_ENABLE);
|
||
|
STREAM_U8 (p, 1);
|
||
|
STREAM_U8 (p, onoff);
|
||
|
|
||
|
lua_settop (L, 2);
|
||
|
return send_hci_command (L, buf, sizeof (buf));
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lbthci_adv_setdata (lua_State *L)
|
||
|
{
|
||
|
size_t len;
|
||
|
const char *data = luaL_checklstring (L, 1, &len);
|
||
|
if (len > ADV_MAX_DATA)
|
||
|
len = ADV_MAX_DATA;
|
||
|
|
||
|
uint8_t buf[SZ_HCI_BLE_ADV_WRI_DATA];
|
||
|
uint8_t *p = buf;
|
||
|
STREAM_U8 (p, H4_TYPE_COMMAND);
|
||
|
STREAM_U16(p, HCI_BLE_ADV_WRI_DATA);
|
||
|
STREAM_U8 (p, sizeof (buf) - SZ_HDR);
|
||
|
|
||
|
STREAM_U8 (p, len);
|
||
|
STREAM_ARRAY(p, data, len);
|
||
|
|
||
|
lua_settop (L, 2);
|
||
|
return send_hci_command (L, buf, sizeof (buf));
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lbthci_adv_setparams (lua_State *L)
|
||
|
{
|
||
|
uint16_t adv_interval_min = 0x0400; // 0.64s
|
||
|
uint16_t adv_interval_max = 0x0800; // 1.28s
|
||
|
uint8_t adv_type = ADV_IND;
|
||
|
uint8_t own_addr_type = ADV_OWN_ADDR_PUB;
|
||
|
uint8_t peer_addr_type = ADV_PEER_ADDR_PUB;
|
||
|
bd_addr_t peer_addr;
|
||
|
uint8_t adv_chan_map = ADV_CHAN_ALL;
|
||
|
uint8_t adv_filter_pol = ADV_FILTER_NONE;
|
||
|
|
||
|
luaL_checkanytable (L, 1);
|
||
|
lua_settop (L, 2); // Pad a nil into the function slot if necessary
|
||
|
|
||
|
get_opt_field_int (1, adv_interval_min, "interval_min");
|
||
|
get_opt_field_int (1, adv_interval_max, "interval_max");
|
||
|
get_opt_field_int (1, adv_type, "type");
|
||
|
get_opt_field_int (1, own_addr_type, "own_addr_type");
|
||
|
get_opt_field_int (1, peer_addr_type, "peer_addr_type");
|
||
|
// TODO: peer addr
|
||
|
get_opt_field_int (1, adv_chan_map, "channel_map");
|
||
|
get_opt_field_int (1, adv_filter_pol, "filter_policy");
|
||
|
|
||
|
|
||
|
if (adv_type == ADV_SCAN_IND || adv_type == ADV_NONCONN_IND)
|
||
|
{
|
||
|
if (adv_interval_min < ADV_MIN_INTERVAL)
|
||
|
adv_interval_min = ADV_MIN_INTERVAL;
|
||
|
if (adv_interval_max < ADV_MIN_INTERVAL)
|
||
|
adv_interval_max = ADV_MIN_INTERVAL;
|
||
|
}
|
||
|
else if (adv_type == ADV_DIRECT_IND_HI_DUTY ||
|
||
|
adv_type == ADV_DIRECT_IND_LO_DUTY)
|
||
|
{
|
||
|
// TODO: enforce peer addr validity
|
||
|
}
|
||
|
|
||
|
if (adv_chan_map == 0 || adv_chan_map > ADV_CHAN_ALL)
|
||
|
adv_chan_map = ADV_CHAN_ALL;
|
||
|
|
||
|
if (adv_filter_pol > ADV_FILTER_SCAN_CONN_WHITELIST)
|
||
|
adv_filter_pol = ADV_FILTER_SCAN_CONN_WHITELIST;
|
||
|
|
||
|
uint8_t buf[SZ_HCI_BLE_ADV_WRI_PARAMS];
|
||
|
uint8_t *p = buf;
|
||
|
STREAM_U8 (p, H4_TYPE_COMMAND);
|
||
|
STREAM_U16(p, HCI_BLE_ADV_WRI_PARAMS);
|
||
|
STREAM_U8 (p, sizeof (buf) - SZ_HDR);
|
||
|
|
||
|
STREAM_U16(p, adv_interval_min);
|
||
|
STREAM_U16(p, adv_interval_max);
|
||
|
STREAM_U8 (p, adv_type);
|
||
|
STREAM_U8 (p, own_addr_type);
|
||
|
STREAM_U8 (p, peer_addr_type);
|
||
|
STREAM_BD_ADDR(p, peer_addr);
|
||
|
STREAM_U8 (p, adv_chan_map);
|
||
|
STREAM_U8 (p, adv_filter_pol);
|
||
|
|
||
|
lua_settop (L, 2);
|
||
|
return send_hci_command (L, buf, sizeof (buf));
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lbthci_scan (lua_State *L)
|
||
|
{
|
||
|
bool onoff = lua_tointeger (L, 1) > 0;
|
||
|
|
||
|
uint8_t buf[SZ_HCI_BLE_SET_SCAN_ENABLE];
|
||
|
uint8_t *p = buf;
|
||
|
STREAM_U8 (p, H4_TYPE_COMMAND);
|
||
|
STREAM_U16(p, HCI_BLE_SET_SCAN_ENABLE);
|
||
|
STREAM_U8 (p, sizeof (buf) - SZ_HDR);
|
||
|
STREAM_U8 (p, onoff ? 0x01 : 0x00);
|
||
|
STREAM_U8 (p, 0x00); // no filter duplicates
|
||
|
|
||
|
lua_settop (L, 2);
|
||
|
return send_hci_command (L, buf, sizeof (buf));
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lbthci_scan_setparams (lua_State *L)
|
||
|
{
|
||
|
uint8_t scan_mode = 0;
|
||
|
uint16_t scan_interval = 0x0010;
|
||
|
uint16_t scan_window = 0x0010;
|
||
|
uint8_t own_addr_type = 0;
|
||
|
uint8_t filter_policy = 0;
|
||
|
|
||
|
luaL_checkanytable (L, 1);
|
||
|
lua_settop (L, 2); // Pad a nil into the function slot if necessary
|
||
|
|
||
|
get_opt_field_int (1, scan_mode, "mode");
|
||
|
get_opt_field_int (1, scan_interval, "interval");
|
||
|
get_opt_field_int (1, scan_window, "window");
|
||
|
get_opt_field_int (1, own_addr_type, "own_addr_type");
|
||
|
get_opt_field_int (1, filter_policy, "filter_policy");
|
||
|
|
||
|
// TODO clamp ranges
|
||
|
|
||
|
uint8_t buf[SZ_HCI_BLE_SET_SCAN_PARAMS];
|
||
|
uint8_t *p = buf;
|
||
|
STREAM_U8 (p, H4_TYPE_COMMAND);
|
||
|
STREAM_U16(p, HCI_BLE_SET_SCAN_PARAMS);
|
||
|
STREAM_U8 (p, sizeof (buf) - SZ_HDR);
|
||
|
|
||
|
STREAM_U8 (p, scan_mode);
|
||
|
STREAM_U16(p, scan_interval);
|
||
|
STREAM_U16(p, scan_window);
|
||
|
STREAM_U8 (p, own_addr_type);
|
||
|
STREAM_U8 (p, filter_policy);
|
||
|
|
||
|
lua_settop (L, 2);
|
||
|
return send_hci_command (L, buf, sizeof (buf));
|
||
|
}
|
||
|
|
||
|
|
||
|
static int lbthci_scan_on (lua_State *L)
|
||
|
{
|
||
|
const char *on_what = luaL_checkstring (L, 1);
|
||
|
if (strcmp (on_what, "adv_report") == 0)
|
||
|
{
|
||
|
lua_settop (L, 2);
|
||
|
luaL_unref (L, LUA_REGISTRYINDEX, adv_rep_cb_ref);
|
||
|
adv_rep_cb_ref = LUA_NOREF;
|
||
|
if (!lua_isnil (L, 2))
|
||
|
adv_rep_cb_ref = luaL_ref (L, LUA_REGISTRYINDEX);
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return luaL_error (L, "unknown event '%s'", on_what);
|
||
|
}
|
||
|
|
||
|
static int lbthci_rawhci (lua_State *L)
|
||
|
{
|
||
|
size_t len;
|
||
|
uint8_t *data = (uint8_t *)luaL_checklstring (L, 1, &len);
|
||
|
if (len < SZ_HDR || data[0] != H4_TYPE_COMMAND)
|
||
|
return luaL_error (L, "definitely not a valid HCI command");
|
||
|
|
||
|
lua_settop (L, 2);
|
||
|
return send_hci_command (L, data, len);
|
||
|
}
|
||
|
|
||
|
|
||
|
static const LUA_REG_TYPE bthci_adv_map[] =
|
||
|
{
|
||
|
{ LSTRKEY( "enable" ), LFUNCVAL( lbthci_adv_enable ) },
|
||
|
{ LSTRKEY( "setdata" ), LFUNCVAL( lbthci_adv_setdata ) },
|
||
|
{ LSTRKEY( "setparams" ), LFUNCVAL( lbthci_adv_setparams ) },
|
||
|
|
||
|
// Advertising types
|
||
|
{ LSTRKEY( "CONN_UNDIR"), LNUMVAL( ADV_IND ) },
|
||
|
{ LSTRKEY( "CONN_DIR_HI"), LNUMVAL( ADV_DIRECT_IND_HI_DUTY ) },
|
||
|
{ LSTRKEY( "SCAN_UNDIR"), LNUMVAL( ADV_SCAN_IND ) },
|
||
|
{ LSTRKEY( "NONCONN_UNDIR"), LNUMVAL( ADV_NONCONN_IND ) },
|
||
|
{ LSTRKEY( "CONN_DIR_LO"), LNUMVAL( ADV_DIRECT_IND_LO_DUTY ) },
|
||
|
|
||
|
{ LSTRKEY( "CHAN_37" ), LNUMVAL( ADV_CHAN_37 ) },
|
||
|
{ LSTRKEY( "CHAN_38" ), LNUMVAL( ADV_CHAN_38 ) },
|
||
|
{ LSTRKEY( "CHAN_39" ), LNUMVAL( ADV_CHAN_39 ) },
|
||
|
{ LSTRKEY( "CHAN_ALL" ), LNUMVAL( ADV_CHAN_ALL ) },
|
||
|
|
||
|
{ LNILKEY, LNILVAL }
|
||
|
};
|
||
|
|
||
|
|
||
|
static const LUA_REG_TYPE bthci_scan_map[] =
|
||
|
{
|
||
|
{ LSTRKEY( "enable" ), LFUNCVAL( lbthci_scan ) },
|
||
|
{ LSTRKEY( "setparams" ), LFUNCVAL( lbthci_scan_setparams ) },
|
||
|
{ LSTRKEY( "on" ), LFUNCVAL( lbthci_scan_on ) },
|
||
|
{ LNILKEY, LNILVAL }
|
||
|
};
|
||
|
|
||
|
|
||
|
static const LUA_REG_TYPE bthci_map[] =
|
||
|
{
|
||
|
{ LSTRKEY( "rawhci" ), LFUNCVAL( lbthci_rawhci ) },
|
||
|
{ LSTRKEY( "reset" ), LFUNCVAL( lbthci_reset ) },
|
||
|
{ LSTRKEY( "adv" ), LROVAL( bthci_adv_map ) },
|
||
|
{ LSTRKEY( "scan" ), LROVAL( bthci_scan_map ) },
|
||
|
|
||
|
{ LNILKEY, LNILVAL }
|
||
|
};
|
||
|
|
||
|
NODEMCU_MODULE(BTHCI, "bthci", bthci_map, lbthci_init);
|