Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions boards/ugl/uhk-80/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,33 @@ if BT_DIS && BT_DIS_PNP
endif # BT_DIS && BT_DIS_PNP

if BOARD_UHK_80_LEFT
config HAS_PM
default y
config HW_STACK_PROTECTION
default ARCH_HAS_STACK_PROTECTION

config USB_DEVICE_PRODUCT
default "UHK 80 left half bootloader" if MCUBOOT
config USB_DEVICE_PID
default 0x0006 if MCUBOOT
endif # BOARD_UHK_80_LEFT

if BOARD_UHK_80_RIGHT
config HAS_PM
default y
config HW_STACK_PROTECTION
default ARCH_HAS_STACK_PROTECTION

config USB_DEVICE_PRODUCT
default "UHK 80 right half bootloader" if MCUBOOT
config USB_DEVICE_PID
default 0x0008 if MCUBOOT
endif # BOARD_UHK_80_RIGHT

if BOARD_UHK_DONGLE
config HW_STACK_PROTECTION
default ARCH_HAS_STACK_PROTECTION

config USB_DEVICE_PRODUCT
default "UHK dongle bootloader" if MCUBOOT
config USB_DEVICE_PID
Expand All @@ -59,6 +72,3 @@ if BT
config BT_DEVICE_APPEARANCE
default 961
endif # BT

config BT_CTLR
default BT
13 changes: 13 additions & 0 deletions boards/ugl/uhk-80/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@

#include <zephyr/init.h>
#include <hal/nrf_power.h>
#include <zephyr/pm/pm.h>

__weak void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
{
ARG_UNUSED(state);
ARG_UNUSED(substate_id);
}

__weak void pm_state_set(enum pm_state state, uint8_t substate_id)
{
ARG_UNUSED(state);
ARG_UNUSED(substate_id);
}

#ifdef CONFIG_BOARD_UHK_DONGLE_NRF52840
static int board_uhk_dongle_nrf52840_init(void)
Expand Down
2 changes: 1 addition & 1 deletion boards/ugl/uhk-80/board.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000")
board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000")
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake)
3 changes: 3 additions & 0 deletions boards/ugl/uhk-80/board.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
boards:
- name: uhk-80-left
full_name: Ultimate Hacking Keyboard 80 Left
vendor: ugl
socs:
- name: nrf52840
- name: uhk-80-right
full_name: Ultimate Hacking Keyboard 80 Right
vendor: ugl
socs:
- name: nrf52840
- name: uhk-dongle
full_name: Ultimate Hacking Keyboard Dongle
vendor: ugl
socs:
- name: nrf52840
1 change: 1 addition & 0 deletions device/prj.conf.overlays/ble_hid.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ CONFIG_BT_MAX_PAIRED=20

# use BLE HID over GATT from c2usb
CONFIG_C2USB_BLUETOOTH=y
CONFIG_C2USB_HOGP_LOG_LEVEL_DBG=y
# battery status to generic client
CONFIG_BT_BAS=y
# device information to generic client
Expand Down
4 changes: 1 addition & 3 deletions device/prj.conf.overlays/c2usb.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ CONFIG_NEWLIB_LIBC_NANO=y
# CONFIG_WARN_EXPERIMENTAL=y
#
CONFIG_UDC_DRIVER_LOG_LEVEL_DBG=y
CONFIG_C2USB_HOGP_LOG_LEVEL_DBG=y
CONFIG_C2USB_UDC_MAC_LOG_LEVEL_DBG=y

CONFIG_WARN_EXPERIMENTAL=n
Expand All @@ -18,5 +17,4 @@ CONFIG_C2USB_UDC_MAC=y
CONFIG_C2USB_UDC_MAC_THREAD_STACK_SIZE=2048

# needed as at suspend the msgq is flooded otherwise
CONFIG_C2USB_UDC_MAC_MSGQ_SIZE=32

CONFIG_C2USB_UDC_MAC_MSGQ_SIZE=24
2 changes: 2 additions & 0 deletions device/prj.conf.overlays/nrf_shared.conf
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,5 @@ CONFIG_SPEED_OPTIMIZATIONS=y
# macro evaluation can take some depth...
CONFIG_MAIN_STACK_SIZE=4096

# we are out of RAM, limit required heap to minimum
CONFIG_NEWLIB_LIBC_MIN_REQUIRED_HEAP_SIZE=0
20 changes: 7 additions & 13 deletions device/src/bt_advertise.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ uint8_t BtAdvertise_Start(adv_config_t advConfig)
case ADVERTISE_NUS | ADVERTISE_HID:
LOG_DBG("Adv: advertise nus+hid.");
/* our devices don't check service uuids, so hid advertisement effectively advertises nus too */
advParam = *BT_LE_ADV_CONN_ONE_TIME;
advParam = *BT_LE_ADV_CONN_FAST_1;
err = BT_LE_ADV_START(&advParam, adHid, sdHid);

break;
Expand All @@ -152,13 +152,13 @@ uint8_t BtAdvertise_Start(adv_config_t advConfig)
LOG_DBG("Adv: advertise nus, with allow list.");
setFilters(advConfig);

advParam = *BT_LE_ADV_CONN_ONE_TIME;
advParam.options = BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | BT_LE_ADV_OPT_FILTER_CONN | BT_LE_ADV_OPT_USE_IDENTITY;
advParam = *BT_LE_ADV_CONN_FAST_1;
advParam.options = BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_FILTER_CONN | BT_LE_ADV_OPT_USE_IDENTITY;

err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus);
} else {
LOG_DBG("Adv: advertise nus, without allow list.");
advParam = *BT_LE_ADV_CONN_ONE_TIME;
advParam = *BT_LE_ADV_CONN_FAST_1;
err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus);
}
break;
Expand All @@ -167,21 +167,15 @@ uint8_t BtAdvertise_Start(adv_config_t advConfig)
LOG_DBG("Adv: direct advertise nus, with allow list.");
setFilters(advConfig);

advParam = *BT_LE_ADV_CONN_ONE_TIME;
advParam.options = BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | BT_LE_ADV_OPT_FILTER_CONN | BT_LE_ADV_OPT_USE_IDENTITY;
advParam = *BT_LE_ADV_CONN_FAST_1;
advParam.options = BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_FILTER_CONN | BT_LE_ADV_OPT_USE_IDENTITY;

err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus);
} else {
LOG_DBG("Adv: direct advertise nus, without allow list.");
advParam = *BT_LE_ADV_CONN_ONE_TIME;
advParam = *BT_LE_ADV_CONN_FAST_1;
err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus);
}

//// TODO: fix and reenable this?
// printk("Advertising against %s", GetAddrString(advConfig.addr));
// advParam = *BT_LE_ADV_CONN_DIR_LOW_DUTY(advConfig.addr);
// advParam.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA;
// err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus);
break;
default:
LOG_INF("Adv: Attempted to start advertising without any type! Ignoring.");
Expand Down
138 changes: 103 additions & 35 deletions device/src/bt_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,28 @@ uint32_t BleHidReportIntervalMs = 11;
static void disconnectAllHids();
static void auth_cancel(struct bt_conn *conn);

// BLE APIs that allocate HCI/ATT buffers (data length update, MTU exchange,
// GATT discovery, security elevation) must not run directly in connection
// callbacks: those execute on the BT RX workqueue, and a blocking buffer
// allocation there can stall/deadlock the very thread that frees the buffers.
// We defer that work onto the system workqueue instead. Because scanning and
// advertising are mutually exclusive (see bt_manager.c) and separated by a
// rescheduling delay, connection callbacks are effectively serialized, so a
// single slot is sufficient; if it is ever occupied we refuse rather than
// clobber it.
typedef enum {
BtFlow_Connected,
BtFlow_AuthenticatedConnection,
} bt_flow_t;

static struct k_work btDeferredWork;
static struct bt_conn *btDeferredConn = NULL;
static bt_flow_t btDeferredAction;
static connection_id_t btDeferredConnectionId;
static connection_type_t btDeferredConnectionType;

static void scheduleBtFlow(struct bt_conn *conn, bt_flow_t action, connection_id_t connectionId, connection_type_t connectionType);

peer_t Peers[PeerCount] = {
{
.id = PeerIdUnknown,
Expand Down Expand Up @@ -622,46 +644,21 @@ static void connectUnknown(struct bt_conn *conn) {
#endif
}

static void connected(struct bt_conn *conn, uint8_t err) {
static void connectedCallback(struct bt_conn *conn, uint8_t err) {
BT_TRACE_AND_ASSERT("bc1");

LOG_DBG("Connected cb");
Bt_LastConnectedTime = Timer_GetCurrentTime();

// Without this, linux pairing fails, because tiny 27 byte packets
// exhaust acl buffers easily
enableDataLengthExtension(conn);
if (!DEBUG_STRESS_GATT) {
requestMtuExchange(conn);
}

if (err) {
LOG_WRN("Failed to connect (in connected cb) to %s, err %u", GetPeerStringByConn(conn), err);
BtManager_StartScanningAndAdvertisingAsync(true, "connected with error");
return;
}

const bt_addr_le_t * addr = bt_conn_get_dst(conn);
connection_id_t connectionId = Connections_GetConnectionIdByHostAddr(addr);
connection_type_t connectionType = Connections_Type(connectionId);

LOG_INF("Connected %s, %d %d", GetPeerStringByConn(conn), connectionId, connectionType);

if (connectionId == ConnectionId_Invalid) {
connectUnknown(conn);
BtManager_StartScanningAndAdvertisingAsync(true, "connected - invalid connection");
} else {

if (isWanted(conn, false, connectionId, connectionType)) {
bt_conn_set_security(conn, BT_SECURITY_L4);
// advertising/scanning needs to be started only after peers are assigned :-/
} else {
youAreNotWanted(conn);
BtManager_StartScanningAndAdvertisingAsync(true, "connected - they are not wanted");
}
}


// The buffer-allocating setup (data length, MTU, security) is deferred off
// the BT RX workqueue. See submitDeferredBtWork / handleConnectedDeferred.
scheduleBtFlow(conn, BtFlow_Connected, ConnectionId_Invalid, ConnectionType_Unknown);

return;
}
Expand Down Expand Up @@ -729,7 +726,7 @@ static bool isUhkDeviceConnection(connection_type_t connectionType) {
}
}

static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) {
static void authenticatedConnectionFlow(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) {
// in case we don't have free connection slots and this is not the selected connection, then refuse
if (!isWanted(conn, true, connectionId, connectionType)) {
LOG_WRN("Refusing authenticated connenction %d (this is not a selected connection(%d))", connectionId, SelectedHostConnectionId);
Expand All @@ -755,6 +752,77 @@ static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t
}
}

// Runs on the system workqueue; performs the buffer-allocating setup that used
// to live at the top of connected().
static void connectedFlow(struct bt_conn *conn) {
// Without this, linux pairing fails, because tiny 27 byte packets
// exhaust acl buffers easily
enableDataLengthExtension(conn);
if (!DEBUG_STRESS_GATT) {
requestMtuExchange(conn);
}

const bt_addr_le_t * addr = bt_conn_get_dst(conn);
connection_id_t connectionId = Connections_GetConnectionIdByHostAddr(addr);
connection_type_t connectionType = Connections_Type(connectionId);

LOG_INF("Connected %s, %d %d", GetPeerStringByConn(conn), connectionId, connectionType);

if (connectionId == ConnectionId_Invalid) {
connectUnknown(conn);
BtManager_StartScanningAndAdvertisingAsync(true, "connected - invalid connection");
} else {
if (isWanted(conn, false, connectionId, connectionType)) {
bt_conn_set_security(conn, BT_SECURITY_L4);
// advertising/scanning needs to be started only after peers are assigned :-/
} else {
youAreNotWanted(conn);
BtManager_StartScanningAndAdvertisingAsync(true, "connected - they are not wanted");
}
}
}


static void btDeferredWorkHandler(struct k_work *work) {
struct bt_conn *conn = btDeferredConn;
bt_flow_t action = btDeferredAction;
connection_id_t connectionId = btDeferredConnectionId;
connection_type_t connectionType = btDeferredConnectionType;

switch (action) {
case BtFlow_Connected:
connectedFlow(conn);
break;
case BtFlow_AuthenticatedConnection:
authenticatedConnectionFlow(conn, connectionId, connectionType);
break;
}

btDeferredConn = NULL;
bt_conn_unref(conn);
}

static void scheduleBtFlow(struct bt_conn *conn, bt_flow_t action, connection_id_t connectionId, connection_type_t connectionType) {
bool accepted = false;
DISABLE_IRQ();
if (btDeferredConn == NULL) {
btDeferredConn = conn;
accepted = true;
}
ENABLE_IRQ();

if (!accepted) {
LOG_ERR("BT deferred work slot busy, dropping action %d for %s", action, GetPeerStringByConn(conn));
return;
}

bt_conn_ref(conn);
btDeferredAction = action;
btDeferredConnectionId = connectionId;
btDeferredConnectionType = connectionType;
k_work_submit(&btDeferredWork);
}

static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) {
BT_TRACE_AND_ASSERT("bc3");
// In case of failure, disconnect
Expand Down Expand Up @@ -784,7 +852,7 @@ static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_s
const bt_addr_le_t *addr = bt_conn_get_dst(conn);
connection_id_t connectionId = Connections_GetConnectionIdByHostAddr(addr);
connection_type_t connectionType = Connections_Type(connectionId);
connectAuthenticatedConnection(conn, connectionId, connectionType);
scheduleBtFlow(conn, BtFlow_AuthenticatedConnection, connectionId, connectionType);
}

__attribute__((unused)) static void infoLatencyParamsUpdated(struct bt_conn* conn, uint16_t interval, uint16_t latency, uint16_t timeout)
Expand All @@ -808,7 +876,7 @@ __attribute__((unused)) static void infoLatencyParamsUpdated(struct bt_conn* con
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.connected = connectedCallback,
.disconnected = disconnected,
.security_changed = securityChanged,
.le_param_updated = infoLatencyParamsUpdated,
Expand All @@ -827,8 +895,6 @@ static void auth_passkey_entry(struct bt_conn *conn) {

LOG_INF("Received passkey pairing inquiry.");

enableDataLengthExtension(conn);

if (!auth_conn) {
LOG_INF("Returning: no auth conn");
return;
Expand Down Expand Up @@ -932,7 +998,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) {
LOG_INF("Pairing complete, passing connection %d to authenticatedConnection handler. Selected conn is %d", connectionId, SelectedHostConnectionId);

// we have to connect from here, because central changes its address *after* setting security
connectAuthenticatedConnection(conn, connectionId, connectionType);
scheduleBtFlow(conn, BtFlow_AuthenticatedConnection, connectionId, connectionType);
}

if (auth_conn) {
Expand Down Expand Up @@ -1020,6 +1086,8 @@ void BtConn_Init(void) {
BT_TRACE_AND_ASSERT("bc6");
int err = 0;

k_work_init(&btDeferredWork, btDeferredWorkHandler);

for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) {
Peers[peerId].id = peerId;
Peers[peerId].conn = NULL;
Expand Down
1 change: 0 additions & 1 deletion device/src/keyboard/charger.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "charger.h"
#include "battery_window_calculator.h"
#include "keyboard/charger.h"
#include "nrf52840.h"
#include "oled/screens/notification_screen.h"
#include "power_mode.h"
#include "shell.h"
Expand Down
1 change: 1 addition & 0 deletions device/sysbuild.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_PARTITION_MANAGER=y
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y
SB_CONFIG_MCUBOOT_APP_SYNC_UPDATEABLE_IMAGES=n
Loading
Loading