diff --git a/CMakeLists.txt b/CMakeLists.txt index 54a7e43d..033c0b06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,10 +16,9 @@ if(NOT CONFIG_DEVICE_ID) endif() project(uhk) -add_library(${PROJECT_NAME}) +add_subdirectory(shared) +add_subdirectory(device/src) +add_subdirectory(right/src) target_link_libraries(${PROJECT_NAME} PUBLIC zephyr_interface ) -add_subdirectory(device/src) -add_subdirectory(right/src) -add_subdirectory(shared) diff --git a/device/CMakePresets.json b/device/CMakePresets.json index ae40300c..e28b6c47 100644 --- a/device/CMakePresets.json +++ b/device/CMakePresets.json @@ -44,7 +44,7 @@ "BOARD": "uhk-80-right", "BOARD_ROOT": "${sourceParentDir}/", "mcuboot_OVERLAY_CONFIG": "${sourceDir}/child_image/mcuboot.conf;${sourceDir}/child_image/uhk-80-right.mcuboot.conf", - "EXTRA_CONF_FILE": "${sourceDir}/prj.conf.overlays/nrf_shared.conf;${sourceDir}/prj.conf.overlays/c2usb.conf;${sourceDir}/prj.conf.overlays/uhk-80.conf;${sourceDir}/prj.conf.overlays/ble_nus.conf;${sourceDir}/prj.conf.overlays/ble_nus_client.conf;${sourceDir}/prj.conf.overlays/ble_hid.conf" + "EXTRA_CONF_FILE": "${sourceDir}/prj.conf.overlays/nrf_shared.conf;${sourceDir}/prj.conf.overlays/c2usb.conf;${sourceDir}/prj.conf.overlays/uhk-80.conf;${sourceDir}/prj.conf.overlays/ble_nus.conf;${sourceDir}/prj.conf.overlays/ble_nus_client.conf;${sourceDir}/prj.conf.overlays/ble_hid.conf;${sourceDir}/prj.conf.overlays/right.conf" } }, { @@ -74,4 +74,4 @@ } } ] -} \ No newline at end of file +} diff --git a/device/prj.conf.overlays/nrf_shared.conf b/device/prj.conf.overlays/nrf_shared.conf index ba52c6f1..b0fc034a 100644 --- a/device/prj.conf.overlays/nrf_shared.conf +++ b/device/prj.conf.overlays/nrf_shared.conf @@ -17,10 +17,8 @@ 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_MAX_CONN=2 +CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=1 CONFIG_BT_FILTER_ACCEPT_LIST=y diff --git a/device/prj.conf.overlays/right.conf b/device/prj.conf.overlays/right.conf new file mode 100644 index 00000000..215f3fa5 --- /dev/null +++ b/device/prj.conf.overlays/right.conf @@ -0,0 +1,3 @@ +# config for right half host switching +CONFIG_BT_MAX_CONN=4 +CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=3 diff --git a/device/src/bt_advertise.c b/device/src/bt_advertise.c index 853d654f..eeb3414e 100644 --- a/device/src/bt_advertise.c +++ b/device/src/bt_advertise.c @@ -1,21 +1,18 @@ #include "bt_advertise.h" #include #include +#include "attributes.h" #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 -#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) +#define LEN(NAME) (sizeof(NAME) - 1) // Advertisement packets -#define AD_NUS_DATA \ +#define AD_NUS_DATA(NAME) \ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), \ - BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), + BT_DATA(BT_DATA_NAME_COMPLETE, NAME, LEN(NAME)), #define AD_HID_DATA \ BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, (CONFIG_BT_DEVICE_APPEARANCE >> 0) & 0xff, \ @@ -24,75 +21,95 @@ BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_HIDS_VAL), \ BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)), -static const struct bt_data adNus[] = {AD_NUS_DATA}; - +ATTR_UNUSED static const struct bt_data adNusLeft[] = {AD_NUS_DATA("UHK80 Left Nus")}; +ATTR_UNUSED static const struct bt_data adNusRight[] = {AD_NUS_DATA("UHK 80 Right")}; static const struct bt_data adHid[] = {AD_HID_DATA}; -static const struct bt_data adNusHid[] = {AD_NUS_DATA AD_HID_DATA}; - // Scan response packets #define SD_NUS_DATA BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL), -#define SD_HID_DATA BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), +#define SD_HID_DATA(NAME) BT_DATA(BT_DATA_NAME_COMPLETE, NAME, LEN(NAME)), static const struct bt_data sdNus[] = {SD_NUS_DATA}; - -static const struct bt_data sdHid[] = {SD_HID_DATA}; - -static const struct bt_data sdNusHid[] = {SD_NUS_DATA SD_HID_DATA}; - -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; - } +static const struct bt_data sdHid[] = {SD_HID_DATA("UHK 80")}; + +#if DEVICE_IS_UHK80_RIGHT +#define BY_SIDE(LEFT, RIGHT) RIGHT +#else +#define BY_SIDE(LEFT, RIGHT) LEFT +#endif + +#define BT_LE_ADV_START(PARAM, AD, SD) bt_le_adv_start(PARAM, AD, ARRAY_SIZE(AD), SD, ARRAY_SIZE(SD)); + +static const char * advertisingString(uint8_t advType) { + switch (advType) { + case ADVERTISE_NUS: + return "NUS"; + case ADVERTISE_HID: + return "HID"; + case ADVERTISE_NUS | ADVERTISE_HID: + return "HID \"and NUS\""; + case ADVERTISE_DIRECTED_NUS: + return "Directed NUS"; + default: + return "None"; } } -uint8_t BtAdvertise_Start(uint8_t adv_type) +uint8_t BtAdvertise_Start(adv_config_t advConfig) { - setFilters(); - - int err; - const char *adv_type_string; - if (adv_type == (ADVERTISE_NUS | ADVERTISE_HID)) { - adv_type_string = "NUS and HID"; - err = bt_le_adv_start( - BT_LE_ADV_CONN, adNusHid, ARRAY_SIZE(adNusHid), sdNusHid, ARRAY_SIZE(sdNusHid)); - } else if (adv_type == ADVERTISE_NUS) { - adv_type_string = "NUS"; - err = bt_le_adv_start(BT_LE_ADV_CONN, adNus, ARRAY_SIZE(adNus), sdNus, ARRAY_SIZE(sdNus)); - } else if (adv_type == ADVERTISE_HID) { - adv_type_string = "HID"; - 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 0; + int err = 0; + + const char * advTypeString = advertisingString(advConfig.advType); + + // Start advertising + static struct bt_le_adv_param advParam; + switch (advConfig.advType) { + case ADVERTISE_HID: + case ADVERTISE_NUS | ADVERTISE_HID: + /* our devices don't check service uuids, so hid advertisement effectively advertises nus too */ + advParam = *BT_LE_ADV_CONN_ONE_TIME; + err = BT_LE_ADV_START(&advParam, adHid, sdHid); + break; + case ADVERTISE_NUS: + advParam = *BT_LE_ADV_CONN_ONE_TIME; + + err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); + break; + case ADVERTISE_DIRECTED_NUS: + advParam = *BT_LE_ADV_CONN_ONE_TIME; + err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); + break; + + //// TODO: fix and reenable this? + // printk("Advertising against %s\n", GetAddrString(advConfig.addr)); + // advParam = *BT_LE_ADV_CONN_DIR_LOW_DUTY(advConfig.addr); + // advParam.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA; + // err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); + break; + default: + printk("Adv: Attempted to start advertising without any type! Ignoring.\n"); + return 0; } + // Log it if (err == 0) { - printk("%s advertising successfully started\n", adv_type_string); + printk("Adv: %s advertising successfully started\n", advTypeString); return 0; } else if (err == -EALREADY) { - printk("%s advertising continued\n", adv_type_string); + printk("Adv: %s advertising continued\n", advTypeString); return 0; } else { - printk("%s advertising failed to start (err %d), free connections: %d\n", adv_type_string, err, BtConn_UnusedPeripheralConnectionCount()); + printk("Adv: %s advertising failed to start (err %d), free connections: %d\n", advTypeString, err, BtConn_UnusedPeripheralConnectionCount()); return err; } } -void BtAdvertise_Stop() { +void BtAdvertise_Stop(void) { int err = bt_le_adv_stop(); if (err) { - printk("Advertising failed to stop (err %d)\n", err); + printk("Adv: Advertising failed to stop (err %d)\n", err); } } @@ -106,26 +123,51 @@ static uint8_t connectedHidCount() { return connectedHids; } -uint8_t BtAdvertise_Type() { +#define ADVERTISEMENT(TYPE) ((adv_config_t) { .advType = TYPE }) +#define ADVERTISEMENT_DIRECT_NUS(ADDR) ((adv_config_t) { .advType = ADVERTISE_DIRECTED_NUS, .addr = ADDR }) + +adv_config_t BtAdvertise_Config() { switch (DEVICE_ID) { case DeviceId_Uhk80_Left: - return ADVERTISE_NUS; + if (Peers[PeerIdRight].conn == NULL) { + return ADVERTISEMENT_DIRECT_NUS(&Peers[PeerIdRight].addr); + } else { + return ADVERTISEMENT( 0 ); + } + case DeviceId_Uhk80_Right: if (BtConn_UnusedPeripheralConnectionCount() > 0) { - if (connectedHidCount() > 0) { - return ADVERTISE_NUS; + if (BtConn_UnusedPeripheralConnectionCount() <= 1 && SelectedHostConnectionId != ConnectionId_Invalid) { + /* we need to reserve last peripheral slot for a specific target */ + connection_type_t selectedConnectionType = Connections_Type(SelectedHostConnectionId); + if (selectedConnectionType == ConnectionType_NusDongle) { + return ADVERTISEMENT_DIRECT_NUS(&HostConnection(SelectedHostConnectionId)->bleAddress); + } else if (selectedConnectionType == ConnectionType_BtHid) { + return ADVERTISEMENT(ADVERTISE_HID); + } else { + printk("Adv: Selected connection is neither BLE HID nor NUS. Can't advertise!"); + return ADVERTISEMENT( 0 ); + } + } + else if (connectedHidCount() > 0) { + /** we can't handle multiple HID connections, so don't advertise it when one HID is already connected */ + return ADVERTISEMENT(ADVERTISE_NUS); } else { - return ADVERTISE_NUS | ADVERTISE_HID; + /** we can connect both NUS and HID */ + return ADVERTISEMENT(ADVERTISE_NUS | ADVERTISE_HID); } } else { - printk("Current slot count %d, not advertising\n", BtConn_UnusedPeripheralConnectionCount()); + /** advertising needs a peripheral slot. When it is not free and we try to advertise, it will fail, and our code will try to + * disconnect other devices in order to restore proper function. */ + printk("Adv: Current slot count is zero, not advertising!\n"); BtConn_ListCurrentConnections(); - return 0; + return ADVERTISEMENT( 0 ); } case DeviceId_Uhk_Dongle: - return 0; + return ADVERTISEMENT( 0 ); default: printk("unknown device!\n"); - return 0; + return ADVERTISEMENT( 0 ); } } + diff --git a/device/src/bt_advertise.h b/device/src/bt_advertise.h index 7f67e9e5..c058094d 100644 --- a/device/src/bt_advertise.h +++ b/device/src/bt_advertise.h @@ -4,16 +4,27 @@ // Includes: #include + #include + #include "shared/attributes.h" // Macros: #define ADVERTISE_NUS (1 << 0) #define ADVERTISE_HID (1 << 1) + #define ADVERTISE_DIRECTED_NUS (1 << 2) + +// Typedefs: + + typedef struct { + uint8_t advType; + bt_addr_le_t* addr; + } ATTR_PACKED adv_config_t; + // Functions: - uint8_t BtAdvertise_Start(uint8_t adv_type); - void BtAdvertise_Stop(); - uint8_t BtAdvertise_Type(); + uint8_t BtAdvertise_Start(adv_config_t advConfig); + void BtAdvertise_Stop(void); + adv_config_t BtAdvertise_Config(); #endif // __BT_ADVERTISE_H__ diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 9264546a..40892991 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -358,17 +358,19 @@ static void connected(struct bt_conn *conn, uint8_t err) { if (connectionId == ConnectionId_Invalid) { connectUnknown(conn); + BtManager_StartScanningAndAdvertisingAsync(); } else { if (isWanted(conn, connectionType)) { bt_conn_set_security(conn, BT_SECURITY_L4); + // advertising/scanning needs to be started only after peers are assigned :-/ } else { printk("Refusing connenction %d (this is not a selected connection)\n", connectionId); bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + BtManager_StartScanningAndAdvertisingAsync(); } } - BtManager_StartScanningAndAdvertisingAsync(); return; @@ -455,6 +457,8 @@ static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); break; } + + BtManager_StartScanningAndAdvertisingAsync(); } static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 629de63c..8c5d4f27 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -52,7 +52,7 @@ void BtManager_StartBt() { } if (DEVICE_IS_UHK80_LEFT || DEVICE_IS_UHK80_RIGHT) { - BtAdvertise_Start(BtAdvertise_Type()); + BtAdvertise_Start(BtAdvertise_Config()); } if (DEVICE_IS_UHK80_RIGHT || DEVICE_IS_UHK_DONGLE) { @@ -124,15 +124,20 @@ void BtManager_StartScanningAndAdvertising() { printk("Starting %s, try %d!\n", label, try); } +#ifdef CONFIG_BT_PERIPHERAL if (leftShouldAdvertise || rightShouldAdvertise) { - err = BtAdvertise_Start(BtAdvertise_Type()); + err = BtAdvertise_Start(BtAdvertise_Config()); success &= err == 0; } +#endif +#ifdef CONFIG_BT_CENTRAL if (rightShouldScan || dongleShouldScan) { err = BtScan_Start(); success &= err == 0; } +#endif + if (!success && try > 0) { BtConn_DisconnectAll(); diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index 7e4298b1..ced0da35 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -66,7 +66,7 @@ void BtPair_PairPeripheral() { pairingAsCentral = false; Settings_Reload(); bt_le_oob_set_sc_flag(true); - BtAdvertise_Start(ADVERTISE_NUS); + BtAdvertise_Start((adv_config_t) { .advType = ADVERTISE_NUS }); printk ("Waiting for central to pair to me.\n"); EventScheduler_Reschedule(k_uptime_get_32() + PAIRING_TIMEOUT, EventSchedulerEvent_EndBtPairing, "Oob pairing timeout."); } diff --git a/device/src/shell.c b/device/src/shell.c index 82ba77e8..59d59504 100644 --- a/device/src/shell.c +++ b/device/src/shell.c @@ -192,7 +192,7 @@ void InitShell(void) SHELL_CMD_ARG(gamepad, NULL, "switch gamepad on/off", cmd_uhk_gamepad, 1, 1), SHELL_CMD_ARG(passkey, NULL, "send passkey for bluetooth pairing", cmd_uhk_passkey, 2, 0), 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_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/right/src/host_connection.h b/right/src/host_connection.h index f8a358a8..fb7766da 100644 --- a/right/src/host_connection.h +++ b/right/src/host_connection.h @@ -36,12 +36,12 @@ // Typedefs: typedef enum { - HostConnectionType_Empty, - HostConnectionType_UsbHidRight, - HostConnectionType_UsbHidLeft, - HostConnectionType_BtHid, - HostConnectionType_Dongle, - HostConnectionType_NewBtHid, + HostConnectionType_Empty = 0, + HostConnectionType_UsbHidRight = 1, + HostConnectionType_UsbHidLeft = 2, + HostConnectionType_BtHid = 3, + HostConnectionType_Dongle = 4, + HostConnectionType_NewBtHid = 5, HostConnectionType_Count, } host_connection_type_t; diff --git a/right/src/stubs.c b/right/src/stubs.c index 319e0aca..aedb216a 100644 --- a/right/src/stubs.c +++ b/right/src/stubs.c @@ -23,9 +23,6 @@ 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() {}; ATTRS void Oled_RequestRedraw() {}; diff --git a/right/src/stubs.h b/right/src/stubs.h index 50f82e2a..08fbbd19 100644 --- a/right/src/stubs.h +++ b/right/src/stubs.h @@ -43,9 +43,6 @@ 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(); extern void Oled_RequestRedraw(); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 99acfa65..1edf5f80 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -1,3 +1,6 @@ +# the library target is created here, so version generation will work without extra steps +add_library(${PROJECT_NAME}) + target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} # this really needs to be changed when time and restructuring permits