mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 23:58:36 -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:
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
|
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user