|
2 | 2 | #include <bluetooth/hid_over_gatt.hpp> |
3 | 3 | #include <zephyr/bluetooth/conn.h> |
4 | 4 | #include <zephyr/kernel.h> |
| 5 | +#include <new> |
| 6 | +extern "C" { |
| 7 | +#include "bt_conn.h" |
| 8 | +} |
5 | 9 |
|
6 | 10 | using namespace magic_enum::bitwise_operators; |
7 | 11 |
|
| 12 | +static_assert(sizeof(ble_session) <= BLE_HID_SESSION_STORAGE_SIZE, "BLE_HID_SESSION_STORAGE_SIZE too small for ble_session"); |
| 13 | +static_assert(alignof(ble_session) <= 8, "ble_session alignment exceeds peer storage alignment"); |
| 14 | + |
| 15 | +static ble_session *peerSession(peer_t *peer) |
| 16 | +{ |
| 17 | + return std::launder(reinterpret_cast<ble_session *>(peer->hidSessionStorage)); |
| 18 | +} |
| 19 | + |
8 | 20 | static auto &hog_service() |
9 | 21 | { |
10 | 22 | using namespace magic_enum::bitwise_operators; |
@@ -34,39 +46,88 @@ ::bt_conn *ble_session::get_conn() |
34 | 46 |
|
35 | 47 | hid::session &ble_app::start(const hid::session_params ¶ms) |
36 | 48 | { |
37 | | - ::bt_conn *conn = static_cast<bluetooth::hid_over_gatt::session_params>(params).conn; |
38 | | - ble_session *sess; |
39 | | - // TODO |
40 | | - // create session from memory pool (allocate + construct) |
41 | | - // Connections_SetStateAsync |
| 49 | + ::bt_conn *conn = static_cast<const bluetooth::hid_over_gatt::session_params &>(params).conn; |
| 50 | + int8_t peerId = GetPeerIdByConn(conn); |
| 51 | + if (peerId < PeerIdFirstHost || peerId > PeerIdLastHost) { |
| 52 | + // A HOGP session should only ever start for a connected host peer |
| 53 | + printk("ble_app::start: no host peer for conn (peerId %d)\n", peerId); |
| 54 | + static ble_session fallback; |
| 55 | + return fallback; |
| 56 | + } |
| 57 | + peer_t *peer = &Peers[peerId]; |
| 58 | + |
| 59 | + // destroy any stale session left in this slot |
| 60 | + if (peer->hidSessionActive) { |
| 61 | + peerSession(peer)->~ble_session(); |
| 62 | + peer->hidSessionActive = false; |
| 63 | + } |
42 | 64 |
|
43 | | - // mouse_resolution_changed_callback(*sess, sess->resolution_report()); |
| 65 | + ble_session *sess = new (peer->hidSessionStorage) ble_session(); |
| 66 | + peer->hidSessionActive = true; |
| 67 | + |
| 68 | + if (peer->connectionId != ConnectionId_Invalid) { |
| 69 | + printk("ble_app::start: marking BtHid connection %d (peer %d) ready\n", |
| 70 | + peer->connectionId, peerId); |
| 71 | + Connections_SetStateAsync((connection_id_t)peer->connectionId, ConnectionState_Ready); |
| 72 | + } else { |
| 73 | + printk("ble_app::start: peer %d has invalid connectionId, cannot mark ready\n", peerId); |
| 74 | + } |
44 | 75 | return *sess; |
45 | 76 | } |
46 | 77 |
|
47 | 78 | void ble_app::stop(hid::session &sess) |
48 | 79 | { |
49 | | - // TODO |
50 | | - // return session to memory pool (destruct + dealloc) |
51 | | - // Connections_SetStateAsync |
| 80 | + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { |
| 81 | + peer_t *peer = &Peers[peerId]; |
| 82 | + if (!peer->hidSessionActive) { |
| 83 | + continue; |
| 84 | + } |
| 85 | + ble_session *ps = peerSession(peer); |
| 86 | + if (static_cast<hid::session *>(ps) == &sess) { |
| 87 | + // On a real disconnect bt_conn.c may already have cleared connectionId. |
| 88 | + if (peer->connectionId != ConnectionId_Invalid) { |
| 89 | + printk("ble_app::stop: marking BtHid connection %d (peer %d) disconnected\n", |
| 90 | + peer->connectionId, peerId); |
| 91 | + Connections_SetStateAsync( |
| 92 | + (connection_id_t)peer->connectionId, ConnectionState_Disconnected); |
| 93 | + } else { |
| 94 | + printk("ble_app::stop: peer %d has invalid connectionId\n", peerId); |
| 95 | + } |
| 96 | + ps->~ble_session(); |
| 97 | + peer->hidSessionActive = false; |
| 98 | + return; |
| 99 | + } |
| 100 | + } |
52 | 101 | } |
53 | 102 |
|
54 | | -extern "C" int HOGP_HealthCheck() |
| 103 | +extern "C" void HOGP_Register() |
55 | 104 | { |
56 | | - // TODO: refactoring needed |
57 | | - struct bt_conn *peer = nullptr; |
58 | | - if (!peer) { |
59 | | - printk("HOGP HealthCheck: service registered, no active peer\n"); |
60 | | - return -2; |
61 | | - } |
| 105 | + // Forces the function-local static HOGP service (and its bt_gatt_service_static |
| 106 | + // entry) to be constructed so bt_enable() can register it. |
| 107 | + (void)hog_service(); |
| 108 | +} |
62 | 109 |
|
63 | | - struct bt_conn_info info; |
64 | | - int err = bt_conn_get_info(peer, &info); |
65 | | - if (err) { |
66 | | - printk("HOGP HealthCheck: active peer has INVALID conn pointer (err %d)\n", err); |
67 | | - return -3; |
| 110 | +extern "C" int HOGP_HealthCheck() |
| 111 | +{ |
| 112 | + int activeSessions = 0; |
| 113 | + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { |
| 114 | + peer_t *peer = &Peers[peerId]; |
| 115 | + if (!peer->hidSessionActive) { |
| 116 | + continue; |
| 117 | + } |
| 118 | + activeSessions++; |
| 119 | + if (peer->conn == nullptr) { |
| 120 | + printk("HOGP HealthCheck: peer %s has an active session but no conn\n", peer->name); |
| 121 | + return -2; |
| 122 | + } |
| 123 | + struct bt_conn_info info; |
| 124 | + int err = bt_conn_get_info(peer->conn, &info); |
| 125 | + if (err) { |
| 126 | + printk("HOGP HealthCheck: peer %s has INVALID conn pointer (err %d)\n", peer->name, err); |
| 127 | + return -3; |
| 128 | + } |
68 | 129 | } |
69 | 130 |
|
70 | | - printk("HOGP HealthCheck: OK (registered, peer connected, interval %u)\n", info.le.interval_us); |
| 131 | + printk("HOGP HealthCheck: OK (%d active HID session(s))\n", activeSessions); |
71 | 132 | return 0; |
72 | 133 | } |
0 commit comments