mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 14:48:35 -07:00
Adds LED state report characteristic into bt GATT hid service (for now writing by host isn't working)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,13.5,,
|
||||
Version,v,13.9,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.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_release,_Bool,uint16_t
|
||||
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_press,_Bool,uint16_t
|
||||
Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t
|
||||
|
||||
|
@@ -14,10 +14,36 @@ typedef struct {
|
||||
uint16_t report_map_char_handle;
|
||||
uint16_t info_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;
|
||||
|
||||
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) {
|
||||
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
|
||||
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) {
|
||||
// Process notification confirmation
|
||||
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;
|
||||
@@ -55,8 +102,8 @@ void hid_svc_start() {
|
||||
PRIMARY_SERVICE,
|
||||
2 + /* protocol mode */
|
||||
(4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) +
|
||||
(3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 +
|
||||
2, /* Service + Report Map + HID Information + HID Control Point */
|
||||
(3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 +
|
||||
4, /* Service + Report Map + HID Information + HID Control Point + LED state */
|
||||
&hid_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(TAG, "Failed to add HID service: %d", status);
|
||||
@@ -198,6 +245,44 @@ void hid_svc_start() {
|
||||
}
|
||||
}
|
||||
#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
|
||||
char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID;
|
||||
status = aci_gatt_add_char(
|
||||
@@ -247,6 +332,9 @@ void hid_svc_start() {
|
||||
if(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) {
|
||||
@@ -288,6 +376,15 @@ bool hid_svc_update_info(uint8_t* data, uint16_t len) {
|
||||
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() {
|
||||
return hid_svc != NULL;
|
||||
}
|
||||
@@ -320,6 +417,10 @@ void hid_svc_stop() {
|
||||
if(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
|
||||
status = aci_gatt_del_service(hid_svc->svc_handle);
|
||||
if(status) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#define HID_SVC_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_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_info(uint8_t* data, uint16_t len);
|
||||
|
||||
void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context);
|
||||
@@ -20,6 +20,7 @@ enum HidReportId {
|
||||
ReportIdKeyboard = 1,
|
||||
ReportIdMouse = 2,
|
||||
ReportIdConsumer = 3,
|
||||
ReportIdLEDState = 4,
|
||||
};
|
||||
// Report numbers corresponded to the report id with an offset of 1
|
||||
enum HidInputNumber {
|
||||
@@ -63,12 +64,6 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = {
|
||||
HID_REPORT_COUNT(1),
|
||||
HID_REPORT_SIZE(8),
|
||||
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_SIZE(8),
|
||||
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_MAXIMUM(101),
|
||||
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,
|
||||
// Mouse Report
|
||||
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||
@@ -125,6 +127,62 @@ FuriHalBtHidKbReport* kb_report = NULL;
|
||||
FuriHalBtHidMouseReport* mouse_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() {
|
||||
// Start device info
|
||||
if(!dev_info_svc_is_started()) {
|
||||
@@ -139,6 +197,8 @@ void furi_hal_bt_hid_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));
|
||||
mouse_report = malloc(sizeof(FuriHalBtHidMouseReport));
|
||||
consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport));
|
||||
@@ -161,6 +221,9 @@ void furi_hal_bt_hid_stop() {
|
||||
furi_assert(kb_report);
|
||||
furi_assert(mouse_report);
|
||||
furi_assert(consumer_report);
|
||||
|
||||
hid_svc_register_led_state_callback(NULL, NULL);
|
||||
|
||||
// Stop all services
|
||||
if(dev_info_svc_is_started()) {
|
||||
dev_info_svc_stop();
|
||||
|
||||
Reference in New Issue
Block a user