diff --git a/build.sh b/build.sh index 54b07be7f..adb956ccc 100755 --- a/build.sh +++ b/build.sh @@ -71,11 +71,16 @@ function processArguments() { DEVICES="$DEVICES uhk-80-left uhk-80-right uhk-dongle" shift ;; - clean|setup|update|build|make|flash|flashUsb|shell|release) + clean|setup|update|build|make|flash|shell|release) ACTIONS="$ACTIONS $1" TARGET_TMUX_SESSION=$BUILD_SESSION_NAME shift ;; + flashUsb) + ACTIONS="$ACTIONS make $1" + TARGET_TMUX_SESSION=$BUILD_SESSION_NAME + shift + ;; addrline) shift ADDR=$1 @@ -374,7 +379,7 @@ function run() { if [ `echo $DEVICES | wc -w` -gt 1 ] then runPerDevice - elif [ `echo $DEVICES | wc -w` -eq 0 ] + elif [ `echo $DEVICES | wc -w` -le 1 ] then performActions $DEVICES else diff --git a/device/prj.conf.overlays/nrf_shared.conf b/device/prj.conf.overlays/nrf_shared.conf index 6193e8f0f..ba52c6f15 100644 --- a/device/prj.conf.overlays/nrf_shared.conf +++ b/device/prj.conf.overlays/nrf_shared.conf @@ -17,6 +17,13 @@ CONFIG_BT_SMP=y CONFIG_BT_L2CAP_TX_BUF_COUNT=5 +# Todo: place these where they belong +# config for right half host switching +CONFIG_BT_MAX_CONN=4 +CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=3 +CONFIG_BT_FILTER_ACCEPT_LIST=y + + # negotiate larger MTU for NUS CONFIG_BT_USER_DATA_LEN_UPDATE=y CONFIG_BT_BUF_ACL_RX_SIZE=251 diff --git a/device/src/CMakeLists.txt b/device/src/CMakeLists.txt index adf9d11d4..d288f39ec 100644 --- a/device/src/CMakeLists.txt +++ b/device/src/CMakeLists.txt @@ -8,7 +8,6 @@ set(usb_cpp_sources usb/command_app.cpp usb/controls_app.cpp usb/gamepad_app.cpp - usb/hid_battery_app.cpp usb/keyboard_app.cpp usb/mouse_app.cpp usb/usb_compatibility.cpp @@ -29,6 +28,7 @@ if(NOT CONFIG_BOARD_UHK_60_RIGHT) bt_conn.c bt_manager.c bt_pair.c + connections.c debug_eventloop_timing.c device_state.c flash.c diff --git a/device/src/bt_advertise.c b/device/src/bt_advertise.c index 728caf86d..853d654f8 100644 --- a/device/src/bt_advertise.c +++ b/device/src/bt_advertise.c @@ -1,7 +1,11 @@ #include "bt_advertise.h" #include #include +#include "bt_conn.h" +#include "connections.h" #include "device.h" +#include "event_scheduler.h" +#include "bt_scan.h" #undef DEVICE_NAME #define DEVICE_NAME CONFIG_BT_DEVICE_NAME @@ -38,8 +42,24 @@ static const struct bt_data sdHid[] = {SD_HID_DATA}; static const struct bt_data sdNusHid[] = {SD_NUS_DATA SD_HID_DATA}; -void BtAdvertise_Start(uint8_t adv_type) +static struct bt_le_adv_param advertisementParams[] = BT_LE_ADV_CONN; + +static void setFilters() { + bt_le_filter_accept_list_clear(); + if (DEVICE_IS_UHK80_RIGHT) { + if (BtConn_UnusedPeripheralConnectionCount() <= 1 && SelectedHostConnectionId != ConnectionId_Invalid) { + bt_le_filter_accept_list_add(&HostConnection(SelectedHostConnectionId)->bleAddress); + advertisementParams->options = BT_LE_ADV_OPT_FILTER_CONN; + } else { + advertisementParams->options = BT_LE_ADV_OPT_NONE; + } + } +} + +uint8_t BtAdvertise_Start(uint8_t adv_type) { + setFilters(); + int err; const char *adv_type_string; if (adv_type == (ADVERTISE_NUS | ADVERTISE_HID)) { @@ -54,15 +74,18 @@ void BtAdvertise_Start(uint8_t adv_type) err = bt_le_adv_start(BT_LE_ADV_CONN, adHid, ARRAY_SIZE(adHid), sdHid, ARRAY_SIZE(sdHid)); } else { printk("Attempted to start advertising without any type! Ignoring.\n"); - return; + return 0; } if (err == 0) { printk("%s advertising successfully started\n", adv_type_string); + return 0; } else if (err == -EALREADY) { printk("%s advertising continued\n", adv_type_string); + return 0; } else { - printk("%s advertising failed to start (err %d)\n", adv_type_string, err); + printk("%s advertising failed to start (err %d), free connections: %d\n", adv_type_string, err, BtConn_UnusedPeripheralConnectionCount()); + return err; } } @@ -70,17 +93,35 @@ void BtAdvertise_Stop() { int err = bt_le_adv_stop(); if (err) { printk("Advertising failed to stop (err %d)\n", err); - } else { - printk("Advertising successfully stopped\n"); } } +static uint8_t connectedHidCount() { + uint8_t connectedHids = 0; + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + if (Peers[peerId].conn && Connections_Type(Peers[peerId].connectionId) == ConnectionType_BtHid) { + connectedHids++; + } + } + return connectedHids; +} + uint8_t BtAdvertise_Type() { switch (DEVICE_ID) { case DeviceId_Uhk80_Left: return ADVERTISE_NUS; case DeviceId_Uhk80_Right: - return ADVERTISE_NUS | ADVERTISE_HID; + if (BtConn_UnusedPeripheralConnectionCount() > 0) { + if (connectedHidCount() > 0) { + return ADVERTISE_NUS; + } else { + return ADVERTISE_NUS | ADVERTISE_HID; + } + } else { + printk("Current slot count %d, not advertising\n", BtConn_UnusedPeripheralConnectionCount()); + BtConn_ListCurrentConnections(); + return 0; + } case DeviceId_Uhk_Dongle: return 0; default: diff --git a/device/src/bt_advertise.h b/device/src/bt_advertise.h index f43a39c30..7f67e9e50 100644 --- a/device/src/bt_advertise.h +++ b/device/src/bt_advertise.h @@ -12,7 +12,7 @@ // Functions: - void BtAdvertise_Start(uint8_t adv_type); + uint8_t BtAdvertise_Start(uint8_t adv_type); void BtAdvertise_Stop(); uint8_t BtAdvertise_Type(); diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 037ea9076..818039b0f 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -1,4 +1,6 @@ +#include "keyboard/oled/framebuffer.h" #include +#include #include #include #ifdef CONFIG_BT_SCAN @@ -7,9 +9,11 @@ #include "bt_advertise.h" #include "bt_conn.h" #include "bt_scan.h" +#include "connections.h" #include "device_state.h" #include "keyboard/oled/screens/screen_manager.h" #include "keyboard/oled/widgets/widget.h" +#include "event_scheduler.h" #include "host_connection.h" #include "nus_client.h" #include "nus_server.h" @@ -21,60 +25,72 @@ #include "bt_pair.h" #include "bt_manager.h" #include +#include "config_manager.h" +#include "zephyr/kernel.h" +#include bool Bt_NewPairedDevice = false; +struct bt_conn *auth_conn; + #define BLE_KEY_LEN 16 #define BLE_ADDR_LEN 6 peer_t Peers[PeerCount] = { + { + .id = PeerIdUnknown, + .name = "unknown", + .connectionId = ConnectionId_Invalid, + }, { .id = PeerIdLeft, .name = "left", + .connectionId = ConnectionId_NusServerLeft, }, { .id = PeerIdRight, .name = "right", - }, - { - .id = PeerIdDongle, - .name = "dongle", - }, - { - .id = PeerIdHid, - .name = "bthid", +#if DEVICE_IS_UHK_DONGLE + .connectionId = ConnectionId_NusServerRight, +#elif DEVICE_IS_UHK80_LEFT + .connectionId = ConnectionId_NusClientRight, +#endif }, }; - peer_t *getPeerByAddr(const bt_addr_le_t *addr) { - for (uint8_t i = 0; i < PeerCount; i++) { - if (bt_addr_le_eq(addr, &Peers[i].addr)) { + for (uint8_t i = PeerIdFirst; i < PeerCount; i++) { + if (BtAddrEq(addr, &Peers[i].addr)) { return &Peers[i]; } } - for (uint8_t hostConnectionId = 0; hostConnectionId < HOST_CONNECTION_COUNT_MAX; hostConnectionId++) { - host_connection_t* hostConnection = &HostConnections[hostConnectionId]; + return NULL; +} - if (hostConnection->type == HostConnectionType_Dongle && bt_addr_le_eq(addr, &hostConnection->bleAddress)) { - return &Peers[PeerIdDongle]; - } - if (hostConnection->type == HostConnectionType_Ble && bt_addr_le_eq(addr, &hostConnection->bleAddress)) { - return &Peers[PeerIdHid]; +peer_t *getPeerByConn(const struct bt_conn *conn) { + for (uint8_t i = PeerIdFirst; i < PeerCount; i++) { + if (conn == Peers[i].conn) { + return &Peers[i]; } } return NULL; } -int8_t getPeerIdByConn(const struct bt_conn *conn) { - const bt_addr_le_t *addr = bt_conn_get_dst(conn); - peer_t *peer = getPeerByAddr(addr); +int8_t GetPeerIdByConn(const struct bt_conn *conn) { + peer_t *peer = getPeerByConn(conn); int8_t peerId = peer ? peer->id : PeerIdUnknown; return peerId; } +char* GetAddrString(const bt_addr_le_t *addr) +{ + static char addr_str[BT_ADDR_LE_STR_LEN]; // Length defined by Zephyr + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + return addr_str; +} + char *GetPeerStringByAddr(const bt_addr_le_t *addr) { char addrStr[BT_ADDR_STR_LEN]; for (uint8_t i=0; i %d\n", Peers[peerId].name, currentId, newId); + if (newId != ConnectionId_Invalid && newId != currentId) { + Connections_MoveConnection(peerId, currentId, newId); + } + } + } +} + +static uint8_t allocateHostPeer(uint8_t connectionType) { + switch (connectionType) { + case ConnectionType_NusLeft: + return PeerIdLeft; + case ConnectionType_NusRight: + return PeerIdRight; + default: + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + if (!Peers[peerId].conn) { + return peerId; + } + } + break; + } + return PeerIdUnknown; +} + +static uint8_t assignPeer(struct bt_conn* conn, uint8_t connectionId, uint8_t connectionType) { + bt_addr_le_t addr = *bt_conn_get_dst(conn); + + // be idempotent + for (uint8_t i = PeerIdFirst; i < PeerCount; i++) { + if (Peers[i].conn == conn) { + Peers[i].addr = addr; + if (Peers[i].connectionId != connectionId) { + Connections_MoveConnection(i, Peers[i].connectionId, connectionId); + } + return i; + } + } + + uint8_t peerId = allocateHostPeer(connectionType); + + if (peerId == PeerIdUnknown) { + printk("No peer slot available for connection %d\n", connectionId); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return PeerIdUnknown; + } + + Peers[peerId].conn = bt_conn_ref(conn); + Peers[peerId].addr = addr; + Peers[peerId].connectionId = connectionId; + Connections[connectionId].peerId = peerId; + Connections_SetState(connectionId, ConnectionState_Connected); + + return peerId; +} + +void bt_foreach_list_current_connections(struct bt_conn *conn, void *data) +{ + int8_t peerId = GetPeerIdByConn(conn); + if (peerId == PeerIdUnknown) { + printk(" - %s\n", GetPeerStringByConn(conn)); + } else { + printk(" - peer %d(%s), connection %d\n", peerId, GetPeerStringByConn(conn), Peers[peerId].connectionId); + } +} + +void BtConn_ListCurrentConnections() { + printk("Current connections:\n"); + bt_conn_foreach(BT_CONN_TYPE_LE, bt_foreach_list_current_connections, NULL); +} + + +static void bt_foreach_print_bond(const struct bt_bond_info *info, void *user_data) +{ + printk(" - %s\n", GetAddrString(&info->addr)); +} + +void BtConn_ListAllBonds() { + printk("All bonds:\n"); + bt_foreach_bond(BT_ID_DEFAULT, bt_foreach_print_bond, NULL); +} + + +// If last available slot is reserved for a selected connection, refuse other connections +static bool isWanted(struct bt_conn *conn, connection_type_t connectionType) { + return + connectionType == ConnectionType_NusLeft || + BtConn_UnusedPeripheralConnectionCount() > 1 || + SelectedHostConnectionId == ConnectionId_Invalid || + BtAddrEq(bt_conn_get_dst(conn), &HostConnection(SelectedHostConnectionId)->bleAddress); +} + +static void connectNus(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) { + uint8_t peerId = assignPeer(conn, connectionId, connectionType); + + printf("Bt connected to %s\n", GetPeerStringByConn(conn)); + + configureLatency(conn, LatencyMode_NUS); + enableDataLengthExtension(conn); + + bool isRightClient = DEVICE_IS_UHK80_RIGHT && peerId == PeerIdLeft; + bool isDongleClient = DEVICE_IS_UHK_DONGLE && peerId == PeerIdRight; + if ( isRightClient || isDongleClient ) { + printk("Initiating NUS connection with %s\n", GetPeerStringByConn(conn)); + NusClient_Connect(conn); + } +} + +static void connectHid(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) { + assignPeer(conn, connectionId, connectionType); + + printf("Bt connected to %s\n", GetPeerStringByConn(conn)); + + configureLatency(conn, LatencyMode_NUS); + + // Assume that HOGP is ready + printf("Established HID connection with %s\n", GetPeerStringByConn(conn)); + Connections_SetState(connectionId, ConnectionState_Ready); +} + +#define BT_UUID_NUS_VAL BT_UUID_128_ENCODE(0x6e400001, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e) +#define BT_UUID_NUS BT_UUID_DECLARE_128(BT_UUID_NUS_VAL) +ATTR_UNUSED static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) +{ + if (!attr) { + printk("Service discovery completed, connection wasn't matched.\n"); + // TODO: consider setting a timer to disconnect the connection if neither auth nor security is Established + return BT_GATT_ITER_STOP; + } + + if (attr->user_data) { + struct bt_gatt_service_val *service_val = (struct bt_gatt_service_val *)attr->user_data; + if (service_val && service_val->uuid) { + if (service_val->uuid->type == BT_UUID_TYPE_128 && !bt_uuid_cmp(service_val->uuid, BT_UUID_NUS)) { + if (!BtPair_OobPairingInProgress && DEVICE_IS_UHK80_RIGHT) { + printk("Unknown NUS trying to connect. Refusing!\n"); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + return BT_GATT_ITER_STOP; + } + } + } + + return BT_GATT_ITER_CONTINUE; +} + +// check if the connection is NUS. If yes, disconnect it. +// Otherwise, assume that this is anonymous ble hid connection, and +// it to (try to) increase its security. +static void connectUnknown(struct bt_conn *conn) { +#if DEVICE_IS_UHK80_RIGHT || DEVICE_IS_UHK_DONGLE + int err; + + printk("Bt connected to unknown. Starting discovery.\n"); + static struct bt_gatt_discover_params discover_params; + discover_params.uuid = NULL; // Will discover all services + discover_params.start_handle = 0x0001; + discover_params.end_handle = 0xFFFF; + discover_params.func = discover_func; + discover_params.type = BT_GATT_DISCOVER_PRIMARY; + + err = bt_gatt_discover(conn, &discover_params); + if (err) { + printk("Service discovery failed (err %u)\n", err); + return; + } +#endif +} + +static void connected(struct bt_conn *conn, uint8_t err) { if (err) { printk("Failed to connect to %s, err %u\n", GetPeerStringByConn(conn), err); + BtManager_StartScanningAndAdvertising(); + return; + } -#if DEVICE_IS_UHK80_RIGHT - err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE); - if (err) { - printk("Scanning failed to start (err %d)\n", err); + 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); + + printk("connected %s, %d %d\n", GetPeerStringByConn(conn), connectionId, connectionType); + + if (connectionId == ConnectionId_Invalid) { + connectUnknown(conn); + } else { + + if (isWanted(conn, connectionType)) { + bt_conn_set_security(conn, BT_SECURITY_L4); + } else { + printk("Refusing connenction %d (this is not a selected connection)\n", connectionId); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); } -#endif + } + BtManager_StartScanningAndAdvertisingAsync(); + + + return; + + /* + *+ switch (connectionType) { ++ case ConnectionType_NusLeft: ++ case ConnectionType_NusRight: ++ case ConnectionType_NusDongle: ++ connectNus(conn, connectionId, connectionType); ++ break; ++ case ConnectionType_BtHid: ++ connectHid(conn, connectionId, connectionType); ++ break; ++ case ConnectionType_Unknown: ++ default: ++ connectedUnknown(conn); ++ break; ++ } ++ ++ if (DEVICE_IS_UHK80_RIGHT) { ++ BtManager_StartScanningAndAdvertising(); ++ } +*/ + +/* + const bt_addr_le_t * addr = bt_conn_get_dst(conn); + connection_id_t connectionId = Connections_GetConnectionIdByBtAddr(addr); + connection_type_t connectionType = Connections_Type(connectionId); + + if (err) { + printk("Failed to connect to %s, err %u\n", GetPeerStringByConn(conn), err); + BtManager_StartScanningAndAdvertising(); return; } - printk("Bt connected to %s\n", GetPeerStringByConn(conn)); + // If last available slot is reserved for a selected connection, refuse other connections + if ( + connectionType != ConnectionType_NusLeft && + BtConn_UnusedPeripheralConnectionCount() <= 1 && + SelectedHostConnectionId != ConnectionId_Invalid && + !BtAddrEq(bt_conn_get_dst(conn), &HostConnection(SelectedHostConnectionId)->bleAddress) + ) { + printk("Refusing connenction %d (this is not a selected connection)\n", connectionId); + err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return; + } - if (peerId == PeerIdUnknown || peerId == PeerIdHid) { + if (connectionType == ConnectionType_BtHid || connectionType == ConnectionType_Unknown) { static const struct bt_le_conn_param conn_params = BT_LE_CONN_PARAM_INIT( 6, 9, // keep it low, lowest allowed is 6 (7.5ms), lowest supported widely is 9 (11.25ms) 10, // keeping it higher allows power saving on peripheral when there's nothing to send (keep it under 30 though) 100 // connection timeout (*10ms) ); bt_conn_le_param_update(conn, &conn_params); -#if DEVICE_IS_UHK80_RIGHT - USB_DisableHid(); -#endif } - if (peerId == PeerIdUnknown) { + if (connectionType == ConnectionType_Unknown) { + printk("Bt connected to UNKNOWN %s\n", GetPeerStringByConn(conn)); return; } - assignPeer(bt_conn_get_dst(conn), peerId); + assignPeer(conn, connectionId, connectionType); + + printk("Bt connected to %s\n", GetPeerStringByConn(conn)); - if (peerId == PeerIdHid) { + if (connectionType == ConnectionType_BtHid) { } else { bt_conn_set_security(conn, BT_SECURITY_L4); // continue connection process in in `connectionSecured()` } - if (DEVICE_IS_UHK80_RIGHT && (peerId != PeerIdLeft)) { - BtAdvertise_Start(BtAdvertise_Type()); + if (DEVICE_IS_UHK80_RIGHT) { + BtManager_StartScanningAndAdvertising(); } + */ } static void disconnected(struct bt_conn *conn, uint8_t reason) { - int8_t peerId = getPeerIdByConn(conn); + int8_t peerId = GetPeerIdByConn(conn); + connection_type_t connectionType = Connections_Type(Peers[peerId].connectionId); + ARG_UNUSED(peerId); printk("Bt disconnected from %s, reason %u\n", GetPeerStringByConn(conn), reason); - if (!BtPair_OobPairingInProgress && !BtManager_Restarting) { - if (DEVICE_IS_UHK80_RIGHT) { - if (peerId == PeerIdHid || peerId == PeerIdUnknown) { - BtAdvertise_Start(BtAdvertise_Type()); - USB_EnableHid(); - } - if (peerId == PeerIdDongle) { - BtAdvertise_Start(BtAdvertise_Type()); - } - if (peerId == PeerIdLeft) { - BtScan_Start(); - } - } - - if (DEVICE_IS_UHK_DONGLE && peerId == PeerIdRight) { - BtScan_Start(); - } - - if (DEVICE_IS_UHK80_LEFT && peerId == PeerIdRight) { - BtAdvertise_Start(BtAdvertise_Type()); - } - } - if (DEVICE_IS_UHK80_LEFT && peerId == PeerIdRight) { NusServer_Disconnected(); } - if (DEVICE_IS_UHK80_RIGHT && peerId == PeerIdDongle) { + if (DEVICE_IS_UHK80_RIGHT && connectionType == ConnectionType_NusDongle) { NusServer_Disconnected(); } if (DEVICE_IS_UHK80_RIGHT && peerId == PeerIdLeft) { @@ -228,47 +457,116 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) { } if (peerId != PeerIdUnknown) { - Peers[peerId].isConnected = false; - Peers[peerId].isConnectedAndConfigured = false; - DeviceState_TriggerUpdate(); + Connections_SetState(Peers[peerId].connectionId, ConnectionState_Disconnected); + bt_conn_unref(Peers[peerId].conn); + Peers[peerId].conn = NULL; + Connections[Peers[peerId].connectionId].peerId = PeerIdUnknown; + if (peerId >= PeerIdFirstHost) { + Peers[peerId].connectionId = ConnectionId_Invalid; + memset(&Peers[peerId].addr, 0, sizeof(bt_addr_le_t)); + } + } + + if (!BtPair_OobPairingInProgress && !BtManager_Restarting) { + BtManager_StartScanningAndAdvertisingAsync(); } + + if (conn == auth_conn) { + bt_conn_unref(auth_conn); + auth_conn = NULL; + } +} + +void Bt_SetConnectionConfigured(struct bt_conn* conn) { + uint8_t peerId = GetPeerIdByConn(conn); + Connections_SetState(Peers[peerId].connectionId, ConnectionState_Ready); } -bool Bt_DeviceIsConnected(device_id_t deviceId) { - switch (deviceId) { - case DeviceId_Uhk80_Left: - return Peers[PeerIdLeft].isConnectedAndConfigured; - case DeviceId_Uhk80_Right: - return Peers[PeerIdRight].isConnectedAndConfigured; - case DeviceId_Uhk_Dongle: - return Peers[PeerIdDongle].isConnectedAndConfigured; +static bool isUhkDeviceConnection(connection_type_t connectionType) { + switch (connectionType) { + case ConnectionType_NusLeft: + case ConnectionType_NusRight: + case ConnectionType_NusDongle: + return true; default: return false; } } -void Bt_SetDeviceConnected(device_id_t deviceId) { - switch (deviceId) { - case DeviceId_Uhk80_Left: - Peers[PeerIdLeft].isConnectedAndConfigured = true; - break; - case DeviceId_Uhk80_Right: - Peers[PeerIdRight].isConnectedAndConfigured = true; +static void connectAuthenticatedConnection(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, connectionType)) { + printk("Refusing connenction %d (this is not a selected connection)\n", connectionId); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return; + } + + switch (connectionType) { + case ConnectionType_NusLeft: + case ConnectionType_NusRight: + case ConnectionType_NusDongle: + connectNus(conn, connectionId, connectionType); break; - case DeviceId_Uhk_Dongle: - Peers[PeerIdDongle].isConnectedAndConfigured = true; + case ConnectionType_BtHid: + connectHid(conn, connectionId, connectionType); break; + case ConnectionType_Unknown: default: + printk("Authenticated connection is not known. Disconnecting %s", GetPeerStringByConn(conn)); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); break; } - DeviceState_TriggerUpdate(); } static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { - int8_t peerId = getPeerIdByConn(conn); + // In case of failure, disconnect + if (err || (level < BT_SECURITY_L4 && !Cfg.AllowUnsecuredConnections)) { + printk("Bt security failed: %s, level %u, err %d, disconnecting\n", GetPeerStringByConn(conn), level, err); + bt_conn_auth_cancel(conn); + return; + } + + + // Ignore connection that is being paired. At this point, the central is + // probably talking to us via an anonymous address, and it will yet change. + if (conn == auth_conn) { + printk("Bt connection secured: %s, level %u. It is auth_conn, so ignoring.\n", GetPeerStringByConn(conn), level); + 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); + connectAuthenticatedConnection(conn, connectionId, connectionType); +} + + +/* +static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { + int8_t peerId = GetPeerIdByConn(conn); + uint8_t connectionId; + uint8_t connectionType; - bool isUhkPeer = peerId == PeerIdLeft || peerId == PeerIdRight || peerId == PeerIdDongle; - if (err || (isUhkPeer && level < BT_SECURITY_L4)) { + if (peerId == PeerIdUnknown) { + connectionId = Connections_GetConnectionIdByBtAddr(bt_conn_get_dst(conn)); + connectionType = Connections_Type(connectionId); + + if (connectionType == ConnectionType_BtHid) { + connectHid(conn, connectionId, connectionType); + } + + if (connectionId == ConnectionId_Invalid && conn != auth_conn) { + printk("Unknown and non-autheticating connection secured. Disconnecting %s\n", GetPeerStringByConn(conn)); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return; + } + } else { + connectionId = Peers[peerId].connectionId; + connectionType = Connections_Type(connectionId); + } + + bool isUhkPeer = isUhkDeviceConnection(connectionType); + if (err || (isUhkPeer && level < BT_SECURITY_L4 && !Cfg.AllowUnsecuredConnections)) { printk("Bt security failed: %s, level %u, err %d, disconnecting\n", GetPeerStringByConn(conn), level, err); bt_conn_auth_cancel(conn); return; @@ -293,11 +591,14 @@ static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_s // gatt_discover(conn); // Taken from bt_central_uart.c #endif } +*/ __attribute__((unused)) static void infoLatencyParamsUpdated(struct bt_conn* conn, uint16_t interval, uint16_t latency, uint16_t timeout) { - uint8_t peerId = getPeerIdByConn(conn); - if (peerId == PeerIdUnknown || peerId == PeerIdHid) { + uint8_t peerId = GetPeerIdByConn(conn); + connection_type_t connectionType = Connections_Type(Peers[peerId].connectionId); + + if (connectionType == ConnectionType_BtHid || connectionType == ConnectionType_Unknown) { printk("BLE HID conn params: interval=%u ms, latency=%u, timeout=%u ms\n", interval * 5 / 4, latency, timeout * 10); } @@ -312,14 +613,18 @@ BT_CONN_CB_DEFINE(conn_callbacks) = { // Auth callbacks -struct bt_conn *auth_conn; - static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey) { printk("Passkey for %s: %06u\n", GetPeerStringByConn(conn), passkey); } static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey) { + if (auth_conn) { + bt_conn_disconnect(auth_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + bt_conn_unref(auth_conn); + auth_conn = NULL; + } + auth_conn = bt_conn_ref(conn); printk("Received passkey pairing inquiry.\n"); @@ -329,8 +634,9 @@ static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey) { return; } - int8_t peerId = getPeerIdByConn(conn); - bool isUhkPeer = peerId == PeerIdLeft || peerId == PeerIdRight || peerId == PeerIdDongle; + int8_t peerId = GetPeerIdByConn(conn); + connection_type_t connectionType = Connections_Type(Peers[peerId].connectionId); + bool isUhkPeer = isUhkDeviceConnection(connectionType); if (isUhkPeer || BtPair_OobPairingInProgress) { printk("refusing passkey authentification for %s\n", GetPeerStringByConn(conn)); bt_conn_auth_cancel(conn); @@ -347,6 +653,11 @@ static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey) { static void auth_cancel(struct bt_conn *conn) { printk("Pairing cancelled: peer %s\n", GetPeerStringByConn(conn)); + + if (auth_conn) { + bt_conn_unref(auth_conn); + auth_conn = NULL; + } } static void auth_oob_data_request(struct bt_conn *conn, struct bt_conn_oob_info *oob_info) { @@ -384,14 +695,36 @@ static struct bt_conn_auth_cb conn_auth_callbacks = { static void pairing_complete(struct bt_conn *conn, bool bonded) { printk("Pairing completed: %s, bonded %d\n", GetPeerStringByConn(conn), bonded); - BtPair_EndPairing(true, "Successfuly bonded!"); bt_addr_le_t addr = *bt_conn_get_dst(conn); - uint8_t peerId = getPeerIdByConn(conn); - bool isUhkPeer = peerId == PeerIdLeft || peerId == PeerIdRight || peerId == PeerIdDongle; - if (!HostConnections_IsKnownBleAddress(&addr) && !isUhkPeer) { - Bt_NewPairedDevice = true; + + if (BtPair_OobPairingInProgress) { + BtPair_EndPairing(true, "Successfuly bonded!"); + + // Disconnect it so that the connection is established only after it is identified as a host connection + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } else { + connection_id_t connectionId = Connections_GetConnectionIdByBtAddr(&addr); + connection_type_t connectionType = Connections_Type(connectionId); + + if (connectionId == ConnectionId_Invalid) { + connectionId = Connections_GetNewBtHidConnectionId(); + connectionType = ConnectionType_BtHid; + HostConnection(connectionId)->bleAddress = addr; + Bt_NewPairedDevice = true; + } + + // we have to connect from here, because central changes its address *after* setting security + connectAuthenticatedConnection(conn, connectionId, connectionType); } + + + if (auth_conn) { + bt_conn_unref(auth_conn); + auth_conn = NULL; + } + + BtManager_StartScanningAndAdvertisingAsync(); } static void bt_foreach_conn_cb(struct bt_conn *conn, void *user_data) { @@ -425,6 +758,14 @@ static struct bt_conn_auth_info_cb conn_auth_info_callbacks = { void BtConn_Init(void) { int err = 0; + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + Peers[peerId].id = peerId; + Peers[peerId].conn = NULL; + Peers[peerId].connectionId = ConnectionId_Invalid; + strcpy(Peers[peerId].name, "host0"); + Peers[peerId].name[4] = '1' + peerId - PeerIdFirstHost; + } + err = bt_conn_auth_cb_register(&conn_auth_callbacks); if (err) { printk("Failed to register authorization callbacks.\n"); @@ -455,8 +796,55 @@ void num_comp_reply(uint8_t accept) { } else { bt_conn_auth_cancel(conn); printk("Numeric Reject, conn %p\n", conn); + bt_conn_unref(auth_conn); + auth_conn = NULL; + } +} + +uint8_t BtConn_UnusedPeripheralConnectionCount() { + uint8_t count = 0; + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + if (!Peers[peerId].conn) { + count++; + } + } + if (auth_conn) { + count--; + } + return count; +} + +static void disconnectOldestHost() { + uint32_t oldestSwitchover = UINT32_MAX; + uint8_t oldestPeerId = PeerIdUnknown; + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + if (Peers[peerId].conn && Peers[peerId].lastSwitchover < oldestSwitchover) { + oldestSwitchover = Peers[peerId].lastSwitchover; + oldestPeerId = peerId; + } } - bt_conn_unref(auth_conn); - auth_conn = NULL; + if (oldestPeerId != PeerIdUnknown) { + printk("Disconnecting oldest host %d\n", oldestPeerId); + bt_conn_disconnect(Peers[oldestPeerId].conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } +} + +void BtConn_ReserveConnections() { + bool hostSelected = SelectedHostConnectionId != ConnectionId_Invalid; + bool hostActive = hostSelected && Connections_IsReady(SelectedHostConnectionId); + bool selectionIsSatisfied = !hostSelected || hostActive; + uint8_t unusedConnectionCount = BtConn_UnusedPeripheralConnectionCount(); + + if (!selectionIsSatisfied) { + // clear filters and restart advertising + BtAdvertise_Stop(); + if (unusedConnectionCount == 0) { + disconnectOldestHost(); + // Advertising will get started when the host actually gets disconnected + } else { + BtManager_StartScanningAndAdvertising(); + } + } } + diff --git a/device/src/bt_conn.h b/device/src/bt_conn.h index 911ddd70b..31eb7bfe7 100644 --- a/device/src/bt_conn.h +++ b/device/src/bt_conn.h @@ -5,18 +5,21 @@ #include #include - #include "device.h" // Macros: #define PeerNameMaxLength 8 - #define PeerIdUnknown -1 - #define PeerIdLeft 0 - #define PeerIdRight 1 - #define PeerIdDongle 2 - #define PeerIdHid 3 - #define PeerCount 4 + // please edit this in prj.conf + #define PERIPHERAL_CONNECTION_COUNT CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT + + #define PeerIdUnknown 0 + #define PeerIdLeft 1 + #define PeerIdFirst 1 + #define PeerIdRight 2 + #define PeerIdFirstHost (PeerIdRight+1) + #define PeerIdLastHost (PeerIdFirstHost+PERIPHERAL_CONNECTION_COUNT-1) + #define PeerCount (PeerIdLastHost+1) #define BLE_ADDR_LEN 6 @@ -24,14 +27,27 @@ // Typedefs: + typedef enum { + PeerType_Left, + PeerType_Right, + PeerType_Dongle, + PeerType_BleHid, + } peer_type_t; + typedef struct { uint8_t id; + uint8_t connectionId; char name[PeerNameMaxLength + 1]; bt_addr_le_t addr; - bool isConnected; - bool isConnectedAndConfigured; + struct bt_conn* conn; + uint32_t lastSwitchover; } peer_t; +typedef enum { + LatencyMode_NUS, + LatencyMode_BleHid, +} latency_mode_t; + // Variables: extern peer_t Peers[]; @@ -39,13 +55,24 @@ // Functions: + int8_t GetPeerIdByConn(const struct bt_conn *conn); char *GetPeerStringByAddr(const bt_addr_le_t *addr); char *GetPeerStringByConn(const struct bt_conn *conn); + char* GetAddrString(const bt_addr_le_t *addr); extern void num_comp_reply(uint8_t accept); - void Bt_SetDeviceConnected(device_id_t deviceId); - bool Bt_DeviceIsConnected(uint8_t deviceId); void BtConn_Init(void); void BtConn_DisconnectAll(); + void BtConn_ReserveConnections(); + void Bt_SetConnectionConfigured(struct bt_conn* conn); + uint8_t BtConn_UnusedPeripheralConnectionCount(); + + void BtConn_ListCurrentConnections(); + void BtConn_ListAllBonds(); + + static inline bool BtAddrEq(const bt_addr_le_t *a, const bt_addr_le_t *b) { + return 0 == memcmp(a->a.val, b->a.val, sizeof(a->a.val)); + } + #endif // __BT_CONN_H__ diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 7cd68f4dc..629de63c5 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -1,4 +1,6 @@ #include "bt_manager.h" +#include "connections.h" +#include "device_state.h" #include "event_scheduler.h" #include "usb/usb.h" #include "bt_advertise.h" @@ -13,6 +15,8 @@ #include "bt_scan.h" #include "settings.h" +#define BT_SHORT_RETRY_DELAY 1000 + bool BtManager_Restarting = false; static void bt_ready(int err) @@ -71,6 +75,78 @@ void BtManager_StopBt() { } } + +void BtManager_StartScanningAndAdvertisingAsync() { + uint32_t delay = 50; + EventScheduler_Reschedule(CurrentTime + delay, EventSchedulerEvent_BtStartScanningAndAdvertising, "BtManager_StartScanningAndAdvertising"); +} + +/* + * Retry logic: + * - first try: start advertising and scanning + * - second try: stop and then start advertising and scanning + * - third try: disconnect all connections, stop and then start advertising and scanning + * + * This should be called from the event scheduler. + */ +void BtManager_StartScanningAndAdvertising() { + bool success = true; + static uint8_t try = 0; + int err = 0; + + if (try > 0) { + if (DEVICE_IS_UHK80_LEFT || DEVICE_IS_UHK80_RIGHT) { + BtAdvertise_Stop(); + } + + if (DEVICE_IS_UHK80_RIGHT || DEVICE_IS_UHK_DONGLE) { + BtScan_Stop(); + } + } + + bool leftShouldAdvertise = DEVICE_IS_UHK80_LEFT && Peers[PeerIdRight].conn == NULL; + bool rightShouldAdvertise = DEVICE_IS_UHK80_RIGHT && true; + bool shouldAdvertise = leftShouldAdvertise || rightShouldAdvertise; + + bool rightShouldScan = DEVICE_IS_UHK80_RIGHT && !DeviceState_IsTargetConnected(ConnectionTarget_Left); + bool dongleShouldScan = DEVICE_IS_UHK_DONGLE && Peers[PeerIdRight].conn == NULL; + bool shouldScan = rightShouldScan || dongleShouldScan; + + if (shouldAdvertise || shouldScan) { + const char* label = ""; + if (shouldAdvertise && shouldScan) { + label = "advertising and scanning"; + } else if (shouldAdvertise) { + label = "advertising"; + } else if (shouldScan) { + label = "scanning"; + } + printk("Starting %s, try %d!\n", label, try); + } + + if (leftShouldAdvertise || rightShouldAdvertise) { + err = BtAdvertise_Start(BtAdvertise_Type()); + success &= err == 0; + } + + if (rightShouldScan || dongleShouldScan) { + err = BtScan_Start(); + success &= err == 0; + } + + if (!success && try > 0) { + BtConn_DisconnectAll(); + } + + if (success) { + try = 0; + } else { + try++; + uint32_t delay = try < 3 ? BT_SHORT_RETRY_DELAY : 10000; + EventScheduler_Reschedule(CurrentTime + delay, EventSchedulerEvent_BtStartScanningAndAdvertising, "BtManager_StartScanningAndAdvertising failed"); + } +} + void BtManager_RestartBt() { printk("Going to reset bluetooth stack\n"); @@ -110,5 +186,5 @@ void BtManager_RestartBt() { } void BtManager_RestartBtAsync() { - EventScheduler_Schedule(k_uptime_get()+10, EventSchedulerEvent_RestartBt, "Restart bluetooth"); + EventScheduler_Schedule(k_uptime_get()+BT_SHORT_RETRY_DELAY, EventSchedulerEvent_RestartBt, "Restart bluetooth"); } diff --git a/device/src/bt_manager.h b/device/src/bt_manager.h index 76c5b6532..e9ce24399 100644 --- a/device/src/bt_manager.h +++ b/device/src/bt_manager.h @@ -21,5 +21,7 @@ void BtManager_StartBt(); void BtManager_StopBt(); void BtManager_RestartBt(); + void BtManager_StartScanningAndAdvertising(); + void BtManager_StartScanningAndAdvertisingAsync(); #endif // __BT_MANAGER_H__ diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index 9be643bee..7e4298b1b 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -110,15 +110,12 @@ static void deleteBond(const struct bt_bond_info *info) { struct bt_conn* conn; - if (bt_addr_le_cmp(&Peers[PeerIdLeft].addr, &info->addr) == 0) { + if (BtAddrEq(&Peers[PeerIdLeft].addr, &info->addr)) { settings_delete("uhk/addr/left"); } - if (bt_addr_le_cmp(&Peers[PeerIdRight].addr, &info->addr) == 0) { + if (BtAddrEq(&Peers[PeerIdRight].addr, &info->addr)) { settings_delete("uhk/addr/right"); } - if (bt_addr_le_cmp(&Peers[PeerIdDongle].addr, &info->addr) == 0) { - settings_delete("uhk/addr/dongle"); - } // Get the connection object if the device is currently connected conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &info->addr); @@ -144,7 +141,7 @@ static void bt_foreach_bond_cb_delete(const struct bt_bond_info *info, void *use { struct delete_args_t* args = (struct delete_args_t*)user_data; - if (!args->all && bt_addr_le_cmp(args->addr, &info->addr) != 0) { + if (!args->all && !BtAddrEq(args->addr, &info->addr) ) { char addr[32]; bt_addr_le_to_str(&info->addr, addr, sizeof(addr)); printk("Not deleting bond for %s\n", addr); @@ -195,7 +192,7 @@ void checkBondedDevice(const struct bt_bond_info *info, void *user_data) { bt_addr_le_to_str(&info->addr, addr, sizeof(addr)); char ref[32]; bt_addr_le_to_str(args->addr, ref, sizeof(ref)); - if (bt_addr_le_cmp(&info->addr, args->addr) == 0) { + if (BtAddrEq(&info->addr, args->addr)) { *args->bonded = true; printk("Device %s is bonded, ref %s\n", addr, ref); } diff --git a/device/src/bt_scan.c b/device/src/bt_scan.c index 572dc550f..39b4cde55 100644 --- a/device/src/bt_scan.c +++ b/device/src/bt_scan.c @@ -2,6 +2,8 @@ #include "bt_conn.h" #include "bt_pair.h" #include "device.h" +#include "bt_pair.h" +#include "event_scheduler.h" #include "host_connection.h" static void scan_filter_match(struct bt_scan_device_info *device_info, @@ -54,8 +56,6 @@ int BtScan_Stop(void) { err = bt_scan_stop(); if (err) { printk("Stop LE scan failed (err %d)\n", err); - } else { - printk("Scanning successfully stopped.\n"); } bt_scan_filter_disable(); @@ -91,8 +91,6 @@ int BtScan_Start(void) { err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE); if (err) { printk("Scanning failed to start (err %d)\n", err); - } else { - printk("Scanning successfully started\n"); } return err; diff --git a/device/src/connections.c b/device/src/connections.c new file mode 100644 index 000000000..1ecf15c1e --- /dev/null +++ b/device/src/connections.c @@ -0,0 +1,386 @@ +#include "connections.h" +#include "bt_conn.h" +#include "device.h" +#include "host_connection.h" +#include "device_state.h" +#include "host_connection.h" +#include "keyboard/oled/widgets/widget_store.h" +#include "messenger.h" +#include "state_sync.h" +#include +#include "connections.h" +#include "stubs.h" +#include "usb_compatibility.h" + +connection_t Connections[ConnectionId_Count] = { + [ConnectionId_UsbHidRight] = { .isAlias = true }, + [ConnectionId_BtHid] = { .isAlias = true }, +}; + +connection_id_t ActiveHostConnectionId = ConnectionId_Invalid; +connection_id_t SelectedHostConnectionId = ConnectionId_Invalid; + +static connection_id_t resolveAliases(connection_id_t connectionId) { + if (!Connections[connectionId].isAlias) { + return connectionId; + } + + if (connectionId == ConnectionId_UsbHidRight) { + for (uint8_t i = ConnectionId_HostConnectionFirst; i <= ConnectionId_HostConnectionLast; i++) { + if (HostConnection(i)->type == HostConnectionType_UsbHidRight) { + return i; + } + } + } + + if (connectionId == ConnectionId_BtHid) { + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + if (Peers[peerId].conn && Connections_Type(Peers[peerId].connectionId) == ConnectionType_BtHid) { + return Peers[peerId].connectionId; + } + } + } + + return connectionId; +} + +static const char* getStateString(connection_state_t state) { + switch (state) { + case ConnectionState_Disconnected: + return "Disconnected"; + case ConnectionState_Connected: + return "Connected"; + case ConnectionState_Ready: + return "Ready"; + default: + return "Invalid"; + } +} + +static void reportConnectionState(connection_id_t connectionId, const char* message) { + connectionId = resolveAliases(connectionId); + + +#define CASE_FOR(NAME) case ConnectionId_##NAME: name = #NAME; break; + + bool isHostConnection = false; + const char* name = "Invalid"; + + switch (connectionId) { + case ConnectionId_HostConnectionFirst ... ConnectionId_HostConnectionLast: + isHostConnection = true; + name = "HostConnection"; + break; + CASE_FOR(UsbHidLeft); + CASE_FOR(UsbHidRight); + CASE_FOR(UartLeft); + CASE_FOR(UartRight); + CASE_FOR(NusServerLeft); + CASE_FOR(NusServerRight); + CASE_FOR(NusClientRight); + CASE_FOR(BtHid); + CASE_FOR(Invalid); + CASE_FOR(Count); + } + + int8_t peerId = Connections[connectionId].peerId; + const char* peerLabel = ""; + const char* peerString = ""; + if (peerId != PeerIdUnknown) { + peerLabel = ", Peer "; + peerString = Peers[peerId].name; + } + + const char* activeLabel = connectionId == ActiveHostConnectionId ? ", Active" : ""; + const char* selectedLabel = connectionId == SelectedHostConnectionId ? ", Selected" : ""; + + if (isHostConnection) { + host_connection_t* hc = HostConnection(connectionId); + printk("%s: %s%d(%.*s, %s)%s%s%s%s\n", message, name, connectionId - ConnectionId_HostConnectionFirst, hc->name.end - hc->name.start, hc->name.start, getStateString(Connections[connectionId].state), peerLabel, peerString, activeLabel, selectedLabel); + } else { + printk("%s: %s(%s)%s%s%s%s\n", message, name, getStateString(Connections[connectionId].state), peerLabel, peerString, activeLabel, selectedLabel); + } +} + +void Connections_ReportState(connection_id_t connectionId) { + reportConnectionState(connectionId, "Connection state"); +} + +connection_state_t Connections_GetState(connection_id_t connectionId) { + connectionId = resolveAliases(connectionId); + return Connections[connectionId].state; +} + +void Connections_SetState(connection_id_t connectionId, connection_state_t state) { + connectionId = resolveAliases(connectionId); + + if ( Connections[connectionId].state != state ) { + Connections[connectionId].state = state; + reportConnectionState(connectionId, "Connection state"); + if (Connections_Target(connectionId) == ConnectionTarget_Host && DEVICE_IS_UHK80_RIGHT) { + Connections_HandleSwitchover(connectionId, false); + // Connections_HandleSwitchover calls DeviceState_Update for us + } else { + DeviceState_Update(Connections_Target(connectionId)); + } + } +} + +void Connections_SetPeerId(connection_id_t connectionId, uint8_t peerId) { + Connections[connectionId].peerId = peerId; +} + +connection_type_t Connections_Type(connection_id_t connectionId) { + switch (connectionId) { + case ConnectionId_HostConnectionFirst ... ConnectionId_HostConnectionLast: { + host_connection_type_t hostConnectionType = HostConnection(connectionId)->type; + switch (hostConnectionType) { + case HostConnectionType_NewBtHid: + return ConnectionType_BtHid; + default: + return (connection_type_t)hostConnectionType; + } + } + case ConnectionId_UsbHidLeft: + return ConnectionType_UsbHidLeft; + case ConnectionId_UsbHidRight: + return ConnectionType_UsbHidRight; + case ConnectionId_UartLeft: + return ConnectionType_UartLeft; + case ConnectionId_UartRight: + return ConnectionType_UartRight; + case ConnectionId_NusServerLeft: + return ConnectionType_NusLeft; + case ConnectionId_NusServerRight: + return ConnectionType_NusRight; + case ConnectionId_NusClientRight: + return ConnectionType_NusRight; + case ConnectionId_BtHid: + return ConnectionType_BtHid; + case ConnectionId_Count: + case ConnectionId_Invalid: + return ConnectionType_Unknown; + break; + } + printk("Unhandled connectionId %d\n", connectionId); + return ConnectionType_Unknown; +} + +connection_target_t Connections_Target(connection_id_t connectionId) { + switch (connectionId) { + case ConnectionId_HostConnectionFirst ... ConnectionId_HostConnectionLast: + switch (HostConnection(connectionId)->type) { + case HostConnectionType_UsbHidLeft: + case HostConnectionType_UsbHidRight: + return ConnectionTarget_Host; + case HostConnectionType_BtHid: + case HostConnectionType_NewBtHid: + return ConnectionTarget_Host; + case HostConnectionType_Dongle: + return ConnectionTarget_Host; + case HostConnectionType_Empty: + case HostConnectionType_Count: + return ConnectionTarget_None; + } + case ConnectionId_UartLeft: + case ConnectionId_NusServerLeft: + return ConnectionTarget_Left; + + case ConnectionId_UartRight: + case ConnectionId_NusServerRight: + case ConnectionId_NusClientRight: + return ConnectionTarget_Right; + case ConnectionId_UsbHidRight: + case ConnectionId_UsbHidLeft: + return ConnectionTarget_Host; + case ConnectionId_BtHid: + return ConnectionTarget_Host; + case ConnectionId_Count: + case ConnectionId_Invalid: + return ConnectionTarget_None; + } + printk("Unhandled connectionId %d\n", connectionId); + return ConnectionTarget_None; +} + +static connection_id_t getConnIdByPeer(const bt_addr_le_t *addr, bool searchHosts) { + uint8_t stopAt = searchHosts ? PeerCount : PeerIdFirstHost; + for (uint8_t peerId = 0; peerId < stopAt; peerId++) { + bool addressMatches = BtAddrEq(addr, &Peers[peerId].addr); + bool isValidPeer = peerId < PeerIdFirstHost || Peers[peerId].conn; + if (addressMatches && isValidPeer) { + return Peers[peerId].connectionId; + } + } + + return ConnectionId_Invalid; +} + +static connection_id_t getConnIdByAddr(const bt_addr_le_t *addr) { + for (uint8_t connectionId = ConnectionId_HostConnectionFirst; connectionId <= ConnectionId_HostConnectionLast; connectionId++) { + host_connection_t *hostConnection = HostConnection(connectionId); + switch (hostConnection->type) { + case HostConnectionType_NewBtHid: + case HostConnectionType_Dongle: + case HostConnectionType_BtHid: + if (BtAddrEq(addr, &hostConnection->bleAddress)) { + return connectionId; + } + break; + case HostConnectionType_Empty: + case HostConnectionType_UsbHidLeft: + case HostConnectionType_UsbHidRight: + case HostConnectionType_Count: + break; + } + } + + return ConnectionId_Invalid; +} + +connection_id_t Connections_GetConnectionIdByBtAddr(const bt_addr_le_t *addr) { + connection_id_t res = ConnectionId_Invalid; + + res = getConnIdByPeer(addr, true); + if (res != ConnectionId_Invalid) { + return res; + } + + res = getConnIdByAddr(addr); + if (res != ConnectionId_Invalid) { + return res; + } + + return ConnectionId_Invalid; +} + +connection_id_t Connections_GetConnectionIdByHostAddr(const bt_addr_le_t *addr) { + connection_id_t res = ConnectionId_Invalid; + + res = getConnIdByPeer(addr, false); + if (res != ConnectionId_Invalid) { + return res; + } + + res = getConnIdByAddr(addr); + if (res != ConnectionId_Invalid) { + return res; + } + + return ConnectionId_Invalid; +} + +connection_id_t Connections_GetNewBtHidConnectionId() { + for (uint8_t connectionId = ConnectionId_HostConnectionFirst; connectionId <= ConnectionId_HostConnectionLast; connectionId++) { + host_connection_t *hostConnection = HostConnection(connectionId); + switch (hostConnection->type) { + case HostConnectionType_NewBtHid: + return connectionId; + break; + default: + break; + } + } + return ConnectionId_Invalid; +} + +void Connections_MoveConnection(uint8_t peerId, connection_id_t oldConnectionId, connection_id_t newConnectionId) { + bool isActiveConnection = oldConnectionId == ActiveHostConnectionId; + bool isSelectedConnection = oldConnectionId == SelectedHostConnectionId; + + Connections[newConnectionId].peerId = Connections[oldConnectionId].peerId; + Connections[newConnectionId].state = Connections[oldConnectionId].state; + + Peers[Connections[oldConnectionId].peerId].connectionId = newConnectionId; + + Connections[oldConnectionId].peerId = PeerIdUnknown; + Connections[oldConnectionId].state = ConnectionState_Disconnected; + + ActiveHostConnectionId = isActiveConnection ? newConnectionId : ActiveHostConnectionId; + SelectedHostConnectionId = isSelectedConnection ? newConnectionId : ActiveHostConnectionId; + + if (isActiveConnection) { + WIDGET_REFRESH(&TargetWidget); + } +} + +bool Connections_IsHostConnection(connection_id_t connectionId) { + return ConnectionId_HostConnectionFirst <= connectionId && connectionId <= ConnectionId_HostConnectionLast; +} + +bool Connections_IsReady(connection_id_t connectionId) { + connectionId = resolveAliases(connectionId); + return Connections[connectionId].state == ConnectionState_Ready; +} + +bool Connections_IsActiveHostConnection(connection_id_t connectionId) { + return resolveAliases(connectionId) == ActiveHostConnectionId; +} + +static void setCurrentDongleToStandby() { + if (Connections_IsHostConnection(ActiveHostConnectionId) && HostConnection(ActiveHostConnectionId)->type == HostConnectionType_Dongle) { + bool standby = true; + Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_StateSync, StateSyncPropertyId_DongleStandby, (const uint8_t*)&standby, 1); + } +} + +static void switchOver(connection_id_t connectionId) { + ActiveHostConnectionId = connectionId; + Peers[Connections[connectionId].peerId].lastSwitchover = k_uptime_get_32(); + + if (connectionId == SelectedHostConnectionId) { + SelectedHostConnectionId = ConnectionId_Invalid; + } + + UsbCompatibility_UpdateKeyboardLedsState(); +} + +void Connections_HandleSwitchover(connection_id_t connectionId, bool forceSwitch) { + connectionId = resolveAliases(connectionId); + bool isReady = Connections_IsReady(connectionId); + + // Unset if disconnected + if (connectionId == ActiveHostConnectionId && !isReady) { + ActiveHostConnectionId = ConnectionId_Invalid; + } + + // Decide whether to switch to the supplied connection + if (isReady && Connections_IsHostConnection(connectionId)) { + host_connection_t *hostConnection = HostConnection(connectionId); + bool connectionIsSelected = SelectedHostConnectionId == connectionId; + bool noHostIsConnected = ActiveHostConnectionId == ConnectionId_Invalid; + if (hostConnection->switchover || noHostIsConnected || forceSwitch || connectionIsSelected) { + setCurrentDongleToStandby(); + switchOver(connectionId); + reportConnectionState(connectionId, "Connection state"); + } + } + + // If current target is not usable + if (ActiveHostConnectionId == ConnectionId_Invalid) { + // Find the first ready host connection + for (uint8_t i = ConnectionId_HostConnectionFirst; i <= ConnectionId_HostConnectionLast; i++) { + if (Connections[i].state == ConnectionState_Ready) { + switchOver(i); + reportConnectionState(i, "Connection state"); + break; + } + } + } + + DeviceState_Update(Connections_Target(connectionId)); +} + +connection_target_t Connections_DeviceToTarget(device_id_t deviceId) { + switch (deviceId) { + case DeviceId_Uhk80_Left: + return ConnectionTarget_Left; + case DeviceId_Uhk80_Right: + return ConnectionTarget_Right; + case DeviceId_Uhk_Dongle: + return ConnectionTarget_Host; + default: + return ConnectionTarget_None; + } +} + diff --git a/device/src/connections.h b/device/src/connections.h new file mode 100644 index 000000000..4474c79ba --- /dev/null +++ b/device/src/connections.h @@ -0,0 +1,99 @@ +#ifndef __CONNECTIONS_H__ +#define __CONNECTIONS_H__ + +// Includes: + + #include + #include + #include "device.h" + #include "host_connection.h" + +// Macros: + +// Typedefs: + + typedef enum { + ConnectionType_Empty = HostConnectionType_Empty, + ConnectionType_UsbHidRight = HostConnectionType_UsbHidRight, + ConnectionType_UsbHidLeft = HostConnectionType_UsbHidLeft, + ConnectionType_BtHid = HostConnectionType_BtHid, + ConnectionType_NewBtHid = HostConnectionType_NewBtHid, + ConnectionType_NusDongle = HostConnectionType_Dongle, + ConnectionType_NusLeft = HostConnectionType_Count, + ConnectionType_NusRight, + ConnectionType_UartLeft, + ConnectionType_UartRight, + ConnectionType_Unknown, + } connection_type_t; + + typedef enum { + ConnectionId_Invalid, + ConnectionId_HostConnectionFirst, + ConnectionId_HostConnectionLast = ConnectionId_HostConnectionFirst + HOST_CONNECTION_COUNT_MAX - 1, + ConnectionId_UsbHidLeft, + ConnectionId_UsbHidRight, //alias to some host connection + ConnectionId_UartLeft, + ConnectionId_UartRight, + ConnectionId_NusServerLeft, + ConnectionId_NusServerRight, + ConnectionId_NusClientRight, + ConnectionId_BtHid, //alias to some host connection + ConnectionId_Count, + } connection_id_t; + + typedef enum { + ConnectionTarget_None, + ConnectionTarget_Left, + ConnectionTarget_Right, + ConnectionTarget_Host, + ConnectionTarget_Count, + } connection_target_t; + + typedef enum { + ConnectionChannel_Uart, + ConnectionChannel_Bt, + ConnectionChannel_Usb, + } connection_channel_t; + + typedef enum { + ConnectionState_Disconnected, + ConnectionState_Connected, + ConnectionState_Ready, + ConnectionState_Count, + } connection_state_t; + + typedef struct { + uint8_t peerId; + connection_state_t state; + bool isAlias; + } ATTR_PACKED connection_t; + +// Variables: + + extern connection_id_t ActiveHostConnectionId; + extern connection_id_t SelectedHostConnectionId; + extern connection_t Connections[ConnectionId_Count]; + +// Functions: + + connection_state_t Connections_GetState(connection_id_t connectionId); + void Connections_SetState(connection_id_t connectionId, connection_state_t state); + void Connections_ReportState(connection_id_t connectionId); + void Connections_SetPeerId(connection_id_t connectionId, uint8_t peerId); + connection_type_t Connections_Type(connection_id_t connectionId); + connection_target_t Connections_Target(connection_id_t connectionId); + connection_target_t Connections_DeviceToTarget(device_id_t deviceId); + + connection_id_t Connections_GetNewBtHidConnectionId(); + connection_id_t Connections_GetConnectionIdByBtAddr(const bt_addr_le_t *addr); + connection_id_t Connections_GetConnectionIdByHostAddr(const bt_addr_le_t *addr); + + void Connections_MoveConnection(uint8_t peerId, connection_id_t oldConnectionId, connection_id_t newConnectionId); + + bool Connections_IsHostConnection(connection_id_t connectionId); + bool Connections_IsReady(connection_id_t connectionId); + bool Connections_IsActiveHostConnection(connection_id_t connectionId); + + void Connections_HandleSwitchover(connection_id_t connectionId, bool forceSwitch); + +#endif // __CONNECTIONS_H__ diff --git a/device/src/device_state.c b/device/src/device_state.c index 9ed03d174..621ea7e54 100644 --- a/device/src/device_state.c +++ b/device/src/device_state.c @@ -1,5 +1,7 @@ #include "device_state.h" #include "bt_conn.h" +#include "bt_manager.h" +#include "connections.h" #include "device.h" #include "keyboard/uart.h" #include "keyboard/oled/widgets/widgets.h" @@ -10,27 +12,25 @@ #include "power_mode.h" #include "dongle_leds.h" -static connection_type_t isConnected[ConnectionId_Count] = {}; +static connection_id_t targetsConnection[ConnectionTarget_Count] = {}; -bool DeviceState_IsDeviceConnected(device_id_t deviceId) { - return deviceId == DEVICE_ID || isConnected[deviceId - DEVICE_STATE_FIRST_DEVICE] != ConnectionType_None; -} - -bool DeviceState_IsConnected(connection_id_t connectionId) { - return isConnected[connectionId] != ConnectionType_None; -} +bool DongleStandby = false; -void handleStateTransition(connection_id_t remoteId, bool connected) { - switch (DEVICE_ID) { - case DeviceId_Uhk80_Left: - if (remoteId == ConnectionId_Right && connected) { +// void handleStateTransition(connection_id_t remoteId, bool connected) { +void handleStateTransition(connection_target_t remote, connection_id_t connectionId, bool connected) { + connection_target_t local = Connections_DeviceToTarget(DEVICE_ID); + switch (local) { + case ConnectionTarget_Left: + if (remote == ConnectionTarget_Right && connected) { StateSync_ResetRightLeftLink(true); } break; - case DeviceId_Uhk80_Right: - switch (remoteId) { - case ConnectionId_Left: + case ConnectionTarget_Right: + switch (remote) { + case ConnectionTarget_Left: +#if DEVICE_HAS_OLED Widget_Refresh(&StatusWidget); +#endif if (connected) { StateSync_ResetRightLeftLink(true); ModuleConnectionStates[UhkModuleDriverId_LeftKeyboardHalf].moduleId = ModuleId_LeftKeyboardHalf; @@ -52,28 +52,26 @@ void handleStateTransition(connection_id_t remoteId, bool connected) { EventVector_Set(EventVector_StateMatrix); EventVector_WakeMain(); } + + BtManager_StartScanningAndAdvertisingAsync(); } break; - case ConnectionId_Dongle: - if (connected) { + case ConnectionTarget_Host: + if (HostConnection(connectionId)->type == HostConnectionType_Dongle && connected) { StateSync_ResetRightDongleLink(true); } +#if DEVICE_HAS_OLED Widget_Refresh(&TargetWidget); - EventScheduler_Reschedule(CurrentTime + POWER_MODE_UPDATE_DELAY, EventSchedulerEvent_PowerMode, "update sleep mode from device state dongle"); - break; - case ConnectionId_UsbHid: - Widget_Refresh(&TargetWidget); - break; - case ConnectionId_BluetoothHid: - Widget_Refresh(&TargetWidget); - EventScheduler_Reschedule(CurrentTime + POWER_MODE_UPDATE_DELAY, EventSchedulerEvent_PowerMode, "update sleep mode from device state bluetooth hid"); +#endif + EventScheduler_Reschedule(CurrentTime + POWER_MODE_UPDATE_DELAY, EventSchedulerEvent_PowerMode, "update sleep mode from device state"); break; default: break; } break; - case DeviceId_Uhk_Dongle: - if (remoteId == ConnectionId_Right && connected) { + case ConnectionTarget_Host: + // Dongle actually + if (remote == ConnectionTarget_Right && connected) { StateSync_ResetRightDongleLink(true); } DongleLeds_Update(); @@ -83,48 +81,60 @@ void handleStateTransition(connection_id_t remoteId, bool connected) { } } -void DeviceState_SetConnection(connection_id_t connection, connection_type_t type) { - if (isConnected[connection] != type) { - isConnected[connection] = type; - handleStateTransition(connection, isConnected[connection] != ConnectionType_None); - } -} +#define RETURN_IF_CONNECTED(CONNECTION_ID) if (Connections[CONNECTION_ID].state == ConnectionState_Ready) { return CONNECTION_ID; } -static connection_id_t deviceToConnection(device_id_t deviceId) { - switch (deviceId) { - case DeviceId_Uhk80_Left: - return ConnectionId_Left; - case DeviceId_Uhk80_Right: - return ConnectionId_Right; - case DeviceId_Uhk_Dongle: - return ConnectionId_Dongle; +static connection_id_t findPreferredConnection(connection_target_t target) { + switch (target) { + case ConnectionTarget_Left: + if (DEVICE_IS_UHK80_LEFT) { + return true; + } + RETURN_IF_CONNECTED(ConnectionId_UartLeft); + RETURN_IF_CONNECTED(ConnectionId_NusServerLeft); + return ConnectionId_Invalid; + case ConnectionTarget_Right: + if (DEVICE_IS_UHK80_RIGHT) { + return true; + } + RETURN_IF_CONNECTED(ConnectionId_UartRight); + RETURN_IF_CONNECTED(ConnectionId_NusServerRight); + RETURN_IF_CONNECTED(ConnectionId_NusClientRight); + return ConnectionId_Invalid; + case ConnectionTarget_Host: + return ActiveHostConnectionId; default: return ConnectionId_Invalid; } } -void DeviceState_TriggerUpdate() { - for (uint8_t devId = DEVICE_STATE_FIRST_DEVICE; devId <= DEVICE_STATE_LAST_DEVICE; devId++) { - connection_type_t newIsConnected = ConnectionType_None; +void DeviceState_Update(uint8_t connectionTarget) { + connection_id_t oldConnection = targetsConnection[connectionTarget]; + connection_id_t newConnection = findPreferredConnection(connectionTarget); - if (Bt_DeviceIsConnected(devId)) { - newIsConnected = ConnectionType_Bt; - } + if (oldConnection != newConnection) { + targetsConnection[connectionTarget] = newConnection; - if (devId == DeviceId_Uhk80_Left && DEVICE_ID == DeviceId_Uhk80_Right && Uart_IsConnected()) { - newIsConnected = ConnectionType_Uart; - } + handleStateTransition(connectionTarget, newConnection, Connections[newConnection].state == ConnectionState_Ready); + } +} - if (devId == DeviceId_Uhk80_Right && DEVICE_ID == DeviceId_Uhk80_Left && Uart_IsConnected()) { - newIsConnected = ConnectionType_Uart; - } +bool DeviceState_IsDeviceConnected(device_id_t deviceId) { + connection_target_t target = Connections_DeviceToTarget(deviceId); + connection_id_t connectionId = findPreferredConnection(target); - connection_id_t conId = deviceToConnection(devId); - connection_type_t oldIsConnected = isConnected[conId]; - isConnected[conId] = newIsConnected; - if (newIsConnected != oldIsConnected) { - handleStateTransition(conId, newIsConnected); + if (deviceId == DeviceId_Uhk_Dongle) { + host_connection_t* hostConnection = HostConnection(connectionId); + if (hostConnection == NULL || hostConnection->type != HostConnectionType_Dongle) { + return false; } } + + return connectionId != ConnectionId_Invalid && Connections[connectionId].state == ConnectionState_Ready; +} + +bool DeviceState_IsTargetConnected(uint8_t target) { + connection_id_t connectionId = findPreferredConnection(target); + + return connectionId != ConnectionId_Invalid && Connections[connectionId].state == ConnectionState_Ready; } diff --git a/device/src/device_state.h b/device/src/device_state.h index e1b4b6738..2352887e7 100644 --- a/device/src/device_state.h +++ b/device/src/device_state.h @@ -13,6 +13,7 @@ #define DEVICE_STATE_LAST_DEVICE DeviceId_Uhk_Dongle +/* typedef enum { ConnectionType_None, ConnectionType_Uart, @@ -30,16 +31,18 @@ ConnectionId_Count, ConnectionId_Invalid = ConnectionId_Count, } connection_id_t; + */ // Typedefs: +// Variables: + + extern bool DongleStandby; + // Functions: - bool DeviceState_IsConnected(connection_id_t connection); + bool DeviceState_IsTargetConnected(uint8_t target); bool DeviceState_IsDeviceConnected(device_id_t deviceId); - void DeviceState_SetConnection(connection_id_t connection, connection_type_t type); - void DeviceState_TriggerUpdate(); - -// Variables: + void DeviceState_Update(uint8_t connectionTarget); #endif // __DEVICE_STATE_H__ diff --git a/device/src/dongle_leds.c b/device/src/dongle_leds.c index f423d6e86..756f09e33 100644 --- a/device/src/dongle_leds.c +++ b/device/src/dongle_leds.c @@ -1,3 +1,4 @@ +#include "connections.h" #include "device.h" #if DEVICE_IS_UHK_DONGLE #include "dongle_leds.h" @@ -8,7 +9,6 @@ const struct pwm_dt_spec red_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(red_pwm_led)); const struct pwm_dt_spec green_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(green_pwm_led)); const struct pwm_dt_spec blue_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(blue_pwm_led)); - // There is also the following zero led. // const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0_green), gpios); // gpio_pin_configure_dt(&led0, GPIO_OUTPUT); @@ -21,22 +21,31 @@ void set_dongle_led(const struct pwm_dt_spec *device, uint8_t percentage) { } -void DongleLeds_Set(bool r, bool g, bool b) { - set_dongle_led(&red_pwm_led, r ? 100 : 0); - set_dongle_led(&green_pwm_led, g ? 100 : 0); - set_dongle_led(&blue_pwm_led, b ? 100 : 0); +void DongleLeds_Set(uint8_t r, uint8_t g, uint8_t b) { + set_dongle_led(&red_pwm_led, r); + set_dongle_led(&green_pwm_led, g); + set_dongle_led(&blue_pwm_led, b); } void DongleLeds_Update(void) { - if (DeviceState_IsConnected(ConnectionId_Right)) { - DongleLeds_Set(false, true, false); - return; + if (Connections_IsReady(ConnectionId_NusServerRight)) { + if (!DongleStandby) { + // connected and receiving: green + DongleLeds_Set(0, 100, 0); + return; + } else { + // connected in standby: blue + DongleLeds_Set(0, 100, 100); + return; + } } if (RightAddressIsSet) { - DongleLeds_Set(false, false, true); + // trying to connect: violet + DongleLeds_Set(100, 0, 70); return; } - DongleLeds_Set(true, false, false); + // disconnected: red + DongleLeds_Set(100, 0, 0); } #endif // DEVICE_IS_UHK_DONGLE diff --git a/device/src/dongle_leds.h b/device/src/dongle_leds.h index a5e1106f5..785068845 100644 --- a/device/src/dongle_leds.h +++ b/device/src/dongle_leds.h @@ -12,7 +12,7 @@ // Functions: - extern void DongleLeds_Set(bool r, bool g, bool b); + extern void DongleLeds_Set(uint8_t r, uint8_t g, uint8_t b); extern void DongleLeds_Update(void); extern void set_dongle_led(const struct pwm_dt_spec *device, uint8_t percentage); diff --git a/device/src/keyboard/charger.c b/device/src/keyboard/charger.c index 2c85073bc..39c9d0035 100644 --- a/device/src/keyboard/charger.c +++ b/device/src/keyboard/charger.c @@ -126,10 +126,6 @@ void Charger_UpdateBatteryState() { #ifdef CONFIG_BT_BAS bt_bas_set_battery_level(batteryState.batteryPercentage); -#endif -#if DEVICE_HAS_BATTERY - extern void HID_SetBatteryStatus(uint8_t remaining_capacity, bool charging); - HID_SetBatteryStatus(batteryState.batteryPercentage, batteryState.batteryCharging); #endif } } diff --git a/device/src/keyboard/key_scanner.c b/device/src/keyboard/key_scanner.c index d85b30d6c..ea2d75d9b 100644 --- a/device/src/keyboard/key_scanner.c +++ b/device/src/keyboard/key_scanner.c @@ -22,6 +22,7 @@ #include "attributes.h" #include "layouts/key_layout.h" #include "layouts/key_layout_80_to_universal.h" +#include "test_switches.h" // Thread definitions @@ -108,8 +109,6 @@ static void scanKeys() { memset(compressedBuffer, 0, compressedLength); } - backlighting_mode_t currentBacklightingMode = Ledmap_GetEffectiveBacklightMode(); - uint8_t slotId = DEVICE_IS_UHK80_LEFT ? SlotId_LeftKeyboardHalf : SlotId_RightKeyboardHalf; for (uint8_t rowId=0; rowId= scalingFadeStep) { + currentScaling += scalingFadeStep; + } else { + currentScaling = KeyBacklightBrightness; + } + } + + if (currentScaling > KeyBacklightBrightness) { + if (currentScaling > KeyBacklightBrightness && currentScaling - KeyBacklightBrightness >= scalingFadeStep) { + currentScaling -= scalingFadeStep; + } else { + currentScaling = KeyBacklightBrightness; + } + } +} + +void UpdateLedAudioRegisters(uint8_t phaseDelay, uint8_t spreadSpectrum, uint8_t pwmFrequency) { + k_mutex_lock(&SpiMutex, K_FOREVER); + + // Set phase delay + setLedsCs(true); + writeSpi(LedPagePrefix | 2); + writeSpi(0x02); + writeSpi(phaseDelay | 0b00110011); + setLedsCs(false); + printk("Phase delay: %d\n", phaseDelay); + + // Set spread spectrum + setLedsCs(true); + writeSpi(LedPagePrefix | 2); + writeSpi(0x25); + writeSpi(spreadSpectrum); + setLedsCs(false); + printk("Spread spectrum: %d\n", spreadSpectrum); + + // Enter test mode to set PWM frequency + setLedsCs(true); + writeSpi(LedPagePrefix | 2); + writeSpi(0x52); + writeSpi(0xe0); + writeSpi(1); + setLedsCs(false); + + // Set PWM frequency + setLedsCs(true); + writeSpi(LedPagePrefix | 2); + writeSpi(0x52); + writeSpi(0xe2); + writeSpi(pwmFrequency); + setLedsCs(false); + printk("PWM frequency: %d\n", pwmFrequency); + + k_mutex_unlock(&SpiMutex); +} + void ledUpdater() { k_sleep(K_MSEC(100)); while (true) { @@ -66,11 +131,11 @@ void ledUpdater() { writeSpi(0b10110011); setLedsCs(false); - // Enable spread spectrum with 15% range and 1980us cycle time, which substantially reduces audible noise + // Enable spread spectrum with 5% range and 1980us cycle time, which substantially reduces audible noise setLedsCs(true); writeSpi(LedPagePrefix | 2); writeSpi(0x25); - writeSpi(0x14); + writeSpi(0x10); setLedsCs(false); setLedsCs(true); @@ -91,7 +156,7 @@ void ledUpdater() { writeSpi(LedPagePrefix | 1); writeSpi(0x00); for (int i=0; i<255; i++) { - writeSpi(KeyBacklightBrightness); + writeSpi(currentScaling); } setLedsCs(false); @@ -101,11 +166,16 @@ void ledUpdater() { k_sleep(K_FOREVER); } - if (KeyBacklightBrightness == 0) { + if (currentScaling == 0 && KeyBacklightBrightness == 0) { sleepLeds(); } - ledsNeedUpdate = false; + if (currentScaling != KeyBacklightBrightness) { + k_sleep(K_MSEC(scalingFadeDelay)); + recalculateScaling(); + } + + ledsNeedUpdate = currentScaling != KeyBacklightBrightness; } } diff --git a/device/src/keyboard/leds.h b/device/src/keyboard/leds.h index 147bc5a8f..0e1c68e8b 100644 --- a/device/src/keyboard/leds.h +++ b/device/src/keyboard/leds.h @@ -18,5 +18,6 @@ extern void Uhk80_UpdateLeds(); extern void InitLeds(void); + extern void UpdateLedAudioRegisters(uint8_t spreadSpectrum, uint8_t phaseDelay, uint8_t pwmFrequency); #endif // __LEDS_H__ diff --git a/device/src/keyboard/oled/screens/screen_manager.c b/device/src/keyboard/oled/screens/screen_manager.c index 8c79d9d30..97c418415 100644 --- a/device/src/keyboard/oled/screens/screen_manager.c +++ b/device/src/keyboard/oled/screens/screen_manager.c @@ -11,11 +11,14 @@ screen_id_t ActiveScreen = ScreenId_Main; +bool InteractivePairingInProgress = false; + static void onExit(screen_id_t screen) { switch(screen) { case ScreenId_Pairing: + InteractivePairingInProgress = false; Ledmap_ResetTemporaryLedBacklightingMode(); - Ledmap_UpdateBacklightLeds(); + EventVector_Set(EventVector_LedManagerFullUpdateNeeded); break; default: break; @@ -30,8 +33,10 @@ void ScreenManager_ActivateScreen(screen_id_t screen) switch(screen) { case ScreenId_Pairing: + InteractivePairingInProgress = true; screenPtr = PairingScreen; Ledmap_SetTemporaryLedBacklightingMode(BacklightingMode_Numpad); + EventVector_Set(EventVector_LedManagerFullUpdateNeeded); Ledmap_UpdateBacklightLeds(); break; case ScreenId_Debug: diff --git a/device/src/keyboard/oled/screens/screen_manager.h b/device/src/keyboard/oled/screens/screen_manager.h index 4e8be73cd..a440dce82 100644 --- a/device/src/keyboard/oled/screens/screen_manager.h +++ b/device/src/keyboard/oled/screens/screen_manager.h @@ -27,6 +27,8 @@ extern bool ScreenManager_AwaitsInput; extern screen_id_t ActiveScreen; + extern bool InteractivePairingInProgress; + // Functions: void ScreenManager_ActivateScreen(screen_id_t screen); diff --git a/device/src/keyboard/oled/widgets/widget_store.c b/device/src/keyboard/oled/widgets/widget_store.c index ac4e50d7f..22adf674f 100644 --- a/device/src/keyboard/oled/widgets/widget_store.c +++ b/device/src/keyboard/oled/widgets/widget_store.c @@ -20,6 +20,8 @@ #include "device_state.h" #include "usb/usb_compatibility.h" #include "macros/status_buffer.h" +#include "host_connection.h" +#include "connections.h" widget_t KeymapWidget; widget_t LayerWidget; @@ -43,14 +45,36 @@ static string_segment_t getKeymapText() { static string_segment_t getTargetText() { - if (DeviceState_IsConnected(ConnectionId_UsbHid)) { - return (string_segment_t){ .start = "USB Cable", .end = NULL }; - } else if (DeviceState_IsConnected(ConnectionId_Dongle)) { - return (string_segment_t){ .start = "UHK Dongle", .end = NULL }; - } else if (DeviceState_IsConnected(ConnectionId_BluetoothHid)) { - return (string_segment_t){ .start = "Bluetooth", .end = NULL }; - } else { - return (string_segment_t){ .start = "Disconnected", .end = NULL }; + switch (ActiveHostConnectionId) { + case ConnectionId_UsbHidRight: + return (string_segment_t){ .start = "USB Cable", .end = NULL }; + case ConnectionId_BtHid: + return (string_segment_t){ .start = "Bluetooth", .end = NULL }; + case ConnectionId_HostConnectionFirst ... ConnectionId_HostConnectionLast: { + host_connection_t* hostConnection = HostConnection(ActiveHostConnectionId); + + if (SegmentLen(hostConnection->name) > 0) { + return hostConnection->name; + } + + switch(hostConnection->type) { + case HostConnectionType_UsbHidRight: + return (string_segment_t){ .start = "USB Cable", .end = NULL }; + case HostConnectionType_UsbHidLeft: + return (string_segment_t){ .start = "USB Cable", .end = NULL }; + case HostConnectionType_BtHid: + return (string_segment_t){ .start = "Bluetooth", .end = NULL }; + case HostConnectionType_Dongle: + return (string_segment_t){ .start = "UHK Dongle", .end = NULL }; + default: + return (string_segment_t){ .start = "Unknown", .end = NULL }; + + } + } + case ConnectionId_Invalid: + return (string_segment_t){ .start = "Disconnected", .end = NULL }; + default: + return (string_segment_t){ .start = "Unknown", .end = NULL }; } } @@ -74,9 +98,9 @@ static string_segment_t getLeftStatusText() { static char buffer [BUFFER_LENGTH] = { [BUFFER_LENGTH-1] = 0 }; font_icons_t icon = FontIcon_CircleXmarkLarge; if (DEVICE_ID == DeviceId_Uhk80_Right) { - if (Uart_IsConnected()) { + if (Connections_IsReady(ConnectionId_UartLeft)) { icon = FontIcon_PlugsConnected; - } else if (Bt_DeviceIsConnected(DeviceId_Uhk80_Left)) { + } else if (Connections_IsReady(ConnectionId_NusServerLeft)) { icon = FontIcon_SignalStream; } } diff --git a/device/src/keyboard/uart.c b/device/src/keyboard/uart.c index e38e74f8f..752c6f921 100644 --- a/device/src/keyboard/uart.c +++ b/device/src/keyboard/uart.c @@ -8,6 +8,7 @@ #include "device_state.h" #include "debug.h" #include "event_scheduler.h" +#include "connections.h" // Thread definitions @@ -42,8 +43,6 @@ uint8_t rxbuf2[BUF_SIZE]; uint32_t lastPingTime = -2*UART_TIMEOUT; -bool isConnected = false; - static void appendRxByte(uint8_t byte) { if (rxPosition < RX_BUF_SIZE) { rxBuffer[rxPosition++] = byte; @@ -64,10 +63,12 @@ static void rxPacketReceived() { rxBuffer = MessengerQueue_AllocateMemory(); rxPosition = 0; + connection_id_t connectionId = DEVICE_IS_UHK80_LEFT ? ConnectionId_UartRight : ConnectionId_UartLeft; + if (DEVICE_IS_UHK80_RIGHT) { - Messenger_Enqueue(DeviceId_Uhk80_Left, oldPacket, len); + Messenger_Enqueue(connectionId, DeviceId_Uhk80_Left, oldPacket, len); } else if (DEVICE_IS_UHK80_LEFT) { - Messenger_Enqueue(DeviceId_Uhk80_Right, oldPacket, len); + Messenger_Enqueue(connectionId, DeviceId_Uhk80_Right, oldPacket, len); } } } @@ -215,17 +216,14 @@ static void ping() { Uart_SendPacket(NULL, 0); } -bool Uart_IsConnected() { - return isConnected; -} - static void updateConnectionState() { uint32_t pingDiff = (k_uptime_get() - lastPingTime); + connection_id_t connectionId = DEVICE_IS_UHK80_LEFT ? ConnectionId_UartRight : ConnectionId_UartLeft; + bool oldIsConnected = Connections_IsReady(connectionId); bool newIsConnected = pingDiff < UART_TIMEOUT; - if (isConnected != newIsConnected) { - isConnected = newIsConnected; - DeviceState_TriggerUpdate(); - if (!isConnected) { + if (oldIsConnected != newIsConnected) { + Connections_SetState(connectionId, newIsConnected ? ConnectionState_Ready : ConnectionState_Disconnected); + if (!newIsConnected) { k_sem_init(&txBufferBusy, UART_SLOTS, UART_SLOTS); } } diff --git a/device/src/main.c b/device/src/main.c index 8dab8ccac..9499a208f 100644 --- a/device/src/main.c +++ b/device/src/main.c @@ -158,7 +158,7 @@ int main(void) { } } - USB_EnableHid(); // has to be after USB_SetSerialNumber + USB_Enable(); // has to be after USB_SetSerialNumber // has to be after InitSettings BtManager_InitBt(); diff --git a/device/src/messenger.c b/device/src/messenger.c index c84d544aa..5a48dad10 100644 --- a/device/src/messenger.c +++ b/device/src/messenger.c @@ -1,5 +1,6 @@ #include "messenger.h" #include "bt_conn.h" +#include "connections.h" #include "device.h" #include "autoconf.h" #include "link_protocol.h" @@ -17,6 +18,8 @@ #include "str_utils.h" #include "event_scheduler.h" #include "slave_drivers/uhk_module_driver.h" +#include "macros/status_buffer.h" +#include "connections.h" #if DEVICE_IS_KEYBOARD #include "keyboard/uart.h" @@ -31,56 +34,71 @@ typedef enum { MessengerChannel_None, } messenger_channel_t; -static messenger_channel_t determineChannel(device_id_t dst) { -#if DEVICE_IS_UHK80_LEFT - if (Uart_IsConnected()) { +static connection_id_t determineChannel(device_id_t dst) { +#if DEVICE_IS_KEYBOARD + if (DEVICE_IS_UHK80_LEFT && Connections_IsReady(ConnectionId_UartRight)) { switch (dst) { case DeviceId_Uhk_Dongle: case DeviceId_Uhk80_Right: - return MessengerChannel_Uart; + return ConnectionId_UartRight; default: break; } } - if (Bt_DeviceIsConnected(DeviceId_Uhk80_Right)) { + + if (DEVICE_IS_UHK80_RIGHT && Connections_IsReady(ConnectionId_UartLeft)) { switch (dst) { case DeviceId_Uhk_Dongle: - case DeviceId_Uhk80_Right: - return MessengerChannel_NusServer; + break; + case DeviceId_Uhk80_Left: + return ConnectionId_UartLeft; default: break; } } #endif -#if DEVICE_IS_UHK80_RIGHT - if (Uart_IsConnected() && (dst == DeviceId_Uhk80_Left)) { - return MessengerChannel_Uart; + if (DEVICE_IS_UHK80_LEFT && Connections_IsReady(ConnectionId_NusClientRight)) { + switch (dst) { + case DeviceId_Uhk_Dongle: + case DeviceId_Uhk80_Right: + return ConnectionId_NusClientRight; + default: + return ConnectionId_Invalid; + } } - if (Bt_DeviceIsConnected(dst)) { + + if (DEVICE_IS_UHK80_RIGHT) { switch (dst) { case DeviceId_Uhk_Dongle: - return MessengerChannel_NusServer; + if (Connections_IsReady(ActiveHostConnectionId) && Connections_Type(ActiveHostConnectionId) == ConnectionType_NusDongle) { + return ActiveHostConnectionId; + } + break; case DeviceId_Uhk80_Left: - return MessengerChannel_NusClient; - default: + if (Connections_IsReady(ConnectionId_NusServerLeft)) { + return ConnectionId_NusServerLeft; + } break; + default: + return ConnectionId_Invalid; } } -#endif -#if DEVICE_IS_UHK_DONGLE - if (Bt_DeviceIsConnected(DeviceId_Uhk80_Right)) { + if (DEVICE_IS_UHK_DONGLE) { switch (dst) { case DeviceId_Uhk80_Right: case DeviceId_Uhk80_Left: - return MessengerChannel_NusClient; - default: + if (Connections_IsReady(ConnectionId_NusServerRight)) { + return ConnectionId_NusServerRight; + } break; + default: + return ConnectionId_Invalid; } } -#endif - return MessengerChannel_None; + + return ConnectionId_Invalid; } static char getDeviceAbbrev(device_id_t src) { @@ -208,6 +226,7 @@ static void receive(const uint8_t* data, uint16_t len) { .idsUsed = 0, .src = src, .dst = dst, + .connectionId = determineChannel(dst), }; printk("Forwarding message from %d to %d\n", msg.src, msg.dst); Messenger_SendMessage(msg); @@ -226,13 +245,17 @@ static void receive(const uint8_t* data, uint16_t len) { } } -static bool isSpam(const uint8_t* data) { +static bool isSpam(const uint8_t* data, connection_id_t connectionId) { if (data[MessageOffset_MsgId1] == MessageId_Ping) { return true; } if (data[MessageOffset_MsgId1] == MessageId_StateSync && data[MessageOffset_MsgId1+1] == StateSyncPropertyId_Battery) { return DEBUG_EVENTLOOP_SCHEDULE; } + if (DEVICE_IS_UHK80_RIGHT && Connections_Type(connectionId) == ConnectionType_NusDongle && connectionId != ActiveHostConnectionId) { + StateSync_UpdateProperty(StateSyncPropertyId_DongleStandby, NULL); + return true; + } return false; } @@ -261,8 +284,10 @@ ATTR_UNUSED static void getMessageDescription(const uint8_t* data, const char** } } -void Messenger_Enqueue(uint8_t src, const uint8_t* data, uint16_t len) { - if (!isSpam(data)) { +void Messenger_Enqueue(uint8_t srcConnectionId, uint8_t src, const uint8_t* data, uint16_t len) { + if (isSpam(data, srcConnectionId)) { + MessengerQueue_FreeMemory(data); + } else { MessengerQueue_Put(src, data, len); EventVector_Set(EventVector_NewMessage); LOG_SCHEDULE( @@ -287,22 +312,25 @@ void Messenger_ProcessQueue() { } bool Messenger_Availability(device_id_t dst, messenger_availability_op_t operation) { - messenger_channel_t channel = determineChannel(dst); + connection_id_t connection = determineChannel(dst); - switch (channel) { - case MessengerChannel_Uart: + switch (connection) { + case ConnectionId_UartLeft: + case ConnectionId_UartRight: #if DEVICE_IS_KEYBOARD return Uart_Availability(operation); #else return false; #endif - case MessengerChannel_NusServer: + case ConnectionId_NusClientRight: + case ConnectionId_HostConnectionFirst ... ConnectionId_HostConnectionLast: #ifdef CONFIG_BT_NUS return NusServer_Availability(operation); #else return false; #endif - case MessengerChannel_NusClient: + case ConnectionId_NusServerRight: + case ConnectionId_NusServerLeft: #ifdef CONFIG_BT_NUS_CLIENT return NusClient_Availability(operation); #else @@ -314,21 +342,33 @@ bool Messenger_Availability(device_id_t dst, messenger_availability_op_t operati } void Messenger_SendMessage(message_t message) { - messenger_channel_t channel = determineChannel(message.dst); + connection_id_t connectionId = message.connectionId; device_id_t dst = message.dst; - switch (channel) { - case MessengerChannel_Uart: + + switch (connectionId) { + case ConnectionId_UartLeft: + case ConnectionId_UartRight: #if DEVICE_IS_KEYBOARD Uart_SendMessage(message); #endif break; - case MessengerChannel_NusServer: + case ConnectionId_NusClientRight: #if defined(CONFIG_BT_NUS) && defined(CONFIG_BT_PERIPHERAL) NusServer_SendMessage(message); #endif break; - case MessengerChannel_NusClient: + case ConnectionId_HostConnectionFirst ... ConnectionId_HostConnectionLast: + if (Connections_Type(connectionId) == ConnectionType_NusDongle) { +#if defined(CONFIG_BT_NUS) && defined(CONFIG_BT_PERIPHERAL) + NusServer_SendMessageTo(message, Peers[Connections[connectionId].peerId].conn); +#endif + } else { + printk("Failed to send message from %s to %s; incompatible connection type\n", Utils_DeviceIdToString(DEVICE_ID), Utils_DeviceIdToString(dst)); + } + break; + case ConnectionId_NusServerRight: + case ConnectionId_NusServerLeft: #ifdef CONFIG_BT_NUS_CLIENT NusClient_SendMessage(message); #endif @@ -347,6 +387,7 @@ void Messenger_Send(device_id_t dst, uint8_t messageId, const uint8_t* data, uin .idsUsed = 1, .src = DEVICE_ID, .dst = dst, + .connectionId = determineChannel(dst), }; Messenger_SendMessage(msg); } @@ -360,6 +401,21 @@ void Messenger_Send2(device_id_t dst, uint8_t messageId, uint8_t messageId2, con .idsUsed = 2, .src = DEVICE_ID, .dst = dst, + .connectionId = determineChannel(dst), + }; + Messenger_SendMessage(msg); +} + +void Messenger_Send2Via(device_id_t dst, connection_id_t connectionId, uint8_t messageId, uint8_t messageId2, const uint8_t* data, uint16_t len) { + message_t msg = { + .data = data, + .len = len, + .messageId[0] = messageId, + .messageId[1] = messageId2, + .idsUsed = 2, + .src = DEVICE_ID, + .dst = dst, + .connectionId = connectionId, }; Messenger_SendMessage(msg); } diff --git a/device/src/messenger.h b/device/src/messenger.h index cf7007a36..eb7b5ea06 100644 --- a/device/src/messenger.h +++ b/device/src/messenger.h @@ -4,8 +4,10 @@ // Includes: #include "shared/attributes.h" -#include + #include #include + #include "device.h" + #include "connections.h" // Typedefs: @@ -36,17 +38,18 @@ typedef enum { uint8_t idsUsed; uint8_t src; uint8_t dst; + uint8_t connectionId; } ATTR_PACKED message_t; // Functions: - void Messenger_Receive(uint8_t src, const uint8_t* data, uint16_t len); void Messenger_SendMessage(message_t message); - void Messenger_Send(uint8_t dst, uint8_t messageId, const uint8_t* data, uint16_t len); - void Messenger_Send2(uint8_t dst, uint8_t messageId, uint8_t messageId2, const uint8_t* data, uint16_t len); - bool Messenger_Availability(uint8_t dst, messenger_availability_op_t operation); + void Messenger_Send(device_id_t dst, uint8_t messageId, const uint8_t* data, uint16_t len); + void Messenger_Send2(device_id_t dst, uint8_t messageId, uint8_t messageId2, const uint8_t* data, uint16_t len); + void Messenger_Send2Via(device_id_t dst, connection_id_t connectionId, uint8_t messageId, uint8_t messageId2, const uint8_t* data, uint16_t len); + bool Messenger_Availability(device_id_t dst, messenger_availability_op_t operation); - void Messenger_Enqueue(uint8_t src, const uint8_t* data, uint16_t len); + void Messenger_Enqueue(uint8_t srcConnectionId, uint8_t src, const uint8_t* data, uint16_t len); void Messenger_ProcessQueue(); void Messenger_Init(); diff --git a/device/src/messenger_queue.h b/device/src/messenger_queue.h index b8fbff2b3..8dc06abd1 100644 --- a/device/src/messenger_queue.h +++ b/device/src/messenger_queue.h @@ -3,6 +3,7 @@ // Includes: + #include "connections.h" #include "shared/attributes.h" #include #include "device.h" diff --git a/device/src/nus_client.c b/device/src/nus_client.c index 98d422258..f19be5ea8 100644 --- a/device/src/nus_client.c +++ b/device/src/nus_client.c @@ -3,6 +3,7 @@ #include #include "bt_scan.h" #include "bt_conn.h" +#include "connections.h" #include "device.h" #include "usb_interfaces/usb_interface_basic_keyboard.h" #include "usb_interfaces/usb_interface_mouse.h" @@ -39,10 +40,10 @@ static uint8_t ble_data_received(struct bt_nus_client *nus, const uint8_t *data, switch (DEVICE_ID) { case DeviceId_Uhk80_Right: - Messenger_Enqueue(DeviceId_Uhk80_Left, copy, len); + Messenger_Enqueue(ConnectionId_NusServerLeft, DeviceId_Uhk80_Left, copy, len); break; case DeviceId_Uhk_Dongle: - Messenger_Enqueue(DeviceId_Uhk80_Right, copy, len); + Messenger_Enqueue(ConnectionId_NusServerRight, DeviceId_Uhk80_Right, copy, len); break; default: printk("Ble received message from unknown source."); @@ -81,10 +82,10 @@ static void discovery_complete(struct bt_gatt_dm *dm, void *context) { printk("NUS connection with %s successfully established\n", GetPeerStringByConn(nus->conn)); if (DEVICE_ID == DeviceId_Uhk80_Right) { - Bt_SetDeviceConnected(DeviceId_Uhk80_Left); + Bt_SetConnectionConfigured(Peers[PeerIdLeft].conn); } if (DEVICE_ID == DeviceId_Uhk_Dongle) { - Bt_SetDeviceConnected(DeviceId_Uhk80_Right); + Bt_SetConnectionConfigured(Peers[PeerIdRight].conn); } } diff --git a/device/src/nus_server.c b/device/src/nus_server.c index ec45f292e..30f3ab900 100644 --- a/device/src/nus_server.c +++ b/device/src/nus_server.c @@ -3,6 +3,7 @@ #include "bt_conn.h" #include "bt_advertise.h" #include "bt_conn.h" +#include "connections.h" #include "messenger.h" #include "device.h" #include "messenger_queue.h" @@ -13,32 +14,21 @@ static K_SEM_DEFINE(nusBusy, NUS_SLOTS, NUS_SLOTS); -static void setPeerConnected(uint8_t peerId, device_id_t peerDeviceId, struct bt_conn *conn) { - const bt_addr_le_t *addr = bt_conn_get_dst(conn); - if (!Peers[peerId].isConnected || bt_addr_le_eq(&Peers[peerId].addr, addr) != 0) { - Peers[peerId].addr = *addr; - Bt_SetDeviceConnected(peerDeviceId); - } -} - static void received(struct bt_conn *conn, const uint8_t *const data, uint16_t len) { uint8_t* copy = MessengerQueue_AllocateMemory(); memcpy(copy, data, len); - if (DEVICE_ID == DeviceId_Uhk80_Left) { - setPeerConnected(PeerIdRight, DeviceId_Uhk80_Right, conn); - } + Bt_SetConnectionConfigured(conn); - if (DEVICE_ID == DeviceId_Uhk80_Right) { - setPeerConnected(PeerIdDongle, DeviceId_Uhk_Dongle, conn); - } + uint8_t peerId = GetPeerIdByConn(conn); + uint8_t connectionId = peerId == PeerIdUnknown ? ConnectionId_Invalid : Peers[peerId].connectionId; switch (DEVICE_ID) { case DeviceId_Uhk80_Left: - Messenger_Enqueue(DeviceId_Uhk80_Right, copy, len); + Messenger_Enqueue(connectionId, DeviceId_Uhk80_Right, copy, len); break; case DeviceId_Uhk80_Right: - Messenger_Enqueue(DeviceId_Uhk_Dongle, copy, len); + Messenger_Enqueue(connectionId, DeviceId_Uhk_Dongle, copy, len); break; default: printk("Ble received message from unknown source."); @@ -83,9 +73,9 @@ void NusServer_Disconnected() { k_sem_init(&nusBusy, NUS_SLOTS, NUS_SLOTS); } -static void send_raw_buffer(const uint8_t *data, uint16_t len) { +static void send_raw_buffer(const uint8_t *data, uint16_t len, struct bt_conn* conn) { SEM_TAKE(&nusBusy); - int err = bt_nus_send(NULL, data, len); + int err = bt_nus_send(conn, data, len); if (err) { k_sem_give(&nusBusy); printk("Failed to send data over BLE connection (err: %d)\n", err); @@ -107,7 +97,7 @@ bool NusServer_Availability(messenger_availability_op_t operation) { } } -void NusServer_SendMessage(message_t msg) { +void NusServer_SendMessageTo(message_t msg, struct bt_conn* conn) { uint8_t buffer[MAX_LINK_PACKET_LENGTH]; uint8_t bufferIdx = 0; @@ -125,5 +115,9 @@ void NusServer_SendMessage(message_t msg) { memcpy(&buffer[bufferIdx], msg.data, msg.len); - send_raw_buffer(buffer, msg.len+msg.idsUsed+2); + send_raw_buffer(buffer, msg.len+msg.idsUsed+2, conn); +} + +void NusServer_SendMessage(message_t msg) { + NusServer_SendMessageTo(msg, NULL); } diff --git a/device/src/nus_server.h b/device/src/nus_server.h index 8e2cc45f8..af04123e0 100644 --- a/device/src/nus_server.h +++ b/device/src/nus_server.h @@ -11,6 +11,7 @@ extern int NusServer_Init(void); extern void NusServer_Disconnected(); extern void NusServer_SendMessage(message_t msg); + extern void NusServer_SendMessageTo(message_t msg, struct bt_conn* conn); extern bool NusServer_Availability(messenger_availability_op_t operation); #endif // __NUS_SERVER_H__ diff --git a/device/src/settings.c b/device/src/settings.c index 18e3f5e75..520a63929 100644 --- a/device/src/settings.c +++ b/device/src/settings.c @@ -47,6 +47,7 @@ struct settings_handler settingsHandler = { void InitSettings(void) { + DongleLeds_Update(); settings_subsys_init(); settings_register(&settingsHandler); settings_load(); diff --git a/device/src/shell.c b/device/src/shell.c index b9c94baa5..b64cc7a7f 100644 --- a/device/src/shell.c +++ b/device/src/shell.c @@ -12,6 +12,7 @@ #include "keyboard/charger.h" #include "ledmap.h" #include "event_scheduler.h" +#include "host_connection.h" shell_t Shell = { .keyLog = 0, @@ -152,6 +153,14 @@ static int cmd_uhk_btunpair(const struct shell *shell, size_t argc, char *argv[] return 0; } +static int cmd_uhk_connections(const struct shell *shell, size_t argc, char *argv[]) +{ + HostConnections_ListKnownBleConnections(); + BtConn_ListAllBonds(); + BtConn_ListCurrentConnections(); + return 0; +} + void InitShell(void) { SHELL_STATIC_SUBCMD_SET_CREATE(uhk_cmds, @@ -177,6 +186,7 @@ void InitShell(void) SHELL_CMD_ARG(gamepad, NULL, "switch gamepad on/off", cmd_uhk_gamepad, 1, 1), SHELL_CMD_ARG(btacc, NULL, "accept bluetooth pairing", cmd_uhk_btacc, 1, 1), SHELL_CMD_ARG(btunpair, NULL, "unpair bluetooth devices", cmd_uhk_btunpair, 1, 1), + [10]=SHELL_CMD_ARG(connections, NULL, "list BLE connections", cmd_uhk_connections, 1, 0), SHELL_SUBCMD_SET_END); SHELL_CMD_REGISTER(uhk, &uhk_cmds, "UHK commands", NULL); diff --git a/device/src/state_sync.c b/device/src/state_sync.c index 5d7b3915b..512b68abf 100644 --- a/device/src/state_sync.c +++ b/device/src/state_sync.c @@ -1,4 +1,6 @@ #include "state_sync.h" +#include "bt_conn.h" +#include "connections.h" #include "device.h" #include "device_state.h" #include "event_scheduler.h" @@ -24,6 +26,8 @@ #include #include "peripherals/merge_sensor.h" #include "power_mode.h" +#include "test_switches.h" +#include "dongle_leds.h" #define WAKE(TID) if (TID != 0) { k_wakeup(TID); } @@ -128,8 +132,12 @@ static state_sync_prop_t stateSyncProps[StateSyncPropertyId_Count] = { SIMPLE(FunctionalColors, SyncDirection_RightToLeft, DirtyState_Clean, &Cfg.KeyActionColors), SIMPLE(PowerMode, SyncDirection_RightToLeft, DirtyState_Clean, &CurrentPowerMode), CUSTOM(Config, SyncDirection_RightToLeft, DirtyState_Clean), + CUSTOM(SwitchTestMode, SyncDirection_RightToLeft, DirtyState_Clean), + SIMPLE(DongleStandby, SyncDirection_RightToDongle, DirtyState_Clean, &DongleStandby), }; + + static void invalidateProperty(state_sync_prop_id_t propId) { STATE_SYNC_LOG("<<< Invalidating property %s\n", stateSyncProps[propId].name); if (StateSyncPropertyId_LayerActionFirst <= propId && @@ -352,6 +360,19 @@ static void receiveProperty(device_id_t src, state_sync_prop_id_t propId, const break; case StateSyncPropertyId_MergeSensor: break; + case StateSyncPropertyId_SwitchTestMode: + if (!isLocalUpdate) { + bool newMode = *(bool*)data; + if (newMode != TestSwitches) { + newMode ? TestSwitches_Activate() : TestSwitches_Deactivate(); + Main_Wake(); + } + } + case StateSyncPropertyId_DongleStandby: + if (DEVICE_IS_UHK_DONGLE) { + DongleLeds_Update(); + } + break; default: printk("Property %i ('%s') has no receive handler. If this is correct, please add a " "separate empty case...\n", @@ -493,6 +514,10 @@ static void prepareData(device_id_t dst, const uint8_t *propDataPtr, state_sync_ submitPreparedData(dst, propId, (const uint8_t *)&buffer, sizeof(buffer)); return; } break; + case StateSyncPropertyId_SwitchTestMode: { + submitPreparedData(dst, propId, (const uint8_t *)&TestSwitches, sizeof(TestSwitches)); + return; + } default: break; } @@ -536,6 +561,7 @@ static void updateProperty(state_sync_prop_id_t propId) { static bool handlePropertyUpdateRightToLeft() { UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_ResetRightLeftLink); UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_Config); + UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_SwitchTestMode); if (KeyBacklightBrightness != 0) { // Update relevant data @@ -610,11 +636,26 @@ static void updateLoopRightLeft() { } } +static void updateStandbys() { + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + uint8_t connectionId = Peers[peerId].connectionId; + if (Connections_Type(connectionId) == ConnectionType_NusDongle) { + bool standby = !(ActiveHostConnectionId == connectionId); + Messenger_Send2Via(DeviceId_Uhk_Dongle, connectionId, MessageId_StateSync, StateSyncPropertyId_DongleStandby, (const uint8_t*)&standby, 1); + } + } +} + static void updateLoopRightDongle() { if (DEVICE_ID == DeviceId_Uhk80_Right) { while (true) { bool isConnected = DeviceState_IsDeviceConnected(DeviceId_Uhk_Dongle); STATE_SYNC_LOG("--- Right to dongle update loop, connected: %i\n", isConnected); + + if (stateSyncProps[StateSyncPropertyId_DongleStandby].dirtyState != DirtyState_Clean) { \ + updateStandbys(); \ + } + if (!isConnected || handlePropertyUpdateRightToDongle()) { k_sleep(K_FOREVER); } else { @@ -627,7 +668,7 @@ static void updateLoopRightDongle() { while (true) { bool isConnected = DeviceState_IsDeviceConnected(DeviceId_Uhk80_Right); STATE_SYNC_LOG("--- Dongle update loop, connected: %i\n", isConnected); - if (!isConnected || handlePropertyUpdateDongleToRight()) { + if (!isConnected || DongleStandby || handlePropertyUpdateDongleToRight()) { k_sleep(K_FOREVER); } else { k_sleep(K_MSEC(STATE_SYNC_SEND_DELAY)); @@ -692,6 +733,7 @@ void StateSync_ResetRightDongleLink(bool bidirectional) { invalidateProperty(StateSyncPropertyId_ResetRightDongleLink); } if (DEVICE_ID == DeviceId_Uhk_Dongle) { + DongleStandby = false; invalidateProperty(StateSyncPropertyId_KeyboardLedsState); } } diff --git a/device/src/state_sync.h b/device/src/state_sync.h index a332e5813..e5b087751 100644 --- a/device/src/state_sync.h +++ b/device/src/state_sync.h @@ -94,6 +94,8 @@ StateSyncPropertyId_FunctionalColors = 25, StateSyncPropertyId_PowerMode = 26, StateSyncPropertyId_Config = 27, + StateSyncPropertyId_SwitchTestMode = 28, + StateSyncPropertyId_DongleStandby = 29, StateSyncPropertyId_Count, } state_sync_prop_id_t; diff --git a/device/src/usb/app_base.hpp b/device/src/usb/app_base.hpp index c57b3449f..8577842d5 100644 --- a/device/src/usb/app_base.hpp +++ b/device/src/usb/app_base.hpp @@ -1,6 +1,9 @@ #ifndef __APP_BASE_HEADER__ #define __APP_BASE_HEADER__ +extern "C" { +#include "device.h" +} #include "hid/application.hpp" #include "hid/rdf/descriptor.hpp" #include "hid/report_protocol.hpp" diff --git a/device/src/usb/command_app.cpp b/device/src/usb/command_app.cpp index 899dec924..f93236b83 100644 --- a/device/src/usb/command_app.cpp +++ b/device/src/usb/command_app.cpp @@ -12,7 +12,7 @@ command_app &command_app::usb_handle() } #if DEVICE_IS_UHK80_RIGHT -command_app &command_app::handle() +command_app &command_app::ble_handle() { static command_app ble_app{}; return ble_app; diff --git a/device/src/usb/command_app.hpp b/device/src/usb/command_app.hpp index a31bfbcc6..9c08f9106 100644 --- a/device/src/usb/command_app.hpp +++ b/device/src/usb/command_app.hpp @@ -64,7 +64,7 @@ class command_app : public hid::application { static command_app& usb_handle(); #if DEVICE_IS_UHK80_RIGHT - static command_app& handle(); + static command_app& ble_handle(); #endif void start(hid::protocol prot) override; diff --git a/device/src/usb/controls_app.cpp b/device/src/usb/controls_app.cpp index 7696705ee..e49f9748d 100644 --- a/device/src/usb/controls_app.cpp +++ b/device/src/usb/controls_app.cpp @@ -1,11 +1,19 @@ #include "controls_app.hpp" -controls_app &controls_app::handle() +controls_app &controls_app::usb_handle() { static controls_app app{}; return app; } +#if DEVICE_IS_UHK80_RIGHT +controls_app &controls_app::ble_handle() +{ + static controls_app ble_app{}; + return ble_app; +} +#endif + void controls_app::start(hid::protocol prot) { // TODO start handling controls events diff --git a/device/src/usb/controls_app.hpp b/device/src/usb/controls_app.hpp index ef1b93e0f..48251a541 100644 --- a/device/src/usb/controls_app.hpp +++ b/device/src/usb/controls_app.hpp @@ -72,7 +72,10 @@ class controls_app : public app_base { // clang-format on } - static controls_app &handle(); + static controls_app &usb_handle(); +#if DEVICE_IS_UHK80_RIGHT + static controls_app &ble_handle(); +#endif void set_report_state(const controls_report_base<0> &data); @@ -82,7 +85,7 @@ class controls_app : public app_base { void start(hid::protocol prot) override; using controls_report = controls_report_base; - C2USB_USB_TRANSFER_ALIGN(controls_report, report_buffer_){}; + C2USB_USB_TRANSFER_ALIGN(controls_report, report_buffer_) {}; }; using controls_buffer = controls_app::controls_report_base<0>; diff --git a/device/src/usb/gamepad_app.cpp b/device/src/usb/gamepad_app.cpp index faee5c8b6..867787a29 100644 --- a/device/src/usb/gamepad_app.cpp +++ b/device/src/usb/gamepad_app.cpp @@ -1,10 +1,17 @@ #include "gamepad_app.hpp" -gamepad_app &gamepad_app::handle() +gamepad_app &gamepad_app::usb_handle() { static gamepad_app app{}; return app; } +#if DEVICE_IS_UHK80_RIGHT +gamepad_app &gamepad_app::ble_handle() +{ + static gamepad_app ble_app{}; + return ble_app; +} +#endif void gamepad_app::start(hid::protocol prot) { diff --git a/device/src/usb/gamepad_app.hpp b/device/src/usb/gamepad_app.hpp index bf1d24407..16e4f4107 100644 --- a/device/src/usb/gamepad_app.hpp +++ b/device/src/usb/gamepad_app.hpp @@ -169,7 +169,10 @@ class gamepad_app : public hid::application { } }; - static gamepad_app &handle(); + static gamepad_app &usb_handle(); +#if DEVICE_IS_UHK80_RIGHT + static gamepad_app &ble_handle(); +#endif void set_report_state(const gamepad_report &data); diff --git a/device/src/usb/hid_battery_app.cpp b/device/src/usb/hid_battery_app.cpp deleted file mode 100644 index d28460ae2..000000000 --- a/device/src/usb/hid_battery_app.cpp +++ /dev/null @@ -1,57 +0,0 @@ -extern "C" { -#include "device.h" -} -#if DEVICE_HAS_BATTERY - #include "hid_battery_app.hpp" - -extern "C" void HID_SetBatteryStatus(uint8_t remaining_capacity, bool charging) -{ - return hid_battery_app::handle().send(remaining_capacity, charging); -} - -hid_battery_app &hid_battery_app::handle() -{ - static hid_battery_app app{}; - return app; -} - -void hid_battery_app::send(uint8_t remaining_capacity, bool charging) -{ - auto buf_idx = report_buffer_.active_side(); - auto &r = report_buffer_[buf_idx]; - r.remaining_capacity = remaining_capacity; - r.charging = charging; - send_buffer(buf_idx); -} - -void hid_battery_app::send_buffer(uint8_t buf_idx) -{ - if (!report_buffer_.differs()) { - return; - } - if (send_report(&report_buffer_[buf_idx]) == hid::result::OK) { - report_buffer_.compare_swap_copy(buf_idx); - } -} - -void hid_battery_app::in_report_sent(const std::span &data) -{ - auto buf_idx = report_buffer_.indexof(data.data()); - if (!report_buffer_.compare_swap_copy(buf_idx)) { - send_buffer(1 - buf_idx); - } -} - -void hid_battery_app::get_report(hid::report::selector select, const std::span &buffer) -{ - if (select != report::selector()) { - return; - } - // copy to buffer to avoid overwriting data in transit - auto &report = report_buffer_[report_buffer_.inactive_side()]; - assert(buffer.size() >= sizeof(report)); - memcpy(buffer.data(), report.data(), sizeof(report)); - send_report(buffer.subspan(0, sizeof(report))); -} - -#endif // DEVICE_HAS_BATTERY diff --git a/device/src/usb/hid_battery_app.hpp b/device/src/usb/hid_battery_app.hpp deleted file mode 100644 index ef92c7772..000000000 --- a/device/src/usb/hid_battery_app.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef __HID_BATTERY_APP_HEADER__ -#define __HID_BATTERY_APP_HEADER__ - -#include "double_buffer.hpp" -#include "hid/application.hpp" -#include "hid/page/battery_system.hpp" -#include "hid/page/generic_desktop.hpp" -#include "hid/page/generic_device.hpp" -#include "hid/page/power.hpp" -#include "hid/rdf/descriptor.hpp" -#include "hid/report_protocol.hpp" - -class hid_battery_app : public hid::application { - hid_battery_app() : application(report_protocol()) {} - - public: - static constexpr auto report_desc() - { - using namespace hid::page; - using namespace hid::rdf; - - // clang-format off - return descriptor( - usage_page(), - usage(generic_desktop::SYSTEM_CONTROL), - collection::application( - usage_page(), - usage(battery_system::ABSOLUTE_STATE_OF_CHARGE), - logical_limits<1, 1>(0, 100), - report_size(7), - report_count(1), - input::absolute_variable(), - usage(battery_system::CHARGING), - logical_limits<1, 1>(0, 1), - report_size(1), - input::absolute_variable() - ) - ); - // clang-format off - } - - struct report : public hid::report::base - { - uint8_t remaining_capacity : 7; - bool charging : 1; - - bool operator==(const report &other) const = default; - }; - - static hid_battery_app& handle(); - - void send(uint8_t remaining_capacity, bool charging); - - void set_report(hid::report::type type, const std::span &data) override - { - // no FEATURE or OUTPUT reports - } - void get_report(hid::report::selector select, const std::span& buffer) override; - void in_report_sent(const std::span& data) override; - - private: - void send_buffer(uint8_t buf_idx); - static hid::report_protocol report_protocol() - { - static constexpr const auto rd{report_desc()}; - constexpr hid::report_protocol rp{rd}; - return rp; - } - - double_buffer report_buffer_{}; -}; - -#endif // __HID_BATTERY_APP_HEADER__ diff --git a/device/src/usb/keyboard_app.cpp b/device/src/usb/keyboard_app.cpp index 3262e10b0..9d1caa044 100644 --- a/device/src/usb/keyboard_app.cpp +++ b/device/src/usb/keyboard_app.cpp @@ -1,15 +1,22 @@ #include "keyboard_app.hpp" -#include "zephyr/sys/printk.h" - extern "C" { -#include "usb/usb_compatibility.h" +#include "connections.h" +#include "usb_compatibility.h" +#include "zephyr/sys/printk.h" } -keyboard_app &keyboard_app::handle() +keyboard_app &keyboard_app::usb_handle() { static keyboard_app app{}; return app; } +#if DEVICE_IS_UHK80_RIGHT +keyboard_app &keyboard_app::ble_handle() +{ + static keyboard_app ble_app{}; + return ble_app; +} +#endif void keyboard_app::set_rollover(rollover mode) { @@ -38,7 +45,10 @@ void keyboard_app::reset_keys() } } -extern void hidmgr_set_transport(const hid::transport *tp); +static inline connection_id_t usbHidConnId() +{ + return DEVICE_IS_UHK80_LEFT ? ConnectionId_UsbHidLeft : ConnectionId_UsbHidRight; +} void keyboard_app::start(hid::protocol prot) { @@ -51,14 +61,16 @@ void keyboard_app::start(hid::protocol prot) // TODO start handling keyboard events reset_keys(); - hidmgr_set_transport(get_transport()); + Connections_SetState( + (this == &usb_handle()) ? usbHidConnId() : ConnectionId_BtHid, ConnectionState_Ready); } void keyboard_app::stop() { sending_sem_.release(); // TODO stop handling keyboard events - hidmgr_set_transport(get_transport()); + Connections_SetState((this == &usb_handle()) ? usbHidConnId() : ConnectionId_BtHid, + ConnectionState_Disconnected); } bool keyboard_app::using_nkro() const @@ -82,7 +94,7 @@ void keyboard_app::set_report_state(const keys_nkro_report_base<> &data) keys_6kro.modifiers = data.modifiers; keys_6kro.scancodes.reset(); for (auto code = LOWEST_SCANCODE; code <= HIGHEST_SCANCODE; - code = static_cast(static_cast(code) + 1)) { + code = static_cast(static_cast(code) + 1)) { keys_6kro.scancodes.set(code, data.test(code)); } } else { @@ -90,7 +102,7 @@ void keyboard_app::set_report_state(const keys_nkro_report_base<> &data) keys_6kro.modifiers = data.modifiers; keys_6kro.scancodes.reset(); for (auto code = LOWEST_SCANCODE; code <= HIGHEST_SCANCODE; - code = static_cast(static_cast(code) + 1)) { + code = static_cast(static_cast(code) + 1)) { keys_6kro.scancodes.set(code, data.test(code)); } } @@ -122,7 +134,7 @@ void keyboard_app::set_report_state(const keys_nkro_report_base<> &data) keys_.sixkro.modifiers = data.modifiers; keys_.sixkro.scancodes.reset(); for (auto code = LOWEST_SCANCODE; code <= HIGHEST_SCANCODE; - code = static_cast(static_cast(code) + 1)) { + code = static_cast(static_cast(code) + 1)) { keys_.sixkro.scancodes.set(code, data.test(code)); } @@ -149,7 +161,8 @@ void keyboard_app::set_report(hid::report::type type, const std::span; diff --git a/device/src/usb/mouse_app.cpp b/device/src/usb/mouse_app.cpp index 352d19383..d34ea1171 100644 --- a/device/src/usb/mouse_app.cpp +++ b/device/src/usb/mouse_app.cpp @@ -1,11 +1,18 @@ #include "mouse_app.hpp" #include "zephyr/sys/printk.h" -mouse_app &mouse_app::handle() +mouse_app &mouse_app::usb_handle() { static mouse_app app{}; return app; } +#if DEVICE_IS_UHK80_RIGHT +mouse_app &mouse_app::ble_handle() +{ + static mouse_app ble_app{}; + return ble_app; +} +#endif void mouse_app::start(hid::protocol prot) { diff --git a/device/src/usb/mouse_app.hpp b/device/src/usb/mouse_app.hpp index 87c8259b7..5bab00cd6 100644 --- a/device/src/usb/mouse_app.hpp +++ b/device/src/usb/mouse_app.hpp @@ -90,7 +90,10 @@ class mouse_app : public app_base { bool operator!=(const mouse_report_base &other) const = default; }; - static mouse_app &handle(); + static mouse_app &usb_handle(); +#if DEVICE_IS_UHK80_RIGHT + static mouse_app &ble_handle(); +#endif void set_report_state(const mouse_report_base<> &data); @@ -100,7 +103,7 @@ class mouse_app : public app_base { void start(hid::protocol prot) override; using mouse_report = mouse_report_base; - C2USB_USB_TRANSFER_ALIGN(mouse_report, report_buffer_){}; + C2USB_USB_TRANSFER_ALIGN(mouse_report, report_buffer_) {}; }; using mouse_buffer = mouse_app::mouse_report_base<>; diff --git a/device/src/usb/usb.cpp b/device/src/usb/usb.cpp index 522146f6c..a430c5744 100644 --- a/device/src/usb/usb.cpp +++ b/device/src/usb/usb.cpp @@ -1,14 +1,16 @@ extern "C" { #include "usb.h" +#include "connections.h" #include "device.h" +#include "device_state.h" #include "key_states.h" #include "keyboard/charger.h" #include "keyboard/key_scanner.h" +#include "logger.h" #include "power_mode.h" #include "timer.h" +#include "usb_report_updater.h" #include "user_logic.h" -#include "logger.h" -#include #include } #include "command_app.hpp" @@ -23,13 +25,6 @@ extern "C" { #include "usb/df/vendor/microsoft_xinput.hpp" #include -extern "C" { -#include "device_state.h" -#include "usb_report_updater.h" -} -#if DEVICE_HAS_BATTERY - #include "hid_battery_app.hpp" -#endif #if DEVICE_IS_UHK80_RIGHT #include "port/zephyr/bluetooth/hid.hpp" @@ -55,7 +50,7 @@ class multi_hid : public hid::multi_application { } private: - std::array app_array_{(&Args::handle())..., nullptr}; + std::array app_array_{(&Args::ble_handle())..., nullptr}; multi_hid() : multi_application({{}, 0, 0, 0}, app_array_) { static constexpr const auto desc = report_desc(); @@ -100,7 +95,7 @@ struct usb_manager { // attempt to avoid unnecessary races int new_conf = next_config_.fetch_and(~(launch_flag | change_flag)); for (; new_conf & (launch_flag | change_flag); - new_conf = next_config_.fetch_and(~(launch_flag | change_flag))) { + new_conf = next_config_.fetch_and(~(launch_flag | change_flag))) { k_msleep(50); // TODO: this is guesswork so far } @@ -111,15 +106,12 @@ struct usb_manager { { static constexpr auto speed = usb::speed::FULL; static usb::df::hid::function usb_kb{ - keyboard_app::handle(), usb::hid::boot_protocol_mode::KEYBOARD}; - static usb::df::hid::function usb_mouse{mouse_app::handle()}; + keyboard_app::usb_handle(), usb::hid::boot_protocol_mode::KEYBOARD}; + static usb::df::hid::function usb_mouse{mouse_app::usb_handle()}; static usb::df::hid::function usb_command{command_app::usb_handle()}; - static usb::df::hid::function usb_controls{controls_app::handle()}; - static usb::df::hid::function usb_gamepad{gamepad_app::handle()}; - static usb::df::microsoft::xfunction usb_xpad{gamepad_app::handle()}; -#if DEVICE_HAS_BATTERY - static usb::df::hid::function usb_battery{hid_battery_app::handle()}; -#endif + static usb::df::hid::function usb_controls{controls_app::usb_handle()}; + static usb::df::hid::function usb_gamepad{gamepad_app::usb_handle()}; + static usb::df::microsoft::xfunction usb_xpad{gamepad_app::usb_handle()}; constexpr auto config_header = usb::df::config::header(usb::df::config::power::bus(500, true)); @@ -127,20 +119,7 @@ struct usb_manager { usb::df::hid::config(usb_kb, speed, usb::endpoint::address(0x81), 1), usb::df::hid::config(usb_mouse, speed, usb::endpoint::address(0x82), 1), usb::df::hid::config(usb_command, speed, usb::endpoint::address(0x83), 10), - usb::df::hid::config(usb_controls, speed, usb::endpoint::address(0x84), 1) -#if DEVICE_HAS_BATTERY - , - usb::df::hid::config(usb_battery, speed, usb::endpoint::address(0x86), 1) - // not very useful at the moment -#endif - ); - static const auto inactive_config = usb::df::config::make_config(config_header, - usb::df::hid::config(usb_command, speed, usb::endpoint::address(0x83), 10) -#if DEVICE_HAS_BATTERY - , - usb::df::hid::config(usb_battery, speed, usb::endpoint::address(0x86), 1) -#endif - ); + usb::df::hid::config(usb_controls, speed, usb::endpoint::address(0x84), 1)); static const auto base_config = usb::df::config::make_config(config_header, shared_config_elems); @@ -155,19 +134,12 @@ struct usb_manager { usb_xpad, usb::endpoint::address(0x85), 1, usb::endpoint::address(0x05), 255)); printk("USB config changing to %s\n", magic_enum::enum_name(conf).data()); - switch (conf) { - case Hid_Empty: - ms_enum_.set_config({}); - device_.set_config(inactive_config); - break; - case Hid_NoGamepad: + if (conf == Hid_NoGamepad) { ms_enum_.set_config({}); device_.set_config(base_config); - break; - default: + } else { ms_enum_.set_config(xpad_config); device_.set_config(gamepad_config); - break; } device_.open(); } @@ -215,16 +187,11 @@ struct usb_manager { std::atomic next_config_{0xff}; }; -extern "C" void USB_EnableHid() +extern "C" void USB_Enable() { usb_manager::instance().select_config(HID_GetGamepadActive() ? Hid_Full : Hid_NoGamepad); } -extern "C" void USB_DisableHid() -{ - usb_manager::instance().select_config(Hid_Empty); -} - extern "C" void USB_RemoteWakeup() { printk("USB: requesting remote wakeup\n"); @@ -307,23 +274,6 @@ bool app_base::active() const return usb_manager::instance().device().power_state() == usb::power::state::L0_ON; } -void hidmgr_set_transport(const hid::transport *tp) -{ - // tp is the transport of the keyboard app - if (tp == nullptr) { - DeviceState_SetConnection(ConnectionId_BluetoothHid, ConnectionType_None); - DeviceState_SetConnection(ConnectionId_UsbHid, ConnectionType_None); - } -#if DEVICE_IS_UHK80_RIGHT - else if (tp == &hogp_manager::instance().main_service()) { - DeviceState_SetConnection(ConnectionId_BluetoothHid, ConnectionType_Bt); - } -#endif - else { - DeviceState_SetConnection(ConnectionId_UsbHid, ConnectionType_Usb); - } -} - static bool gamepadActive = true; extern "C" bool HID_GetGamepadActive() @@ -335,7 +285,7 @@ extern "C" void HID_SetGamepadActive(bool active) { gamepadActive = active; if (usb_manager::active()) { - USB_EnableHid(); + USB_Enable(); } #if DEVICE_IS_UHK80_RIGHT if (hogp_manager::active()) { @@ -348,12 +298,15 @@ extern "C" rollover_t HID_GetKeyboardRollover() { static_assert(((uint8_t)Rollover_6Key == (uint8_t)keyboard_app::rollover::SIX_KEY) && ((uint8_t)Rollover_NKey == (uint8_t)keyboard_app::rollover::N_KEY)); - return (rollover_t)keyboard_app::handle().get_rollover(); + return (rollover_t)keyboard_app::usb_handle().get_rollover(); } extern "C" void HID_SetKeyboardRollover(rollover_t mode) { - keyboard_app::handle().set_rollover((keyboard_app::rollover)mode); + keyboard_app::usb_handle().set_rollover((keyboard_app::rollover)mode); +#if DEVICE_IS_UHK80_RIGHT + keyboard_app::ble_handle().set_rollover((keyboard_app::rollover)mode); +#endif } extern "C" void USB_SetSerialNumber(uint32_t serialNumber) diff --git a/device/src/usb/usb.h b/device/src/usb/usb.h index fb8e00364..90f70ede8 100644 --- a/device/src/usb/usb.h +++ b/device/src/usb/usb.h @@ -33,8 +33,7 @@ bool HID_GetGamepadActive(void); void HOGP_Enable(void); void HOGP_Disable(void); - void USB_DisableHid(void); - void USB_EnableHid(void); + void USB_Enable(void); void USB_SetSerialNumber(uint32_t serialNumber); void USB_RemoteWakeup(void); diff --git a/device/src/usb/usb_compatibility.cpp b/device/src/usb/usb_compatibility.cpp index 02f0016c9..e8c2ca01f 100644 --- a/device/src/usb/usb_compatibility.cpp +++ b/device/src/usb/usb_compatibility.cpp @@ -13,6 +13,7 @@ extern "C" { #include "usb_interfaces/usb_interface_media_keyboard.h" #include "usb_interfaces/usb_interface_mouse.h" #include "usb_interfaces/usb_interface_system_keyboard.h" +#include "connections.h" } #include "command_app.hpp" #include "controls_app.hpp" @@ -22,33 +23,87 @@ extern "C" { #include "mouse_app.hpp" #include "usb/df/class/hid.hpp" -static scancode_buffer keys; -static mouse_buffer mouseState; static controls_buffer controls; keyboard_led_state_t KeyboardLedsState; -extern "C" void UsbCompatibility_SendKeyboardReport(const usb_basic_keyboard_report_t *report) -{ - keyboard_app &keyboard_app = keyboard_app::handle(); +keyboard_led_state_t KeyboardLedsStateBle; +keyboard_led_state_t KeyboardLedsStateUsb; + +typedef enum { + ReportSink_Invalid, + ReportSink_Usb, + ReportSink_BleHid, + ReportSink_Dongle, +} report_sink_t; + +static report_sink_t determineSink() { + if (DEVICE_IS_UHK_DONGLE) { + return ReportSink_Usb; + } + + connection_type_t connectionType = Connections_Type(ActiveHostConnectionId); + + if (!Connections_IsReady(ActiveHostConnectionId)) { + printk("Can't send report - selected connection is not ready!\n"); + Connections_HandleSwitchover(ConnectionId_Invalid, false); + if (!Connections_IsReady(ActiveHostConnectionId)) { + printk("Giving report to c2usb anyways!\n"); + return ReportSink_Usb; + } + } - if (keyboard_app.active()) { - keyboard_app.set_report_state(*reinterpret_cast(report)); - } else if (DEVICE_IS_UHK80_RIGHT && Bt_DeviceIsConnected(DeviceId_Uhk_Dongle)) { - Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, - SyncablePropertyId_KeyboardReport, (const uint8_t *)report, sizeof(*report)); + switch (connectionType) { + case ConnectionType_BtHid: + return ReportSink_BleHid; + case ConnectionType_UsbHidRight: + return ReportSink_Usb; + case ConnectionType_NusDongle: + if (DEVICE_IS_UHK80_RIGHT) { + return ReportSink_Dongle; + } + default: + printk("Unhandled sink type. Is this connection really meant to be a report target?\n"); + return ReportSink_Usb; } } -extern "C" void UsbCompatibility_SendMouseReport(const usb_mouse_report_t *report) +extern "C" void UsbCompatibility_SendKeyboardReport(const usb_basic_keyboard_report_t* report) { - mouse_app &mouse_app = mouse_app::handle(); + switch (determineSink()) { + case ReportSink_Usb: + keyboard_app::usb_handle().set_report_state(*reinterpret_cast(report)); + break; +#if DEVICE_IS_UHK80_RIGHT + case ReportSink_BleHid: + keyboard_app::ble_handle().set_report_state(*reinterpret_cast(report)); + printk("Giving report to c2usb ble hid!\n"); + break; +#endif + case ReportSink_Dongle: + Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, SyncablePropertyId_KeyboardReport, (const uint8_t*)report, sizeof(*report)); + break; + default: + printk("Unhandled and unexpected switch state!\n"); + } +} - if (mouse_app.active()) { - mouse_app.set_report_state(*reinterpret_cast(report)); - } else if (DEVICE_IS_UHK80_RIGHT && Bt_DeviceIsConnected(DeviceId_Uhk_Dongle)) { - Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, - SyncablePropertyId_MouseReport, (const uint8_t *)report, sizeof(*report)); +extern "C" void UsbCompatibility_SendMouseReport(const usb_mouse_report_t *report) +{ + switch (determineSink()) { + case ReportSink_Usb: + mouse_app::usb_handle().set_report_state(*reinterpret_cast(report)); + break; +#if DEVICE_IS_UHK80_RIGHT + case ReportSink_BleHid: + mouse_app::ble_handle().set_report_state(*reinterpret_cast(report)); + break; +#endif + case ReportSink_Dongle: + Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, SyncablePropertyId_MouseReport, (const uint8_t*)report, sizeof(*report)); + break; + default: + printk("Unhandled and unexpected switch state!\n"); } } @@ -67,32 +122,60 @@ extern "C" void UsbCompatibility_SendConsumerReport(const usb_media_keyboard_rep } UsbSystemKeyboard_ForeachScancode(systemReport, &UsbCompatibility_ConsumerKeyboardAddScancode); - controls_app &controls_app = controls_app::handle(); - - if (controls_app.active()) { - controls_app.set_report_state(controls); - } else if (DEVICE_IS_UHK80_RIGHT && Bt_DeviceIsConnected(DeviceId_Uhk_Dongle)) { - Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, - SyncablePropertyId_ControlsReport, (const uint8_t *)(&controls), sizeof(controls)); + switch (determineSink()) { + case ReportSink_Usb: + controls_app::usb_handle().set_report_state(controls); + break; +#if DEVICE_IS_UHK80_RIGHT + case ReportSink_BleHid: + controls_app::ble_handle().set_report_state(controls); + break; +#endif + case ReportSink_Dongle: + Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, SyncablePropertyId_ControlsReport, (const uint8_t*)&controls, sizeof(controls)); + break; + default: + printk("Unhandled and unexpected switch state!\n"); } } extern "C" void UsbCompatibility_SendConsumerReport2(const uint8_t *report) { - controls_app &controls_app = controls_app::handle(); - - if (controls_app.active()) { - controls_app.set_report_state(*(const controls_buffer *)report); + switch (determineSink()) { + case ReportSink_Usb: + controls_app::usb_handle().set_report_state(*(const controls_buffer *)report); + break; + default: + printk("This wasn't expected. Is this a dongle?\n"); + break; } } -extern "C" bool UsbCompatibility_UsbConnected() +extern "C" void UsbCompatibility_UpdateKeyboardLedsState() { - return keyboard_app::handle().has_transport(); + switch (Connections_Type(ActiveHostConnectionId)) { + case ConnectionType_UsbHidRight: + case ConnectionType_UsbHidLeft: + UsbCompatibility_SetCurrentKeyboardLedsState(KeyboardLedsStateUsb); + break; + case ConnectionType_BtHid: + case ConnectionType_NewBtHid: + UsbCompatibility_SetCurrentKeyboardLedsState(KeyboardLedsStateBle); + break; + case ConnectionType_NusDongle: + UsbCompatibility_SetCurrentKeyboardLedsState(KeyboardLedsState); + break; + default: + printk("Unhandled connection type %d\n", Connections_Type(ActiveHostConnectionId)); + break; + } } -extern "C" void UsbCompatibility_SetKeyboardLedsState(bool capsLock, bool numLock, bool scrollLock) -{ +extern "C" void UsbCompatibility_SetCurrentKeyboardLedsState(keyboard_led_state_t state) { + bool capsLock = state.capsLock; + bool numLock = state.numLock; + bool scrollLock = state.scrollLock; + if (KeyboardLedsState.capsLock != capsLock) { KeyboardLedsState.capsLock = capsLock; UsbBasicKeyboard_CapsLockOn = capsLock; @@ -114,3 +197,32 @@ extern "C" void UsbCompatibility_SetKeyboardLedsState(bool capsLock, bool numLoc StateSync_UpdateProperty(StateSyncPropertyId_KeyboardLedsState, NULL); } + +extern "C" void UsbCompatibility_SetKeyboardLedsState(connection_id_t connectionId, bool capsLock, bool numLock, bool scrollLock) +{ + keyboard_led_state_t state = { capsLock, numLock, scrollLock }; + +#if DEVICE_IS_UHK80_RIGHT + switch (Connections_Type(connectionId)) { + case ConnectionType_UsbHidLeft: + case ConnectionType_UsbHidRight: + KeyboardLedsStateUsb = state; + break; + case ConnectionType_BtHid: + case ConnectionType_NewBtHid: + KeyboardLedsStateBle = state; + break; + case ConnectionType_NusDongle: + break; + default: + printk("Unhandled connection type %d\n", Connections_Type(connectionId)); + return; + } + + if (Connections_IsActiveHostConnection(connectionId)) { + UsbCompatibility_SetCurrentKeyboardLedsState(state); + } +#else + UsbCompatibility_SetCurrentKeyboardLedsState(state); +#endif +} diff --git a/device/src/usb/usb_compatibility.h b/device/src/usb/usb_compatibility.h index 7f94758a8..c64ab6719 100644 --- a/device/src/usb/usb_compatibility.h +++ b/device/src/usb/usb_compatibility.h @@ -9,6 +9,7 @@ #include "usb_interfaces/usb_interface_media_keyboard.h" #include "usb_interfaces/usb_interface_system_keyboard.h" #include "usb_interfaces/usb_interface_mouse.h" + #include "connections.h" // Macros: @@ -26,11 +27,15 @@ // Functions: + // report sending void UsbCompatibility_SendKeyboardReport(const usb_basic_keyboard_report_t* report); void UsbCompatibility_SendMouseReport(const usb_mouse_report_t* report) ; void UsbCompatibility_SendConsumerReport(const usb_media_keyboard_report_t* mediaReport, const usb_system_keyboard_report_t* systemReport); void UsbCompatibility_SendConsumerReport2(const uint8_t* report); - void UsbCompatibility_SetKeyboardLedsState(bool capsLock, bool numLock, bool scrollLock); - bool UsbCompatibility_UsbConnected(); + + // num lock, caps lock, scroll lock state handling + void UsbCompatibility_UpdateKeyboardLedsState(); + void UsbCompatibility_SetCurrentKeyboardLedsState(keyboard_led_state_t state); + void UsbCompatibility_SetKeyboardLedsState(connection_id_t connectionId, bool capsLock, bool numLock, bool scrollLock); #endif // __USB_HEADER__ diff --git a/doc-dev/reference-manual.md b/doc-dev/reference-manual.md index 79ba37f25..f76373def 100644 --- a/doc-dev/reference-manual.md +++ b/doc-dev/reference-manual.md @@ -114,6 +114,7 @@ COMMAND = setVar COMMAND = {pressKey|holdKey|tapKey|releaseKey} SHORTCUT COMMAND = tapKeySeq [SHORTCUT]+ COMMAND = powerMode [toggle] { wake | lightSleep | sleep | deepSleep } +COMMAND = switchHost { last | next | previous | | } COMMAND = set module.MODULEID.navigationMode.LAYERID_BASIC NAVIGATION_MODE COMMAND = set module.MODULEID.baseSpeed COMMAND = set module.MODULEID.speed diff --git a/right/src/config_manager.c b/right/src/config_manager.c index 4c7fa7856..036906e73 100644 --- a/right/src/config_manager.c +++ b/right/src/config_manager.c @@ -272,6 +272,7 @@ const config_t DefaultCfg = (config_t){ #else .I2cBaudRate = I2C_MAIN_BUS_NORMAL_BAUD_RATE, #endif + .AllowUnsecuredConnections = false, .EmergencyKey = NULL, .KeyActionColors = { {0x00, 0x00, 0x00}, // KeyActionColor_None diff --git a/right/src/config_manager.h b/right/src/config_manager.h index de63a339a..9774d0cdc 100644 --- a/right/src/config_manager.h +++ b/right/src/config_manager.h @@ -80,6 +80,7 @@ // others uint32_t I2cBaudRate; + bool AllowUnsecuredConnections; } config_t; // Variables: diff --git a/right/src/config_parser/parse_config.c b/right/src/config_parser/parse_config.c index 6fa61df7e..a86af2ca1 100644 --- a/right/src/config_parser/parse_config.c +++ b/right/src/config_parser/parse_config.c @@ -27,6 +27,10 @@ #include "versioning.h" #include "stubs.h" +#if DEVICE_HAS_OLED +#include "keyboard/oled/widgets/widgets.h" +#endif + version_t DataModelVersion = {0, 0, 0}; bool PerKeyRgbPresent = false; @@ -55,7 +59,12 @@ parser_error_t parseConfig(config_buffer_t *buffer) #ifdef __ZEPHYR__ if (!ParserRunDry) { - printk("Flashed User Config version: %u.%u.%u\n", DataModelVersion.major, DataModelVersion.minor, DataModelVersion.patch); + printk( + "Flashed User Config version: %u.%u.%u (native version: %u.%u.%u., at %s)\n", + DataModelVersion.major, DataModelVersion.minor, DataModelVersion.patch, + userConfigVersion.major, userConfigVersion.minor, userConfigVersion.patch, + gitTag + ); } #endif @@ -330,6 +339,8 @@ parser_error_t parseConfig(config_buffer_t *buffer) LedManager_RecalculateLedBrightness(); LedManager_UpdateSleepModes(); BtPair_ClearUnknownBonds(); + BtConn_UpdateHostConnectionPeerAllocations(); + WIDGET_REFRESH(&TargetWidget); // the target may have been renamed // Update counts diff --git a/right/src/config_parser/parse_host_connection.c b/right/src/config_parser/parse_host_connection.c index c033080e5..dd4f3ace2 100644 --- a/right/src/config_parser/parse_host_connection.c +++ b/right/src/config_parser/parse_host_connection.c @@ -18,11 +18,11 @@ static parser_error_t parseHostConnection(config_buffer_t* buffer, host_connecti return ParserError_InvalidHostType; } - if (hostConnection->type == HostConnectionType_Ble || hostConnection->type == HostConnectionType_Dongle) { - hostConnection->bleAddress.type = 1; + if (hostConnection->type == HostConnectionType_BtHid || hostConnection->type == HostConnectionType_Dongle) { for (uint8_t i = 0; i < BLE_ADDRESS_LENGTH; i++) { hostConnection->bleAddress.a.val[i] = ReadUInt8(buffer); } + hostConnection->bleAddress.type = hostConnection->bleAddress.a.val[0] & 0x01; } if (hostConnection->type != HostConnectionType_Empty) { @@ -41,7 +41,7 @@ static parser_error_t parseHostConnection(config_buffer_t* buffer, host_connecti parser_error_t ParseHostConnections(config_buffer_t *buffer) { int errorCode; - for (uint8_t hostConnectionId = 0; hostConnectionId < HOST_CONNECTION_COUNT_MAX; hostConnectionId++) { + for (uint8_t hostConnectionId = 0; hostConnectionId < SERIALIZED_HOST_CONNECTION_COUNT_MAX; hostConnectionId++) { host_connection_t dummy; host_connection_t* hostConnection = &dummy; diff --git a/right/src/event_scheduler.c b/right/src/event_scheduler.c index dd5aeebf7..91ae2a51f 100644 --- a/right/src/event_scheduler.c +++ b/right/src/event_scheduler.c @@ -17,6 +17,7 @@ #include "keyboard/charger.h" #include "keyboard/uart.h" #include "main.h" +#include "bt_manager.h" #else #include "segment_display.h" #endif @@ -109,6 +110,9 @@ static void processEvt(event_scheduler_event_t evt) case EventSchedulerEvent_RestartBt: BtManager_RestartBt(); break; + case EventSchedulerEvent_BtStartScanningAndAdvertising: + BtManager_StartScanningAndAdvertising(); + break; default: return; } diff --git a/right/src/event_scheduler.h b/right/src/event_scheduler.h index 31a73f587..b015f6ff9 100644 --- a/right/src/event_scheduler.h +++ b/right/src/event_scheduler.h @@ -35,6 +35,7 @@ EventSchedulerEvent_PowerMode, EventSchedulerEvent_EndBtPairing, EventSchedulerEvent_RestartBt, + EventSchedulerEvent_BtStartScanningAndAdvertising, EventSchedulerEvent_Count } event_scheduler_event_t; diff --git a/right/src/host_connection.c b/right/src/host_connection.c index 14b340d8a..884a1eee7 100644 --- a/right/src/host_connection.c +++ b/right/src/host_connection.c @@ -1,30 +1,46 @@ #include "host_connection.h" +#include "str_utils.h" +#include "macros/string_reader.h" #ifdef __ZEPHYR__ #include "bt_conn.h" +#include "connections.h" -host_connection_t HostConnections[HOST_CONNECTION_COUNT_MAX] = {}; +host_connection_t HostConnections[HOST_CONNECTION_COUNT_MAX] = { + [HOST_CONNECTION_COUNT_MAX - 2] = { + .type = HostConnectionType_NewBtHid, + .name = (string_segment_t){ .start = "New Bluetooth Device", .end = NULL }, + .switchover = true, + }, + [HOST_CONNECTION_COUNT_MAX - 1] = { + .type = HostConnectionType_UsbHidRight, + .name = (string_segment_t){ .start = "USB Device (Backup)", .end = NULL }, + .switchover = true, + }, +}; bool HostConnections_IsKnownBleAddress(const bt_addr_le_t *address) { for (int i = 0; i < HOST_CONNECTION_COUNT_MAX; i++) { switch (HostConnections[i].type) { case HostConnectionType_Empty: - case HostConnectionType_UsbRight: - case HostConnectionType_UsbLeft: + case HostConnectionType_UsbHidRight: + case HostConnectionType_UsbHidLeft: + case HostConnectionType_NewBtHid: + case HostConnectionType_Count: break; case HostConnectionType_Dongle: - case HostConnectionType_Ble: - if (bt_addr_le_cmp(address, &HostConnections[i].bleAddress) == 0) { + case HostConnectionType_BtHid: + if (BtAddrEq(address, &HostConnections[i].bleAddress)) { return true; } break; - default: - break; } } - for (int peerIdx = 0; peerIdx < PeerIdHid; peerIdx++) { - if (bt_addr_le_cmp(address, &Peers[peerIdx].addr) == 0) { + // Don't count new ble connections + // Do check devices that are paired via settings - left, right, dongle + for (int peerIdx = 0; peerIdx < PeerIdFirstHost; peerIdx++) { + if (BtAddrEq(address, &Peers[peerIdx].addr)) { return true; } } @@ -32,6 +48,82 @@ bool HostConnections_IsKnownBleAddress(const bt_addr_le_t *address) { return false; } +host_connection_t* HostConnection(uint8_t connectionId) { + if (connectionId < ConnectionId_HostConnectionFirst || connectionId > ConnectionId_HostConnectionLast) { + printk("Supplied connection (%d) doesn't correspond to a host connection!\n", connectionId); + return NULL; + } + + return &HostConnections[connectionId - ConnectionId_HostConnectionFirst]; +} + +static void selectConnection(uint8_t connectionId) { + SelectedHostConnectionId = connectionId; + if (Connections_IsReady(connectionId)) { + Connections_HandleSwitchover(connectionId, true); + } else { + BtConn_ReserveConnections(); + } + Connections_ReportState(connectionId); +} + +static void selectNextConnection(int8_t direction) { + for (int8_t i = ActiveHostConnectionId + direction; i != ActiveHostConnectionId; i += direction) { + if (i > ConnectionId_HostConnectionLast) { + i = ConnectionId_HostConnectionFirst; + } + if (i < ConnectionId_HostConnectionFirst) { + i = ConnectionId_HostConnectionLast; + } + + if (Connections_IsReady(i)) { + Connections_HandleSwitchover(i, true); + break; + } + } + SelectedHostConnectionId = ConnectionId_Invalid; +} + +void HostConnections_SelectNextConnection(void) { + selectNextConnection(1); +} + +void HostConnections_SelectPreviousConnection(void) { + selectNextConnection(-1); +} + +void HostConnections_SelectByName(parser_context_t* ctx) { + for (uint8_t i = ConnectionId_HostConnectionFirst; i <= ConnectionId_HostConnectionLast; i++) { + if (Macros_CompareStringToken(ctx, HostConnection(i)->name)) { + selectConnection(i); + return; + } + } +} + +void HostConnections_SelectById(uint8_t connectionId) { + selectConnection(connectionId); +} + +void HostConnections_ListKnownBleConnections() { + printk("Known host connection ble addresses:\n"); + for (int i = 0; i < HOST_CONNECTION_COUNT_MAX; i++) { + host_connection_type_t type = HostConnections[i].type; + switch (type) { + case HostConnectionType_Empty: + case HostConnectionType_UsbHidRight: + case HostConnectionType_UsbHidLeft: + case HostConnectionType_Count: + break; + case HostConnectionType_NewBtHid: + case HostConnectionType_Dongle: + case HostConnectionType_BtHid: + printk(" - %d '%.*s': address: %s\n", i, EXPAND_SEGMENT(HostConnections[i].name), GetAddrString(&HostConnections[i].bleAddress)); + break; + } + } +} + #endif diff --git a/right/src/host_connection.h b/right/src/host_connection.h index 04bae7ff6..f8a358a8e 100644 --- a/right/src/host_connection.h +++ b/right/src/host_connection.h @@ -28,7 +28,8 @@ // Macros: - #define HOST_CONNECTION_COUNT_MAX 22 + #define SERIALIZED_HOST_CONNECTION_COUNT_MAX 22 + #define HOST_CONNECTION_COUNT_MAX (SERIALIZED_HOST_CONNECTION_COUNT_MAX+2) #define BLE_ADDRESS_LENGTH 6 @@ -36,10 +37,11 @@ typedef enum { HostConnectionType_Empty, - HostConnectionType_UsbRight, - HostConnectionType_UsbLeft, - HostConnectionType_Ble, + HostConnectionType_UsbHidRight, + HostConnectionType_UsbHidLeft, + HostConnectionType_BtHid, HostConnectionType_Dongle, + HostConnectionType_NewBtHid, HostConnectionType_Count, } host_connection_type_t; @@ -57,5 +59,13 @@ // Functions: bool HostConnections_IsKnownBleAddress(const bt_addr_le_t *address); + host_connection_t* HostConnection(uint8_t connectionId); + + void HostConnections_ListKnownBleConnections(); + + void HostConnections_SelectById(uint8_t connectionId); + void HostConnections_SelectNextConnection(void); + void HostConnections_SelectPreviousConnection(void); + void HostConnections_SelectByName(parser_context_t* ctx); #endif diff --git a/right/src/layer_switcher.c b/right/src/layer_switcher.c index 09c531e89..a0bddb383 100644 --- a/right/src/layer_switcher.c +++ b/right/src/layer_switcher.c @@ -209,7 +209,7 @@ static bool layerMeetsHoldConditions(uint8_t layer, uint8_t* maskOutput) { } // Gathers states set by LayerSwitcher_HoldLayer during previous update cycle and updates heldLayer. -void LayerSwitcher_UpdateActiveLayer() { +void LayerSwitcher_UpdateHeldLayer() { EventVector_Unset(EventVector_LayerHolds); layer_id_t previousHeldLayer = heldLayer; diff --git a/right/src/layer_switcher.h b/right/src/layer_switcher.h index 0ae1a242c..27f7a6b7d 100644 --- a/right/src/layer_switcher.h +++ b/right/src/layer_switcher.h @@ -27,7 +27,7 @@ // Functions - hooks: void LayerSwitcher_ResetHolds(); - void LayerSwitcher_UpdateActiveLayer(); + void LayerSwitcher_UpdateHeldLayer(); void LayerSwitcher_RecalculateLayerComposition(); diff --git a/right/src/led_manager.c b/right/src/led_manager.c index 0f286c089..46f409bc3 100644 --- a/right/src/led_manager.c +++ b/right/src/led_manager.c @@ -18,6 +18,12 @@ #include "device.h" #endif +#if DEVICE_IS_UHK80_RIGHT +#include "keyboard/oled/screens/screen_manager.h" +#else +#define InteractivePairingInProgress false; +#endif + bool KeyBacklightSleepModeActive = false; bool DisplaySleepModeActive = false; @@ -27,7 +33,7 @@ uint8_t KeyBacklightBrightness = 0xff; static void recalculateLedBrightness() { bool globalSleepMode = !Cfg.LedsEnabled || CurrentPowerMode > PowerMode_Awake || Cfg.LedBrightnessMultiplier == 0.0f; - bool globalAlwaysOn = Cfg.LedsAlwaysOn || Ledmap_AlwaysOn; + bool globalAlwaysOn = Cfg.LedsAlwaysOn || Ledmap_AlwaysOn || InteractivePairingInProgress; if (!globalAlwaysOn && (globalSleepMode || KeyBacklightSleepModeActive)) { KeyBacklightBrightness = 0; diff --git a/right/src/ledmap.c b/right/src/ledmap.c index deb1eac74..0eacadd59 100644 --- a/right/src/ledmap.c +++ b/right/src/ledmap.c @@ -10,6 +10,7 @@ #include "slot.h" #include "config_manager.h" #include "event_scheduler.h" +#include "test_switches.h" #ifdef __ZEPHYR__ #include "keyboard/leds.h" @@ -29,14 +30,7 @@ static const rgb_t white = RGB(0xff, 0xff, 0xff); bool Ledmap_AlwaysOn = false; backlighting_mode_t TemporaryBacklightingMode = BacklightingMode_Unspecified; -typedef enum { - BacklightingLedTestModeState_All, - BacklightingLedTestModeState_Additive, -} backlighting_led_test_mode_state_t; - -bool Ledmap_LedTestActive = false; static uint32_t backlightingLedTestStart = 0; -static backlighting_led_test_mode_state_t backlightingLedTestModeState = BacklightingLedTestModeState_All; #if DEVICE_ID == DEVICE_ID_UHK60V1 @@ -594,38 +588,26 @@ static void setEntireMatrix(uint8_t v) { #endif } -static void setAllTo(const rgb_t* color) { - for (uint8_t slotId=0; slotId (b) ? (a) : (b)) #endif @@ -1731,6 +1737,40 @@ static macro_result_t processResetConfigurationCommand(parser_context_t* ctx) return MacroResult_Finished; } +static macro_result_t processSwitchHostCommand(parser_context_t* ctx) +{ +#define DRY_RUN_FINISH() if (Macros_DryRun) { return MacroResult_Finished; } + +#ifdef __ZEPHYR__ + static uint8_t lastConnection = 0; + uint8_t currentConnection = ActiveHostConnectionId; + + if (ConsumeToken(ctx, "next")) { + DRY_RUN_FINISH(); + HostConnections_SelectNextConnection(); + } + else if (ConsumeToken(ctx, "prev") || ConsumeToken(ctx, "previous")) { + DRY_RUN_FINISH(); + HostConnections_SelectPreviousConnection(); + } + else if (ConsumeToken(ctx, "last")) { + DRY_RUN_FINISH(); + HostConnections_SelectById(lastConnection); + } else { + if (!Macros_DryRun) { + HostConnections_SelectByName(ctx); + } + Macros_ConsumeStringToken(ctx); + } + + lastConnection = currentConnection; +#endif + +#undef DRY_RUN_FINISH + + return MacroResult_Finished; +} + static macro_result_t processCommand(parser_context_t* ctx) { if (*ctx->at == '$') { @@ -2311,6 +2351,9 @@ static macro_result_t processCommand(parser_context_t* ctx) Macros_ReportError("Command deprecated. Please, replace switchKeymapLayer by toggleKeymapLayer or holdKeymapLayer. Or complain on github that you actually need this command.", ctx->at, ctx->at); return MacroResult_Finished; } + else if (ConsumeToken(ctx, "switchHost")) { + return processSwitchHostCommand(ctx); + } else { goto failed; } diff --git a/right/src/macros/set_command.c b/right/src/macros/set_command.c index 2b790bf4d..1e37819b4 100644 --- a/right/src/macros/set_command.c +++ b/right/src/macros/set_command.c @@ -874,6 +874,9 @@ static macro_variable_t root(parser_context_t* ctx, set_command_action_t action) DEFINE_INT_LIMITS(0, 65535); ASSIGN_INT(Cfg.AutoShiftDelay); } + else if (ConsumeToken(ctx, "allowUnsecuredConnections")) { + ASSIGN_BOOL(Cfg.AllowUnsecuredConnections); + } #ifndef __ZEPHYR__ else if (ConsumeToken(ctx, "i2cBaudRate")) { if (action == SetCommandAction_Read) { diff --git a/right/src/macros/status_buffer.h b/right/src/macros/status_buffer.h index 081116404..d7ebf8fdf 100644 --- a/right/src/macros/status_buffer.h +++ b/right/src/macros/status_buffer.h @@ -13,6 +13,8 @@ // Typedefs: + #define PRINTM(...) Macros_ReportPrintf(NULL, __VA_ARGS__) + // Variables: // extern bool Macros_ConsumeStatusCharDirtyFlag; diff --git a/right/src/macros/string_reader.c b/right/src/macros/string_reader.c index 692d646e9..e3a197ac4 100644 --- a/right/src/macros/string_reader.c +++ b/right/src/macros/string_reader.c @@ -5,6 +5,7 @@ #include #include #include "macros/vars.h" +#include "str_utils.h" #if !defined(MAX) #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -152,6 +153,39 @@ static char tryConsumeAnotherStringLiteral(parser_context_t* ctx, uint16_t* stri } } +bool Macros_CompareStringToken(parser_context_t* ctx, string_segment_t str) { + parser_context_t ctx2 = *ctx; + uint16_t stringOffset = 0, textIndex = 0, textSubIndex = 0; + const char* str2 = str.start; + + char c1, c2; + while (true) { + c1 = Macros_ConsumeCharOfString(&ctx2, &stringOffset, &textIndex, &textSubIndex); + c2 = *str2; + + bool c1Ended = c1 == '\0'; + bool c2Ended = c2 == '\0' || str2 >= str.end; + + if (c1Ended || c2Ended) { + return c1Ended && c2Ended; + } + + if (c1 != c2) { + return false; + } + + str2++; + } +} + +void Macros_ConsumeStringToken(parser_context_t* ctx) { + uint16_t stringOffset = 0, textIndex = 0, textSubIndex = 0; + char c = 'a'; + while (c != '\0') { + c = Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex); + } +} + char Macros_ConsumeCharOfString(parser_context_t* ctx, uint16_t* stringOffset, uint16_t* index, uint16_t* subIndex) { const char* a = ctx->at; diff --git a/right/src/macros/string_reader.h b/right/src/macros/string_reader.h index 601dbf3be..efa1d9f99 100644 --- a/right/src/macros/string_reader.h +++ b/right/src/macros/string_reader.h @@ -26,6 +26,8 @@ // Functions: char Macros_ConsumeCharOfString(parser_context_t* ctx, uint16_t* stringOffset, uint16_t* index, uint16_t* subIndex); + bool Macros_CompareStringToken(parser_context_t* ctx, string_segment_t str); + void Macros_ConsumeStringToken(parser_context_t* ctx); #endif diff --git a/right/src/power_mode.c b/right/src/power_mode.c index 0158d5cb4..1beb5066c 100644 --- a/right/src/power_mode.c +++ b/right/src/power_mode.c @@ -6,6 +6,7 @@ #ifdef __ZEPHYR__ #include "device_state.h" #include "usb/usb.h" + #include "connections.h" #else #include "slave_drivers/is31fl3xxx_driver.h" #include "usb_composite_device.h" @@ -28,7 +29,7 @@ void PowerMode_SetUsbAwake(bool awake) { void PowerMode_Update() { #ifdef __ZEPHYR__ - bool someoneAwake = usbAwake || DeviceState_IsConnected(ConnectionId_BluetoothHid) || DeviceState_IsConnected(ConnectionId_Dongle); + bool someoneAwake = DeviceState_IsTargetConnected(ConnectionTarget_Host); #else bool someoneAwake = CurrentPowerMode == PowerMode_Awake; #endif diff --git a/right/src/slave_drivers/touchpad_driver.c b/right/src/slave_drivers/touchpad_driver.c index 2b82c7e38..caa85d5fd 100644 --- a/right/src/slave_drivers/touchpad_driver.c +++ b/right/src/slave_drivers/touchpad_driver.c @@ -166,10 +166,10 @@ slave_result_t TouchpadDriver_Update(uint8_t uhkModuleDriverId) ModuleConnectionStates[UhkModuleDriverId_RightModule].lastTimeConnected = CurrentTime; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" - if (noFingers || deltaX || deltaY || *(uint16_t*)&gestureEvents || *(uint16_t*)&TouchpadEvents) { -#pragma GCC diagnostic pop + static bool somethingWasNonZero = false; + bool somethingIsNonZero = noFingers || deltaX || deltaY || ((uint8_t*)&gestureEvents)[0] || ((uint8_t*)&gestureEvents)[1]; + + if (somethingIsNonZero || somethingWasNonZero) { bool somethingChanged = false; if ( @@ -208,6 +208,7 @@ slave_result_t TouchpadDriver_Update(uint8_t uhkModuleDriverId) res.status = I2cAsyncWrite(address, closeCommunicationWindow, sizeof(closeCommunicationWindow)); res.hold = false; + somethingWasNonZero = somethingIsNonZero; phase = 3; break; diff --git a/right/src/slave_drivers/uhk_module_driver.c b/right/src/slave_drivers/uhk_module_driver.c index 6d1b9bd4c..c2d7a10f5 100644 --- a/right/src/slave_drivers/uhk_module_driver.c +++ b/right/src/slave_drivers/uhk_module_driver.c @@ -118,7 +118,7 @@ static void reloadKeymapIfNeeded() EventVector_WakeMain(); } #else - if (!someoneElseWillDoTheJob && !TestSwitches) { + if (!someoneElseWillDoTheJob) { EventVector_Set(EventVector_KeymapReloadNeeded); } diff --git a/right/src/slave_drivers/uhk_module_driver.h b/right/src/slave_drivers/uhk_module_driver.h index 4ac618ef4..e4c65b627 100644 --- a/right/src/slave_drivers/uhk_module_driver.h +++ b/right/src/slave_drivers/uhk_module_driver.h @@ -31,60 +31,60 @@ typedef enum { // Sync communication - UhkModulePhase_RequestSync, - UhkModulePhase_ReceiveSync, - UhkModulePhase_ProcessSync, + UhkModulePhase_RequestSync = 0, + UhkModulePhase_ReceiveSync = 1, + UhkModulePhase_ProcessSync = 2, // Get protocol version - UhkModulePhase_RequestModuleProtocolVersion, - UhkModulePhase_ReceiveModuleProtocolVersion, - UhkModulePhase_ProcessModuleProtocolVersion, + UhkModulePhase_RequestModuleProtocolVersion = 3, + UhkModulePhase_ReceiveModuleProtocolVersion = 4, + UhkModulePhase_ProcessModuleProtocolVersion = 5, // Get firmware version - UhkModulePhase_RequestFirmwareVersion, - UhkModulePhase_ReceiveFirmwareVersion, - UhkModulePhase_ProcessFirmwareVersion, + UhkModulePhase_RequestFirmwareVersion = 6, + UhkModulePhase_ReceiveFirmwareVersion = 7, + UhkModulePhase_ProcessFirmwareVersion = 8, // Get module id - UhkModulePhase_RequestModuleId, - UhkModulePhase_ReceiveModuleId, - UhkModulePhase_ProcessModuleId, + UhkModulePhase_RequestModuleId = 9, + UhkModulePhase_ReceiveModuleId = 10, + UhkModulePhase_ProcessModuleId = 11, // Get module key count - UhkModulePhase_RequestModuleKeyCount, - UhkModulePhase_ReceiveModuleKeyCount, - UhkModulePhase_ProcessModuleKeyCount, + UhkModulePhase_RequestModuleKeyCount = 12, + UhkModulePhase_ReceiveModuleKeyCount = 13, + UhkModulePhase_ProcessModuleKeyCount = 14, // Get module key count - UhkModulePhase_RequestModulePointerCount, - UhkModulePhase_ReceiveModulePointerCount, - UhkModulePhase_ProcessModulePointerCount, + UhkModulePhase_RequestModulePointerCount = 15, + UhkModulePhase_ReceiveModulePointerCount = 16, + UhkModulePhase_ProcessModulePointerCount = 17, // Get key states - UhkModulePhase_RequestKeyStates, - UhkModulePhase_ReceiveKeystates, - UhkModulePhase_ProcessKeystates, + UhkModulePhase_RequestKeyStates = 18, + UhkModulePhase_ReceiveKeystates = 19, + UhkModulePhase_ProcessKeystates = 20, // Get git tag - UhkModulePhase_RequestGitTag, - UhkModulePhase_ReceiveGitTag, - UhkModulePhase_ProcessGitTag, + UhkModulePhase_RequestGitTag = 21, + UhkModulePhase_ReceiveGitTag = 22, + UhkModulePhase_ProcessGitTag = 23, // Get git repo - UhkModulePhase_RequestGitRepo, - UhkModulePhase_ReceiveGitRepo, - UhkModulePhase_ProcessGitRepo, + UhkModulePhase_RequestGitRepo = 24, + UhkModulePhase_ReceiveGitRepo = 25, + UhkModulePhase_ProcessGitRepo = 26, // Get firmware checksum - UhkModulePhase_RequestFirmwareChecksum, - UhkModulePhase_ReceiveFirmwareChecksum, - UhkModulePhase_ProcessFirmwareChecksum, + UhkModulePhase_RequestFirmwareChecksum = 27, + UhkModulePhase_ReceiveFirmwareChecksum = 28, + UhkModulePhase_ProcessFirmwareChecksum = 29, // Misc phases - UhkModulePhase_SetTestLed, - UhkModulePhase_SetLedPwmBrightness, - UhkModulePhase_JumpToBootloader, - UhkModulePhase_ResetTrackpoint, + UhkModulePhase_SetTestLed = 30, + UhkModulePhase_SetLedPwmBrightness = 31, + UhkModulePhase_JumpToBootloader = 32, + UhkModulePhase_ResetTrackpoint = 33, } uhk_module_phase_t; diff --git a/right/src/str_utils.c b/right/src/str_utils.c index bf86f620a..c38e08ffa 100644 --- a/right/src/str_utils.c +++ b/right/src/str_utils.c @@ -15,13 +15,19 @@ static bool consumeCommentsAsWhite = true; static bool isIdentifierChar(char c); uint8_t SegmentLen(string_segment_t str) { - if (str.end == NULL) { + if (str.start == NULL) { + return 0; + } else if (str.end == NULL) { return strlen(str.start); } else { return str.end - str.start; } } +bool SegmentEqual(string_segment_t str1, string_segment_t str2) { + return StrEqual(str1.start, str1.end, str2.start, str2.end); +} + bool StrLessOrEqual(const char* a, const char* aEnd, const char* b, const char* bEnd) { while(true) { diff --git a/right/src/str_utils.h b/right/src/str_utils.h index 27647853e..237a3c368 100644 --- a/right/src/str_utils.h +++ b/right/src/str_utils.h @@ -16,8 +16,13 @@ #include "device.h" #endif +// Macros: + + #define EXPAND_SEGMENT(SEGMENT) SegmentLen(SEGMENT), SEGMENT.start + // Typedefs: + typedef struct macro_state_t macro_state_t; typedef struct { @@ -41,6 +46,7 @@ // Functions: uint8_t SegmentLen(string_segment_t str); + bool SegmentEqual(string_segment_t str1, string_segment_t str2); bool StrLessOrEqual(const char* a, const char* aEnd, const char* b, const char* bEnd); bool StrEqual(const char* a, const char* aEnd, const char* b, const char* bEnd); const char* FindChar(char c, const char* str, const char* strEnd); diff --git a/right/src/stubs.c b/right/src/stubs.c index 807b12d22..2e13701df 100644 --- a/right/src/stubs.c +++ b/right/src/stubs.c @@ -29,3 +29,8 @@ ATTRS void BtManager_RestartBt() {}; ATTRS void DongleLeds_Update(void) {}; ATTRS void BtPair_ClearUnknownBonds() {}; + ATTRS uint8_t BtAdvertise_Start(uint8_t adv_type) { return 0; }; + ATTRS uint8_t BtAdvertise_Type() { return 0; }; + ATTRS int BtScan_Start(void) { return 0; }; + ATTRS void BtManager_StartScanningAndAdvertising() {}; + ATTRS void BtConn_UpdateHostConnectionPeerAllocations() {}; diff --git a/right/src/stubs.h b/right/src/stubs.h index 349239c21..b0571e734 100644 --- a/right/src/stubs.h +++ b/right/src/stubs.h @@ -43,6 +43,11 @@ extern void BtManager_RestartBt(); extern void DongleLeds_Update(void); extern void BtPair_ClearUnknownBonds(); + extern uint8_t BtAdvertise_Start(uint8_t adv_type); + extern uint8_t BtAdvertise_Type(); + extern int BtScan_Start(void); + extern void BtManager_StartScanningAndAdvertising(); + extern void BtConn_UpdateHostConnectionPeerAllocations(); #if DEVICE_HAS_OLED #define WIDGET_REFRESH(W) Widget_Refresh(W) diff --git a/right/src/test_switches.c b/right/src/test_switches.c index f0ca30f3a..6cd6e89d8 100644 --- a/right/src/test_switches.c +++ b/right/src/test_switches.c @@ -5,6 +5,13 @@ #include "keymap.h" #include "segment_display.h" #include "slave_drivers/is31fl3xxx_driver.h" +#include "ledmap.h" +#include "led_manager.h" +#include "event_scheduler.h" + +#ifdef __ZEPHYR__ +#include "state_sync.h" +#endif bool TestSwitches = false; @@ -111,9 +118,31 @@ static const key_action_t TestKeymap[1][2][MAX_KEY_COUNT_PER_MODULE] = { void TestSwitches_Activate(void) { + TestSwitches = true; memcpy(&CurrentKeymap, &TestKeymap, sizeof TestKeymap); SegmentDisplay_SetText(3, "TES", SegmentDisplaySlot_Keymap); + #ifndef __ZEPHYR__ LedSlaveDriver_EnableAllLeds(); #endif + + Ledmap_ActivateTestLedMode(true); + +#if DEVICE_IS_UHK80_RIGHT + StateSync_UpdateProperty(StateSyncPropertyId_SwitchTestMode, NULL); +#endif +} + +void TestSwitches_Deactivate(void) +{ + TestSwitches = false; + Ledmap_ActivateTestLedMode(false); + +#if DEVICE_IS_UHK80_RIGHT + StateSync_UpdateProperty(StateSyncPropertyId_SwitchTestMode, NULL); +#endif + +#if DEVICE_IS_MASTER + EventVector_Set(EventVector_KeymapReloadNeeded); +#endif } diff --git a/right/src/test_switches.h b/right/src/test_switches.h index 20b974330..da29c07af 100644 --- a/right/src/test_switches.h +++ b/right/src/test_switches.h @@ -8,6 +8,7 @@ // Functions: void TestSwitches_Activate(void); + void TestSwitches_Deactivate(void); // Variables: diff --git a/right/src/usb_commands/usb_command_get_new_pairings.c b/right/src/usb_commands/usb_command_get_new_pairings.c index c2b7686df..9edb50f81 100644 --- a/right/src/usb_commands/usb_command_get_new_pairings.c +++ b/right/src/usb_commands/usb_command_get_new_pairings.c @@ -43,6 +43,7 @@ static void bt_foreach_bond_cb(const struct bt_bond_info *info, void *user_data) } void UsbCommand_UpdateNewPairingsFlag() { + Bt_NewPairedDevice = false; CommandUserData data = { .OutBuffer = NULL, diff --git a/right/src/usb_commands/usb_command_launch_storage_transfer.c b/right/src/usb_commands/usb_command_launch_storage_transfer.c index 259b1494e..2316313a1 100644 --- a/right/src/usb_commands/usb_command_launch_storage_transfer.c +++ b/right/src/usb_commands/usb_command_launch_storage_transfer.c @@ -1,4 +1,6 @@ #include "usb_commands/usb_command_launch_storage_transfer.h" +#include "event_scheduler.h" +#include "ledmap.h" #include "usb_protocol_handler.h" #include "config_parser/config_globals.h" @@ -39,6 +41,12 @@ void UsbCommand_LaunchStorageTransfer(const uint8_t *GenericHidOutBuffer, uint8_ status_t status = EEPROM_LaunchTransfer(storageOperation, configBufferId, NULL); #endif + if (configBufferId == ConfigBufferId_HardwareConfig) { + // reload is/ansi led settings + Ledmap_InitLedLayout(); + EventVector_Set(EventVector_LedMapUpdateNeeded); + } + if (status != kStatus_Success) { SetUsbTxBufferUint8(0, UsbStatusCode_LaunchStorageTransferTransferError); SetUsbTxBufferUint32(1, status); diff --git a/right/src/usb_commands/usb_command_pairing.c b/right/src/usb_commands/usb_command_pairing.c index b1a1bee22..e0dd38934 100644 --- a/right/src/usb_commands/usb_command_pairing.c +++ b/right/src/usb_commands/usb_command_pairing.c @@ -25,7 +25,7 @@ void UsbCommand_GetPairingData(const uint8_t *GenericHidOutBuffer, uint8_t *Gene void UsbCommand_SetPairingData(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer) { struct bt_le_oob oob; - uint8_t peerId = GenericHidOutBuffer[BUF_PEER_POS]; + uint8_t peerId = GenericHidOutBuffer[BUF_PEER_POS] + 1; //+1 because I refactored the enum so that unknown is zero. oob.addr = GetUsbRxBufferBleAddress(1 + BUF_ADR_POS); memcpy(oob.le_sc_data.r, GenericHidOutBuffer + 1 + BUF_KEY_R_POS, BLE_KEY_LEN); @@ -46,10 +46,8 @@ void UsbCommand_SetPairingData(const uint8_t *GenericHidOutBuffer, uint8_t *Gene case PeerIdRight: settings_save_one("uhk/addr/right", addr, BLE_ADDR_LEN); break; - case PeerIdDongle: - settings_save_one("uhk/addr/dongle", addr, BLE_ADDR_LEN); - break; default: + break; } } diff --git a/right/src/usb_commands/usb_command_set_variable.c b/right/src/usb_commands/usb_command_set_variable.c index 729413e85..171730508 100644 --- a/right/src/usb_commands/usb_command_set_variable.c +++ b/right/src/usb_commands/usb_command_set_variable.c @@ -8,6 +8,10 @@ #include "config_manager.h" #include "ledmap.h" +#if defined(__ZEPHYR__) && DEVICE_IS_KEYBOARD +#include "keyboard/leds.h" +#endif + void UsbCommand_SetVariable(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidInBuffer) { usb_variable_id_t variableId = GetUsbRxBufferUint8(1); @@ -15,14 +19,9 @@ void UsbCommand_SetVariable(const uint8_t *GenericHidOutBuffer, uint8_t *Generic switch (variableId) { case UsbVariable_TestSwitches: if (GetUsbRxBufferUint8(2)) { - TestSwitches = true; TestSwitches_Activate(); - Ledmap_ActivateTestLedMode(true); } else { - TestSwitches = false; - Ledmap_ActivateTestLedMode(false); - SwitchKeymapById(CurrentKeymapIndex); - LedManager_FullUpdate(); + TestSwitches_Deactivate(); } break; case UsbVariable_TestUsbStack: @@ -39,5 +38,15 @@ void UsbCommand_SetVariable(const uint8_t *GenericHidOutBuffer, uint8_t *Generic break; case UsbVariable_StatusBuffer: break; + case UsbVariable_LedAudioRegisters: +#if defined(__ZEPHYR__) && DEVICE_IS_KEYBOARD + uint8_t phaseDelay = GetUsbRxBufferUint8(2); + uint8_t spreadSpectrum = GetUsbRxBufferUint8(3); + uint8_t pwmFrequency = GetUsbRxBufferUint8(4); + UpdateLedAudioRegisters(phaseDelay, spreadSpectrum, pwmFrequency); +#endif + break; + default: + break; } } diff --git a/right/src/usb_protocol_handler.c b/right/src/usb_protocol_handler.c index 0c7619430..13603174b 100644 --- a/right/src/usb_protocol_handler.c +++ b/right/src/usb_protocol_handler.c @@ -141,10 +141,10 @@ void UsbProtocolHandler(const uint8_t *GenericHidOutBuffer, uint8_t *GenericHidI #ifdef __ZEPHYR__ bt_addr_le_t GetBufferBleAddress(const uint8_t *GenericHidOutBuffer, uint32_t offset) { bt_addr_le_t addr; - addr.type = 1; for (uint8_t i = 0; i < BLE_ADDR_LEN; i++) { addr.a.val[i] = GenericHidOutBuffer[offset + i]; } + addr.type = addr.a.val[0] & 0x01; return addr; } diff --git a/right/src/usb_protocol_handler.h b/right/src/usb_protocol_handler.h index 96a30e36e..e8fc91d4c 100644 --- a/right/src/usb_protocol_handler.h +++ b/right/src/usb_protocol_handler.h @@ -75,6 +75,7 @@ UsbVariable_DebounceTimeRelease, UsbVariable_UsbReportSemaphore, UsbVariable_StatusBuffer, + UsbVariable_LedAudioRegisters, } usb_variable_id_t; typedef enum { diff --git a/right/src/usb_report_updater.c b/right/src/usb_report_updater.c index e9560491d..5eba9bc8b 100644 --- a/right/src/usb_report_updater.c +++ b/right/src/usb_report_updater.c @@ -452,7 +452,7 @@ static void commitKeyState(key_state_t *keyState, bool active) } #endif - if (Ledmap_LedTestActive && active) { + if (TestSwitches && active) { key_coordinates_t key = Utils_KeyIdToKeyCoordinates(Utils_KeyStateToKeyId(keyState)); Ledmap_ActivateTestled(key.slotId, key.inSlotId); return; @@ -522,7 +522,7 @@ static void handleUsbStackTestMode() { static void handleLayerChanges() { static layer_id_t previousLayer = LayerId_Base; - LayerSwitcher_UpdateActiveLayer(); + LayerSwitcher_UpdateHeldLayer(); if(ActiveLayer != previousLayer) { previousLayer = ActiveLayer; diff --git a/shared/crc16.c b/shared/crc16.c index d7a088d77..1aaa0cb04 100644 --- a/shared/crc16.c +++ b/shared/crc16.c @@ -51,5 +51,5 @@ bool CRC16_IsMessageValid(i2c_message_t *message) crc16_init(&crc16data); crc16_update(&crc16data, message->data, message->length); crc16_finalize(&crc16data, &hash); - return message->crc == hash; + return message->crc == hash && message->length != 0; } diff --git a/shared/slave_protocol.h b/shared/slave_protocol.h index 882f506ad..85fc136f5 100644 --- a/shared/slave_protocol.h +++ b/shared/slave_protocol.h @@ -34,15 +34,15 @@ } module_specific_command_t; typedef enum { - SlaveProperty_Sync, - SlaveProperty_ModuleProtocolVersion, - SlaveProperty_FirmwareVersion, - SlaveProperty_ModuleId, - SlaveProperty_KeyCount, - SlaveProperty_PointerCount, - SlaveProperty_GitTag, - SlaveProperty_GitRepo, - SlaveProperty_FirmwareChecksum, + SlaveProperty_Sync = 0, + SlaveProperty_ModuleProtocolVersion = 1, + SlaveProperty_FirmwareVersion = 2, + SlaveProperty_ModuleId = 3, + SlaveProperty_KeyCount = 4, + SlaveProperty_PointerCount = 5, + SlaveProperty_GitTag = 6, + SlaveProperty_GitRepo = 7, + SlaveProperty_FirmwareChecksum = 8, } slave_property_t; typedef enum {