[FL-2119] BT HID App (#888)
* view_dispatcher: add default back processing for Long events * assets: add ble connected and disconnected assets * bt keyboard: introduce new application * bt keyboard: add logic to keyboard mode * bt: remove debug ble hid application * bt hid: introduce media controller * gui canvas: rename CanvasFontDirection -> CanvasDirection * gui canvas: add arrow element * assets: update finilized assets * bt hid: finalise keynote GUI * bt hid: finalise media player GUI * bt: add media key buttons support * bt: add exit confirm view * bt: change Clicker -> Remote * bt: support f6 target * bt: hopefully final bt hid design * bt hid: add blue led notification when device is connected * bt: leave only bt clicker for now * bt: add display notification on pin code show event
This commit is contained in:
@@ -75,7 +75,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
|
||||
}
|
||||
if(gap->enable_adv) {
|
||||
// Restart advertising
|
||||
gap_advertise_start(GapCommandAdvFast);
|
||||
gap_advertise_start(GapStateAdvFast);
|
||||
furi_hal_power_insomnia_exit();
|
||||
}
|
||||
BleEvent event = {.type = BleEventTypeDisconnected};
|
||||
@@ -446,7 +446,11 @@ void gap_thread_stop() {
|
||||
static int32_t gap_app(void *context) {
|
||||
GapCommand command;
|
||||
while(1) {
|
||||
furi_check(osMessageQueueGet(gap->command_queue, &command, NULL, osWaitForever) == osOK);
|
||||
osStatus status = osMessageQueueGet(gap->command_queue, &command, NULL, osWaitForever);
|
||||
if(status != osOK) {
|
||||
FURI_LOG_E(TAG, "Message queue get error: %d", status);
|
||||
continue;
|
||||
}
|
||||
osMutexAcquire(gap->state_mutex, osWaitForever);
|
||||
if(command == GapCommandKillThread) {
|
||||
break;
|
||||
|
@@ -3,8 +3,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HID_SVC_REPORT_MAP_MAX_LEN (80)
|
||||
#define HID_SVC_REPORT_MAX_LEN (8)
|
||||
#define HID_SVC_REPORT_MAP_MAX_LEN (120)
|
||||
#define HID_SVC_REPORT_MAX_LEN (9)
|
||||
#define HID_SVC_BOOT_KEYBOARD_INPUT_REPORT_MAX_LEN (8)
|
||||
#define HID_SVC_REPORT_REF_LEN (2)
|
||||
#define HID_SVC_INFO_LEN (4)
|
||||
|
@@ -13,16 +13,23 @@
|
||||
#define FURI_HAL_BT_HID_KB_KEYS_MAX (6)
|
||||
|
||||
typedef struct {
|
||||
// uint8_t report_id;
|
||||
uint8_t mods;
|
||||
uint8_t reserved;
|
||||
uint8_t key[FURI_HAL_BT_HID_KB_KEYS_MAX];
|
||||
} FuriHalBtHidKbReport;
|
||||
|
||||
// TODO rework with HID defines
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
uint8_t key;
|
||||
} FuriHalBtHidMediaReport;
|
||||
|
||||
// TODO make composite HID device
|
||||
static uint8_t furi_hal_bt_hid_report_map_data[] = {
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
// 0x85, 0x01, // Report ID (1)
|
||||
0x05, 0x07, // Usage Page (Key Codes)
|
||||
0x19, 0xe0, // Usage Minimum (224)
|
||||
0x29, 0xe7, // Usage Maximum (231)
|
||||
@@ -62,10 +69,31 @@ static uint8_t furi_hal_bt_hid_report_map_data[] = {
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0xB1, 0x02, // Feature (Data, Variable, Absolute)
|
||||
|
||||
0xC0 // End Collection (Application)
|
||||
0xC0, // End Collection (Application)
|
||||
|
||||
// 0x05, 0x0C, // Usage Page (Consumer)
|
||||
// 0x09, 0x01, // Usage (Consumer Control)
|
||||
// 0xA1, 0x01, // Collection (Application)
|
||||
// 0x85, 0x02, // Report ID (2)
|
||||
// 0x05, 0x0C, // Usage Page (Consumer)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x25, 0x01, // Logical Maximum (1)
|
||||
// 0x75, 0x01, // Report Size (1)
|
||||
// 0x95, 0x07, // Report Count (7)
|
||||
// 0x09, 0xB5, // Usage (Scan Next Track)
|
||||
// 0x09, 0xB6, // Usage (Scan Previous Track)
|
||||
// 0x09, 0xB7, // Usage (Stop)
|
||||
// 0x09, 0xB8, // Usage (Eject)
|
||||
// 0x09, 0xCD, // Usage (Play/Pause)
|
||||
// 0x09, 0xE2, // Usage (Mute)
|
||||
// 0x09, 0xE9, // Usage (Volume Increment)
|
||||
// 0x09, 0xEA, // Usage (Volume Decrement)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0xC0, // End Collection
|
||||
};
|
||||
|
||||
FuriHalBtHidKbReport* kb_report = NULL;
|
||||
FuriHalBtHidMediaReport* media_report = NULL;
|
||||
|
||||
void furi_hal_bt_hid_start() {
|
||||
// Start device info
|
||||
@@ -82,6 +110,7 @@ void furi_hal_bt_hid_start() {
|
||||
}
|
||||
// Configure HID Keyboard
|
||||
kb_report = furi_alloc(sizeof(FuriHalBtHidKbReport));
|
||||
media_report = furi_alloc(sizeof(FuriHalBtHidMediaReport));
|
||||
// Configure Report Map characteristic
|
||||
hid_svc_update_report_map(furi_hal_bt_hid_report_map_data, sizeof(furi_hal_bt_hid_report_map_data));
|
||||
// Configure HID Information characteristic
|
||||
@@ -107,11 +136,14 @@ void furi_hal_bt_hid_stop() {
|
||||
hid_svc_stop();
|
||||
}
|
||||
free(kb_report);
|
||||
free(media_report);
|
||||
media_report = NULL;
|
||||
kb_report = NULL;
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_kb_press(uint16_t button) {
|
||||
furi_assert(kb_report);
|
||||
// kb_report->report_id = 0x01;
|
||||
for (uint8_t i = 0; i < FURI_HAL_BT_HID_KB_KEYS_MAX; i++) {
|
||||
if (kb_report->key[i] == 0) {
|
||||
kb_report->key[i] = button & 0xFF;
|
||||
@@ -124,6 +156,7 @@ bool furi_hal_bt_hid_kb_press(uint16_t button) {
|
||||
|
||||
bool furi_hal_bt_hid_kb_release(uint16_t button) {
|
||||
furi_assert(kb_report);
|
||||
// kb_report->report_id = 0x01;
|
||||
for (uint8_t i = 0; i < FURI_HAL_BT_HID_KB_KEYS_MAX; i++) {
|
||||
if (kb_report->key[i] == (button & 0xFF)) {
|
||||
kb_report->key[i] = 0;
|
||||
@@ -136,6 +169,28 @@ bool furi_hal_bt_hid_kb_release(uint16_t button) {
|
||||
|
||||
bool furi_hal_bt_hid_kb_release_all() {
|
||||
furi_assert(kb_report);
|
||||
// kb_report->report_id = 0x01;
|
||||
memset(kb_report, 0, sizeof(FuriHalBtHidKbReport));
|
||||
return hid_svc_update_input_report((uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport));
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_media_press(uint8_t button) {
|
||||
furi_assert(media_report);
|
||||
media_report->report_id = 0x02;
|
||||
media_report->key |= (0x01 << button);
|
||||
return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport));
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_media_release(uint8_t button) {
|
||||
furi_assert(media_report);
|
||||
media_report->report_id = 0x02;
|
||||
media_report->key &= ~(0x01 << button);
|
||||
return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport));
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_media_release_all() {
|
||||
furi_assert(media_report);
|
||||
media_report->report_id = 0x02;
|
||||
media_report->key = 0x00;
|
||||
return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport));
|
||||
}
|
||||
|
@@ -126,9 +126,9 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, BleEventCallback event_cb,
|
||||
} else if(profile == FuriHalBtProfileHidKeyboard) {
|
||||
// Change MAC address for HID profile
|
||||
config->mac_address[2]++;
|
||||
// Change name Flipper -> Clicker
|
||||
const char* clicker_str = "Clicker";
|
||||
memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str) - 1);
|
||||
// Change name Flipper -> Keynote
|
||||
const char* clicker_str = "Keynote";
|
||||
memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str));
|
||||
}
|
||||
ret = gap_init(config, event_cb, context);
|
||||
if(!ret) {
|
||||
|
@@ -75,7 +75,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
|
||||
}
|
||||
if(gap->enable_adv) {
|
||||
// Restart advertising
|
||||
gap_advertise_start(GapCommandAdvFast);
|
||||
gap_advertise_start(GapStateAdvFast);
|
||||
furi_hal_power_insomnia_exit();
|
||||
}
|
||||
BleEvent event = {.type = BleEventTypeDisconnected};
|
||||
@@ -446,7 +446,11 @@ void gap_thread_stop() {
|
||||
static int32_t gap_app(void *context) {
|
||||
GapCommand command;
|
||||
while(1) {
|
||||
furi_check(osMessageQueueGet(gap->command_queue, &command, NULL, osWaitForever) == osOK);
|
||||
osStatus status = osMessageQueueGet(gap->command_queue, &command, NULL, osWaitForever);
|
||||
if(status != osOK) {
|
||||
FURI_LOG_E(TAG, "Message queue get error: %d", status);
|
||||
continue;
|
||||
}
|
||||
osMutexAcquire(gap->state_mutex, osWaitForever);
|
||||
if(command == GapCommandKillThread) {
|
||||
break;
|
||||
|
@@ -3,8 +3,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HID_SVC_REPORT_MAP_MAX_LEN (80)
|
||||
#define HID_SVC_REPORT_MAX_LEN (8)
|
||||
#define HID_SVC_REPORT_MAP_MAX_LEN (120)
|
||||
#define HID_SVC_REPORT_MAX_LEN (9)
|
||||
#define HID_SVC_BOOT_KEYBOARD_INPUT_REPORT_MAX_LEN (8)
|
||||
#define HID_SVC_REPORT_REF_LEN (2)
|
||||
#define HID_SVC_INFO_LEN (4)
|
||||
|
@@ -13,16 +13,23 @@
|
||||
#define FURI_HAL_BT_HID_KB_KEYS_MAX (6)
|
||||
|
||||
typedef struct {
|
||||
// uint8_t report_id;
|
||||
uint8_t mods;
|
||||
uint8_t reserved;
|
||||
uint8_t key[FURI_HAL_BT_HID_KB_KEYS_MAX];
|
||||
} FuriHalBtHidKbReport;
|
||||
|
||||
// TODO rework with HID defines
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
uint8_t key;
|
||||
} FuriHalBtHidMediaReport;
|
||||
|
||||
// TODO make composite HID device
|
||||
static uint8_t furi_hal_bt_hid_report_map_data[] = {
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
// 0x85, 0x01, // Report ID (1)
|
||||
0x05, 0x07, // Usage Page (Key Codes)
|
||||
0x19, 0xe0, // Usage Minimum (224)
|
||||
0x29, 0xe7, // Usage Maximum (231)
|
||||
@@ -62,10 +69,31 @@ static uint8_t furi_hal_bt_hid_report_map_data[] = {
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0xB1, 0x02, // Feature (Data, Variable, Absolute)
|
||||
|
||||
0xC0 // End Collection (Application)
|
||||
0xC0, // End Collection (Application)
|
||||
|
||||
// 0x05, 0x0C, // Usage Page (Consumer)
|
||||
// 0x09, 0x01, // Usage (Consumer Control)
|
||||
// 0xA1, 0x01, // Collection (Application)
|
||||
// 0x85, 0x02, // Report ID (2)
|
||||
// 0x05, 0x0C, // Usage Page (Consumer)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x25, 0x01, // Logical Maximum (1)
|
||||
// 0x75, 0x01, // Report Size (1)
|
||||
// 0x95, 0x07, // Report Count (7)
|
||||
// 0x09, 0xB5, // Usage (Scan Next Track)
|
||||
// 0x09, 0xB6, // Usage (Scan Previous Track)
|
||||
// 0x09, 0xB7, // Usage (Stop)
|
||||
// 0x09, 0xB8, // Usage (Eject)
|
||||
// 0x09, 0xCD, // Usage (Play/Pause)
|
||||
// 0x09, 0xE2, // Usage (Mute)
|
||||
// 0x09, 0xE9, // Usage (Volume Increment)
|
||||
// 0x09, 0xEA, // Usage (Volume Decrement)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0xC0, // End Collection
|
||||
};
|
||||
|
||||
FuriHalBtHidKbReport* kb_report = NULL;
|
||||
FuriHalBtHidMediaReport* media_report = NULL;
|
||||
|
||||
void furi_hal_bt_hid_start() {
|
||||
// Start device info
|
||||
@@ -82,6 +110,7 @@ void furi_hal_bt_hid_start() {
|
||||
}
|
||||
// Configure HID Keyboard
|
||||
kb_report = furi_alloc(sizeof(FuriHalBtHidKbReport));
|
||||
media_report = furi_alloc(sizeof(FuriHalBtHidMediaReport));
|
||||
// Configure Report Map characteristic
|
||||
hid_svc_update_report_map(furi_hal_bt_hid_report_map_data, sizeof(furi_hal_bt_hid_report_map_data));
|
||||
// Configure HID Information characteristic
|
||||
@@ -107,11 +136,14 @@ void furi_hal_bt_hid_stop() {
|
||||
hid_svc_stop();
|
||||
}
|
||||
free(kb_report);
|
||||
free(media_report);
|
||||
media_report = NULL;
|
||||
kb_report = NULL;
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_kb_press(uint16_t button) {
|
||||
furi_assert(kb_report);
|
||||
// kb_report->report_id = 0x01;
|
||||
for (uint8_t i = 0; i < FURI_HAL_BT_HID_KB_KEYS_MAX; i++) {
|
||||
if (kb_report->key[i] == 0) {
|
||||
kb_report->key[i] = button & 0xFF;
|
||||
@@ -124,6 +156,7 @@ bool furi_hal_bt_hid_kb_press(uint16_t button) {
|
||||
|
||||
bool furi_hal_bt_hid_kb_release(uint16_t button) {
|
||||
furi_assert(kb_report);
|
||||
// kb_report->report_id = 0x01;
|
||||
for (uint8_t i = 0; i < FURI_HAL_BT_HID_KB_KEYS_MAX; i++) {
|
||||
if (kb_report->key[i] == (button & 0xFF)) {
|
||||
kb_report->key[i] = 0;
|
||||
@@ -136,6 +169,28 @@ bool furi_hal_bt_hid_kb_release(uint16_t button) {
|
||||
|
||||
bool furi_hal_bt_hid_kb_release_all() {
|
||||
furi_assert(kb_report);
|
||||
// kb_report->report_id = 0x01;
|
||||
memset(kb_report, 0, sizeof(FuriHalBtHidKbReport));
|
||||
return hid_svc_update_input_report((uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport));
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_media_press(uint8_t button) {
|
||||
furi_assert(media_report);
|
||||
media_report->report_id = 0x02;
|
||||
media_report->key |= (0x01 << button);
|
||||
return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport));
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_media_release(uint8_t button) {
|
||||
furi_assert(media_report);
|
||||
media_report->report_id = 0x02;
|
||||
media_report->key &= ~(0x01 << button);
|
||||
return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport));
|
||||
}
|
||||
|
||||
bool furi_hal_bt_hid_media_release_all() {
|
||||
furi_assert(media_report);
|
||||
media_report->report_id = 0x02;
|
||||
media_report->key = 0x00;
|
||||
return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport));
|
||||
}
|
||||
|
@@ -126,9 +126,9 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, BleEventCallback event_cb,
|
||||
} else if(profile == FuriHalBtProfileHidKeyboard) {
|
||||
// Change MAC address for HID profile
|
||||
config->mac_address[2]++;
|
||||
// Change name Flipper -> Clicker
|
||||
const char* clicker_str = "Clicker";
|
||||
memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str) - 1);
|
||||
// Change name Flipper -> Keynote
|
||||
const char* clicker_str = "Keynote";
|
||||
memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str));
|
||||
}
|
||||
ret = gap_init(config, event_cb, context);
|
||||
if(!ret) {
|
||||
|
@@ -3,6 +3,17 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum FuriHalBtHidMediKeys{
|
||||
FuriHalBtHidMediaScanNext,
|
||||
FuriHalBtHidMediaScanPrevious,
|
||||
FuriHalBtHidMediaStop,
|
||||
FuriHalBtHidMediaEject,
|
||||
FuriHalBtHidMediaPlayPause,
|
||||
FuriHalBtHidMediaMute,
|
||||
FuriHalBtHidMediaVolumeUp,
|
||||
FuriHalBtHidMediaVolumeDown,
|
||||
};
|
||||
|
||||
/** Start Hid Keyboard Profile
|
||||
*/
|
||||
void furi_hal_bt_hid_start();
|
||||
@@ -11,7 +22,7 @@ void furi_hal_bt_hid_start();
|
||||
*/
|
||||
void furi_hal_bt_hid_stop();
|
||||
|
||||
/** Press key button
|
||||
/** Press keyboard button
|
||||
*
|
||||
* @param button button code from HID specification
|
||||
*
|
||||
@@ -19,7 +30,7 @@ void furi_hal_bt_hid_stop();
|
||||
*/
|
||||
bool furi_hal_bt_hid_kb_press(uint16_t button);
|
||||
|
||||
/** Release key button
|
||||
/** Release keyboard button
|
||||
*
|
||||
* @param button button code from HID specification
|
||||
*
|
||||
@@ -27,8 +38,26 @@ bool furi_hal_bt_hid_kb_press(uint16_t button);
|
||||
*/
|
||||
bool furi_hal_bt_hid_kb_release(uint16_t button);
|
||||
|
||||
/** Release all key buttons
|
||||
/** Release all keyboard buttons
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_bt_hid_kb_release_all();
|
||||
|
||||
/** Release all media buttons
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_bt_hid_media_press(uint8_t button);
|
||||
|
||||
/** Release all media buttons
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_bt_hid_media_release(uint8_t button);
|
||||
|
||||
/** Release all media buttons
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_bt_hid_media_release_all();
|
||||
|
Reference in New Issue
Block a user