Compare commits
2 Commits
2854c45848
...
b3174c8572
Author | SHA1 | Date | |
---|---|---|---|
|
b3174c8572 | ||
|
ef0660c499 |
@ -6,10 +6,33 @@
|
|||||||
#include "wm_bt_util.h"
|
#include "wm_bt_util.h"
|
||||||
#include "bluetooth_sig_values.h"
|
#include "bluetooth_sig_values.h"
|
||||||
|
|
||||||
|
#define USABLE_DEFAULT_MTU (20) //23 - 3 of header bytes
|
||||||
|
|
||||||
|
/* ble service internal workings attributes */
|
||||||
|
static volatile ble_service_state_e _ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
||||||
|
static nus_data_rx_fn_t _ble_service_nus_data_rx_cb = NULL;
|
||||||
|
|
||||||
static volatile ble_service_state_t ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
|
||||||
/* Connection handle to the connected device : only one simultaneous connection */
|
/* Connection handle to the connected device : only one simultaneous connection */
|
||||||
static uint16_t ble_device_conn_handle = BLE_HS_CONN_HANDLE_NONE;
|
static uint16_t ble_device_conn_handle = BLE_HS_CONN_HANDLE_NONE;
|
||||||
|
static uint16_t usable_mtu = USABLE_DEFAULT_MTU;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure used to store the various data to carry out a chunked notification or indication data transfer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t length; // The length of the data being sent
|
||||||
|
uint16_t offset; // The offset used to be ableto send the next chunk of data in the array
|
||||||
|
uint16_t sent_chunk_size; // The size of the chunk sent
|
||||||
|
const uint8_t *data; // The address of the data to send
|
||||||
|
bool transfer_in_progress; // Is a transfer already in progress ?
|
||||||
|
} data_being_sent_t;
|
||||||
|
|
||||||
|
// Only one transfer of a type () can occur at any given time
|
||||||
|
static data_being_sent_t notification_data = {.data = NULL, .length = 0, .offset = 0, .transfer_in_progress = false};
|
||||||
|
static data_being_sent_t indication_data = {.data = NULL, .length = 0, .offset = 0, .transfer_in_progress = false};
|
||||||
|
|
||||||
static struct ble_gap_event_listener ble_gap_event_listener;
|
static struct ble_gap_event_listener ble_gap_event_listener;
|
||||||
|
|
||||||
static int battery_level_char_access_cb(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
static int battery_level_char_access_cb(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||||
@ -78,15 +101,38 @@ static bool ble_service_advertise(bool enable);
|
|||||||
static int ble_gap_event_cb(struct ble_gap_event *event, void *arg);
|
static int ble_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||||
static int ble_advertise_gap_event_cb(struct ble_gap_event *event, void *arg);
|
static int ble_advertise_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||||
static void print_conn_desc(const struct ble_gap_conn_desc *desc);
|
static void print_conn_desc(const struct ble_gap_conn_desc *desc);
|
||||||
|
// Raw because it doesn't handle payload fragmentation if mtu size is smaller than the payload size
|
||||||
|
static bool ble_service_send_raw_custom_notification(uint16_t characteristic_handle, const uint8_t *data, uint16_t length);
|
||||||
|
static bool ble_service_send_custom_notification(uint16_t characteristic_handle, data_being_sent_t * const data);
|
||||||
|
static void reset_data_being_sent(data_being_sent_t * const data);
|
||||||
|
|
||||||
// Needed to get the reponse after a mtu exchange request
|
// Needed to get the reponse after a mtu exchange request
|
||||||
static int ble_gatt_mtu_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t mtu, void *arg);
|
static int ble_gatt_mtu_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t mtu, void *arg);
|
||||||
|
|
||||||
|
const char *ble_service_state_2_str(ble_service_state_e state)
|
||||||
|
{
|
||||||
|
switch(state) {
|
||||||
|
CASE_RETURN_STR(BLE_SERVICE_MODE_STOPPED)
|
||||||
|
CASE_RETURN_STR(BLE_SERVICE_MODE_IDLE)
|
||||||
|
CASE_RETURN_STR(BLE_SERVICE_MODE_ADVERTISING)
|
||||||
|
CASE_RETURN_STR(BLE_SERVICE_MODE_CONNECTED)
|
||||||
|
CASE_RETURN_STR(BLE_SERVICE_MODE_INDICATING)
|
||||||
|
CASE_RETURN_STR(BLE_SERVICE_MODE_EXITING)
|
||||||
|
default:
|
||||||
|
return "unkown ble service state";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUBLIC FUNCTION DEFINITION
|
||||||
|
*/
|
||||||
|
|
||||||
bool ble_service_start(void)
|
bool ble_service_start(void)
|
||||||
{
|
{
|
||||||
int status = BLE_HS_ENOERR;
|
int status = BLE_HS_ENOERR;
|
||||||
|
|
||||||
// 1 We first check if the BLE service is stopped
|
// 1 We first check if the BLE service is stopped
|
||||||
if(ble_service_state != BLE_SERVICE_MODE_STOPPED)
|
if(_ble_service_state != BLE_SERVICE_MODE_STOPPED)
|
||||||
{
|
{
|
||||||
TLS_BT_APPL_TRACE_WARNING("%s, ble service already running"NEW_LINE, __FUNCTION__);
|
TLS_BT_APPL_TRACE_WARNING("%s, ble service already running"NEW_LINE, __FUNCTION__);
|
||||||
return true;
|
return true;
|
||||||
@ -137,7 +183,7 @@ bool ble_service_start(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ble_service_state = BLE_SERVICE_MODE_ADVERTISING;
|
_ble_service_state = BLE_SERVICE_MODE_ADVERTISING;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -147,7 +193,7 @@ bool ble_service_stop(void)
|
|||||||
int status = BLE_HS_ENOERR;
|
int status = BLE_HS_ENOERR;
|
||||||
|
|
||||||
// 1 We first check if the BLE service is not stopped
|
// 1 We first check if the BLE service is not stopped
|
||||||
if(ble_service_state == BLE_SERVICE_MODE_STOPPED)
|
if(_ble_service_state == BLE_SERVICE_MODE_STOPPED)
|
||||||
{
|
{
|
||||||
TLS_BT_APPL_TRACE_WARNING("%s, ble service already stopped"NEW_LINE, __FUNCTION__);
|
TLS_BT_APPL_TRACE_WARNING("%s, ble service already stopped"NEW_LINE, __FUNCTION__);
|
||||||
return true;
|
return true;
|
||||||
@ -160,19 +206,19 @@ bool ble_service_stop(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(ble_service_state)
|
switch(_ble_service_state)
|
||||||
{
|
{
|
||||||
case BLE_SERVICE_MODE_CONNECTED:
|
case BLE_SERVICE_MODE_CONNECTED:
|
||||||
case BLE_SERVICE_MODE_INDICATING:
|
case BLE_SERVICE_MODE_INDICATING:
|
||||||
status = ble_gap_terminate(ble_device_conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
status = ble_gap_terminate(ble_device_conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||||
if(status == BLE_HS_ENOERR)
|
if(status == BLE_HS_ENOERR)
|
||||||
{
|
{
|
||||||
ble_service_state = BLE_SERVICE_MODE_EXITING;
|
_ble_service_state = BLE_SERVICE_MODE_EXITING;
|
||||||
}
|
}
|
||||||
else //BLE_HS_EDISABLED || BLE_HS_ENOTCONN or any other error
|
else //BLE_HS_EDISABLED || BLE_HS_ENOTCONN or any other error
|
||||||
{
|
{
|
||||||
TLS_BT_APPL_TRACE_WARNING("%s, ble_gap_terminate %s"NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
TLS_BT_APPL_TRACE_WARNING("%s, ble_gap_terminate %s"NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
||||||
ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
_ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
||||||
//Unregister gap event listener
|
//Unregister gap event listener
|
||||||
if((status = ble_gap_event_listener_unregister(&ble_gap_event_listener)) != BLE_HS_ENOERR)
|
if((status = ble_gap_event_listener_unregister(&ble_gap_event_listener)) != BLE_HS_ENOERR)
|
||||||
{
|
{
|
||||||
@ -187,7 +233,7 @@ bool ble_service_stop(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
_ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
||||||
//Unregister gap event listener
|
//Unregister gap event listener
|
||||||
if((status = ble_gap_event_listener_unregister(&ble_gap_event_listener)) != BLE_HS_ENOERR)
|
if((status = ble_gap_event_listener_unregister(&ble_gap_event_listener)) != BLE_HS_ENOERR)
|
||||||
{
|
{
|
||||||
@ -199,7 +245,7 @@ bool ble_service_stop(void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ble_service_state == BLE_SERVICE_MODE_STOPPED)
|
if(_ble_service_state == BLE_SERVICE_MODE_STOPPED)
|
||||||
{
|
{
|
||||||
// We finally clean the gatt registered services
|
// We finally clean the gatt registered services
|
||||||
if((status = ble_gatts_reset()) != BLE_HS_ENOERR)
|
if((status = ble_gatts_reset()) != BLE_HS_ENOERR)
|
||||||
@ -212,6 +258,21 @@ bool ble_service_stop(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ble_service_is_started(void)
|
||||||
|
{
|
||||||
|
return _ble_service_state != BLE_SERVICE_MODE_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_service_is_device_connected(void)
|
||||||
|
{
|
||||||
|
return _ble_service_state == BLE_SERVICE_MODE_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_service_state_e ble_service_get_state(void)
|
||||||
|
{
|
||||||
|
return _ble_service_state;
|
||||||
|
}
|
||||||
|
|
||||||
bool ble_service_update_connection_parameters(
|
bool ble_service_update_connection_parameters(
|
||||||
uint16_t itvl_min,
|
uint16_t itvl_min,
|
||||||
uint16_t itvl_max,
|
uint16_t itvl_max,
|
||||||
@ -245,7 +306,51 @@ bool ble_service_update_connection_parameters(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ble_service_send_custom_notification(const uint8_t *data, uint16_t length)
|
bool ble_service_request_mtu_exchange(void)
|
||||||
|
{
|
||||||
|
if(BLE_HS_CONN_HANDLE_NONE == ble_device_conn_handle)
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_ERROR("%s, no active connection" NEW_LINE, __FUNCTION__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = BLE_HS_ENOERR;
|
||||||
|
|
||||||
|
if((status = ble_gattc_exchange_mtu(ble_device_conn_handle, &(ble_gatt_mtu_cb), NULL)) != BLE_HS_ENOERR)
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_ERROR("%s, ble_gattc_exchange_mtu %s" NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_service_nus_send_data(const uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
// The NUS is TX is using notification
|
||||||
|
if(notification_data.transfer_in_progress)
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_WARNING("%s, a transfer is already in progress"NEW_LINE, __FUNCTION__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification_data.transfer_in_progress = true;
|
||||||
|
notification_data.data = data;
|
||||||
|
notification_data.length = length;
|
||||||
|
|
||||||
|
return ble_service_send_custom_notification(gatt_nus_char_tx_handle, ¬ification_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_service_nus_register_data_rx_cb(nus_data_rx_fn_t nus_data_rx_cb)
|
||||||
|
{
|
||||||
|
_ble_service_nus_data_rx_cb = nus_data_rx_cb;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* PRIVATE FUNCTION DEFINITION
|
||||||
|
* Used for the internal workings of the service
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool ble_service_send_raw_custom_notification(uint16_t characteristic_handle, const uint8_t *data, uint16_t length)
|
||||||
{
|
{
|
||||||
if(BLE_HS_CONN_HANDLE_NONE == ble_device_conn_handle)
|
if(BLE_HS_CONN_HANDLE_NONE == ble_device_conn_handle)
|
||||||
{
|
{
|
||||||
@ -268,7 +373,7 @@ bool ble_service_send_custom_notification(const uint8_t *data, uint16_t length)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((status = ble_gattc_notify_custom(ble_device_conn_handle, gatt_nus_char_tx_handle, om_buf)) != BLE_HS_ENOERR)
|
if((status = ble_gattc_notify_custom(ble_device_conn_handle, characteristic_handle, om_buf)) != BLE_HS_ENOERR)
|
||||||
{
|
{
|
||||||
TLS_BT_APPL_TRACE_ERROR("%s, ble_hs_mbuf_from_flat %s" NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
TLS_BT_APPL_TRACE_ERROR("%s, ble_hs_mbuf_from_flat %s" NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
||||||
return false;
|
return false;
|
||||||
@ -277,19 +382,22 @@ bool ble_service_send_custom_notification(const uint8_t *data, uint16_t length)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ble_service_request_mtu_exchange(void)
|
static bool ble_service_send_custom_notification(uint16_t characteristic_handle, data_being_sent_t * const notif_data)
|
||||||
{
|
{
|
||||||
if(BLE_HS_CONN_HANDLE_NONE == ble_device_conn_handle)
|
if(!notif_data)
|
||||||
{
|
{
|
||||||
TLS_BT_APPL_TRACE_ERROR("%s, no active connection" NEW_LINE, __FUNCTION__);
|
TLS_BT_APPL_TRACE_WARNING("%s, notif_data is NULL"NEW_LINE, __FUNCTION__);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = BLE_HS_ENOERR;
|
// We compute the maximum size of the data we can send:
|
||||||
|
uint16_t remaining_data_to_send = notif_data->length - notif_data->offset;
|
||||||
|
notif_data->sent_chunk_size = remaining_data_to_send <= usable_mtu ? remaining_data_to_send : usable_mtu;
|
||||||
|
|
||||||
if((status = ble_gattc_exchange_mtu(ble_device_conn_handle, &(ble_gatt_mtu_cb), NULL)) != BLE_HS_ENOERR)
|
if(!ble_service_send_raw_custom_notification(characteristic_handle, ¬if_data->data[notif_data->offset], notif_data->sent_chunk_size))
|
||||||
{
|
{
|
||||||
TLS_BT_APPL_TRACE_ERROR("%s, ble_gattc_exchange_mtu %s" NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
//Transfer failed :
|
||||||
|
reset_data_being_sent(notif_data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,6 +413,7 @@ int ble_gatt_mtu_cb(uint16_t conn_handle, const struct ble_gatt_error *error, ui
|
|||||||
case 0:
|
case 0:
|
||||||
TLS_BT_APPL_TRACE_DEBUG("mtu exchange complete: conn_handle=%d mtu=%d"NEW_LINE,
|
TLS_BT_APPL_TRACE_DEBUG("mtu exchange complete: conn_handle=%d mtu=%d"NEW_LINE,
|
||||||
conn_handle, mtu);
|
conn_handle, mtu);
|
||||||
|
usable_mtu = mtu - 3;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TLS_BT_APPL_TRACE_ERROR("Update MTU failed...error->status=%d"NEW_LINE, error->status);
|
TLS_BT_APPL_TRACE_ERROR("Update MTU failed...error->status=%d"NEW_LINE, error->status);
|
||||||
@ -314,20 +423,6 @@ int ble_gatt_mtu_cb(uint16_t conn_handle, const struct ble_gatt_error *error, ui
|
|||||||
return BLE_HS_ENOERR;
|
return BLE_HS_ENOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *ble_service_state_2_str(uint8_t state)
|
|
||||||
{
|
|
||||||
switch(state) {
|
|
||||||
CASE_RETURN_STR(BLE_SERVICE_MODE_STOPPED)
|
|
||||||
CASE_RETURN_STR(BLE_SERVICE_MODE_IDLE)
|
|
||||||
CASE_RETURN_STR(BLE_SERVICE_MODE_ADVERTISING)
|
|
||||||
CASE_RETURN_STR(BLE_SERVICE_MODE_CONNECTED)
|
|
||||||
CASE_RETURN_STR(BLE_SERVICE_MODE_INDICATING)
|
|
||||||
CASE_RETURN_STR(BLE_SERVICE_MODE_EXITING)
|
|
||||||
default:
|
|
||||||
return "unkown ble service state";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ble_service_define_gatt(const struct ble_gatt_svc_def *gatt_svc)
|
static bool ble_service_define_gatt(const struct ble_gatt_svc_def *gatt_svc)
|
||||||
{
|
{
|
||||||
int status = BLE_HS_ENOERR;
|
int status = BLE_HS_ENOERR;
|
||||||
@ -459,7 +554,8 @@ static int ble_gap_event_cb(struct ble_gap_event *event, void *arg)
|
|||||||
print_conn_desc(&desc);
|
print_conn_desc(&desc);
|
||||||
if(desc.role == BLE_GAP_ROLE_SLAVE)
|
if(desc.role == BLE_GAP_ROLE_SLAVE)
|
||||||
{
|
{
|
||||||
ble_service_state = BLE_SERVICE_MODE_CONNECTED;
|
_ble_service_state = BLE_SERVICE_MODE_CONNECTED;
|
||||||
|
usable_mtu = USABLE_DEFAULT_MTU;
|
||||||
ble_device_conn_handle = event->connect.conn_handle;
|
ble_device_conn_handle = event->connect.conn_handle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -483,24 +579,24 @@ static int ble_gap_event_cb(struct ble_gap_event *event, void *arg)
|
|||||||
TLS_BT_APPL_TRACE_VERBOSE("Remote device failed to connect, advertise again"NEW_LINE);
|
TLS_BT_APPL_TRACE_VERBOSE("Remote device failed to connect, advertise again"NEW_LINE);
|
||||||
if(!ble_service_advertise(true))
|
if(!ble_service_advertise(true))
|
||||||
{
|
{
|
||||||
ble_service_state = BLE_SERVICE_MODE_IDLE;
|
_ble_service_state = BLE_SERVICE_MODE_IDLE;
|
||||||
return BLE_HS_EUNKNOWN;
|
return BLE_HS_EUNKNOWN;
|
||||||
}
|
}
|
||||||
ble_service_state = BLE_SERVICE_MODE_ADVERTISING;
|
_ble_service_state = BLE_SERVICE_MODE_ADVERTISING;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BLE_GAP_EVENT_DISCONNECT:
|
case BLE_GAP_EVENT_DISCONNECT:
|
||||||
if(event->disconnect.conn.role != BLE_GAP_ROLE_SLAVE) return 0;
|
if(event->disconnect.conn.role != BLE_GAP_ROLE_SLAVE) return 0;
|
||||||
|
|
||||||
TLS_BT_APPL_TRACE_DEBUG("Server disconnect reason=%d[0x%02x],state=%s"NEW_LINE, event->disconnect.reason,event->disconnect.reason-0x200,
|
TLS_BT_APPL_TRACE_DEBUG("Server disconnect reason=%d[0x%02x],state=%s"NEW_LINE, event->disconnect.reason,event->disconnect.reason-0x200,
|
||||||
ble_service_state_2_str(ble_service_state));
|
ble_service_state_2_str(_ble_service_state));
|
||||||
|
|
||||||
// Don't forget to invalidate the connection handle :
|
// Don't forget to invalidate the connection handle :
|
||||||
ble_device_conn_handle = BLE_HS_CONN_HANDLE_NONE;
|
ble_device_conn_handle = BLE_HS_CONN_HANDLE_NONE;
|
||||||
|
|
||||||
if(ble_service_state == BLE_SERVICE_MODE_EXITING)
|
if(_ble_service_state == BLE_SERVICE_MODE_EXITING)
|
||||||
{
|
{
|
||||||
ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
_ble_service_state = BLE_SERVICE_MODE_STOPPED;
|
||||||
if((status = ble_gap_event_listener_unregister(&ble_gap_event_listener)) != BLE_HS_ENOERR)
|
if((status = ble_gap_event_listener_unregister(&ble_gap_event_listener)) != BLE_HS_ENOERR)
|
||||||
{
|
{
|
||||||
TLS_BT_APPL_TRACE_WARNING("%s, ble_gap_event_listener_unregister %s"NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
TLS_BT_APPL_TRACE_WARNING("%s, ble_gap_event_listener_unregister %s"NEW_LINE, __FUNCTION__, tls_bt_rc_2_str(status));
|
||||||
@ -518,10 +614,10 @@ static int ble_gap_event_cb(struct ble_gap_event *event, void *arg)
|
|||||||
TLS_BT_APPL_TRACE_VERBOSE("Service disconnect event, advertise again"NEW_LINE);
|
TLS_BT_APPL_TRACE_VERBOSE("Service disconnect event, advertise again"NEW_LINE);
|
||||||
if(!ble_service_advertise(true))
|
if(!ble_service_advertise(true))
|
||||||
{
|
{
|
||||||
ble_service_state = BLE_SERVICE_MODE_IDLE;
|
_ble_service_state = BLE_SERVICE_MODE_IDLE;
|
||||||
return BLE_HS_EUNKNOWN;
|
return BLE_HS_EUNKNOWN;
|
||||||
}
|
}
|
||||||
ble_service_state = BLE_SERVICE_MODE_ADVERTISING;
|
_ble_service_state = BLE_SERVICE_MODE_ADVERTISING;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||||
@ -550,6 +646,47 @@ static int ble_gap_event_cb(struct ble_gap_event *event, void *arg)
|
|||||||
break;
|
break;
|
||||||
case BLE_GAP_EVENT_MTU:
|
case BLE_GAP_EVENT_MTU:
|
||||||
TLS_BT_APPL_TRACE_VERBOSE("MTU update : %u"NEW_LINE, event->mtu.value);
|
TLS_BT_APPL_TRACE_VERBOSE("MTU update : %u"NEW_LINE, event->mtu.value);
|
||||||
|
usable_mtu = event->mtu.value - 3;
|
||||||
|
break;
|
||||||
|
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||||
|
if(event->notify_tx.indication == 0) // Notification
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_VERBOSE("Type : notification"NEW_LINE);
|
||||||
|
if(event->notify_tx.status != BLE_HS_ENOERR)
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_WARNING("%s, notify_tx notification error %d, transfer in progress : %u"NEW_LINE, __FUNCTION__, event->notify_tx.status, notification_data.transfer_in_progress);
|
||||||
|
reset_data_being_sent(¬ification_data);
|
||||||
|
return BLE_HS_EUNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
TLS_BT_APPL_TRACE_DEBUG("transfer : %u"NEW_LINE"length : %u"NEW_LINE"offset : %u"NEW_LINE,
|
||||||
|
notification_data.transfer_in_progress,
|
||||||
|
notification_data.length,
|
||||||
|
notification_data.offset);
|
||||||
|
|
||||||
|
// We update the offset :
|
||||||
|
notification_data.offset += notification_data.sent_chunk_size;
|
||||||
|
|
||||||
|
if(notification_data.offset < notification_data.length) // Still data to send ?
|
||||||
|
{
|
||||||
|
if(!ble_service_send_custom_notification(event->notify_tx.attr_handle, ¬ification_data))
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_ERROR("Failed to send next notification chunk, aborting"NEW_LINE);
|
||||||
|
reset_data_being_sent(¬ification_data);
|
||||||
|
return BLE_HS_EUNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_VERBOSE("last data chunk sent, end of the transfer"NEW_LINE);
|
||||||
|
// All data has been sent, end of the transfer
|
||||||
|
reset_data_being_sent(¬ification_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Indication
|
||||||
|
{
|
||||||
|
TLS_BT_APPL_TRACE_WARNING("Indication not yet handled"NEW_LINE);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TLS_BT_APPL_TRACE_WARNING("unhandled event !"NEW_LINE);
|
TLS_BT_APPL_TRACE_WARNING("unhandled event !"NEW_LINE);
|
||||||
@ -595,16 +732,21 @@ static int gatt_nus_char_access_cb(uint16_t conn_handle, uint16_t attr_handle, s
|
|||||||
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
||||||
{
|
{
|
||||||
struct os_mbuf *om_buf = ctxt->om;
|
struct os_mbuf *om_buf = ctxt->om;
|
||||||
TLS_BT_APPL_TRACE_VERBOSE("Received data : "NEW_LINE);
|
//TLS_BT_APPL_TRACE_VERBOSE("Received data : "NEW_LINE);
|
||||||
while(om_buf)
|
while(om_buf)
|
||||||
{
|
{
|
||||||
for(uint16_t i = 0; i < om_buf->om_len; i++)
|
/*for(uint16_t i = 0; i < om_buf->om_len; i++)
|
||||||
{
|
{
|
||||||
if(om_buf->om_data[i] < 32)
|
if(om_buf->om_data[i] < 32)
|
||||||
printf("[%u]", om_buf->om_data[i]);
|
printf("[%u]", om_buf->om_data[i]);
|
||||||
else
|
else
|
||||||
putchar(om_buf->om_data[i]);
|
putchar(om_buf->om_data[i]);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
// Call the nus rx cb function if one is registered
|
||||||
|
if(_ble_service_nus_data_rx_cb)
|
||||||
|
_ble_service_nus_data_rx_cb(om_buf->om_data, om_buf->om_len);
|
||||||
|
|
||||||
om_buf = SLIST_NEXT(om_buf, om_next);
|
om_buf = SLIST_NEXT(om_buf, om_next);
|
||||||
}
|
}
|
||||||
printf(NEW_LINE);
|
printf(NEW_LINE);
|
||||||
@ -631,3 +773,11 @@ static void print_conn_desc(const struct ble_gap_conn_desc *desc)
|
|||||||
desc->sec_state.authenticated,
|
desc->sec_state.authenticated,
|
||||||
desc->sec_state.bonded);
|
desc->sec_state.bonded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reset_data_being_sent(data_being_sent_t * const data)
|
||||||
|
{
|
||||||
|
if(data)
|
||||||
|
{
|
||||||
|
memset(data, 0, sizeof(data_being_sent_t));
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@
|
|||||||
#define CASE_RETURN_STR(const) case const: return #const;
|
#define CASE_RETURN_STR(const) case const: return #const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef void (*nus_data_rx_fn_t)(const uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
BLE_SERVICE_MODE_STOPPED = 0x00,
|
BLE_SERVICE_MODE_STOPPED = 0x00,
|
||||||
@ -15,7 +17,7 @@ typedef enum
|
|||||||
BLE_SERVICE_MODE_CONNECTED,
|
BLE_SERVICE_MODE_CONNECTED,
|
||||||
BLE_SERVICE_MODE_INDICATING,
|
BLE_SERVICE_MODE_INDICATING,
|
||||||
BLE_SERVICE_MODE_EXITING
|
BLE_SERVICE_MODE_EXITING
|
||||||
} ble_service_state_t;
|
} ble_service_state_e;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Resturns the corresponding enum name as a string
|
* @brief Resturns the corresponding enum name as a string
|
||||||
@ -23,7 +25,7 @@ typedef enum
|
|||||||
* @param state the enum value
|
* @param state the enum value
|
||||||
* @return const char* the enum name as a string
|
* @return const char* the enum name as a string
|
||||||
*/
|
*/
|
||||||
const char *ble_service_state_2_str(uint8_t state);
|
const char *ble_service_state_2_str(ble_service_state_e state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configures and starts the BLE service
|
* @brief Configures and starts the BLE service
|
||||||
@ -41,6 +43,29 @@ bool ble_service_start(void);
|
|||||||
*/
|
*/
|
||||||
bool ble_service_stop(void);
|
bool ble_service_stop(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the ble service is running or not
|
||||||
|
*
|
||||||
|
* @return true if it is running
|
||||||
|
* @return false if it is stopped
|
||||||
|
*/
|
||||||
|
bool ble_service_is_started(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether a device is connected to the ble service or not
|
||||||
|
*
|
||||||
|
* @return true if a device is connected
|
||||||
|
* @return false if no device is connected
|
||||||
|
*/
|
||||||
|
bool ble_service_is_device_connected(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the current state of the ble service
|
||||||
|
*
|
||||||
|
* @return ble_service_state_e
|
||||||
|
*/
|
||||||
|
ble_service_state_e ble_service_get_state(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Asks to update the current connection parameters
|
* @brief Asks to update the current connection parameters
|
||||||
* /!\ A connection should be already active before calling this function.
|
* /!\ A connection should be already active before calling this function.
|
||||||
@ -62,16 +87,6 @@ bool ble_service_update_connection_parameters(
|
|||||||
uint16_t min_ce_len,
|
uint16_t min_ce_len,
|
||||||
uint16_t max_ce_len);
|
uint16_t max_ce_len);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sends a custom notification with the provided payload of size length
|
|
||||||
*
|
|
||||||
* @param data the data to send in the notification
|
|
||||||
* @param length the lenght in byte of the data to send
|
|
||||||
* @return true on success
|
|
||||||
* @return false on failure
|
|
||||||
*/
|
|
||||||
bool ble_service_send_custom_notification(const uint8_t *data, uint16_t length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Requests a MTU (Maximum Transmission Unit) update in order to hopefully, get something bigger than 20 bytes ...
|
* @brief Requests a MTU (Maximum Transmission Unit) update in order to hopefully, get something bigger than 20 bytes ...
|
||||||
*
|
*
|
||||||
@ -80,4 +95,22 @@ bool ble_service_send_custom_notification(const uint8_t *data, uint16_t length);
|
|||||||
*/
|
*/
|
||||||
bool ble_service_request_mtu_exchange(void);
|
bool ble_service_request_mtu_exchange(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends the provided payload of size length using the NUS (Nordic UART Service) TX characteristic
|
||||||
|
*
|
||||||
|
* @param data the data to send through the NUS
|
||||||
|
* @param length the lenght in byte of the data to send
|
||||||
|
* @return true on success
|
||||||
|
* @return false on failure
|
||||||
|
*/
|
||||||
|
bool ble_service_nus_send_data(const uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers a function which will be called every time data is received by the nus rx
|
||||||
|
* @note To unregister a callback, simply pass NULL to the function
|
||||||
|
*
|
||||||
|
* @param nus_data_rx_cb a pointer to the function to call of type nus_data_rx_fn_t
|
||||||
|
*/
|
||||||
|
void ble_service_nus_register_data_rx_cb(nus_data_rx_fn_t nus_data_rx_cb);
|
||||||
|
|
||||||
#endif //BLE_APP_H
|
#endif //BLE_APP_H
|
@ -22,14 +22,14 @@ extern int demo_bt_destroy();
|
|||||||
extern int demo_ble_server_on();
|
extern int demo_ble_server_on();
|
||||||
extern int demo_ble_server_off();
|
extern int demo_ble_server_off();
|
||||||
|
|
||||||
void tls_wifi_client_event_cb(u8 *mac, enum tls_wifi_client_event_type event)
|
static void tls_wifi_client_event_cb(u8 *mac, enum tls_wifi_client_event_type event)
|
||||||
{
|
{
|
||||||
struct tls_sta_info_t *mac_addr = (struct tls_sta_info_t *)mac;
|
struct tls_sta_info_t *mac_addr = (struct tls_sta_info_t *)mac;
|
||||||
|
|
||||||
shell_printf("Client event(%d), MAC : %M"NEW_LINE, event, mac_addr);
|
shell_printf("Client event(%d), MAC : %M"NEW_LINE, event, mac_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wifi_scan_result_cb(void)
|
static void wifi_scan_result_cb(void)
|
||||||
{
|
{
|
||||||
u16 buffer_size = sizeof(struct tls_scan_bss_t) + sizeof(struct tls_bss_info_t) * 20;
|
u16 buffer_size = sizeof(struct tls_scan_bss_t) + sizeof(struct tls_bss_info_t) * 20;
|
||||||
u8 *buf = tls_mem_alloc(buffer_size);
|
u8 *buf = tls_mem_alloc(buffer_size);
|
||||||
@ -67,7 +67,7 @@ void wifi_scan_result_cb(void)
|
|||||||
tls_wifi_scan_result_cb_register(NULL);
|
tls_wifi_scan_result_cb_register(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tls_wifi_data_ext_recv_cb(u8* data, u32 data_len, struct tls_wifi_ext_t *ext)
|
static void tls_wifi_data_ext_recv_cb(u8* data, u32 data_len, struct tls_wifi_ext_t *ext)
|
||||||
{
|
{
|
||||||
shell_printf("recv packet :"NEW_LINE"rssi : %d\nrate : %u"NEW_LINE, (s8)ext->rssi, ext->rx_rate);
|
shell_printf("recv packet :"NEW_LINE"rssi : %d\nrate : %u"NEW_LINE, (s8)ext->rssi, ext->rx_rate);
|
||||||
for(u32 i = 0; i < data_len; i++)
|
for(u32 i = 0; i < data_len; i++)
|
||||||
@ -79,7 +79,7 @@ void tls_wifi_data_ext_recv_cb(u8* data, u32 data_len, struct tls_wifi_ext_t *ex
|
|||||||
shell_printf(NEW_LINE);
|
shell_printf(NEW_LINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tls_rtc_irq_cb(void *arg)
|
static void tls_rtc_irq_cb(void *arg)
|
||||||
{
|
{
|
||||||
struct tm rtc_time;
|
struct tm rtc_time;
|
||||||
tls_get_rtc(&rtc_time);
|
tls_get_rtc(&rtc_time);
|
||||||
@ -95,6 +95,21 @@ void tls_rtc_irq_cb(void *arg)
|
|||||||
tls_rtc_timer_stop();
|
tls_rtc_timer_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nus_data_rx_cb(const uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
shell_printf("%s, received data : ", __FUNCTION__);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (data[i] < 32)
|
||||||
|
shell_printf("[%u]", data[i]);
|
||||||
|
else
|
||||||
|
shell_putc(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_puts(NEW_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
int _system(const shell_cmd_t *pcmd, int argc, char *const argv[])
|
int _system(const shell_cmd_t *pcmd, int argc, char *const argv[])
|
||||||
{
|
{
|
||||||
if(argc > 1)
|
if(argc > 1)
|
||||||
@ -513,25 +528,62 @@ int _bluetooth(const shell_cmd_t *pcmd, int argc, char *const argv[])
|
|||||||
if(strcmp(argv[1], "send_ble_notif") == 0)
|
if(strcmp(argv[1], "send_ble_notif") == 0)
|
||||||
{
|
{
|
||||||
char cmd[200] = "";
|
char cmd[200] = "";
|
||||||
|
bool found = false;
|
||||||
if(strcmp(argv[2], "toast") == 0)
|
if(strcmp(argv[2], "toast") == 0)
|
||||||
{
|
{
|
||||||
sprintf(cmd, "{\"t\":\"info\",\"msg\":\"%s\"} \n", argv[3]);
|
sprintf(cmd, "{\"t\":\"info\",\"msg\":\"%s\"} \n", argv[3]);
|
||||||
shell_printf("Sending ble ntf with content : #%s# -> %s"NEW_LINE, cmd, ble_service_send_custom_notification((const uint8_t *)cmd, strlen(cmd)) ? "success" : "failure");
|
found = true;
|
||||||
}
|
}
|
||||||
else if(strcmp(argv[2], "bat") == 0)
|
else if(strcmp(argv[2], "bat") == 0)
|
||||||
{
|
{
|
||||||
sprintf(cmd, "{\"t\":\"status\",\"bat\":%s} \n", argv[3]);
|
sprintf(cmd, "{\"t\":\"status\",\"bat\":%s,\"chg\":%s,\"volt\":%s} \n", argv[3], argv[4], argv[5]);
|
||||||
shell_printf("Sending ble ntf with content : #%s# -> %s"NEW_LINE, cmd, ble_service_send_custom_notification((const uint8_t *)cmd, strlen(cmd)) ? "success" : "failure");
|
found = true;
|
||||||
}
|
}
|
||||||
else if(strcmp(argv[2], "findPhone") == 0)
|
else if(strcmp(argv[2], "findPhone") == 0)
|
||||||
{
|
{
|
||||||
char cmd[] = "{\"t\":\"findPhone\",\"n\":true} \n";
|
strcpy(cmd, "{\"t\":\"findPhone\",\"n\":true} \n");
|
||||||
shell_printf("Sending ble ntf with content : #%s# -> %s"NEW_LINE, cmd, ble_service_send_custom_notification((const uint8_t *)cmd, strlen(cmd)) ? "success" : "failure");
|
found = true;
|
||||||
}
|
}
|
||||||
|
else if(strcmp(argv[2], "music") == 0)
|
||||||
|
{
|
||||||
|
sprintf(cmd, "{\"t\":\"music\",\"n\":\"%s\"} \n", argv[3]);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
else if(strcmp(argv[2], "notify") == 0)
|
||||||
|
{
|
||||||
|
sprintf(cmd, "{\"t\":\"notify\",\"n\":\"%s\",\"id\":%s,\"tel\":\"%s\",\"msg\":\"%s\"} \n", argv[3], argv[4], argv[5], argv[6]);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(found)
|
||||||
|
{
|
||||||
|
shell_printf("Sending ble ntf with content : #%s# -> %s"NEW_LINE, cmd, ble_service_nus_send_data((const uint8_t *)cmd, strlen(cmd)) ? "success" : "failure");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shell_printf("Unknown %s action, list of send_ble_notif actions :"NEW_LINE"toast \"msg\""NEW_LINE"bat \"%%\""NEW_LINE"findPhone"NEW_LINE"music play|pause|previous|next"NEW_LINE"notify reply|dismiss_all id \"tel\" \"msg\""NEW_LINE, argv[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(argv[1], "up_conn_param") == 0)
|
||||||
|
{
|
||||||
|
if(argc != 8)
|
||||||
|
{
|
||||||
|
shell_printf("6 arguments expected"NEW_LINE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//First we need to retrieve every parameters from the shell
|
||||||
|
uint16_t itvl_min = strtoul(argv[2], NULL, 10);
|
||||||
|
uint16_t itvl_max = strtoul(argv[3], NULL, 10);
|
||||||
|
uint16_t latency = strtoul(argv[4], NULL, 10);
|
||||||
|
uint16_t supervision_timeout = strtoul(argv[5], NULL, 10);
|
||||||
|
uint16_t min_ce_len = strtoul(argv[6], NULL, 10);
|
||||||
|
uint16_t max_ce_len = strtoul(argv[7], NULL, 10);
|
||||||
|
|
||||||
|
shell_printf("BLE connection update request : %d"NEW_LINE, ble_service_update_connection_parameters(itvl_min, itvl_max, latency, supervision_timeout, min_ce_len, max_ce_len));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
shell_printf("Unknown %s action"NEW_LINE"List of send_ble_notif actions :"NEW_LINE"toast \"msg\""NEW_LINE"bat \"%%\""NEW_LINE"findPhone", argv[0]);
|
shell_printf("Unknown %s action"NEW_LINE, argv[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(argc > 1)
|
else if(argc > 1)
|
||||||
@ -546,11 +598,17 @@ int _bluetooth(const shell_cmd_t *pcmd, int argc, char *const argv[])
|
|||||||
}
|
}
|
||||||
else if(strcmp(argv[1], "start_demo") == 0)
|
else if(strcmp(argv[1], "start_demo") == 0)
|
||||||
{
|
{
|
||||||
shell_printf("Starting demo : %d"NEW_LINE"Use a BLE app to find the device"NEW_LINE, ble_service_start() /*demo_ble_server_on()*/);
|
bool result = ble_service_start();
|
||||||
|
shell_printf("Starting demo : %d"NEW_LINE"Use a BLE app to find the device"NEW_LINE, result /*demo_ble_server_on()*/);
|
||||||
|
if(result)
|
||||||
|
{
|
||||||
|
ble_service_nus_register_data_rx_cb(&(nus_data_rx_cb));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(strcmp(argv[1], "stop_demo") == 0)
|
else if(strcmp(argv[1], "stop_demo") == 0)
|
||||||
{
|
{
|
||||||
shell_printf("Stopping demo : %d"NEW_LINE, ble_service_stop() /*demo_ble_server_off()*/);
|
shell_printf("Stopping demo : %d"NEW_LINE, ble_service_stop() /*demo_ble_server_off()*/);
|
||||||
|
ble_service_nus_register_data_rx_cb(NULL);
|
||||||
}
|
}
|
||||||
else if(strcmp(argv[1], "mtu_exch") == 0)
|
else if(strcmp(argv[1], "mtu_exch") == 0)
|
||||||
{
|
{
|
||||||
@ -563,7 +621,8 @@ int _bluetooth(const shell_cmd_t *pcmd, int argc, char *const argv[])
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
shell_printf("List of bluetooth actions :"NEW_LINE"enable"NEW_LINE"disable"NEW_LINE"start_demo"NEW_LINE"stop_demo"NEW_LINE"send_ble_notif toast \"msg\"|bat \"%%\"|findPhone"NEW_LINE"mtu_exch"NEW_LINE);
|
shell_printf("List of bluetooth actions :"NEW_LINE"enable"NEW_LINE"disable"NEW_LINE"start_demo"NEW_LINE"stop_demo"NEW_LINE"send_ble_notif toast \"msg\"|bat \"%%\"|findPhone|music|notify"NEW_LINE"mtu_exch"NEW_LINE
|
||||||
|
"up_conn_param itvl_min itvl_max latency supervision_timeout min_ce_len max_ce_len"NEW_LINE);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,7 @@
|
|||||||
#define NANO_SHELL_INTERFACE_H
|
#define NANO_SHELL_INTERFACE_H
|
||||||
|
|
||||||
extern int shell_printf(const char *format, ...);
|
extern int shell_printf(const char *format, ...);
|
||||||
|
extern void shell_puts(const char *str);
|
||||||
|
extern void shell_putc(char ch);
|
||||||
|
|
||||||
#endif //NANO_SHELL_INTERFACE_H
|
#endif //NANO_SHELL_INTERFACE_H
|
Loading…
Reference in New Issue
Block a user