/* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. */ /********************************************************************* * INCLUDES */ #include #include #include "string.h" #include "config.h" #include "ancs.h" #include "ancs_client.h" #include "ancs_client_internal.h" #include "ancs_attr_parser.h" #include "ancs_app_attr_get.h" /********************************************************************* * MACROS */ /**< Index of the Event ID field when parsing notifications. */ #define BT_ANCS_NOTIF_EVT_ID_INDEX 0 /**< Index of the Flags field when parsing notifications. */ #define BT_ANCS_NOTIF_FLAGS_INDEX 1 /**< Index of the Category ID field when parsing notifications. */ #define BT_ANCS_NOTIF_CATEGORY_ID_INDEX 2 /**< Index of the Category Count field when parsing notifications. */ #define BT_ANCS_NOTIF_CATEGORY_CNT_INDEX 3 /**< Index of the Notification UID field when parsing notifications. */ #define BT_ANCS_NOTIF_NOTIF_UID 4 /********************************************************************* * CONSTANTS */ /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ /********************************************************************* * LOCAL FUNCTIONS */ /**@brief Function for checking whether the data in an iOS notification is out of bounds. * * @param[in] notif An iOS notification. * * @retval 0 If the notification is within bounds. * @retval -1 If the notification is out of bounds. */ static int bt_ancs_verify_notification_format(const struct bt_ancs_evt_notif *notif) { if ((notif->evt_id >= BT_ANCS_EVT_ID_COUNT) || (notif->category_id >= BT_ANCS_CATEGORY_ID_COUNT)) { return -1; } return 0; } /**@brief Function for receiving and validating notifications received from * the Notification Provider. * * @param[in] ancs_c Pointer to an ANCS instance to which the event belongs. * @param[in] data_src Pointer to the data that was received from the Notification Provider. * @param[in] len Length of the data that was received from the Notification Provider. */ static void parse_notif(struct bt_ancs_client *ancs_c, const uint8_t *data_src, const uint16_t len) { int err; struct bt_ancs_evt_notif notif; if (len != BT_ANCS_NOTIF_DATA_LENGTH) { bt_ancs_do_ns_notif_cb(ancs_c, -EINVAL, ¬if); return; } notif.evt_id = (enum bt_ancs_evt_id_values) data_src[BT_ANCS_NOTIF_EVT_ID_INDEX]; notif.evt_flags.silent = (data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >> BT_ANCS_EVENT_FLAG_SILENT) & 0x01; notif.evt_flags.important = (data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >> BT_ANCS_EVENT_FLAG_IMPORTANT) & 0x01; notif.evt_flags.pre_existing = (data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >> BT_ANCS_EVENT_FLAG_PREEXISTING) & 0x01; notif.evt_flags.positive_action = (data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >> BT_ANCS_EVENT_FLAG_POSITIVE_ACTION) & 0x01; notif.evt_flags.negative_action = (data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >> BT_ANCS_EVENT_FLAG_NEGATIVE_ACTION) & 0x01; notif.category_id = (enum bt_ancs_category_id_val) data_src[BT_ANCS_NOTIF_CATEGORY_ID_INDEX]; notif.category_count = data_src[BT_ANCS_NOTIF_CATEGORY_CNT_INDEX]; notif.notif_uid = sys_get_le32(&data_src[BT_ANCS_NOTIF_NOTIF_UID]); err = bt_ancs_verify_notification_format(¬if); bt_ancs_do_ns_notif_cb(ancs_c, err, ¬if); } int bt_ancs_client_init(struct bt_ancs_client *ancs_c) { if (!ancs_c) { return -EINVAL; } memset(ancs_c, 0, sizeof(struct bt_ancs_client)); return 0; } void ancs_reinit(struct bt_ancs_client *ancs_c) { ancs_c->conn = 0; ancs_c->handle_cp = 0; ancs_c->handle_ns = 0; ancs_c->handle_ns_ccc = 0; ancs_c->handle_ds = 0; ancs_c->handle_ds_ccc = 0; ancs_c->state = 0; } int bt_ancs_subscribe_notification_source(struct bt_ancs_client *ancs_c, bt_ancs_ns_notif_cb func) { int err; if (!ancs_c || !func) { return -EINVAL; } if (atomic_test_and_set_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED)) { return -EALREADY; } ancs_c->ns_notif_cb = func; ancs_c->ns_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0); if(!ancs_c->ns_notif_params.pValue){ return -ENOBUFS; } ancs_c->ns_notif_params.len = 2; ancs_c->ns_notif_params.pValue[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY); ancs_c->ns_notif_params.pValue[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY); ancs_c->ns_notif_params.handle = ancs_c->handle_ns_ccc; ancs_c->ns_notif_params.cmd = 0; ancs_c->ns_notif_params.sig = 0; err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ns_notif_params, ancs_taskid); if (err) { atomic_clear_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED); GATT_bm_free((gattMsg_t *)&ancs_c->ns_notif_params, ATT_WRITE_REQ); PRINT("Subscribe Notification Source failed (err %d)\n", err); } else { PRINT("Notification Source subscribed\n"); } return err; } int bt_ancs_subscribe_data_source(struct bt_ancs_client *ancs_c, bt_ancs_ds_notif_cb func) { int err; if (!ancs_c || !func) { return -EINVAL; } if (atomic_test_and_set_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED)) { return -EALREADY; } ancs_c->ds_notif_cb = func; ancs_c->ds_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0); if(!ancs_c->ds_notif_params.pValue){ return -ENOBUFS; } ancs_c->ds_notif_params.len = 2; ancs_c->ds_notif_params.pValue[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY); ancs_c->ds_notif_params.pValue[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY); ancs_c->ds_notif_params.handle = ancs_c->handle_ds_ccc; ancs_c->ds_notif_params.cmd = 0; ancs_c->ds_notif_params.sig = 0; err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ds_notif_params, ancs_taskid); if (err) { atomic_clear_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED); GATT_bm_free((gattMsg_t *)&ancs_c->ds_notif_params, ATT_WRITE_REQ); PRINT("Subscribe Notification Source failed (err %d)\n", err); } else { PRINT("Data Source subscribed\n"); } return err; } int bt_ancs_unsubscribe_notification_source(struct bt_ancs_client *ancs_c) { int err; if (!ancs_c) { return -EINVAL; } if (!atomic_test_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED)) { return -EFAULT; } ancs_c->ns_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0); if(!ancs_c->ns_notif_params.pValue){ return -ENOBUFS; } ancs_c->ns_notif_params.len = 2; ancs_c->ns_notif_params.pValue[0] = 0; ancs_c->ns_notif_params.pValue[1] = 0; ancs_c->ns_notif_params.handle = ancs_c->handle_ds; ancs_c->ns_notif_params.cmd = 0; ancs_c->ns_notif_params.sig = 0; err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ns_notif_params, ancs_taskid); if (err) { PRINT("Unsubscribe Notification Source failed (err %d)\n", err); GATT_bm_free((gattMsg_t *)&ancs_c->ns_notif_params, ATT_WRITE_REQ); } else { atomic_clear_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED); PRINT("Notification Source unsubscribed\n"); } return err; } int bt_ancs_unsubscribe_data_source(struct bt_ancs_client *ancs_c) { int err; if (!ancs_c) { return -EINVAL; } if (!atomic_test_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED)) { return -EFAULT; } ancs_c->ds_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0); if(!ancs_c->ds_notif_params.pValue){ return -ENOBUFS; } ancs_c->ds_notif_params.len = 2; ancs_c->ds_notif_params.pValue[0] = 0; ancs_c->ds_notif_params.pValue[1] = 0; ancs_c->ds_notif_params.handle = ancs_c->handle_ns; ancs_c->ds_notif_params.cmd = 0; ancs_c->ds_notif_params.sig = 0; err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ds_notif_params, ancs_taskid); if (err) { PRINT("Unsubscribe Data Source failed (err %d)\n", err); GATT_bm_free((gattMsg_t *)&ancs_c->ds_notif_params, ATT_WRITE_REQ); } else { atomic_clear_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED); PRINT("Data Source unsubscribed\n"); } return err; } static uint16_t encode_notif_action(uint8_t *encoded_data, uint32_t uid, enum bt_ancs_action_id_values action_id) { uint8_t index = 0; encoded_data[index++] = BT_ANCS_COMMAND_ID_PERFORM_NOTIF_ACTION; sys_put_le32(uid, &encoded_data[index]); index += sizeof(uint32_t); encoded_data[index++] = (uint8_t)action_id; return index; } int bt_ancs_cp_write(struct bt_ancs_client *ancs_c, uint16_t len, bt_ancs_write_cb func) { int err; attWriteReq_t *write_params = &ancs_c->cp_write_params;; write_params->pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, len, NULL, 0); if(!write_params->pValue){ return -ENOBUFS; } ancs_c->cp_write_cb = func; write_params->handle = ancs_c->handle_cp; write_params->len = len; tmos_memcpy(write_params->pValue, ancs_c->cp_data, len); write_params->sig = 0; write_params->cmd = 0; err = GATT_WriteCharValue(ancs_c->conn, write_params, ancs_taskid); if(err) { atomic_clear_bit(&ancs_c->state, ANCS_CP_WRITE_PENDING); GATT_bm_free((gattMsg_t *)write_params, ATT_WRITE_REQ); PRINT("write error: %#x\n", err); } return err; } int bt_ancs_notification_action(struct bt_ancs_client *ancs_c, uint32_t uuid, enum bt_ancs_action_id_values action_id, bt_ancs_write_cb func) { if (atomic_test_and_set_bit(&ancs_c->state, ANCS_CP_WRITE_PENDING)) { return -EBUSY; } uint8_t *data = ancs_c->cp_data; uint16_t len = encode_notif_action(data, uuid, action_id); return bt_ancs_cp_write(ancs_c, len, func); } static int bt_ancs_get_notif_attrs(struct bt_ancs_client *ancs_c, const uint32_t uid, bt_ancs_write_cb func) { if (atomic_test_and_set_bit(&ancs_c->state, ANCS_CP_WRITE_PENDING)) { return -EBUSY; } uint32_t index = 0; uint8_t *data = ancs_c->cp_data; ancs_c->number_of_requested_attr = 0; /* Encode Command ID. */ *(data + index++) = BT_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES; /* Encode Notification UID. */ sys_put_le32(uid, data + index); index += sizeof(uint32_t); /* Encode Attribute ID. */ for(uint32_t attr = 0; attr < BT_ANCS_NOTIF_ATTR_COUNT; attr++) { if (ancs_c->ancs_notif_attr_list[attr].get) { *(data + index++) = (uint8_t)attr; if ((attr == BT_ANCS_NOTIF_ATTR_ID_TITLE) || (attr == BT_ANCS_NOTIF_ATTR_ID_SUBTITLE) || (attr == BT_ANCS_NOTIF_ATTR_ID_MESSAGE)) { /* Encode Length field. Only applicable for * Title, Subtitle, and Message. */ sys_put_le16(ancs_c->ancs_notif_attr_list[attr] .attr_len, data + index); index += sizeof(uint16_t); } ancs_c->number_of_requested_attr++; } } ancs_c->parse_info.expected_number_of_attrs = ancs_c->number_of_requested_attr; return bt_ancs_cp_write(ancs_c, index, func); } int bt_ancs_request_attrs(struct bt_ancs_client *ancs_c, const struct bt_ancs_evt_notif *notif, bt_ancs_write_cb func) { int err; err = bt_ancs_verify_notification_format(notif); if (err) { return err; } ancs_c->parse_info.parse_state = BT_ANCS_PARSE_STATE_COMMAND_ID; return bt_ancs_get_notif_attrs(ancs_c, notif->notif_uid, func); } int bt_ancs_register_attr(struct bt_ancs_client *ancs_c, const enum bt_ancs_notif_attr_id_val id, uint8_t *data, const uint16_t len) { if (!ancs_c || !data) { return -EINVAL; } if (!len || len > BT_ANCS_ATTR_DATA_MAX) { return -EINVAL; } if ((size_t)id >= BT_ANCS_NOTIF_ATTR_COUNT) { return -EINVAL; } ancs_c->ancs_notif_attr_list[id].get = true; ancs_c->ancs_notif_attr_list[id].attr_len = len; ancs_c->ancs_notif_attr_list[id].attr_data = data; return 0; } int bt_ancs_register_app_attr(struct bt_ancs_client *ancs_c, const enum bt_ancs_app_attr_id_val id, uint8_t *data, const uint16_t len) { if (!ancs_c || !data) { return -EINVAL; } if (!len || len > BT_ANCS_ATTR_DATA_MAX) { return -EINVAL; } if ((size_t)id >= BT_ANCS_APP_ATTR_COUNT) { return -EINVAL; } ancs_c->ancs_app_attr_list[id].get = true; ancs_c->ancs_app_attr_list[id].attr_len = len; ancs_c->ancs_app_attr_list[id].attr_data = data; return 0; } int bt_ancs_request_app_attr(struct bt_ancs_client *ancs_c, const uint8_t *app_id, uint32_t len, bt_ancs_write_cb func) { return bt_ancs_app_attr_request(ancs_c, app_id, len, func); } /********************************************************************* * @fn Ancs_handleNotification * * @brief Handle notifications. * * @param pMsg - GATT message. * * @return none */ void Ancs_handleNotification(gattMsgEvent_t *pMsg) { static uint8_t importantAlertCnt = 0; uint8_t i; // Look up the handle in the handle cache for (i = 0; i < HDL_CACHE_LEN; i++) { if (pMsg->msg.handleValueNoti.handle == Ancs_handleCache[i]) { break; } } // Perform processing for this handle switch (i) { case HDL_ANCS_NTF_NOTIF_START: { parse_notif(&ancs_c, pMsg->msg.handleValueNoti.pValue, pMsg->msg.handleValueNoti.len); } break; case HDL_ANCS_DATA_SRC_START: { bt_ancs_parse_get_attrs_response(&ancs_c, pMsg->msg.handleValueNoti.pValue, pMsg->msg.handleValueNoti.len); } break; default: break; } } /********************************************************************* *********************************************************************/