[FL-3267] ble: refactored bt gatt characteristics setup (#2587)
* ble: refactored bt gatt characteristics setup * ble: naming fixes, small optimizations * ble: expanded bitfields; fixed pvs warnings * ble: fixed pvs warnings for real * ble: using FlipperGattCharacteristicDataPropsFixed for char[] props * ble: removed flipper_gatt_characteristic_props_const_char * ble: gatt: naming changes * ble: gatt: fixed device_info service constant attrs sizes * ble: gatt: copy descriptors to char instances; reworked hid chars to be callback-based; moved max size getter to callback with NULL data; added comments * ble: gatt: removed hid_svc_report_data_callback * ble: hid svc: better double loop idx naming * ble: hid svc: simplified hid_svc_update_info * ble: gatt: removed magic values; fixed type for HidSvcGattCharacteristicInfo * ble: gatt: moved long uuids to separate files Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									e5343fdc9a
								
							
						
					
					
						commit
						e3e64e5e83
					
				| @ -1,7 +1,7 @@ | ||||
| #include "bt_i.h" | ||||
| #include "battery_service.h" | ||||
| #include "bt_keys_storage.h" | ||||
| 
 | ||||
| #include <services/battery_service.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <gui/elements.h> | ||||
| #include <assets_icons.h> | ||||
|  | ||||
| @ -196,14 +196,14 @@ static void APPD_SetCPU2GpioConfig(void) { | ||||
|         gpio_config.Pin = gpiob_pin_list; | ||||
|         LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); | ||||
|         LL_GPIO_Init(GPIOB, &gpio_config); | ||||
|         LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); | ||||
|         LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); | ||||
|     } | ||||
| 
 | ||||
|     if(gpioc_pin_list != 0) { | ||||
|         gpio_config.Pin = gpioc_pin_list; | ||||
|         LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); | ||||
|         LL_GPIO_Init(GPIOC, &gpio_config); | ||||
|         LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); | ||||
|         LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -33,37 +33,19 @@ static int32_t ble_app_hci_thread(void* context); | ||||
| static void ble_app_hci_event_handler(void* pPayload); | ||||
| static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); | ||||
| 
 | ||||
| bool ble_app_init() { | ||||
|     SHCI_CmdStatus_t status; | ||||
|     ble_app = malloc(sizeof(BleApp)); | ||||
|     // Allocate semafore and mutex for ble command buffer access
 | ||||
|     ble_app->hci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); | ||||
|     ble_app->hci_sem = furi_semaphore_alloc(1, 0); | ||||
|     // HCI transport layer thread to handle user asynch events
 | ||||
|     ble_app->thread = furi_thread_alloc_ex("BleHciDriver", 1024, ble_app_hci_thread, ble_app); | ||||
|     furi_thread_start(ble_app->thread); | ||||
| 
 | ||||
|     // Initialize Ble Transport Layer
 | ||||
|     HCI_TL_HciInitConf_t hci_tl_config = { | ||||
| static const HCI_TL_HciInitConf_t hci_tl_config = { | ||||
|     .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, | ||||
|     .StatusNotCallBack = ble_app_hci_status_not_handler, | ||||
|     }; | ||||
|     hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); | ||||
| }; | ||||
| 
 | ||||
|     // Configure NVM store for pairing data
 | ||||
|     SHCI_C2_CONFIG_Cmd_Param_t config_param = { | ||||
| static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { | ||||
|     .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, | ||||
|     .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, | ||||
|     .BleNvmRamAddress = (uint32_t)ble_app_nvm, | ||||
|     .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, | ||||
|     }; | ||||
|     status = SHCI_C2_Config(&config_param); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|     // Start ble stack on 2nd core
 | ||||
|     SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { | ||||
| static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { | ||||
|     .Header = {{0, 0, 0}}, // Header unused
 | ||||
|     .Param = { | ||||
|         .pBleBufferAddress = 0, // pBleBufferAddress not used
 | ||||
| @ -95,7 +77,28 @@ bool ble_app_init() { | ||||
|         .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB
 | ||||
|         .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3)
 | ||||
|     }}; | ||||
|     status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); | ||||
| 
 | ||||
| bool ble_app_init() { | ||||
|     SHCI_CmdStatus_t status; | ||||
|     ble_app = malloc(sizeof(BleApp)); | ||||
|     // Allocate semafore and mutex for ble command buffer access
 | ||||
|     ble_app->hci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); | ||||
|     ble_app->hci_sem = furi_semaphore_alloc(1, 0); | ||||
|     // HCI transport layer thread to handle user asynch events
 | ||||
|     ble_app->thread = furi_thread_alloc_ex("BleHciDriver", 1024, ble_app_hci_thread, ble_app); | ||||
|     furi_thread_start(ble_app->thread); | ||||
| 
 | ||||
|     // Initialize Ble Transport Layer
 | ||||
|     hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); | ||||
| 
 | ||||
|     // Configure NVM store for pairing data
 | ||||
|     status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // Start ble stack on 2nd core
 | ||||
|     status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); | ||||
|     } | ||||
|  | ||||
| @ -1,220 +0,0 @@ | ||||
| #include "dev_info_service.h" | ||||
| #include "app_common.h" | ||||
| #include <ble/ble.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <protobuf_version.h> | ||||
| #include <lib/toolbox/version.h> | ||||
| 
 | ||||
| #define TAG "BtDevInfoSvc" | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t service_handle; | ||||
|     uint16_t man_name_char_handle; | ||||
|     uint16_t serial_num_char_handle; | ||||
|     uint16_t firmware_rev_char_handle; | ||||
|     uint16_t software_rev_char_handle; | ||||
|     uint16_t rpc_version_char_handle; | ||||
|     FuriString* version_string; | ||||
|     char hardware_revision[4]; | ||||
| } DevInfoSvc; | ||||
| 
 | ||||
| static DevInfoSvc* dev_info_svc = NULL; | ||||
| 
 | ||||
| static const char dev_info_man_name[] = "Flipper Devices Inc."; | ||||
| static const char dev_info_serial_num[] = "1.0"; | ||||
| static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); | ||||
| 
 | ||||
| static const uint8_t dev_info_rpc_version_uuid[] = | ||||
|     {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; | ||||
| 
 | ||||
| void dev_info_svc_start() { | ||||
|     dev_info_svc = malloc(sizeof(DevInfoSvc)); | ||||
|     dev_info_svc->version_string = furi_string_alloc_printf( | ||||
|         "%s %s %s %s", | ||||
|         version_get_githash(NULL), | ||||
|         version_get_gitbranch(NULL), | ||||
|         version_get_gitbranchnum(NULL), | ||||
|         version_get_builddate(NULL)); | ||||
|     snprintf( | ||||
|         dev_info_svc->hardware_revision, | ||||
|         sizeof(dev_info_svc->hardware_revision), | ||||
|         "%d", | ||||
|         version_get_target(NULL)); | ||||
|     tBleStatus status; | ||||
| 
 | ||||
|     // Add Device Information Service
 | ||||
|     uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; | ||||
|     status = aci_gatt_add_service( | ||||
|         UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // Add characteristics
 | ||||
|     uuid = MANUFACTURER_NAME_UUID; | ||||
|     status = aci_gatt_add_char( | ||||
|         dev_info_svc->service_handle, | ||||
|         UUID_TYPE_16, | ||||
|         (Char_UUID_t*)&uuid, | ||||
|         strlen(dev_info_man_name), | ||||
|         CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &dev_info_svc->man_name_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); | ||||
|     } | ||||
|     uuid = SERIAL_NUMBER_UUID; | ||||
|     status = aci_gatt_add_char( | ||||
|         dev_info_svc->service_handle, | ||||
|         UUID_TYPE_16, | ||||
|         (Char_UUID_t*)&uuid, | ||||
|         strlen(dev_info_serial_num), | ||||
|         CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &dev_info_svc->serial_num_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); | ||||
|     } | ||||
|     uuid = FIRMWARE_REVISION_UUID; | ||||
|     status = aci_gatt_add_char( | ||||
|         dev_info_svc->service_handle, | ||||
|         UUID_TYPE_16, | ||||
|         (Char_UUID_t*)&uuid, | ||||
|         strlen(dev_info_svc->hardware_revision), | ||||
|         CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &dev_info_svc->firmware_rev_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); | ||||
|     } | ||||
|     uuid = SOFTWARE_REVISION_UUID; | ||||
|     status = aci_gatt_add_char( | ||||
|         dev_info_svc->service_handle, | ||||
|         UUID_TYPE_16, | ||||
|         (Char_UUID_t*)&uuid, | ||||
|         furi_string_size(dev_info_svc->version_string), | ||||
|         CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &dev_info_svc->software_rev_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); | ||||
|     } | ||||
|     status = aci_gatt_add_char( | ||||
|         dev_info_svc->service_handle, | ||||
|         UUID_TYPE_128, | ||||
|         (const Char_UUID_t*)dev_info_rpc_version_uuid, | ||||
|         strlen(dev_info_rpc_version), | ||||
|         CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &dev_info_svc->rpc_version_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // Update characteristics
 | ||||
|     status = aci_gatt_update_char_value( | ||||
|         dev_info_svc->service_handle, | ||||
|         dev_info_svc->man_name_char_handle, | ||||
|         0, | ||||
|         strlen(dev_info_man_name), | ||||
|         (uint8_t*)dev_info_man_name); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); | ||||
|     } | ||||
|     status = aci_gatt_update_char_value( | ||||
|         dev_info_svc->service_handle, | ||||
|         dev_info_svc->serial_num_char_handle, | ||||
|         0, | ||||
|         strlen(dev_info_serial_num), | ||||
|         (uint8_t*)dev_info_serial_num); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); | ||||
|     } | ||||
|     status = aci_gatt_update_char_value( | ||||
|         dev_info_svc->service_handle, | ||||
|         dev_info_svc->firmware_rev_char_handle, | ||||
|         0, | ||||
|         strlen(dev_info_svc->hardware_revision), | ||||
|         (uint8_t*)dev_info_svc->hardware_revision); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); | ||||
|     } | ||||
|     status = aci_gatt_update_char_value( | ||||
|         dev_info_svc->service_handle, | ||||
|         dev_info_svc->software_rev_char_handle, | ||||
|         0, | ||||
|         furi_string_size(dev_info_svc->version_string), | ||||
|         (uint8_t*)furi_string_get_cstr(dev_info_svc->version_string)); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); | ||||
|     } | ||||
|     status = aci_gatt_update_char_value( | ||||
|         dev_info_svc->service_handle, | ||||
|         dev_info_svc->rpc_version_char_handle, | ||||
|         0, | ||||
|         strlen(dev_info_rpc_version), | ||||
|         (uint8_t*)dev_info_rpc_version); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void dev_info_svc_stop() { | ||||
|     tBleStatus status; | ||||
|     if(dev_info_svc) { | ||||
|         furi_string_free(dev_info_svc->version_string); | ||||
|         // Delete service characteristics
 | ||||
|         status = | ||||
|             aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); | ||||
|         } | ||||
|         status = | ||||
|             aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); | ||||
|         } | ||||
|         status = aci_gatt_del_char( | ||||
|             dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); | ||||
|         } | ||||
|         status = aci_gatt_del_char( | ||||
|             dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); | ||||
|         } | ||||
|         status = | ||||
|             aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); | ||||
|         } | ||||
|         // Delete service
 | ||||
|         status = aci_gatt_del_service(dev_info_svc->service_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); | ||||
|         } | ||||
|         free(dev_info_svc); | ||||
|         dev_info_svc = NULL; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool dev_info_svc_is_started() { | ||||
|     return dev_info_svc != NULL; | ||||
| } | ||||
| @ -1,332 +0,0 @@ | ||||
| #include "hid_service.h" | ||||
| #include "app_common.h" | ||||
| #include <ble/ble.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define TAG "BtHid" | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t svc_handle; | ||||
|     uint16_t protocol_mode_char_handle; | ||||
|     uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; | ||||
|     uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; | ||||
|     uint16_t report_map_char_handle; | ||||
|     uint16_t info_char_handle; | ||||
|     uint16_t ctrl_point_char_handle; | ||||
| } HIDSvc; | ||||
| 
 | ||||
| static HIDSvc* hid_svc = NULL; | ||||
| 
 | ||||
| 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); | ||||
|     evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; | ||||
|     // aci_gatt_attribute_modified_event_rp0* attribute_modified;
 | ||||
|     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { | ||||
|         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||
|             // Process modification events
 | ||||
|             ret = SVCCTL_EvtAckFlowEnable; | ||||
|         } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { | ||||
|             // Process notification confirmation
 | ||||
|             ret = SVCCTL_EvtAckFlowEnable; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void hid_svc_start() { | ||||
|     tBleStatus status; | ||||
|     hid_svc = malloc(sizeof(HIDSvc)); | ||||
|     Service_UUID_t svc_uuid = {}; | ||||
|     Char_Desc_Uuid_t desc_uuid = {}; | ||||
|     Char_UUID_t char_uuid = {}; | ||||
| 
 | ||||
|     // Register event handler
 | ||||
|     SVCCTL_RegisterSvcHandler(hid_svc_event_handler); | ||||
|     // Add service
 | ||||
|     svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; | ||||
|     /**
 | ||||
|      *  Add Human Interface Device Service | ||||
|      */ | ||||
|     status = aci_gatt_add_service( | ||||
|         UUID_TYPE_16, | ||||
|         &svc_uuid, | ||||
|         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 */ | ||||
|         &hid_svc->svc_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add HID service: %d", status); | ||||
|     } | ||||
|     // Add Protocol mode characteristics
 | ||||
|     char_uuid.Char_UUID_16 = PROTOCOL_MODE_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, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &hid_svc->protocol_mode_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); | ||||
|     } | ||||
|     // Update Protocol mode characteristic
 | ||||
|     uint8_t protocol_mode = 1; | ||||
|     status = aci_gatt_update_char_value( | ||||
|         hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); | ||||
|     } | ||||
| 
 | ||||
| #if(HID_SVC_REPORT_COUNT != 0) | ||||
|     for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { | ||||
|         if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547
 | ||||
|             uint8_t buf[2] = {i + 1, 1}; // 1 input
 | ||||
|             char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; | ||||
|             status = aci_gatt_add_char( | ||||
|                 hid_svc->svc_handle, | ||||
|                 UUID_TYPE_16, | ||||
|                 &char_uuid, | ||||
|                 HID_SVC_REPORT_MAX_LEN, | ||||
|                 CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|                 ATTR_PERMISSION_NONE, | ||||
|                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                 10, | ||||
|                 CHAR_VALUE_LEN_VARIABLE, | ||||
|                 &(hid_svc->report_char_handle[i])); | ||||
|             if(status) { | ||||
|                 FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); | ||||
|             } | ||||
| 
 | ||||
|             desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; | ||||
|             status = aci_gatt_add_char_desc( | ||||
|                 hid_svc->svc_handle, | ||||
|                 hid_svc->report_char_handle[i], | ||||
|                 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->report_ref_desc_handle[i])); | ||||
|             if(status) { | ||||
|                 FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); | ||||
|             } | ||||
|         } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { | ||||
|             uint8_t buf[2] = {i + 1, 2}; // 2 output
 | ||||
|             char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; | ||||
|             status = aci_gatt_add_char( | ||||
|                 hid_svc->svc_handle, | ||||
|                 UUID_TYPE_16, | ||||
|                 &char_uuid, | ||||
|                 HID_SVC_REPORT_MAX_LEN, | ||||
|                 CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|                 ATTR_PERMISSION_NONE, | ||||
|                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                 10, | ||||
|                 CHAR_VALUE_LEN_VARIABLE, | ||||
|                 &(hid_svc->report_char_handle[i])); | ||||
|             if(status) { | ||||
|                 FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); | ||||
|             } | ||||
| 
 | ||||
|             desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; | ||||
|             status = aci_gatt_add_char_desc( | ||||
|                 hid_svc->svc_handle, | ||||
|                 hid_svc->report_char_handle[i], | ||||
|                 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->report_ref_desc_handle[i])); | ||||
|             if(status) { | ||||
|                 FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); | ||||
|             } | ||||
|         } else { | ||||
|             uint8_t buf[2] = {i + 1, 3}; // 3 feature
 | ||||
|             char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; | ||||
|             status = aci_gatt_add_char( | ||||
|                 hid_svc->svc_handle, | ||||
|                 UUID_TYPE_16, | ||||
|                 &char_uuid, | ||||
|                 HID_SVC_REPORT_MAX_LEN, | ||||
|                 CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|                 ATTR_PERMISSION_NONE, | ||||
|                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                 10, | ||||
|                 CHAR_VALUE_LEN_VARIABLE, | ||||
|                 &(hid_svc->report_char_handle[i])); | ||||
|             if(status) { | ||||
|                 FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); | ||||
|             } | ||||
| 
 | ||||
|             desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; | ||||
|             status = aci_gatt_add_char_desc( | ||||
|                 hid_svc->svc_handle, | ||||
|                 hid_svc->report_char_handle[i], | ||||
|                 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->report_ref_desc_handle[i])); | ||||
|             if(status) { | ||||
|                 FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|     // Add Report Map characteristic
 | ||||
|     char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; | ||||
|     status = aci_gatt_add_char( | ||||
|         hid_svc->svc_handle, | ||||
|         UUID_TYPE_16, | ||||
|         &char_uuid, | ||||
|         HID_SVC_REPORT_MAP_MAX_LEN, | ||||
|         CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_NONE, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_VARIABLE, | ||||
|         &hid_svc->report_map_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // Add Information characteristic
 | ||||
|     char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; | ||||
|     status = aci_gatt_add_char( | ||||
|         hid_svc->svc_handle, | ||||
|         UUID_TYPE_16, | ||||
|         &char_uuid, | ||||
|         HID_SVC_INFO_LEN, | ||||
|         CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_NONE, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &hid_svc->info_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); | ||||
|     } | ||||
|     // Add Control Point characteristic
 | ||||
|     char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; | ||||
|     status = aci_gatt_add_char( | ||||
|         hid_svc->svc_handle, | ||||
|         UUID_TYPE_16, | ||||
|         &char_uuid, | ||||
|         HID_SVC_CONTROL_POINT_LEN, | ||||
|         CHAR_PROP_WRITE_WITHOUT_RESP, | ||||
|         ATTR_PERMISSION_NONE, | ||||
|         GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &hid_svc->ctrl_point_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { | ||||
|     furi_assert(data); | ||||
|     furi_assert(hid_svc); | ||||
| 
 | ||||
|     tBleStatus status = aci_gatt_update_char_value( | ||||
|         hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { | ||||
|     furi_assert(data); | ||||
|     furi_assert(hid_svc); | ||||
| 
 | ||||
|     tBleStatus status = aci_gatt_update_char_value( | ||||
|         hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_update_info(uint8_t* data, uint16_t len) { | ||||
|     furi_assert(data); | ||||
|     furi_assert(hid_svc); | ||||
| 
 | ||||
|     tBleStatus status = | ||||
|         aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_is_started() { | ||||
|     return hid_svc != NULL; | ||||
| } | ||||
| 
 | ||||
| void hid_svc_stop() { | ||||
|     tBleStatus status; | ||||
|     if(hid_svc) { | ||||
|         // Delete characteristics
 | ||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); | ||||
|         } | ||||
| #if(HID_SVC_INPUT_REPORT_COUNT != 0) | ||||
|         for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { | ||||
|             status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); | ||||
|             if(status) { | ||||
|                 FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); | ||||
|         } | ||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); | ||||
|         } | ||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); | ||||
|         } | ||||
|         // Delete service
 | ||||
|         status = aci_gatt_del_service(hid_svc->svc_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); | ||||
|         } | ||||
|         // Delete buffer size mutex
 | ||||
|         free(hid_svc); | ||||
|         hid_svc = NULL; | ||||
|     } | ||||
| } | ||||
| @ -1,5 +1,7 @@ | ||||
| #include "battery_service.h" | ||||
| #include "app_common.h" | ||||
| #include "gatt_char.h" | ||||
| 
 | ||||
| #include <ble/ble.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| @ -7,12 +9,6 @@ | ||||
| 
 | ||||
| #define TAG "BtBatterySvc" | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t svc_handle; | ||||
|     uint16_t battery_level_char_handle; | ||||
|     uint16_t power_state_char_handle; | ||||
| } BatterySvc; | ||||
| 
 | ||||
| enum { | ||||
|     // Common states
 | ||||
|     BatterySvcPowerStateUnknown = 0b00, | ||||
| @ -40,13 +36,44 @@ typedef struct { | ||||
| 
 | ||||
| _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); | ||||
| 
 | ||||
| static BatterySvc* battery_svc = NULL; | ||||
| 
 | ||||
| #define BATTERY_POWER_STATE (0x2A1A) | ||||
| 
 | ||||
| static const uint16_t service_uuid = BATTERY_SERVICE_UUID; | ||||
| static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; | ||||
| static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; | ||||
| 
 | ||||
| typedef enum { | ||||
|     BatterySvcGattCharacteristicBatteryLevel = 0, | ||||
|     BatterySvcGattCharacteristicPowerState, | ||||
|     BatterySvcGattCharacteristicCount, | ||||
| } BatterySvcGattCharacteristicId; | ||||
| 
 | ||||
| static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = | ||||
|     {[BatterySvcGattCharacteristicBatteryLevel] = | ||||
|          {.name = "Battery Level", | ||||
|           .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|           .data.fixed.length = 1, | ||||
|           .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, | ||||
|           .uuid_type = UUID_TYPE_16, | ||||
|           .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|           .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|           .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|           .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|      [BatterySvcGattCharacteristicPowerState] = { | ||||
|          .name = "Power State", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = 1, | ||||
|          .uuid.Char_UUID_16 = BATTERY_POWER_STATE, | ||||
|          .uuid_type = UUID_TYPE_16, | ||||
|          .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|          .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|          .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|          .is_variable = CHAR_VALUE_LEN_CONSTANT}}; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t svc_handle; | ||||
|     FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; | ||||
| } BatterySvc; | ||||
| 
 | ||||
| static BatterySvc* battery_svc = NULL; | ||||
| 
 | ||||
| void battery_svc_start() { | ||||
|     battery_svc = malloc(sizeof(BatterySvc)); | ||||
| @ -58,53 +85,19 @@ void battery_svc_start() { | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); | ||||
|     } | ||||
|     // Add Battery level characteristic
 | ||||
|     status = aci_gatt_add_char( | ||||
|         battery_svc->svc_handle, | ||||
|         UUID_TYPE_16, | ||||
|         (Char_UUID_t*)&battery_level_char_uuid, | ||||
|         1, | ||||
|         CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &battery_svc->battery_level_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); | ||||
|     for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { | ||||
|         flipper_gatt_characteristic_init( | ||||
|             battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); | ||||
|     } | ||||
|     // Add Power state characteristic
 | ||||
|     status = aci_gatt_add_char( | ||||
|         battery_svc->svc_handle, | ||||
|         UUID_TYPE_16, | ||||
|         (Char_UUID_t*)&power_state_char_uuid, | ||||
|         1, | ||||
|         CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &battery_svc->power_state_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); | ||||
|     } | ||||
|     // Update power state charachteristic
 | ||||
| 
 | ||||
|     battery_svc_update_power_state(); | ||||
| } | ||||
| 
 | ||||
| void battery_svc_stop() { | ||||
|     tBleStatus status; | ||||
|     if(battery_svc) { | ||||
|         // Delete Battery level characteristic
 | ||||
|         status = | ||||
|             aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); | ||||
|         } | ||||
|         // Delete Power state characteristic
 | ||||
|         status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); | ||||
|         for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { | ||||
|             flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); | ||||
|         } | ||||
|         // Delete Battery service
 | ||||
|         status = aci_gatt_del_service(battery_svc->svc_handle); | ||||
| @ -126,13 +119,10 @@ bool battery_svc_update_level(uint8_t battery_charge) { | ||||
|         return false; | ||||
|     } | ||||
|     // Update battery level characteristic
 | ||||
|     FURI_LOG_D(TAG, "Updating battery level characteristic"); | ||||
|     tBleStatus result = aci_gatt_update_char_value( | ||||
|         battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); | ||||
|     if(result) { | ||||
|         FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); | ||||
|     } | ||||
|     return result != BLE_STATUS_SUCCESS; | ||||
|     return flipper_gatt_characteristic_update( | ||||
|         battery_svc->svc_handle, | ||||
|         &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], | ||||
|         &battery_charge); | ||||
| } | ||||
| 
 | ||||
| bool battery_svc_update_power_state() { | ||||
| @ -152,15 +142,9 @@ bool battery_svc_update_power_state() { | ||||
|         power_state.charging = BatterySvcPowerStateNotCharging; | ||||
|         power_state.discharging = BatterySvcPowerStateDischarging; | ||||
|     } | ||||
|     FURI_LOG_D(TAG, "Updating power state characteristic"); | ||||
|     tBleStatus result = aci_gatt_update_char_value( | ||||
| 
 | ||||
|     return flipper_gatt_characteristic_update( | ||||
|         battery_svc->svc_handle, | ||||
|         battery_svc->power_state_char_handle, | ||||
|         0, | ||||
|         1, | ||||
|         (uint8_t*)&power_state); | ||||
|     if(result) { | ||||
|         FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); | ||||
|     } | ||||
|     return result != BLE_STATUS_SUCCESS; | ||||
|         &battery_svc->chars[BatterySvcGattCharacteristicPowerState], | ||||
|         &power_state); | ||||
| } | ||||
							
								
								
									
										176
									
								
								firmware/targets/f7/ble_glue/services/dev_info_service.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								firmware/targets/f7/ble_glue/services/dev_info_service.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| #include "dev_info_service.h" | ||||
| #include "app_common.h" | ||||
| #include "gatt_char.h" | ||||
| #include <ble/ble.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <protobuf_version.h> | ||||
| #include <lib/toolbox/version.h> | ||||
| 
 | ||||
| #include "dev_info_service_uuid.inc" | ||||
| 
 | ||||
| #define TAG "BtDevInfoSvc" | ||||
| 
 | ||||
| typedef enum { | ||||
|     DevInfoSvcGattCharacteristicMfgName = 0, | ||||
|     DevInfoSvcGattCharacteristicSerial, | ||||
|     DevInfoSvcGattCharacteristicFirmwareRev, | ||||
|     DevInfoSvcGattCharacteristicSoftwareRev, | ||||
|     DevInfoSvcGattCharacteristicRpcVersion, | ||||
|     DevInfoSvcGattCharacteristicCount, | ||||
| } DevInfoSvcGattCharacteristicId; | ||||
| 
 | ||||
| #define DEVICE_INFO_HARDWARE_REV_SIZE 4 | ||||
| typedef struct { | ||||
|     uint16_t service_handle; | ||||
|     FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; | ||||
|     FuriString* version_string; | ||||
|     char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; | ||||
| } DevInfoSvc; | ||||
| 
 | ||||
| static DevInfoSvc* dev_info_svc = NULL; | ||||
| 
 | ||||
| static const char dev_info_man_name[] = "Flipper Devices Inc."; | ||||
| static const char dev_info_serial_num[] = "1.0"; | ||||
| static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); | ||||
| 
 | ||||
| static bool dev_info_char_firmware_rev_callback( | ||||
|     const void* context, | ||||
|     const uint8_t** data, | ||||
|     uint16_t* data_len) { | ||||
|     const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; | ||||
|     *data_len = sizeof(dev_info_svc->hardware_revision); | ||||
|     if(data) { | ||||
|         *data = (const uint8_t*)&dev_info_svc->hardware_revision; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static bool dev_info_char_software_rev_callback( | ||||
|     const void* context, | ||||
|     const uint8_t** data, | ||||
|     uint16_t* data_len) { | ||||
|     const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; | ||||
|     *data_len = furi_string_size(dev_info_svc->version_string); | ||||
|     if(data) { | ||||
|         *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = | ||||
|     {[DevInfoSvcGattCharacteristicMfgName] = | ||||
|          {.name = "Manufacturer Name", | ||||
|           .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|           .data.fixed.length = sizeof(dev_info_man_name) - 1, | ||||
|           .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, | ||||
|           .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, | ||||
|           .uuid_type = UUID_TYPE_16, | ||||
|           .char_properties = CHAR_PROP_READ, | ||||
|           .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|           .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|           .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|      [DevInfoSvcGattCharacteristicSerial] = | ||||
|          {.name = "Serial Number", | ||||
|           .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|           .data.fixed.length = sizeof(dev_info_serial_num) - 1, | ||||
|           .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, | ||||
|           .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, | ||||
|           .uuid_type = UUID_TYPE_16, | ||||
|           .char_properties = CHAR_PROP_READ, | ||||
|           .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|           .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|           .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|      [DevInfoSvcGattCharacteristicFirmwareRev] = | ||||
|          {.name = "Firmware Revision", | ||||
|           .data_prop_type = FlipperGattCharacteristicDataCallback, | ||||
|           .data.callback.context = &dev_info_svc, | ||||
|           .data.callback.fn = dev_info_char_firmware_rev_callback, | ||||
|           .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, | ||||
|           .uuid_type = UUID_TYPE_16, | ||||
|           .char_properties = CHAR_PROP_READ, | ||||
|           .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|           .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|           .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|      [DevInfoSvcGattCharacteristicSoftwareRev] = | ||||
|          {.name = "Software Revision", | ||||
|           .data_prop_type = FlipperGattCharacteristicDataCallback, | ||||
|           .data.callback.context = &dev_info_svc, | ||||
|           .data.callback.fn = dev_info_char_software_rev_callback, | ||||
|           .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, | ||||
|           .uuid_type = UUID_TYPE_16, | ||||
|           .char_properties = CHAR_PROP_READ, | ||||
|           .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|           .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|           .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|      [DevInfoSvcGattCharacteristicRpcVersion] = { | ||||
|          .name = "RPC Version", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = sizeof(dev_info_rpc_version) - 1, | ||||
|          .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, | ||||
|          .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, | ||||
|          .uuid_type = UUID_TYPE_128, | ||||
|          .char_properties = CHAR_PROP_READ, | ||||
|          .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|          .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|          .is_variable = CHAR_VALUE_LEN_CONSTANT}}; | ||||
| 
 | ||||
| void dev_info_svc_start() { | ||||
|     dev_info_svc = malloc(sizeof(DevInfoSvc)); | ||||
|     dev_info_svc->version_string = furi_string_alloc_printf( | ||||
|         "%s %s %s %s", | ||||
|         version_get_githash(NULL), | ||||
|         version_get_gitbranch(NULL), | ||||
|         version_get_gitbranchnum(NULL), | ||||
|         version_get_builddate(NULL)); | ||||
|     snprintf( | ||||
|         dev_info_svc->hardware_revision, | ||||
|         sizeof(dev_info_svc->hardware_revision), | ||||
|         "%d", | ||||
|         version_get_target(NULL)); | ||||
|     tBleStatus status; | ||||
| 
 | ||||
|     // Add Device Information Service
 | ||||
|     uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; | ||||
|     status = aci_gatt_add_service( | ||||
|         UUID_TYPE_16, | ||||
|         (Service_UUID_t*)&uuid, | ||||
|         PRIMARY_SERVICE, | ||||
|         1 + 2 * DevInfoSvcGattCharacteristicCount, | ||||
|         &dev_info_svc->service_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { | ||||
|         flipper_gatt_characteristic_init( | ||||
|             dev_info_svc->service_handle, | ||||
|             &dev_info_svc_chars[i], | ||||
|             &dev_info_svc->characteristics[i]); | ||||
|         flipper_gatt_characteristic_update( | ||||
|             dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void dev_info_svc_stop() { | ||||
|     tBleStatus status; | ||||
|     if(dev_info_svc) { | ||||
|         furi_string_free(dev_info_svc->version_string); | ||||
|         // Delete service characteristics
 | ||||
|         for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { | ||||
|             flipper_gatt_characteristic_delete( | ||||
|                 dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); | ||||
|         } | ||||
|         // Delete service
 | ||||
|         status = aci_gatt_del_service(dev_info_svc->service_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); | ||||
|         } | ||||
|         free(dev_info_svc); | ||||
|         dev_info_svc = NULL; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool dev_info_svc_is_started() { | ||||
|     return dev_info_svc != NULL; | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| #define DEV_INVO_RPC_VERSION_UID \
 | ||||
|     { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } | ||||
| 
 | ||||
							
								
								
									
										123
									
								
								firmware/targets/f7/ble_glue/services/gatt_char.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								firmware/targets/f7/ble_glue/services/gatt_char.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | ||||
| #include "gatt_char.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define TAG "GattChar" | ||||
| 
 | ||||
| #define GATT_MIN_READ_KEY_SIZE (10) | ||||
| 
 | ||||
| void flipper_gatt_characteristic_init( | ||||
|     uint16_t svc_handle, | ||||
|     const FlipperGattCharacteristicParams* char_descriptor, | ||||
|     FlipperGattCharacteristicInstance* char_instance) { | ||||
|     furi_assert(char_descriptor); | ||||
|     furi_assert(char_instance); | ||||
| 
 | ||||
|     // Copy the descriptor to the instance, since it may point to stack memory
 | ||||
|     // TODO: only copy if really comes from stack
 | ||||
|     char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); | ||||
|     memcpy( | ||||
|         (void*)char_instance->characteristic, | ||||
|         char_descriptor, | ||||
|         sizeof(FlipperGattCharacteristicParams)); | ||||
| 
 | ||||
|     uint16_t char_data_size = 0; | ||||
|     if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { | ||||
|         char_data_size = char_descriptor->data.fixed.length; | ||||
|     } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { | ||||
|         char_descriptor->data.callback.fn( | ||||
|             char_descriptor->data.callback.context, NULL, &char_data_size); | ||||
|     } | ||||
| 
 | ||||
|     tBleStatus status = aci_gatt_add_char( | ||||
|         svc_handle, | ||||
|         char_descriptor->uuid_type, | ||||
|         &char_descriptor->uuid, | ||||
|         char_data_size, | ||||
|         char_descriptor->char_properties, | ||||
|         char_descriptor->security_permissions, | ||||
|         char_descriptor->gatt_evt_mask, | ||||
|         GATT_MIN_READ_KEY_SIZE, | ||||
|         char_descriptor->is_variable, | ||||
|         &char_instance->handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); | ||||
|     } | ||||
| 
 | ||||
|     char_instance->descriptor_handle = 0; | ||||
|     if((status == 0) && char_descriptor->descriptor_params) { | ||||
|         uint8_t const* char_data = NULL; | ||||
|         const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = | ||||
|             char_descriptor->descriptor_params; | ||||
|         bool release_data = char_data_descriptor->data_callback.fn( | ||||
|             char_data_descriptor->data_callback.context, &char_data, &char_data_size); | ||||
| 
 | ||||
|         status = aci_gatt_add_char_desc( | ||||
|             svc_handle, | ||||
|             char_instance->handle, | ||||
|             char_data_descriptor->uuid_type, | ||||
|             &char_data_descriptor->uuid, | ||||
|             char_data_descriptor->max_length, | ||||
|             char_data_size, | ||||
|             char_data, | ||||
|             char_data_descriptor->security_permissions, | ||||
|             char_data_descriptor->access_permissions, | ||||
|             char_data_descriptor->gatt_evt_mask, | ||||
|             GATT_MIN_READ_KEY_SIZE, | ||||
|             char_data_descriptor->is_variable, | ||||
|             &char_instance->descriptor_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); | ||||
|         } | ||||
|         if(release_data) { | ||||
|             free((void*)char_data); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void flipper_gatt_characteristic_delete( | ||||
|     uint16_t svc_handle, | ||||
|     FlipperGattCharacteristicInstance* char_instance) { | ||||
|     tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E( | ||||
|             TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); | ||||
|     } | ||||
|     free((void*)char_instance->characteristic); | ||||
| } | ||||
| 
 | ||||
| bool flipper_gatt_characteristic_update( | ||||
|     uint16_t svc_handle, | ||||
|     FlipperGattCharacteristicInstance* char_instance, | ||||
|     const void* source) { | ||||
|     furi_assert(char_instance); | ||||
|     const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; | ||||
|     FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); | ||||
| 
 | ||||
|     const uint8_t* char_data = NULL; | ||||
|     uint16_t char_data_size = 0; | ||||
|     bool release_data = false; | ||||
|     if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { | ||||
|         char_data = char_descriptor->data.fixed.ptr; | ||||
|         if(source) { | ||||
|             char_data = (uint8_t*)source; | ||||
|         } | ||||
|         char_data_size = char_descriptor->data.fixed.length; | ||||
|     } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { | ||||
|         const void* context = char_descriptor->data.callback.context; | ||||
|         if(source) { | ||||
|             context = source; | ||||
|         } | ||||
|         release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); | ||||
|     } | ||||
| 
 | ||||
|     tBleStatus result = aci_gatt_update_char_value( | ||||
|         svc_handle, char_instance->handle, 0, char_data_size, char_data); | ||||
|     if(result) { | ||||
|         FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); | ||||
|     } | ||||
|     if(release_data) { | ||||
|         free((void*)char_data); | ||||
|     } | ||||
|     return result != BLE_STATUS_SUCCESS; | ||||
| } | ||||
							
								
								
									
										96
									
								
								firmware/targets/f7/ble_glue/services/gatt_char.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								firmware/targets/f7/ble_glue/services/gatt_char.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include <ble/ble.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| // Callback signature for getting characteristic data
 | ||||
| // Is called when characteristic is created to get max data length. Data ptr is NULL in this case
 | ||||
| //   The result is passed to aci_gatt_add_char as "Char_Value_Length"
 | ||||
| // For updates, called with a context - see flipper_gatt_characteristic_update
 | ||||
| // Returns true if *data ownership is transferred to the caller and will be freed
 | ||||
| typedef bool (*cbFlipperGattCharacteristicData)( | ||||
|     const void* context, | ||||
|     const uint8_t** data, | ||||
|     uint16_t* data_len); | ||||
| 
 | ||||
| typedef enum { | ||||
|     FlipperGattCharacteristicDataFixed, | ||||
|     FlipperGattCharacteristicDataCallback, | ||||
| } FlipperGattCharacteristicDataType; | ||||
| 
 | ||||
| typedef struct { | ||||
|     Char_Desc_Uuid_t uuid; | ||||
|     struct { | ||||
|         cbFlipperGattCharacteristicData fn; | ||||
|         const void* context; | ||||
|     } data_callback; | ||||
|     uint8_t uuid_type; | ||||
|     uint8_t max_length; | ||||
|     uint8_t security_permissions; | ||||
|     uint8_t access_permissions; | ||||
|     uint8_t gatt_evt_mask; | ||||
|     uint8_t is_variable; | ||||
| } FlipperGattCharacteristicDescriptorParams; | ||||
| 
 | ||||
| typedef struct { | ||||
|     const char* name; | ||||
|     FlipperGattCharacteristicDescriptorParams* descriptor_params; | ||||
|     union { | ||||
|         struct { | ||||
|             const uint8_t* ptr; | ||||
|             uint16_t length; | ||||
|         } fixed; | ||||
|         struct { | ||||
|             cbFlipperGattCharacteristicData fn; | ||||
|             const void* context; | ||||
|         } callback; | ||||
|     } data; | ||||
|     Char_UUID_t uuid; | ||||
|     // Some packed bitfields to save space
 | ||||
|     FlipperGattCharacteristicDataType data_prop_type : 2; | ||||
|     uint8_t is_variable : 2; | ||||
|     uint8_t uuid_type : 2; | ||||
|     uint8_t char_properties; | ||||
|     uint8_t security_permissions; | ||||
|     uint8_t gatt_evt_mask; | ||||
| } FlipperGattCharacteristicParams; | ||||
| 
 | ||||
| _Static_assert( | ||||
|     sizeof(FlipperGattCharacteristicParams) == 36, | ||||
|     "FlipperGattCharacteristicParams size must be 36 bytes"); | ||||
| 
 | ||||
| typedef struct { | ||||
|     const FlipperGattCharacteristicParams* characteristic; | ||||
|     uint16_t handle; | ||||
|     uint16_t descriptor_handle; | ||||
| } FlipperGattCharacteristicInstance; | ||||
| 
 | ||||
| // Initialize a characteristic instance; copies the characteristic descriptor into the instance
 | ||||
| void flipper_gatt_characteristic_init( | ||||
|     uint16_t svc_handle, | ||||
|     const FlipperGattCharacteristicParams* char_descriptor, | ||||
|     FlipperGattCharacteristicInstance* char_instance); | ||||
| 
 | ||||
| // Delete a characteristic instance; frees the copied characteristic descriptor from the instance
 | ||||
| void flipper_gatt_characteristic_delete( | ||||
|     uint16_t svc_handle, | ||||
|     FlipperGattCharacteristicInstance* char_instance); | ||||
| 
 | ||||
| // Update a characteristic instance; if source==NULL, uses the data from the characteristic
 | ||||
| //  - For fixed data, fixed.ptr is used as the source if source==NULL
 | ||||
| //  - For callback-based data, collback.context is passed as the context if source==NULL
 | ||||
| bool flipper_gatt_characteristic_update( | ||||
|     uint16_t svc_handle, | ||||
|     FlipperGattCharacteristicInstance* char_instance, | ||||
|     const void* source); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										293
									
								
								firmware/targets/f7/ble_glue/services/hid_service.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								firmware/targets/f7/ble_glue/services/hid_service.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,293 @@ | ||||
| #include "hid_service.h" | ||||
| #include "app_common.h" | ||||
| #include <ble/ble.h> | ||||
| #include "gatt_char.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define TAG "BtHid" | ||||
| 
 | ||||
| typedef enum { | ||||
|     HidSvcGattCharacteristicProtocolMode = 0, | ||||
|     HidSvcGattCharacteristicReportMap, | ||||
|     HidSvcGattCharacteristicInfo, | ||||
|     HidSvcGattCharacteristicCtrlPoint, | ||||
|     HidSvcGattCharacteristicCount, | ||||
| } HidSvcGattCharacteristicId; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t report_idx; | ||||
|     uint8_t report_type; | ||||
| } HidSvcReportId; | ||||
| 
 | ||||
| static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); | ||||
| 
 | ||||
| static bool | ||||
|     hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { | ||||
|     const HidSvcReportId* report_id = context; | ||||
|     *data_len = sizeof(HidSvcReportId); | ||||
|     if(data) { | ||||
|         *data = (const uint8_t*)report_id; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
|     const void* data_ptr; | ||||
|     uint16_t data_len; | ||||
| } HidSvcDataWrapper; | ||||
| 
 | ||||
| static bool | ||||
|     hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { | ||||
|     const HidSvcDataWrapper* report_data = context; | ||||
|     if(data) { | ||||
|         *data = report_data->data_ptr; | ||||
|         *data_len = report_data->data_len; | ||||
|     } else { | ||||
|         *data_len = HID_SVC_REPORT_MAP_MAX_LEN; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { | ||||
|     [HidSvcGattCharacteristicProtocolMode] = | ||||
|         {.name = "Protocol Mode", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = 1, | ||||
|          .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, | ||||
|          .uuid_type = UUID_TYPE_16, | ||||
|          .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, | ||||
|          .security_permissions = ATTR_PERMISSION_NONE, | ||||
|          .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|          .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|     [HidSvcGattCharacteristicReportMap] = | ||||
|         {.name = "Report Map", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataCallback, | ||||
|          .data.callback.fn = hid_svc_report_data_callback, | ||||
|          .data.callback.context = NULL, | ||||
|          .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, | ||||
|          .uuid_type = UUID_TYPE_16, | ||||
|          .char_properties = CHAR_PROP_READ, | ||||
|          .security_permissions = ATTR_PERMISSION_NONE, | ||||
|          .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|          .is_variable = CHAR_VALUE_LEN_VARIABLE}, | ||||
|     [HidSvcGattCharacteristicInfo] = | ||||
|         {.name = "HID Information", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = HID_SVC_INFO_LEN, | ||||
|          .data.fixed.ptr = NULL, | ||||
|          .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, | ||||
|          .uuid_type = UUID_TYPE_16, | ||||
|          .char_properties = CHAR_PROP_READ, | ||||
|          .security_permissions = ATTR_PERMISSION_NONE, | ||||
|          .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|          .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|     [HidSvcGattCharacteristicCtrlPoint] = | ||||
|         {.name = "HID Control Point", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, | ||||
|          .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, | ||||
|          .uuid_type = UUID_TYPE_16, | ||||
|          .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, | ||||
|          .security_permissions = ATTR_PERMISSION_NONE, | ||||
|          .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|          .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
| }; | ||||
| 
 | ||||
| static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { | ||||
|     .uuid_type = UUID_TYPE_16, | ||||
|     .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, | ||||
|     .max_length = HID_SVC_REPORT_REF_LEN, | ||||
|     .data_callback.fn = hid_svc_char_desc_data_callback, | ||||
|     .security_permissions = ATTR_PERMISSION_NONE, | ||||
|     .access_permissions = ATTR_ACCESS_READ_WRITE, | ||||
|     .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|     .is_variable = CHAR_VALUE_LEN_CONSTANT, | ||||
| }; | ||||
| 
 | ||||
| static const FlipperGattCharacteristicParams hid_svc_report_template = { | ||||
|     .name = "Report", | ||||
|     .data_prop_type = FlipperGattCharacteristicDataCallback, | ||||
|     .data.callback.fn = hid_svc_report_data_callback, | ||||
|     .data.callback.context = NULL, | ||||
|     .uuid.Char_UUID_16 = REPORT_CHAR_UUID, | ||||
|     .uuid_type = UUID_TYPE_16, | ||||
|     .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|     .security_permissions = ATTR_PERMISSION_NONE, | ||||
|     .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|     .is_variable = CHAR_VALUE_LEN_VARIABLE, | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t svc_handle; | ||||
|     FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; | ||||
|     FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; | ||||
|     FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; | ||||
|     FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; | ||||
| } HIDSvc; | ||||
| 
 | ||||
| static HIDSvc* hid_svc = NULL; | ||||
| 
 | ||||
| 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); | ||||
|     evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; | ||||
|     // aci_gatt_attribute_modified_event_rp0* attribute_modified;
 | ||||
|     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { | ||||
|         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||
|             // Process modification events
 | ||||
|             ret = SVCCTL_EvtAckFlowEnable; | ||||
|         } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { | ||||
|             // Process notification confirmation
 | ||||
|             ret = SVCCTL_EvtAckFlowEnable; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void hid_svc_start() { | ||||
|     tBleStatus status; | ||||
|     hid_svc = malloc(sizeof(HIDSvc)); | ||||
|     Service_UUID_t svc_uuid = {}; | ||||
| 
 | ||||
|     // Register event handler
 | ||||
|     SVCCTL_RegisterSvcHandler(hid_svc_event_handler); | ||||
|     // Add service
 | ||||
|     svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; | ||||
|     /**
 | ||||
|      *  Add Human Interface Device Service | ||||
|      */ | ||||
|     status = aci_gatt_add_service( | ||||
|         UUID_TYPE_16, | ||||
|         &svc_uuid, | ||||
|         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 */ | ||||
|         &hid_svc->svc_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add HID service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { | ||||
|         flipper_gatt_characteristic_init( | ||||
|             hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); | ||||
|     } | ||||
|     uint8_t protocol_mode = 1; | ||||
|     flipper_gatt_characteristic_update( | ||||
|         hid_svc->svc_handle, | ||||
|         &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], | ||||
|         &protocol_mode); | ||||
| 
 | ||||
|     // reports
 | ||||
|     FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; | ||||
|     FlipperGattCharacteristicParams report_char; | ||||
|     HidSvcReportId report_id; | ||||
| 
 | ||||
|     memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); | ||||
|     memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); | ||||
| 
 | ||||
|     hid_svc_char_descr.data_callback.context = &report_id; | ||||
|     report_char.descriptor_params = &hid_svc_char_descr; | ||||
| 
 | ||||
|     typedef struct { | ||||
|         uint8_t report_type; | ||||
|         uint8_t report_count; | ||||
|         FlipperGattCharacteristicInstance* chars; | ||||
|     } HidSvcReportCharProps; | ||||
| 
 | ||||
|     HidSvcReportCharProps hid_report_chars[] = { | ||||
|         {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, | ||||
|         {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, | ||||
|         {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, | ||||
|     }; | ||||
| 
 | ||||
|     for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); | ||||
|         report_type_idx++) { | ||||
|         report_id.report_type = hid_report_chars[report_type_idx].report_type; | ||||
|         for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; | ||||
|             report_idx++) { | ||||
|             report_id.report_idx = report_idx + 1; | ||||
|             flipper_gatt_characteristic_init( | ||||
|                 hid_svc->svc_handle, | ||||
|                 &report_char, | ||||
|                 &hid_report_chars[report_type_idx].chars[report_idx]); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { | ||||
|     furi_assert(data); | ||||
|     furi_assert(hid_svc); | ||||
| 
 | ||||
|     HidSvcDataWrapper report_data = { | ||||
|         .data_ptr = data, | ||||
|         .data_len = len, | ||||
|     }; | ||||
|     return flipper_gatt_characteristic_update( | ||||
|         hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { | ||||
|     furi_assert(data); | ||||
|     furi_assert(hid_svc); | ||||
|     furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); | ||||
| 
 | ||||
|     HidSvcDataWrapper report_data = { | ||||
|         .data_ptr = data, | ||||
|         .data_len = len, | ||||
|     }; | ||||
|     return flipper_gatt_characteristic_update( | ||||
|         hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_update_info(uint8_t* data) { | ||||
|     furi_assert(data); | ||||
|     furi_assert(hid_svc); | ||||
| 
 | ||||
|     return flipper_gatt_characteristic_update( | ||||
|         hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); | ||||
| } | ||||
| 
 | ||||
| bool hid_svc_is_started() { | ||||
|     return hid_svc != NULL; | ||||
| } | ||||
| 
 | ||||
| void hid_svc_stop() { | ||||
|     tBleStatus status; | ||||
|     if(hid_svc) { | ||||
|         // Delete characteristics
 | ||||
|         for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { | ||||
|             flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); | ||||
|         } | ||||
| 
 | ||||
|         typedef struct { | ||||
|             uint8_t report_count; | ||||
|             FlipperGattCharacteristicInstance* chars; | ||||
|         } HidSvcReportCharProps; | ||||
| 
 | ||||
|         HidSvcReportCharProps hid_report_chars[] = { | ||||
|             {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, | ||||
|             {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, | ||||
|             {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, | ||||
|         }; | ||||
| 
 | ||||
|         for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); | ||||
|             report_type_idx++) { | ||||
|             for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; | ||||
|                 report_idx++) { | ||||
|                 flipper_gatt_characteristic_delete( | ||||
|                     hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Delete service
 | ||||
|         status = aci_gatt_del_service(hid_svc->svc_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); | ||||
|         } | ||||
|         free(hid_svc); | ||||
|         hid_svc = NULL; | ||||
|     } | ||||
| } | ||||
| @ -25,4 +25,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); | ||||
| // Expects data to be of length HID_SVC_INFO_LEN (4 bytes)
 | ||||
| bool hid_svc_update_info(uint8_t* data); | ||||
| @ -1,17 +1,67 @@ | ||||
| #include "serial_service.h" | ||||
| #include "app_common.h" | ||||
| #include <ble/ble.h> | ||||
| #include "gatt_char.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #include "serial_service_uuid.inc" | ||||
| 
 | ||||
| #define TAG "BtSerialSvc" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SerialSvcGattCharacteristicTx = 0, | ||||
|     SerialSvcGattCharacteristicRx, | ||||
|     SerialSvcGattCharacteristicFlowCtrl, | ||||
|     SerialSvcGattCharacteristicStatus, | ||||
|     SerialSvcGattCharacteristicCount, | ||||
| } SerialSvcGattCharacteristicId; | ||||
| 
 | ||||
| static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { | ||||
|     [SerialSvcGattCharacteristicTx] = | ||||
|         {.name = "TX", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, | ||||
|          .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, | ||||
|          .uuid_type = UUID_TYPE_128, | ||||
|          .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, | ||||
|          .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|          .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|          .is_variable = CHAR_VALUE_LEN_VARIABLE}, | ||||
|     [SerialSvcGattCharacteristicRx] = | ||||
|         {.name = "RX", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, | ||||
|          .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, | ||||
|          .uuid_type = UUID_TYPE_128, | ||||
|          .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, | ||||
|          .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, | ||||
|          .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|          .is_variable = CHAR_VALUE_LEN_VARIABLE}, | ||||
|     [SerialSvcGattCharacteristicFlowCtrl] = | ||||
|         {.name = "Flow control", | ||||
|          .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|          .data.fixed.length = sizeof(uint32_t), | ||||
|          .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, | ||||
|          .uuid_type = UUID_TYPE_128, | ||||
|          .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|          .security_permissions = ATTR_PERMISSION_AUTHEN_READ, | ||||
|          .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, | ||||
|          .is_variable = CHAR_VALUE_LEN_CONSTANT}, | ||||
|     [SerialSvcGattCharacteristicStatus] = { | ||||
|         .name = "RPC status", | ||||
|         .data_prop_type = FlipperGattCharacteristicDataFixed, | ||||
|         .data.fixed.length = sizeof(SerialServiceRpcStatus), | ||||
|         .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, | ||||
|         .uuid_type = UUID_TYPE_128, | ||||
|         .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, | ||||
|         .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, | ||||
|         .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|         .is_variable = CHAR_VALUE_LEN_CONSTANT}}; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t svc_handle; | ||||
|     uint16_t rx_char_handle; | ||||
|     uint16_t tx_char_handle; | ||||
|     uint16_t flow_ctrl_char_handle; | ||||
|     uint16_t rpc_status_char_handle; | ||||
|     FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; | ||||
|     FuriMutex* buff_size_mtx; | ||||
|     uint32_t buff_size; | ||||
|     uint16_t bytes_ready_to_receive; | ||||
| @ -21,17 +71,6 @@ typedef struct { | ||||
| 
 | ||||
| static SerialSvc* serial_svc = NULL; | ||||
| 
 | ||||
| static const uint8_t service_uuid[] = | ||||
|     {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; | ||||
| static const uint8_t char_tx_uuid[] = | ||||
|     {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||
| static const uint8_t char_rx_uuid[] = | ||||
|     {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||
| static const uint8_t flow_ctrl_uuid[] = | ||||
|     {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||
| static const uint8_t rpc_status_uuid[] = | ||||
|     {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||
| 
 | ||||
| static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { | ||||
|     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; | ||||
|     hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); | ||||
| @ -40,11 +79,14 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { | ||||
|     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { | ||||
|         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||
|             attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; | ||||
|             if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { | ||||
|             if(attribute_modified->Attr_Handle == | ||||
|                serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { | ||||
|                 // Descriptor handle
 | ||||
|                 ret = SVCCTL_EvtAckFlowEnable; | ||||
|                 FURI_LOG_D(TAG, "RX descriptor event"); | ||||
|             } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { | ||||
|             } else if( | ||||
|                 attribute_modified->Attr_Handle == | ||||
|                 serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { | ||||
|                 FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||
|                 if(serial_svc->callback) { | ||||
|                     furi_check( | ||||
| @ -70,7 +112,9 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { | ||||
|                     furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); | ||||
|                 } | ||||
|                 ret = SVCCTL_EvtAckFlowEnable; | ||||
|             } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { | ||||
|             } else if( | ||||
|                 attribute_modified->Attr_Handle == | ||||
|                 serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { | ||||
|                 SerialServiceRpcStatus* rpc_status = | ||||
|                     (SerialServiceRpcStatus*)attribute_modified->Attr_Data; | ||||
|                 if(*rpc_status == SerialServiceRpcStatusNotActive) { | ||||
| @ -97,18 +141,12 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { | ||||
| } | ||||
| 
 | ||||
| static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { | ||||
|     tBleStatus ble_status = aci_gatt_update_char_value( | ||||
|         serial_svc->svc_handle, | ||||
|         serial_svc->rpc_status_char_handle, | ||||
|         0, | ||||
|         sizeof(SerialServiceRpcStatus), | ||||
|         (uint8_t*)&status); | ||||
|     if(ble_status) { | ||||
|         FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); | ||||
|     } | ||||
|     flipper_gatt_characteristic_update( | ||||
|         serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); | ||||
| } | ||||
| 
 | ||||
| void serial_svc_start() { | ||||
|     UNUSED(serial_svc_chars); | ||||
|     tBleStatus status; | ||||
|     serial_svc = malloc(sizeof(SerialSvc)); | ||||
|     // Register event handler
 | ||||
| @ -116,72 +154,17 @@ void serial_svc_start() { | ||||
| 
 | ||||
|     // Add service
 | ||||
|     status = aci_gatt_add_service( | ||||
|         UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); | ||||
|         UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // Add RX characteristics
 | ||||
|     status = aci_gatt_add_char( | ||||
|         serial_svc->svc_handle, | ||||
|         UUID_TYPE_128, | ||||
|         (const Char_UUID_t*)char_rx_uuid, | ||||
|         SERIAL_SVC_DATA_LEN_MAX, | ||||
|         CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, | ||||
|         ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, | ||||
|         GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_VARIABLE, | ||||
|         &serial_svc->rx_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); | ||||
|     // Add characteristics
 | ||||
|     for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { | ||||
|         flipper_gatt_characteristic_init( | ||||
|             serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); | ||||
|     } | ||||
| 
 | ||||
|     // Add TX characteristic
 | ||||
|     status = aci_gatt_add_char( | ||||
|         serial_svc->svc_handle, | ||||
|         UUID_TYPE_128, | ||||
|         (const Char_UUID_t*)char_tx_uuid, | ||||
|         SERIAL_SVC_DATA_LEN_MAX, | ||||
|         CHAR_PROP_READ | CHAR_PROP_INDICATE, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_VARIABLE, | ||||
|         &serial_svc->tx_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); | ||||
|     } | ||||
|     // Add Flow Control characteristic
 | ||||
|     status = aci_gatt_add_char( | ||||
|         serial_svc->svc_handle, | ||||
|         UUID_TYPE_128, | ||||
|         (const Char_UUID_t*)flow_ctrl_uuid, | ||||
|         sizeof(uint32_t), | ||||
|         CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||
|         ATTR_PERMISSION_AUTHEN_READ, | ||||
|         GATT_DONT_NOTIFY_EVENTS, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &serial_svc->flow_ctrl_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); | ||||
|     } | ||||
|     // Add RPC status characteristic
 | ||||
|     status = aci_gatt_add_char( | ||||
|         serial_svc->svc_handle, | ||||
|         UUID_TYPE_128, | ||||
|         (const Char_UUID_t*)rpc_status_uuid, | ||||
|         sizeof(SerialServiceRpcStatus), | ||||
|         CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, | ||||
|         ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, | ||||
|         GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|         10, | ||||
|         CHAR_VALUE_LEN_CONSTANT, | ||||
|         &serial_svc->rpc_status_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); | ||||
|     } | ||||
|     serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); | ||||
|     // Allocate buffer size mutex
 | ||||
|     serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); | ||||
| @ -196,13 +179,12 @@ void serial_svc_set_callbacks( | ||||
|     serial_svc->context = context; | ||||
|     serial_svc->buff_size = buff_size; | ||||
|     serial_svc->bytes_ready_to_receive = buff_size; | ||||
| 
 | ||||
|     uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); | ||||
|     aci_gatt_update_char_value( | ||||
|     flipper_gatt_characteristic_update( | ||||
|         serial_svc->svc_handle, | ||||
|         serial_svc->flow_ctrl_char_handle, | ||||
|         0, | ||||
|         sizeof(uint32_t), | ||||
|         (uint8_t*)&buff_size_reversed); | ||||
|         &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], | ||||
|         &buff_size_reversed); | ||||
| } | ||||
| 
 | ||||
| void serial_svc_notify_buffer_is_empty() { | ||||
| @ -213,13 +195,12 @@ void serial_svc_notify_buffer_is_empty() { | ||||
|     if(serial_svc->bytes_ready_to_receive == 0) { | ||||
|         FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); | ||||
|         serial_svc->bytes_ready_to_receive = serial_svc->buff_size; | ||||
| 
 | ||||
|         uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); | ||||
|         aci_gatt_update_char_value( | ||||
|         flipper_gatt_characteristic_update( | ||||
|             serial_svc->svc_handle, | ||||
|             serial_svc->flow_ctrl_char_handle, | ||||
|             0, | ||||
|             sizeof(uint32_t), | ||||
|             (uint8_t*)&buff_size_reversed); | ||||
|             &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], | ||||
|             &buff_size_reversed); | ||||
|     } | ||||
|     furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); | ||||
| } | ||||
| @ -227,22 +208,8 @@ void serial_svc_notify_buffer_is_empty() { | ||||
| void serial_svc_stop() { | ||||
|     tBleStatus status; | ||||
|     if(serial_svc) { | ||||
|         // Delete characteristics
 | ||||
|         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); | ||||
|         } | ||||
|         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); | ||||
|         } | ||||
|         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); | ||||
|         } | ||||
|         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); | ||||
|         if(status) { | ||||
|             FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); | ||||
|         for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { | ||||
|             flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); | ||||
|         } | ||||
|         // Delete service
 | ||||
|         status = aci_gatt_del_service(serial_svc->svc_handle); | ||||
| @ -273,7 +240,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { | ||||
|         tBleStatus result = aci_gatt_update_char_value_ext( | ||||
|             0, | ||||
|             serial_svc->svc_handle, | ||||
|             serial_svc->tx_char_handle, | ||||
|             serial_svc->chars[SerialSvcGattCharacteristicTx].handle, | ||||
|             remained ? 0x00 : 0x02, | ||||
|             data_len, | ||||
|             value_offset, | ||||
| @ -0,0 +1,12 @@ | ||||
| 
 | ||||
| static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ | ||||
|     { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; | ||||
| 
 | ||||
| #define SERIAL_SVC_TX_CHAR_UUID      \
 | ||||
|     { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } | ||||
| #define SERIAL_SVC_RX_CHAR_UUID      \
 | ||||
|     { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } | ||||
| #define SERIAL_SVC_FLOW_CONTROL_UUID \
 | ||||
|     { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } | ||||
| #define SERIAL_SVC_RPC_STATUS_UUID   \
 | ||||
|     { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } | ||||
| @ -8,8 +8,7 @@ | ||||
| #include <furi_hal_bt_hid.h> | ||||
| #include <furi_hal_bt_serial.h> | ||||
| #include <furi_hal_bus.c> | ||||
| #include "battery_service.h" | ||||
| 
 | ||||
| #include <services/battery_service.h> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define TAG "FuriHalBt" | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| #include <furi_hal_bt_hid.h> | ||||
| #include <furi_hal_usb_hid.h> | ||||
| #include "usb_hid.h" | ||||
| #include "dev_info_service.h" | ||||
| #include "battery_service.h" | ||||
| #include "hid_service.h" | ||||
| #include <services/dev_info_service.h> | ||||
| #include <services/battery_service.h> | ||||
| #include <services/hid_service.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <usb_hid.h> | ||||
| 
 | ||||
| #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) | ||||
| #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) | ||||
| @ -153,7 +153,7 @@ void furi_hal_bt_hid_start() { | ||||
|         FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | | ||||
|             FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, | ||||
|     }; | ||||
|     hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); | ||||
|     hid_svc_update_info(hid_info_val); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_bt_hid_stop() { | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include <furi_hal_bt_serial.h> | ||||
| #include "dev_info_service.h" | ||||
| #include "battery_service.h" | ||||
| #include "serial_service.h" | ||||
| #include <services/dev_info_service.h> | ||||
| #include <services/battery_service.h> | ||||
| #include <services/serial_service.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
| #include <furi.h> | ||||
| #include <stdbool.h> | ||||
| #include <gap.h> | ||||
| #include <serial_service.h> | ||||
| #include <services/serial_service.h> | ||||
| #include <ble_glue.h> | ||||
| #include <ble_app.h> | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "serial_service.h" | ||||
| #include <services/serial_service.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 hedger
						hedger