/** * @file app_cls.c * @brief CAIIC Link Service (CLS) application module implementation. * Derived from the SDK rdtss demo (app_rdtss.c), trimmed to a single * custom service with one write characteristic and one notify * characteristic. Single-packet notify API: the caller retries when * the profile reports busy. */ #include "rwip_config.h" // SW configuration #if (BLE_RDTSS_SERVER) /* Includes ------------------------------------------------------------------*/ #include #include "co_utils.h" #include "app_cls.h" // CLS Application Module Definitions #include "rdtss_task.h" // rdtss Profile Task Messages #include "gapm_task.h" // GAP Manager Task API #include "ns_ble.h" #include "prf.h" #include "rdtss.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private constants ---------------------------------------------------------*/ const uint8_t app_cls_svc_uuid[16] = ATT_SERVICE_CLS_128; const struct attm_desc_128 app_cls_att_db[CLS_IDX_NB] = { /* Service Declaration */ [CLS_IDX_SVC] = {{0x00,0x28}, PERM(RD, ENABLE), 0, 0 }, /* Characteristic Declaration */ [CLS_IDX_WRITE_CHAR] = {{0x03,0x28}, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0, 0 }, /* Characteristic Value */ [CLS_IDX_WRITE_VAL] = {ATT_CHAR_CLS_WRITE_128, PERM(WRITE_COMMAND, ENABLE), PERM(RI, ENABLE)| PERM_VAL(UUID_LEN, 0x02), 0x200 }, /* Client Characteristic Configuration Descriptor */ [CLS_IDX_WRITE_CFG] = {{0x01,0x29}, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), PERM(RI, ENABLE), 20 }, /* Characteristic Declaration */ [CLS_IDX_NTF_CHAR] = {{0x03,0x28}, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0, 0 }, /* Characteristic Value */ [CLS_IDX_NTF_VAL] = {ATT_CHAR_CLS_NTF_128, PERM(NTF, ENABLE), PERM(RI, ENABLE)| PERM_VAL(UUID_LEN, 0x02), 0x200 }, /* Client Characteristic Configuration Descriptor */ [CLS_IDX_NTF_CFG] = {{0x02,0x29}, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), PERM(RI, ENABLE), 20 }, }; /* Private variables ---------------------------------------------------------*/ static uint8_t s_cls_notify_en = 0; /* peer enabled notification on CCCD */ static uint8_t s_cls_ntf_busy = 0; /* one notification in flight */ static app_cls_rx_cb_t s_cls_rx_cb = NULL; /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ /** * @brief Create the CLS profile database (called via ns_ble_add_prf_func_register). */ void app_cls_add_cls(void) { struct rdtss_db_cfg *db_cfg; struct gapm_profile_task_add_cmd *req = KE_MSG_ALLOC_DYN(GAPM_PROFILE_TASK_ADD_CMD, TASK_GAPM, TASK_APP, gapm_profile_task_add_cmd, sizeof(struct rdtss_db_cfg)); // Fill message req->operation = GAPM_PROFILE_TASK_ADD; req->sec_lvl = PERM(SVC_AUTH, NO_AUTH); req->prf_task_id = TASK_ID_RDTSS; req->app_task = TASK_APP; req->start_hdl = 0; // Set parameters db_cfg = (struct rdtss_db_cfg *) req->param; // Attribute table. In case the handle offset needs to be saved db_cfg->att_tbl = &app_cls_att_db[0]; db_cfg->svc_uuid = &app_cls_svc_uuid[0]; db_cfg->max_nb_att = CLS_IDX_NB; // Send the message ke_msg_send(req); app_cls_init(); } /** * @brief Read request handler: answer with a fixed device string. */ static int cls_value_req_ind_handler(ke_msg_id_t const msgid, struct rdtss_value_req_ind const *req_value, ke_task_id_t const dest_id, ke_task_id_t const src_id) { // Initialize length uint8_t len = 0; // Pointer to the data const uint8_t *data = (const uint8_t *)"CAIIC"; len = 5; // Allocate confirmation to send the value struct rdtss_value_req_rsp *rsp_value = KE_MSG_ALLOC_DYN(RDTSS_VALUE_REQ_RSP, src_id, dest_id, rdtss_value_req_rsp, len); rsp_value->status = 0; rsp_value->conidx = req_value->conidx; rsp_value->length = len; rsp_value->att_idx = req_value->att_idx; if (len) { // Copy data memcpy(&rsp_value->value[0], data, len); } // Send message ke_msg_send(rsp_value); return (KE_MSG_CONSUMED); } /** * @brief Write indicate handler: CCCD updates and downlink data. */ static int cls_val_write_ind_handler(ke_msg_id_t const msgid, struct rdtss_val_write_ind const *ind_value, ke_task_id_t const dest_id, ke_task_id_t const src_id) { uint16_t handle = ind_value->handle; uint16_t length = ind_value->length; switch (handle) { case CLS_IDX_NTF_CFG: if (length == 2) { uint16_t cfg_value = ind_value->value[0] + ind_value->value[1]; if (cfg_value == PRF_CLI_START_NTF) { //enabled notify s_cls_notify_en = 1; } else if (cfg_value == PRF_CLI_STOP_NTFIND) { s_cls_notify_en = 0; } } break; case CLS_IDX_WRITE_VAL: //ble downlink data receive if (s_cls_rx_cb != NULL) { s_cls_rx_cb(ind_value->value, ind_value->length); } break; default: break; } return (KE_MSG_CONSUMED); } /** * @brief Notification confirm handler: release the busy flag. */ static int cls_val_ntf_cfm_handler(ke_msg_id_t const msgid, struct rdtss_val_ntf_cfm const *cfm_value, ke_task_id_t const dest_id, ke_task_id_t const src_id) { s_cls_ntf_busy = 0; return (KE_MSG_CONSUMED); } /** * @brief Send one notification packet; busy/not-enabled returns -1. */ int app_cls_send_notify(const uint8_t *data, uint16_t length) { if ((s_cls_notify_en == 0) || (s_cls_ntf_busy != 0)) { return -1; } struct rdtss_val_ntf_ind_req *req = KE_MSG_ALLOC_DYN(RDTSS_VAL_NTF_REQ, prf_get_task_from_id(TASK_ID_RDTSS), TASK_APP, rdtss_val_ntf_ind_req, length); req->conidx = app_env.conidx; req->notification = true; req->handle = CLS_IDX_NTF_VAL; req->length = length; memcpy(&req->value[0], data, length); ke_msg_send(req); s_cls_ntf_busy = 1; return 0; } void app_cls_set_rx_callback(app_cls_rx_cb_t cb) { s_cls_rx_cb = cb; } void app_cls_notify_state_reset(void) { s_cls_notify_en = 0; s_cls_ntf_busy = 0; } /// Default State handlers definition const struct ke_msg_handler app_cls_msg_handler_list[] = { {RDTSS_VALUE_REQ_IND, (ke_msg_func_t)cls_value_req_ind_handler}, {RDTSS_VAL_WRITE_IND, (ke_msg_func_t)cls_val_write_ind_handler}, {RDTSS_VAL_NTF_CFM, (ke_msg_func_t)cls_val_ntf_cfm_handler}, }; const struct app_subtask_handlers app_cls_handlers = APP_HANDLERS(app_cls); void app_cls_init(void) { //register application subtask to app task struct prf_task_t prf; prf.prf_task_id = TASK_ID_RDTSS; prf.prf_task_handler = &app_cls_handlers; ns_ble_prf_task_register(&prf); //register get itf function to prf.c struct prf_get_func_t get_func; get_func.task_id = TASK_ID_RDTSS; get_func.prf_itf_get_func = rdtss_prf_itf_get; prf_get_itf_func_register(&get_func); } #endif //BLE_RDTSS_SERVER