pinebuds/services/ble_profiles/lan/lanc/src/lanc.c

687 lines
21 KiB
C
Raw Normal View History

2022-08-15 04:20:27 -05:00
/**
****************************************************************************************
* @addtogroup LANC
* @{
****************************************************************************************
*/
/*
* INCLUDE FILES
****************************************************************************************
*/
#include "rwip_config.h"
#if (BLE_LN_COLLECTOR)
#include "lan_common.h"
#include "lanc.h"
#include "lanc_task.h"
#include "ke_timer.h"
#include "ke_mem.h"
#include "co_utils.h"
/*
* GLOBAL VARIABLES DECLARATION
****************************************************************************************
*/
/*
* LOCAL FUNCTION DEFINITIONS
****************************************************************************************
*/
/**
****************************************************************************************
* @brief Initialization of the LANC module.
* This function performs all the initializations of the Profile module.
* - Creation of database (if it's a service)
* - Allocation of profile required memory
* - Initialization of task descriptor to register application
* - Task State array
* - Number of tasks
* - Default task handler
*
* @param[out] env Collector or Service allocated environment data.
* @param[in|out] start_hdl Service start handle (0 - dynamically allocated), only applies for services.
* @param[in] app_task Application task number.
* @param[in] sec_lvl Security level (AUTH, EKS and MI field of @see enum attm_value_perm_mask)
* @param[in] param Configuration parameters of profile collector or service (32 bits aligned)
*
* @return status code to know if profile initialization succeed or not.
****************************************************************************************
*/
static uint8_t lanc_init(struct prf_task_env* env, uint16_t* start_hdl, uint16_t app_task, uint8_t sec_lvl, void* params)
{
uint8_t idx;
//-------------------- allocate memory required for the profile ---------------------
struct lanc_env_tag* lanc_env =
(struct lanc_env_tag* ) ke_malloc(sizeof(struct lanc_env_tag), KE_MEM_ATT_DB);
// allocate LANC required environment variable
env->env = (prf_env_t*) lanc_env;
lanc_env->prf_env.app_task = app_task
| (PERM_GET(sec_lvl, SVC_MI) ? PERM(PRF_MI, ENABLE) : PERM(PRF_MI, DISABLE));
lanc_env->prf_env.prf_task = env->task | PERM(PRF_MI, ENABLE);
// initialize environment variable
env->id = TASK_ID_LANC;
lanc_task_init(&(env->desc));
for(idx = 0; idx < LANC_IDX_MAX ; idx++)
{
lanc_env->env[idx] = NULL;
// service is ready, go into an Idle state
ke_state_set(KE_BUILD_ID(env->task, idx), LANC_FREE);
}
return GAP_ERR_NO_ERROR;
}
/**
****************************************************************************************
* @brief Clean-up connection dedicated environment parameters
* This function performs cleanup of ongoing operations
* @param[in|out] env Collector or Service allocated environment data.
* @param[in] conidx Connection index
* @param[in] reason Detach reason
****************************************************************************************
*/
static void lanc_cleanup(struct prf_task_env* env, uint8_t conidx, uint8_t reason)
{
struct lanc_env_tag* lanc_env = (struct lanc_env_tag*) env->env;
// clean-up environment variable allocated for task instance
if(lanc_env->env[conidx] != NULL)
{
if (lanc_env->env[conidx]->operation != NULL)
{
ke_free(ke_param2msg(lanc_env->env[conidx]->operation));
}
ke_timer_clear(LANC_TIMEOUT_TIMER_IND, prf_src_task_get(&lanc_env->prf_env, conidx));
ke_free(lanc_env->env[conidx]);
lanc_env->env[conidx] = NULL;
}
/* Put LAN Client in Free state */
ke_state_set(KE_BUILD_ID(env->task, conidx), LANC_FREE);
}
/**
****************************************************************************************
* @brief Destruction of the LANC module - due to a reset for instance.
* This function clean-up allocated memory (attribute database is destroyed by another
* procedure)
*
* @param[in|out] env Collector or Service allocated environment data.
****************************************************************************************
*/
static void lanc_destroy(struct prf_task_env* env)
{
uint8_t idx;
struct lanc_env_tag* lanc_env = (struct lanc_env_tag*) env->env;
// cleanup environment variable for each task instances
for(idx = 0; idx < LANC_IDX_MAX ; idx++)
{
lanc_cleanup(env, idx, 0);
}
// free profile environment variables
env->env = NULL;
ke_free(lanc_env);
}
/**
****************************************************************************************
* @brief Handles Connection creation
*
* @param[in|out] env Collector or Service allocated environment data.
* @param[in] conidx Connection index
****************************************************************************************
*/
static void lanc_create(struct prf_task_env* env, uint8_t conidx)
{
/* Put LAN Client in Idle state */
ke_state_set(KE_BUILD_ID(env->task, conidx), LANC_IDLE);
}
/// LANC Task interface required by profile manager
const struct prf_task_cbs lanc_itf =
{
lanc_init,
lanc_destroy,
lanc_create,
lanc_cleanup,
};
/*
* GLOBAL FUNCTIONS DEFINITIONS
****************************************************************************************
*/
const struct prf_task_cbs* lanc_prf_itf_get(void)
{
return &lanc_itf;
}
void lanc_enable_rsp_send(struct lanc_env_tag *lanc_env, uint8_t conidx, uint8_t status)
{
// Send to APP the details of the discovered attributes on LANS
struct lanc_enable_rsp * rsp = KE_MSG_ALLOC(
LANC_ENABLE_RSP,
prf_dst_task_get(&(lanc_env->prf_env), conidx),
prf_src_task_get(&(lanc_env->prf_env), conidx),
lanc_enable_rsp);
rsp->status = status;
if (status == GAP_ERR_NO_ERROR)
{
rsp->lns = lanc_env->env[conidx]->lans;
// Register LANC task in gatt for indication/notifications
prf_register_atthdl2gatt(&(lanc_env->prf_env), conidx, &(lanc_env->env[conidx]->lans.svc));
// Go to connected state
ke_state_set(prf_src_task_get(&(lanc_env->prf_env), conidx), LANC_IDLE);
}
ke_msg_send(rsp);
}
void lanc_send_no_conn_cmp_evt(uint8_t src_id, uint8_t dest_id, uint8_t operation)
{
// Send the message
struct lanc_cmp_evt *evt = KE_MSG_ALLOC(LANC_CMP_EVT,
dest_id, src_id,
lanc_cmp_evt);
evt->operation = operation;
evt->status = PRF_ERR_REQ_DISALLOWED;
ke_msg_send(evt);
}
void lanc_send_cmp_evt(struct lanc_env_tag *lanc_env, uint8_t conidx, uint8_t operation, uint8_t status)
{
// Free the stored operation if needed
if (lanc_env->env[conidx]->operation != NULL)
{
ke_msg_free(ke_param2msg(lanc_env->env[conidx]->operation));
lanc_env->env[conidx]->operation = NULL;
}
// Go back to the CONNECTED state if the state is busy
if (ke_state_get(prf_src_task_get(&(lanc_env->prf_env), conidx)) == LANC_BUSY)
{
ke_state_set(prf_src_task_get(&(lanc_env->prf_env), conidx), LANC_IDLE);
}
// Send the message
struct lanc_cmp_evt *evt = KE_MSG_ALLOC(LANC_CMP_EVT,
prf_dst_task_get(&(lanc_env->prf_env), conidx),
prf_src_task_get(&(lanc_env->prf_env), conidx),
lanc_cmp_evt);
evt->operation = operation;
evt->status = status;
ke_msg_send(evt);
}
uint16_t lanc_get_read_handle_req (struct lanc_env_tag *lanc_env, uint8_t conidx, struct lanc_read_cmd *param)
{
// Attribute Handle
uint16_t handle = ATT_INVALID_SEARCH_HANDLE;
switch (param->read_code)
{
// Read LN Feature
case (LANC_RD_LN_FEAT):
{
handle = lanc_env->env[conidx]->lans.chars[LANP_LANS_LN_FEAT_CHAR].val_hdl;
} break;
// Read Position Quality
case (LANC_RD_POS_Q):
{
handle = lanc_env->env[conidx]->lans.chars[LANP_LANS_POS_Q_CHAR].val_hdl;
} break;
// Read Location and Speed Characteristic Client Char. Cfg. Descriptor Value
case (LANC_RD_WR_LOC_SPEED_CL_CFG):
{
handle = lanc_env->env[conidx]->lans.descs[LANC_DESC_LOC_SPEED_CL_CFG].desc_hdl;
} break;
// Read Unread Alert Characteristic Client Char. Cfg. Descriptor Value
case (LANC_RD_WR_LN_CTNL_PT_CFG):
{
handle = lanc_env->env[conidx]->lans.descs[LANC_DESC_LN_CTNL_PT_CL_CFG].desc_hdl;
} break;
// Read Navigation Characteristic Server Char. Cfg. Descriptor Value
case (LANC_RD_WR_NAVIGATION_CFG):
{
handle = lanc_env->env[conidx]->lans.descs[LANC_DESC_NAVIGATION_CL_CFG].desc_hdl;
} break;
default:
{
handle = ATT_INVALID_SEARCH_HANDLE;
} break;
}
return handle;
}
uint8_t lanc_get_write_desc_handle_req (uint8_t conidx, struct lanc_cfg_ntfind_cmd *param, struct lanc_env_tag *lanc_env, uint16_t *handle)
{
// Status
uint8_t status = GAP_ERR_NO_ERROR;
switch(param->desc_code)
{
// Write Location and speed Characteristic Client Char. Cfg. Descriptor Value
case (LANC_RD_WR_LOC_SPEED_CL_CFG):
{
if (param->ntfind_cfg <= PRF_CLI_START_NTF)
{
*handle = lanc_env->env[conidx]->lans.descs[LANC_DESC_LOC_SPEED_CL_CFG].desc_hdl;
// The descriptor is mandatory
ASSERT_ERR(*handle != ATT_INVALID_SEARCH_HANDLE);
}
else
{
status = PRF_ERR_INVALID_PARAM;
}
} break;
// Write LN Control Point Characteristic Client Char. Cfg. Descriptor Value
case (LANC_RD_WR_LN_CTNL_PT_CFG):
{
if ((param->ntfind_cfg == PRF_CLI_STOP_NTFIND) ||
(param->ntfind_cfg == PRF_CLI_START_IND))
{
*handle = lanc_env->env[conidx]->lans.descs[LANC_DESC_LN_CTNL_PT_CL_CFG].desc_hdl;
if (*handle == ATT_INVALID_SEARCH_HANDLE)
{
// The descriptor has not been found.
status = PRF_ERR_INEXISTENT_HDL;
}
}
else
{
status = PRF_ERR_INVALID_PARAM;
}
} break;
// Write Navigation Characteristic Client Char. Cfg. Descriptor Value
case (LANC_RD_WR_NAVIGATION_CFG):
{
if (param->ntfind_cfg <= PRF_CLI_START_NTF)
{
*handle = lanc_env->env[conidx]->lans.descs[LANC_DESC_NAVIGATION_CL_CFG].desc_hdl;
if (*handle == ATT_INVALID_SEARCH_HANDLE)
{
// The descriptor has not been found.
status = PRF_ERR_INEXISTENT_HDL;
}
}
else
{
status = PRF_ERR_INVALID_PARAM;
}
}
break;
default:
{
status = PRF_ERR_INVALID_PARAM;
} break;
}
return (status);
}
uint8_t lanc_unpack_loc_speed_ind (uint8_t conidx, struct gattc_event_ind const *param, struct lanc_env_tag *lanc_env)
{
// Offset
uint8_t offset = LANP_LAN_LOC_SPEED_MIN_LEN;
// LN Measurement value has been received
struct lanc_value_ind *ind = KE_MSG_ALLOC(LANC_VALUE_IND,
prf_dst_task_get(&(lanc_env->prf_env), conidx),
prf_src_task_get(&(lanc_env->prf_env), conidx),
lanc_value_ind);
// Attribute code
ind->att_code = LANC_NTF_LOC_SPEED;
if ((param->length >= LANP_LAN_LOC_SPEED_MIN_LEN) &&
(param->length <= LANP_LAN_LOC_SPEED_MAX_LEN))
{
// Flags
ind->value.loc_speed.flags = co_read16p(&param->value[0]);
if (ind->value.loc_speed.flags & LANP_LSPEED_INST_SPEED_PRESENT)
{
//Unpack instantaneous speed
ind->value.loc_speed.inst_speed = co_read16p(&param->value[offset]);
offset += 2;
}
if (ind->value.loc_speed.flags & LANP_LSPEED_TOTAL_DISTANCE_PRESENT)
{
//Unpack Total distance (24 bits)
ind->value.loc_speed.total_dist = co_read24p(&param->value[offset]);
offset += 3;
}
if (ind->value.loc_speed.flags & LANP_LSPEED_LOCATION_PRESENT)
{
//Unpack Location
ind->value.loc_speed.latitude = co_read32p(&param->value[offset]);
offset += 4;
ind->value.loc_speed.longitude = co_read32p(&param->value[offset]);
offset += 4;
}
if (ind->value.loc_speed.flags & LANP_LSPEED_ELEVATION_PRESENT)
{
// Unpack Elevation (24 bits)
ind->value.loc_speed.elevation = co_read24p(&param->value[offset]);
offset += 3;
}
if (ind->value.loc_speed.flags & LANP_LSPEED_HEADING_PRESENT)
{
// Unpack heading
ind->value.loc_speed.heading = co_read16p(&param->value[offset]);
offset += 2;
}
if (ind->value.loc_speed.flags & LANP_LSPEED_ROLLING_TIME_PRESENT)
{
// Unpack rolling time
ind->value.loc_speed.rolling_time = param->value[offset];
offset++;
}
if (ind->value.loc_speed.flags & LANP_LSPEED_UTC_TIME_PRESENT)
{
//Unpack UTC time
offset += prf_unpack_date_time(
(uint8_t *) &(param->value[offset]), &(ind->value.loc_speed.date_time));
}
}
// Send the message
ke_msg_send(ind);
return offset;
}
uint8_t lanc_unpack_navigation_ind (uint8_t conidx, struct gattc_event_ind const *param, struct lanc_env_tag *lanc_env)
{
// Offset
uint8_t offset = LANP_LAN_NAVIGATION_MIN_LEN;
// LN Measurement value has been received
struct lanc_value_ind *ind = KE_MSG_ALLOC(LANC_VALUE_IND,
prf_dst_task_get(&(lanc_env->prf_env), conidx),
prf_src_task_get(&(lanc_env->prf_env), conidx),
lanc_value_ind);
// Attribute code
ind->att_code = LANC_NTF_NAVIGATION;
if ((param->length >= LANP_LAN_NAVIGATION_MIN_LEN) &&
(param->length <= LANP_LAN_NAVIGATION_MAX_LEN))
{
// Flags
ind->value.navigation.flags = co_read16p(&param->value[0]);
// Bearing
ind->value.navigation.bearing = co_read16p(&param->value[2]);
// Heading
ind->value.navigation.heading = co_read16p(&param->value[4]);
if (ind->value.navigation.flags & LANP_NAVI_REMAINING_DIS_PRESENT)
{
//Unpack remaining distance (24 bits)
ind->value.navigation.remaining_distance = co_read24p(&param->value[offset]);
offset += 3;
}
if (ind->value.navigation.flags & LANP_NAVI_REMAINING_VER_DIS_PRESENT)
{
//Unpack remaining vertical distance (24 bits)
ind->value.navigation.remaining_ver_distance = co_read24p(&param->value[offset]);
offset += 3;
}
if (ind->value.navigation.flags & LANP_NAVI_ESTIMATED_TIME_OF_ARRIVAL_PRESENT)
{
//Unpack time
offset += prf_unpack_date_time(
(uint8_t *) &(param->value[offset]), &(ind->value.navigation.estimated_arrival_time));
}
}
// Send the message
ke_msg_send(ind);
return offset;
}
uint8_t lanc_unpack_pos_q_ind (struct gattc_read_ind const *param, struct lanc_value_ind *ind)
{
/*----------------------------------------------------
* Unpack Position Quality ---------------------------
*----------------------------------------------------*/
// Offset
uint8_t offset = LANP_LAN_POSQ_MIN_LEN;
// Flags
ind->value.pos_q.flags = co_read16p(&param->value[0]);
if (ind->value.pos_q.flags & LANP_POSQ_NUMBER_OF_BEACONS_IN_SOLUTION_PRESENT)
{
//Unpack beacons in solution
ind->value.pos_q.n_beacons_solution = param->value[offset];
offset++;
}
if (ind->value.pos_q.flags & LANP_POSQ_NUMBER_OF_BEACONS_IN_VIEW_PRESENT)
{
//Unpack beacons in view
ind->value.pos_q.n_beacons_view = param->value[offset];
offset++;
}
if (ind->value.pos_q.flags & LANP_POSQ_TIME_TO_FIRST_FIX_PRESENT)
{
//Unpack time first fix
ind->value.pos_q.time_first_fix = co_read16p(&param->value[offset]);
offset += 2;
}
if (ind->value.pos_q.flags & LANP_POSQ_EHPE_PRESENT)
{
//Unpack ehpe
ind->value.pos_q.ehpe = co_read32p(&param->value[offset]);
offset += 4;
}
if (ind->value.pos_q.flags & LANP_POSQ_EVPE_PRESENT)
{
//Unpack evpe
ind->value.pos_q.evpe = co_read32p(&param->value[offset]);
offset += 4;
}
if (ind->value.pos_q.flags & LANP_POSQ_HDOP_PRESENT)
{
//Unpack hdop
ind->value.pos_q.hdop = param->value[offset];
offset++;
}
if (ind->value.pos_q.flags & LANP_POSQ_VDOP_PRESENT)
{
//Unpack vdop
ind->value.pos_q.vdop = param->value[offset];
offset++;
}
return offset;
}
uint8_t lanc_pack_ln_ctnl_pt_req (struct lanc_ln_ctnl_pt_cfg_req *param, uint8_t *req, uint8_t *status)
{
// Request Length
uint8_t req_len = LANP_LAN_LN_CNTL_PT_REQ_MIN_LEN;
// Set the operation code
req[0] = param->ln_ctnl_pt.op_code;
// Fulfill the message according to the operation code
switch (param->ln_ctnl_pt.op_code)
{
case (LANP_LN_CTNL_PT_SET_CUMUL_VALUE):
{
// Set the cumulative value (24 bits)
co_write24p(&req[req_len], param->ln_ctnl_pt.value.cumul_val);
// Update length
req_len += 3;
} break;
case (LANP_LN_CTNL_PT_MASK_LSPEED_CHAR_CT):
{
// Set mask content
co_write16p(&req[req_len], param->ln_ctnl_pt.value.mask_content);
// Update length
req_len += 2;
} break;
case (LANP_LN_CTNL_PT_NAVIGATION_CONTROL):
{
// Set control value
req[req_len] = param->ln_ctnl_pt.value.control_value;
// Update length
req_len++;
} break;
case (LANP_LN_CTNL_PT_REQ_NAME_OF_ROUTE):
case (LANP_LN_CTNL_PT_SELECT_ROUTE):
{
// Set route number
co_write16p(&req[req_len], param->ln_ctnl_pt.value.route_number);
// Update length
req_len += 2;
} break;
case (LANP_LN_CTNL_PT_SET_FIX_RATE):
{
// Set the fix rate
req[req_len] = param->ln_ctnl_pt.value.fix_rate;
// Update length
req_len++;
} break;
case (LANP_LN_CTNL_PT_SET_ELEVATION):
{
// Set elevation (24 bits)
co_write24p(&req[req_len], param->ln_ctnl_pt.value.elevation);
// Update length
req_len += 3;
} break;
case (LANP_LN_CTNL_PT_REQ_NUMBER_OF_ROUTES):
{
// Nothing more to do
} break;
default:
{
*status = PRF_ERR_INVALID_PARAM;
} break;
}
return req_len;
}
uint8_t lanc_unpack_ln_ctln_pt_ind (struct gattc_event_ind const *param, ke_task_id_t src, ke_task_id_t dest)
{
// Offset
uint8_t offset = LANP_LAN_LN_CNTL_PT_RSP_MIN_LEN;
// Control Point value has been received
struct lanc_ln_ctnl_pt_rsp *ind = KE_MSG_ALLOC_DYN(LANC_LN_CTNL_PT_RSP,
dest,
src,
lanc_ln_ctnl_pt_rsp,
param->length);
// Requested operation code
ind->rsp.req_op_code = param->value[1];
// Response value
ind->rsp.resp_value = param->value[2];
if ((ind->rsp.resp_value == LANP_LN_CTNL_PT_RESP_SUCCESS) && (param->length >= 3))
{
switch (ind->rsp.req_op_code)
{
case (LANP_LN_CTNL_PT_REQ_NUMBER_OF_ROUTES):
{
ind->rsp.value.number_of_routes = co_read16p(&param->value[offset]);
offset += 2;
} break;
case (LANP_LN_CTNL_PT_REQ_NAME_OF_ROUTE):
{
// Get the length of the route
ind->rsp.value.route.length = (param->length - 3);
for (int i = 0; i<ind->rsp.value.route.length; i++)
{
ind->rsp.value.route.name[i] = param->value[i + 3];
offset++;
}
} break;
case (LANP_LN_CTNL_PT_SET_CUMUL_VALUE):
case (LANP_LN_CTNL_PT_MASK_LSPEED_CHAR_CT):
case (LANP_LN_CTNL_PT_NAVIGATION_CONTROL):
case (LANP_LN_CTNL_PT_SELECT_ROUTE):
case (LANP_LN_CTNL_PT_SET_FIX_RATE):
case (LANP_LN_CTNL_PT_SET_ELEVATION):
{
// No parameters
} break;
default:
{
} break;
}
}
// Send the message
ke_msg_send(ind);
return offset;
}
#endif //(BLE_LN_COLLECTOR)
/// @} LAN