Adds LED state report characteristic into bt GATT hid service (for now writing by host isn't working)

This commit is contained in:
yocvito
2023-02-10 20:04:45 +01:00
parent 9a02a87e82
commit f5219db3fc
6 changed files with 185 additions and 10 deletions

View File

@@ -243,7 +243,7 @@ static bool ducky_is_line_end(const char chr) {
static void ducky_numlock_on(BadKbScript* bad_kb) { static void ducky_numlock_on(BadKbScript* bad_kb) {
if(bad_kb->bt) { if(bad_kb->bt) {
if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME
bt_hid_hold_while_keyboard_buffer_full(1, -1); bt_hid_hold_while_keyboard_buffer_full(1, -1);
furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
furi_delay_ms(bt_timeout); furi_delay_ms(bt_timeout);

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,13.5,, Version,v,13.9,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
@@ -1015,6 +1015,7 @@ Function,+,furi_hal_bt_get_transmitted_packets,uint32_t,
Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t
Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t
Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool,
Function,+,furi_hal_bt_hid_get_led_state,uint8_t,
Function,+,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t Function,+,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t
Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t
Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t
1 entry status name type params
2 Version + v 13.5 13.9
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1015 Function + furi_hal_bt_hid_consumer_key_press _Bool uint16_t
1016 Function + furi_hal_bt_hid_consumer_key_release _Bool uint16_t
1017 Function + furi_hal_bt_hid_consumer_key_release_all _Bool
1018 Function + furi_hal_bt_hid_get_led_state uint8_t
1019 Function + furi_hal_bt_hid_kb_free_slots _Bool uint8_t
1020 Function + furi_hal_bt_hid_kb_press _Bool uint16_t
1021 Function + furi_hal_bt_hid_kb_release _Bool uint16_t

View File

@@ -14,10 +14,36 @@ typedef struct {
uint16_t report_map_char_handle; uint16_t report_map_char_handle;
uint16_t info_char_handle; uint16_t info_char_handle;
uint16_t ctrl_point_char_handle; uint16_t ctrl_point_char_handle;
// led state
uint16_t led_state_char_handle;
uint16_t led_state_desc_handle;
HidLedStateEventCallback led_state_event_callback;
void* led_state_ctx;
} HIDSvc; } HIDSvc;
static HIDSvc* hid_svc = NULL; static HIDSvc* hid_svc = NULL;
// #define N_BYTE_PER_LINE 16
// static void hexdump(uint8_t* data, uint32_t len) {
// uint32_t n_line = len / N_BYTE_PER_LINE + 1;
// char line[len * 3 + n_line + 1];
// memset(line, 0, sizeof(line));
// uint32_t i;
// for(i = 0; i < len; i++) {
// if(i % N_BYTE_PER_LINE == 0) {
// if(i != 0) {
// FURI_LOG_D(TAG, "%s", line);
// }
// memset(line, 0, sizeof(line));
// }
// uint32_t line_len = strlen(line);
// snprintf(line + line_len, sizeof(line) - line_len, "%02X ", data[i]);
// }
// if(strlen(line) > 0) {
// FURI_LOG_D(TAG, "%s", line);
// }
// }
static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) {
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
@@ -30,6 +56,27 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) {
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
// Process notification confirmation // Process notification confirmation
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
} else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) {
// Process write request
aci_gatt_write_permit_req_event_rp0* req =
(aci_gatt_write_permit_req_event_rp0*)blecore_evt->data;
// FURI_LOG_I(TAG, "GATT write request");
// size_t len = 2 + event_pckt->plen;
// hexdump((uint8_t*)event_pckt, len);
// FURI_LOG_D(TAG, "conn_handle = %04x", req->Connection_Handle);
// FURI_LOG_D(TAG, "attr handle = %04x", req->Attribute_Handle);
// FURI_LOG_D(TAG, "led char handle = %04x", hid_svc->led_state_char_handle);
// FURI_LOG_D(TAG, "led state = %02x", req->Data[0]);
furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx);
// this check is likely to be incorrect, it will actually work in our case
// but we need to investigate gatt api to see what is the rules
// that specify attibute handle value from char handle (or the reverse)
if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) {
hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx);
}
} }
} }
return ret; return ret;
@@ -55,8 +102,8 @@ void hid_svc_start() {
PRIMARY_SERVICE, PRIMARY_SERVICE,
2 + /* protocol mode */ 2 + /* protocol mode */
(4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) +
(3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 +
2, /* Service + Report Map + HID Information + HID Control Point */ 4, /* Service + Report Map + HID Information + HID Control Point + LED state */
&hid_svc->svc_handle); &hid_svc->svc_handle);
if(status) { if(status) {
FURI_LOG_E(TAG, "Failed to add HID service: %d", status); FURI_LOG_E(TAG, "Failed to add HID service: %d", status);
@@ -198,6 +245,44 @@ void hid_svc_start() {
} }
} }
#endif #endif
// add led state output report
char_uuid.Char_UUID_16 = REPORT_CHAR_UUID;
status = aci_gatt_add_char(
hid_svc->svc_handle,
UUID_TYPE_16,
&char_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP,
10,
CHAR_VALUE_LEN_CONSTANT,
&(hid_svc->led_state_char_handle));
if(status) {
FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status);
}
// add led state char descriptor specifying it is an output report
uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2};
desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID;
status = aci_gatt_add_char_desc(
hid_svc->svc_handle,
hid_svc->led_state_char_handle,
UUID_TYPE_16,
&desc_uuid,
HID_SVC_REPORT_REF_LEN,
HID_SVC_REPORT_REF_LEN,
buf,
ATTR_PERMISSION_NONE,
ATTR_ACCESS_READ_WRITE,
GATT_DONT_NOTIFY_EVENTS,
MIN_ENCRY_KEY_SIZE,
CHAR_VALUE_LEN_CONSTANT,
&(hid_svc->led_state_desc_handle));
if(status) {
FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status);
}
// Add Report Map characteristic // Add Report Map characteristic
char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID;
status = aci_gatt_add_char( status = aci_gatt_add_char(
@@ -247,6 +332,9 @@ void hid_svc_start() {
if(status) { if(status) {
FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status);
} }
hid_svc->led_state_event_callback = NULL;
hid_svc->led_state_ctx = NULL;
} }
bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) {
@@ -288,6 +376,15 @@ bool hid_svc_update_info(uint8_t* data, uint16_t len) {
return true; return true;
} }
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) {
furi_assert(hid_svc);
furi_assert(callback);
furi_assert(context);
hid_svc->led_state_event_callback = callback;
hid_svc->led_state_ctx = context;
}
bool hid_svc_is_started() { bool hid_svc_is_started() {
return hid_svc != NULL; return hid_svc != NULL;
} }
@@ -320,6 +417,10 @@ void hid_svc_stop() {
if(status) { if(status) {
FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status);
} }
status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle);
if(status) {
FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status);
}
// Delete service // Delete service
status = aci_gatt_del_service(hid_svc->svc_handle); status = aci_gatt_del_service(hid_svc->svc_handle);
if(status) { if(status) {

View File

@@ -15,6 +15,8 @@
#define HID_SVC_REPORT_COUNT \ #define HID_SVC_REPORT_COUNT \
(HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT) (HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT)
typedef uint16_t (*HidLedStateEventCallback)(uint8_t state, void* ctx);
void hid_svc_start(); void hid_svc_start();
void hid_svc_stop(); void hid_svc_stop();
@@ -26,3 +28,5 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len);
bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len);
bool hid_svc_update_info(uint8_t* data, uint16_t len); bool hid_svc_update_info(uint8_t* data, uint16_t len);
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context);

View File

@@ -20,6 +20,7 @@ enum HidReportId {
ReportIdKeyboard = 1, ReportIdKeyboard = 1,
ReportIdMouse = 2, ReportIdMouse = 2,
ReportIdConsumer = 3, ReportIdConsumer = 3,
ReportIdLEDState = 4,
}; };
// Report numbers corresponded to the report id with an offset of 1 // Report numbers corresponded to the report id with an offset of 1
enum HidInputNumber { enum HidInputNumber {
@@ -63,12 +64,6 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = {
HID_REPORT_COUNT(1), HID_REPORT_COUNT(1),
HID_REPORT_SIZE(8), HID_REPORT_SIZE(8),
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_USAGE_PAGE(HID_PAGE_LED),
HID_REPORT_COUNT(8),
HID_REPORT_SIZE(1),
HID_USAGE_MINIMUM(1),
HID_USAGE_MAXIMUM(8),
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_REPORT_COUNT(FURI_HAL_BT_HID_KB_MAX_KEYS), HID_REPORT_COUNT(FURI_HAL_BT_HID_KB_MAX_KEYS),
HID_REPORT_SIZE(8), HID_REPORT_SIZE(8),
HID_LOGICAL_MINIMUM(0), HID_LOGICAL_MINIMUM(0),
@@ -77,6 +72,13 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = {
HID_USAGE_MINIMUM(0), HID_USAGE_MINIMUM(0),
HID_USAGE_MAXIMUM(101), HID_USAGE_MAXIMUM(101),
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
HID_REPORT_ID(ReportIdLEDState),
HID_USAGE_PAGE(HID_PAGE_LED),
HID_REPORT_COUNT(8),
HID_REPORT_SIZE(1),
HID_USAGE_MINIMUM(1),
HID_USAGE_MAXIMUM(8),
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_END_COLLECTION, HID_END_COLLECTION,
// Mouse Report // Mouse Report
HID_USAGE_PAGE(HID_PAGE_DESKTOP), HID_USAGE_PAGE(HID_PAGE_DESKTOP),
@@ -125,6 +127,62 @@ FuriHalBtHidKbReport* kb_report = NULL;
FuriHalBtHidMouseReport* mouse_report = NULL; FuriHalBtHidMouseReport* mouse_report = NULL;
FuriHalBtHidConsumerReport* consumer_report = NULL; FuriHalBtHidConsumerReport* consumer_report = NULL;
typedef struct {
// shortcuts
#define s_undefined data.bits.b_undefined
#define s_num_lock data.bits.b_num_lock
#define s_caps_lock data.bits.b_caps_lock
#define s_scroll_lock data.bits.b_scroll_lock
#define s_compose data.bits.b_compose
#define s_kana data.bits.b_kana
#define s_power data.bits.b_power
#define s_shift data.bits.b_shift
#define s_value data.value
union {
struct {
uint8_t b_undefined : 1;
uint8_t b_num_lock : 1;
uint8_t b_caps_lock : 1;
uint8_t b_scroll_lock : 1;
uint8_t b_compose : 1;
uint8_t b_kana : 1;
uint8_t b_power : 1;
uint8_t b_shift : 1;
} bits;
uint8_t value;
} data;
} __attribute__((__packed__)) FuriHalBtHidLedState;
FuriHalBtHidLedState hid_host_led_state = {.s_value = 0};
uint16_t furi_hal_bt_hid_led_state_cb(uint8_t state, void* ctx) {
FuriHalBtHidLedState* led_state = (FuriHalBtHidLedState*)ctx;
FURI_LOG_D("HalBtHid", "LED state updated !");
led_state->s_value = state;
return 0;
}
uint8_t furi_hal_bt_hid_get_led_state(void) {
FURI_LOG_D(
"HalBtHid",
"LED state: RFU=%d NUMLOCK=%d CAPSLOCK=%d SCROLLLOCK=%d COMPOSE=%d KANA=%d POWER=%d SHIFT=%d",
hid_host_led_state.s_undefined,
hid_host_led_state.s_num_lock,
hid_host_led_state.s_caps_lock,
hid_host_led_state.s_scroll_lock,
hid_host_led_state.s_compose,
hid_host_led_state.s_kana,
hid_host_led_state.s_power,
hid_host_led_state.s_shift);
return (hid_host_led_state.s_value >> 1); // bit 0 is undefined (after shift bit location
// match with HID led state bits defines)
// see bad_kb_script.c (ducky_numlock_on function)
}
void furi_hal_bt_hid_start() { void furi_hal_bt_hid_start() {
// Start device info // Start device info
if(!dev_info_svc_is_started()) { if(!dev_info_svc_is_started()) {
@@ -139,6 +197,8 @@ void furi_hal_bt_hid_start() {
hid_svc_start(); hid_svc_start();
} }
hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state);
kb_report = malloc(sizeof(FuriHalBtHidKbReport)); kb_report = malloc(sizeof(FuriHalBtHidKbReport));
mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); mouse_report = malloc(sizeof(FuriHalBtHidMouseReport));
consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport));
@@ -161,6 +221,9 @@ void furi_hal_bt_hid_stop() {
furi_assert(kb_report); furi_assert(kb_report);
furi_assert(mouse_report); furi_assert(mouse_report);
furi_assert(consumer_report); furi_assert(consumer_report);
hid_svc_register_led_state_callback(NULL, NULL);
// Stop all services // Stop all services
if(dev_info_svc_is_started()) { if(dev_info_svc_is_started()) {
dev_info_svc_stop(); dev_info_svc_stop();

View File

@@ -95,6 +95,12 @@ bool furi_hal_bt_hid_consumer_key_release_all();
*/ */
bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots); bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots);
/** Retrieves LED state from remote BT HID host
*
* (look at HID usage page to know what each bit of the returned byte means)
*/
uint8_t furi_hal_bt_hid_get_led_state(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif