From a6d36d57a7a09b54ba969207dbee083feb728c7f Mon Sep 17 00:00:00 2001 From: Dimitris Zervas Date: Tue, 22 Sep 2020 02:29:58 +0300 Subject: [PATCH] Add multimple connection support --- kmk/ble.py | 73 +++++++++++++++++++++++++++-------- kmk/handlers/stock.py | 10 +++++ kmk/keys.py | 2 + user_keymaps/dzervas/lab68.py | 10 ++--- 4 files changed, 74 insertions(+), 21 deletions(-) diff --git a/kmk/ble.py b/kmk/ble.py index 720835b..8f6ade0 100644 --- a/kmk/ble.py +++ b/kmk/ble.py @@ -4,52 +4,93 @@ from adafruit_ble.services.standard.hid import HIDService from kmk.hid import HID_REPORT_SIZES, AbstractHID BLE_APPEARANCE_HID_KEYBOARD = 961 +# Hardcoded in CPy +MAX_CONNECTIONS = 2 class BLEHID(AbstractHID): def post_init(self, ble_name='KMK Keyboard', **kwargs): - self.conn = [] + self.conn_id = -1 self.ble = BLERadio() self.ble.name = ble_name self.hid = HIDService() + self.hid.protocol_mode = 0 # Boot protocol # Security-wise this is not right. While you're away someone turns # on your keyboard and they can pair with it nice and clean and then # listen to keystrokes. # On the other hand we don't have LESC so it's like shouting your # keystrokes in the air - if not self.ble.connected: + if not self.ble.connected or not self.hid.devices: self.start_advertising() - while not self.ble.connected or not self.hid.devices: - pass - # int, can be looked up in HIDReportTypes - reporting_device_const = self.report_device[0] + self.conn_id = 0 - self.conn = self.hid.devices[reporting_device_const] + @property + def devices(self): + """Search through the provided list of devices to find the ones with the + send_report attribute.""" + if not self.ble.connected: + return [] - self.ble.stop_advertising() + result = [] + # Security issue: + # This introduces a race condition. Let's say you have 2 active + # connections: Alice and Bob - Alice is connection 1 and Bob 2. + # Now Chuck who has already paired with the device in the past + # (this assumption is needed only in the case of LESC) + # wants to gather the keystrokes you send to Alice. You have + # selected right now to talk to Alice (1) and you're typing a secret. + # If Chuck kicks Alice off and is quick enough to connect to you, + # which means quicker than the running interval of this function, + # he'll be earlier in the `self.hid.devices` so will take over the + # selected 1 position in the resulted array. + # If no LESC is in place, Chuck can sniff the keystrokes anyway + for device in self.hid.devices: + if hasattr(device, "send_report"): + result.append(device) + + return result + + def _check_connection(self): + devices = self.devices + if not devices: + return False + + if self.conn_id >= len(devices): + self.conn_id = len(devices) - 1 + + if self.conn_id < 0: + return False + + if not devices[self.conn_id]: + return False + + return True def hid_send(self, evt): - # int, can be looked up in HIDReportTypes - reporting_device_const = self.report_device[0] + if not self._check_connection(): + return - report_size = HID_REPORT_SIZES[reporting_device_const] + device = self.devices[self.conn_id] - while len(evt) < report_size + 1: + while len(evt) < len(device._characteristic.value) + 1: evt.append(0) - print(self.conn) - return self.conn.send_report( - evt[1 : report_size + 1] - ) + return device.send_report(evt[1:]) def clear_bonds(self): import _bleio _bleio.adapter.erase_bonding() + def next_connection(self): + self.conn_id = (self.conn_id + 1) % len(self.devices) + + def previous_connection(self): + self.conn_id = (self.conn_id - 1) % len(self.devices) + def start_advertising(self): advertisement = ProvideServicesAdvertisement(self.hid) advertisement.appearance = BLE_APPEARANCE_HID_KEYBOARD diff --git a/kmk/handlers/stock.py b/kmk/handlers/stock.py index 30c9383..275ecdb 100644 --- a/kmk/handlers/stock.py +++ b/kmk/handlers/stock.py @@ -259,3 +259,13 @@ def led_mode_breathe(key, state, *args, **kwargs): def bt_clear_bonds(key, state, *args, **kwargs): state.config._hid_helper_inst.clear_bonds() return state + + +def bt_next_conn(key, state, *args, **kwargs): + state.config._hid_helper_inst.next_connection() + return state + + +def bt_prev_conn(key, state, *args, **kwargs): + state.config._hid_helper_inst.previous_connection() + return state diff --git a/kmk/keys.py b/kmk/keys.py index 3ba4a97..e71bf0d 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -642,6 +642,8 @@ make_key(names=('LED_AND',), on_press=handlers.led_and) make_key(names=('LED_MODE_PLAIN', 'LED_M_P'), on_press=handlers.led_mode_static) make_key(names=('LED_MODE_BREATHE', 'LED_M_B'), on_press=handlers.led_mode_breathe) make_key(names=('BT_CLEAR_BONDS', 'BT_CLR'), on_press=handlers.bt_clear_bonds) +make_key(names=('BT_NEXT_CONN', 'BT_NXT'), on_press=handlers.bt_next_conn) +make_key(names=('BT_PREV_CONN', 'BT_PRV'), on_press=handlers.bt_prev_conn) make_key( diff --git a/user_keymaps/dzervas/lab68.py b/user_keymaps/dzervas/lab68.py index eea37a3..8fca250 100644 --- a/user_keymaps/dzervas/lab68.py +++ b/user_keymaps/dzervas/lab68.py @@ -61,11 +61,11 @@ keyboard.keymap = [ # `------------------------------------------------------------------------------------------+------+------' # CLR: Clear bonds [ - XXXXXXX, KC.F1, KC.F2, KC.F3, KC.F4, KC.F5, KC.F6, KC.F7, KC.F8, KC.F9, KC.F10, KC.F11, KC.F12, XXXXXXX, KC.BT_CLR, - KC.TAB, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, KC.PSCR, XXXXXXX, KC.PAUSE, _______, XXXXXXX, _______, - KC.ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______, - KC.LSFT, XXXXXXX, KC.MPLY, KC.MSTP, KC.MPRV, KC.MNXT, KC.VOLD, KC.VOLU, KC.MUTE, XXXXXXX, XXXXXXX, KC.RSFT, XXXXXXX, _______, XXXXXXX, - KC.LCTL, KC.LGUI, KC.LALT, XXXXXXX, XXXXXXX, KC.SPC, XXXXXXX, XXXXXXX, FN, KC.RALT, KC.RCTL, _______, XXXXXXX, _______, _______, + XXXXXXX, KC.F1, KC.F2, KC.F3, KC.F4, KC.F5, KC.F6, KC.F7, KC.F8, KC.F9, KC.F10, KC.F11, KC.F12, XXXXXXX, KC.BT_CLR, + KC.TAB, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, KC.PSCR, XXXXXXX, KC.PAUSE, _______, XXXXXXX, _______, + KC.ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______, + KC.LSFT, XXXXXXX, KC.MPLY, KC.MSTP, KC.MPRV, KC.MNXT, KC.VOLD, KC.VOLU, KC.MUTE, XXXXXXX, XXXXXXX, KC.RSFT, XXXXXXX, _______, XXXXXXX, + KC.LCTL, KC.LGUI, KC.LALT, XXXXXXX, XXXXXXX, KC.SPC, XXXXXXX, XXXXXXX, FN, KC.RALT, KC.RCTL, KC.BT_PRV, XXXXXXX, _______, KC.BT_NXT, ], ]