Skip to content
Snippets Groups Projects
epic_ble_api.c 4.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "ble_api.h"
    
    #include "epicardium.h"
    #include "modules/log.h"
    
    #include "modules/modules.h"
    
    
    #include "wsf_buf.h"
    
    #include "app_api.h"
    
    #include "svc_core.h"
    
    
    #include "FreeRTOS.h"
    #include "queue.h"
    
    #include <stdint.h>
    
    #include <string.h>
    
    
    #define BLE_EVENT_QUEUE_SIZE 10
    
    static QueueHandle_t ble_event_queue;
    static uint8_t ble_event_queue_buffer
    	[sizeof(struct epic_ble_event) * BLE_EVENT_QUEUE_SIZE];
    static StaticQueue_t ble_event_queue_data;
    
    
    static uint8_t adv_data_buf[HCI_ADV_DATA_LEN];
    static uint8_t sr_data_buf[HCI_ADV_DATA_LEN];
    
    static dmEvt_t connection_open_event;
    
    int epic_ble_free_event(struct epic_ble_event *e)
    {
    	if (e->data) {
    		if (e->type == BLE_EVENT_ATT_WRITE) {
    			ble_epic_att_api_free_att_write_data(e->att_write);
    		} else {
    			// Generic free
    			WsfBufFree(e->data);
    		}
    	}
    	return 0;
    }
    
    void ble_epic_ble_api_trigger_event(enum epic_ble_event_type type, void *data)
    {
    	bool enabled;
    	epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled);
    
    	struct epic_ble_event e = { .type = type, .data = data };
    
    
    	if (type == BLE_EVENT_DM_EVENT) {
    		dmEvt_t *dm_event = data;
    		if (dm_event->hdr.event == DM_CONN_OPEN_IND) {
    			connection_open = true;
    		}
    
    		if (dm_event->hdr.event == DM_CONN_CLOSE_IND) {
    			connection_open = false;
    		}
    	}
    
    	if (!connection_open &&
    	    (type == BLE_EVENT_ATT_EVENT || type == BLE_EVENT_DM_EVENT)) {
    		// Don't forward DM and ATT events until epicardium is done setting up
    		// the connection
    		epic_ble_free_event(&e);
    		return;
    	}
    
    
    	if (enabled) {
    		if (xQueueSend(ble_event_queue, &e, 0) != pdTRUE) {
    			/* Print a warning if the app is missing events. Missing scan results
    			* is considered OK though, as they are queued and periodic. */
    			if (type != BLE_EVENT_SCAN_REPORT) {
    				LOG_WARN(
    					"ble",
    					"Application missed event %u",
    					type
    				);
    			}
    			epic_ble_free_event(&e);
    
    		interrupt_trigger(EPIC_INT_BLE);
    
    		epic_ble_free_event(&e);
    	}
    }
    
    int epic_ble_get_event(struct epic_ble_event *e)
    {
    	if (xQueueReceive(ble_event_queue, e, 0) != pdTRUE) {
    		return -ENOENT;
    	}
    	return uxQueueMessagesWaiting(ble_event_queue);
    }
    
    void ble_epic_ble_api_init(void)
    {
    	ble_event_queue = xQueueCreateStatic(
    		BLE_EVENT_QUEUE_SIZE,
    		sizeof(struct epic_ble_event),
    		ble_event_queue_buffer,
    		&ble_event_queue_data
    	);
    
    	ble_epic_att_api_init();
    }
    
    static void send_dm_event(dmEvt_t *dm_event)
    
    {
    	dmEvt_t *e = WsfBufAlloc(sizeof(*e));
    
    	if (e) {
    		memcpy(e, dm_event, sizeof(*e));
    		ble_epic_ble_api_trigger_event(BLE_EVENT_DM_EVENT, e);
    	} else {
    		LOG_WARN("ble", "could not allocate dm event");
    	}
    }
    
    void ble_epic_dm_api_event(dmEvt_t *dm_event)
    {
    	if (dm_event->hdr.event == DM_CONN_OPEN_IND) {
    		/* Cache the connection open indication until
    		 * epicardium is done dicovering services. */
    		memcpy(&connection_open_event,
    		       dm_event,
    		       sizeof(connection_open_event));
    	} else {
    		send_dm_event(dm_event);
    	}
    }
    
    void ble_epic_disc_cfg_complete(void)
    {
    	send_dm_event(&connection_open_event);
    }
    
    
    void epic_ble_close_connection(uint8_t connId)
    {
    	AppConnClose(connId);
    }
    
    int epic_ble_is_connection_open(void)
    {
    	return AppConnIsOpen();
    }
    
    
    int epic_ble_set_device_name(const uint8_t *buf, uint16_t len)
    {
    	return epic_ble_atts_set_attr(GAP_DN_HDL, buf, len);
    }
    
    int epic_ble_get_device_name(uint8_t **buf, uint16_t *len)
    {
    	uint8_t ret = AttsGetAttr(GAP_DN_HDL, len, buf);
    	return ret;
    }
    
    
    int epic_ble_advertise(
    	int interval_us,
    	const uint8_t *adv_data,
    	size_t adv_data_len,
    	const uint8_t *sr_data,
    	size_t sr_data_len,
    	bool connectable
    ) {
    	if (adv_data_len > sizeof(adv_data_buf)) {
    		adv_data_len = sizeof(adv_data_buf);
    	}
    
    	if (sr_data_len > sizeof(sr_data_buf)) {
    		sr_data_len = sizeof(sr_data_buf);
    	}
    
    	memcpy(adv_data_buf, adv_data, adv_data_len);
    	memcpy(sr_data_buf, sr_data, sr_data_len);
    
    
    	ble_adv_set_interval(interval_us);
    
    
    	if (connectable) {
    		AppAdvSetData(
    			APP_ADV_DATA_CONNECTABLE, adv_data_len, adv_data_buf
    		);
    		AppAdvSetData(
    			APP_SCAN_DATA_CONNECTABLE, sr_data_len, sr_data_buf
    		);
    		ble_adv_start(APP_MODE_CONNECTABLE);
    	} else {
    		AppAdvSetData(
    			APP_ADV_DATA_DISCOVERABLE, adv_data_len, adv_data_buf
    		);
    		AppAdvSetData(
    			APP_SCAN_DATA_DISCOVERABLE, sr_data_len, sr_data_buf
    		);
    		ble_adv_start(APP_MODE_DISCOVERABLE);
    	}
    	return 0;
    }
    
    
    int epic_ble_advertise_stop(void)
    {
    	ble_adv_stop();
    	return 0;
    }