/* Cesar project {{{ * * Copyright (C) 2008 Spidcom * * <<>> * * }}} */ /** * \file cp/sta/action/src/misc.c * \brief STA action, miscellaneous definitions. * \ingroup cp_sta_action */ #include "common/std.h" #include "cl/data_rate.h" #include "mac/common/store.h" #include "action.h" #include "cp/inc/context.h" #include "cp/inc/trace.h" #include "cp/msg/msg.h" #include "hal/arch/arch.h" // for dsr lock and unlock #include "common/defs/spidcom.h" #include "mac/common/timings.h" void cp_sta_action_process_cc_who_ru_req (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); /* Only for CCo. */ cp_nid_t nid; if (cp_msg_cc_who_ru_req_receive (ctx, mme, &nid)) { /* If NID matches. */ if (nid == cp_sta_own_data_get_nid (ctx)) { cp_msg_cc_who_ru_cnf_t data; data.nid = nid; data.cco_mac = cp_sta_own_data_get_mac_address (ctx); cp_sta_own_data_t *own = cp_sta_mgr_get_sta_own_data (ctx); strcpy (data.avln_hfid, own->hfid_avln); cp_msg_cc_who_ru_cnf_send (ctx, &mme->peer, &data); } } } void cp_sta_action_process_cc_relay_req (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); uint length; uint mmtype; if (cp_msg_cc_relay_req_receive (ctx, mme, &length, &mmtype)) { switch (mmtype) { /* Special cases. */ case CM_ENCRYPTED_PAYLOAD_IND: case CM_ENCRYPTED_PAYLOAD_RSP: mme->relay.ftei = MAC_TEI_BCAST; break; case CC_ASSOC_REQ: if (mme->peer.tei == MAC_TEI_UNASSOCIATED && mme->relay.mac_fa == MAC_BROADCAST && mme->relay.ftei == MAC_TEI_BCAST) { /* Send to CCo. */ cp_mme_peer_t cco_peer; cp_net_get_cco_peer (ctx, cp_sta_mgr_get_our_avln (ctx), &cco_peer); mme->relay.mac_fa = cco_peer.mac; mme->relay.ftei = cco_peer.tei; } break; /* MME between unassociated STA. */ case CM_UNASSOCIATED_STA_IND: case CC_ASSOC_CNF: case CC_WHO_RU_REQ: case CC_WHO_RU_CNF: case CM_HFID_REQ: case CM_HFID_CNF: case CM_SC_JOIN_REQ: case CM_SC_JOIN_CNF: case CM_STA_CAP_REQ: case CM_STA_CAP_CNF: case CM_MME_ERROR_IND: case CC_RELAY_REQ: case CC_RELAY_IND: /* No special condition. */ break; /* MME between non authenticated STA. */ case CC_LEAVE_REQ: case CC_LEAVE_CNF: case CC_LEAVE_IND: case CC_LEAVE_RSP: case CC_SET_TEI_MAP_REQ: case CC_SET_TEI_MAP_IND: case PH_PROXY_APPOINT_IND: case CM_CHAN_EST_IND: case CM_TM_UPDATE_IND: case CM_SET_KEY_REQ: case CM_SET_KEY_CNF: case CM_GET_KEY_REQ: case CM_GET_KEY_CNF: /* Should be associated. */ if (!MAC_TEI_IS_STA (mme->peer.tei)) return; break; /* Other. */ default: /* Should be authenticated. */ if (!MAC_TEI_IS_STA (mme->peer.tei) || !mme->encrypt) return; break; } cp_msg_cc_relay_ind_send (ctx, mme, mme->peer.mac, mme->peer.tei, length); } } void cp_sta_action_process_cm_hfid_req (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); enum cp_msg_cm_hfid_req_reqtype_t type; cp_nid_t nid; char hfid[CP_HFID_SIZE + 1]; const char *hfid_p = ""; if (cp_msg_cm_hfid_req_receive (ctx, mme, &type, &nid, hfid)) { cp_sta_own_data_t *own_data = cp_sta_mgr_get_sta_own_data (ctx); enum cp_msg_cm_hfid_cnf_restype_t res = type; switch (type) { case CM_HFID_REQ_REQTYPE_PROVIDE_MANUFACTURER_SET_HFID: hfid_p = own_data->hfid_manufacturer; break; case CM_HFID_REQ_REQTYPE_PROVIDE_USER_SET_HFID: hfid_p = own_data->hfid_user; break; case CM_HFID_REQ_REQTYPE_PROVIDE_NETWORK_HFID: if (nid == cp_sta_own_data_get_nid (ctx)) hfid_p = own_data->hfid_avln; else res = CM_HFID_CNF_RESTYPE_FAILURE; break; case CM_HFID_REQ_REQTYPE_SET_USER_SET_HFID: if (mme->peer.tei == MAC_TEI_FOREIGN) cp_sta_own_data_set_hfid_user (ctx, hfid); else res = CM_HFID_CNF_RESTYPE_FAILURE; hfid_p = own_data->hfid_user; break; case CM_HFID_REQ_REQTYPE_SET_NETWORK_HFID: if (nid == cp_sta_own_data_get_nid (ctx)) { if (mme->peer.tei == MAC_TEI_FOREIGN) cp_sta_own_data_set_hfid_avln (ctx, hfid); else res = CM_HFID_CNF_RESTYPE_FAILURE; hfid_p = own_data->hfid_avln; } else res = CM_HFID_CNF_RESTYPE_FAILURE; break; default: dbg_assert_default (); } cp_msg_cm_hfid_cnf_send (ctx, &mme->peer, res, hfid_p); } } void cp_sta_action_process_cm_mme_error_ind (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); if (CONFIG_TRACE) { cp_msg_cm_mme_error_ind_t data; if (cp_msg_cm_mme_error_ind_receive (ctx, mme, &data)) { CP_TRACE (MME_ERROR_IND, mme->peer.tei, data.reason, data.rx_mmv, data.rx_mmtype, data.offset); } } } void cp_sta_action_process_cm_nw_info_req (cp_t *ctx, cp_mme_rx_t *rx_mme) { dbg_assert (ctx); dbg_assert (rx_mme); cp_mme_tx_t *tx_mme; cp_msg_cm_nw_info_cnf_t data; cp_net_t* our_avln = NULL; cp_sta_t* cco_sta = NULL; uint NumNWs; if (cp_msg_cm_nw_info_req_receive (ctx, rx_mme)) { /* Determine the number of AVLN */ /* Most likely will be: */ /* 0 if sta is not authenticated */ /* 1 if sta is authenticated */ if (ctx->sta_mgr.sta_own_data.authenticated == true) NumNWs = 1; else NumNWs = 0; tx_mme = cp_msg_cm_nw_info_cnf_send_begin (ctx, &rx_mme->peer, NumNWs); if (NumNWs) { data.nid = cp_sta_own_data_get_nid (ctx); data.snid = cp_sta_own_data_get_snid (ctx); data.tei = cp_sta_own_data_get_tei (ctx); if (cp_sta_own_data_get_pco_status (ctx)) { data.sta_role = CM_NW_INFO_CNF_STA_ROLE_PCO; } else if (cp_sta_own_data_get_cco_status (ctx)) { data.sta_role = CM_NW_INFO_CNF_STA_ROLE_CCO; /* In this case the CCo's mac is own data mac */ data.cco_mac = cp_sta_own_data_get_mac_address (ctx); } else { data.sta_role = CM_NW_INFO_CNF_STA_ROLE_STA; } our_avln = cp_sta_mgr_get_our_avln (ctx); dbg_assert (our_avln); if (our_avln) cco_sta = cp_net_get_cco (ctx, our_avln); if (cco_sta) { data.cco_mac = cp_sta_get_mac_address (cco_sta); slab_release (cco_sta); } data.access = cp_net_get_access (ctx, our_avln); data.num_coord_nets = 0; /* Functionnality limitation */ cp_msg_cm_nw_info_cnf_send (ctx, tx_mme, &data); } cp_msg_cm_nw_info_cnf_send_end (ctx, tx_mme); } } void cp_sta_action_process_cm_nw_stats_req (cp_t *ctx, cp_mme_rx_t *rx_mme) { dbg_assert (ctx); dbg_assert (rx_mme); cp_mme_tx_t *tx_mme; cp_net_t* our_avln; cp_sta_t* sta_list = NULL; sta_t* sta; uint NumSTAs = 0; if (cp_msg_cm_nw_stats_req_receive (ctx, rx_mme)) { our_avln = cp_sta_mgr_get_our_avln (ctx); /* Make sure we have an avln */ if (MAC_TEI_IS_STA (cp_sta_own_data_get_tei (ctx))) { /* Get authenticated stas */ sta_list = cp_net_sta_get_first (ctx, our_avln, CP_NET_STA_ASSOC); while (sta_list) { if (cp_sta_get_authenticated (ctx, sta_list)) { NumSTAs++; } sta_list = cp_net_sta_get_next (ctx, our_avln, sta_list); } } else { NumSTAs = 0; } tx_mme = cp_msg_cm_nw_stats_cnf_send_begin (ctx, &rx_mme->peer, NumSTAs); if (NumSTAs) { sta_list = cp_net_sta_get_first (ctx, our_avln, CP_NET_STA_ASSOC); while (sta_list) { if (cp_sta_get_authenticated (ctx, sta_list)) { mac_t mac; uint phy_dr_tx; uint phy_dr_rx; cl_data_rate_t tx_data_rate; cl_data_rate_t rx_data_rate; mac = cp_sta_get_mac_address(sta_list); /* Get TX : local STA -> avln STA */ /* Get RX : avln STA -> local STA */ sta = mac_store_sta_get (ctx->mac_store, cp_sta_get_tei(sta_list)); arch_dsr_lock(); tx_data_rate = sta->tx_data_rate; rx_data_rate = sta->rx_data_rate; arch_dsr_unlock(); /* Update DR with delay between last DR update and now */ data_rate_update_info(&tx_data_rate, 0); data_rate_update_info(&rx_data_rate, 0); /* Convert DR from octets/s to Mbit/s */ phy_dr_tx = (uint)((tx_data_rate.data_rate * 8 + 500000) /1000000); phy_dr_rx = (uint)((rx_data_rate.data_rate * 8 + 500000) /1000000); cp_msg_cm_nw_stats_cnf_send (ctx, tx_mme, mac, phy_dr_tx, phy_dr_rx); /* Clean. */ blk_release (sta); } sta_list = cp_net_sta_get_next (ctx, our_avln, sta_list); } } cp_msg_cm_nw_stats_cnf_send_end (ctx, tx_mme); } } void reset_link_stats (mfs_t *mfs, cp_msg_cm_link_stats_tlflag_t transmit) { switch (transmit) { case CM_LINK_STATS_TLFLAG_TRANSMIT: link_stats_tx_reset (&mfs->tx.stats); break; case CM_LINK_STATS_TLFLAG_RECEIVE: link_stats_rx_reset (&mfs->rx.stats); break; default: break; } } void cp_sta_action_process_cm_link_stats_req (cp_t *ctx, cp_mme_rx_t *rx_mme) { dbg_assert (ctx); dbg_assert (rx_mme); cp_msg_cm_link_stats_req_t req_data; cp_msg_cm_link_stats_restype_t res_type; cp_tei_t tei; mfs_t *mfs = NULL; res_type = CM_LINK_STATS_RESTYPE_SUCCESS; if (cp_msg_cm_link_stats_req_receive (ctx, rx_mme, &req_data)) { cp_mme_tx_t *tx_mme; /* check req_type */ if (req_data.req_type >= CM_LINK_STATS_REQTYPE_NB) res_type = CM_LINK_STATS_RESTYPE_FAILURE; /* Check nid requested is our avln */ if (res_type == CM_LINK_STATS_RESTYPE_SUCCESS) { cp_net_t* our_avln; our_avln = cp_sta_mgr_get_our_avln (ctx); if (req_data.nid != our_avln->nid) res_type = CM_LINK_STATS_RESTYPE_FAILURE; } /* Get tei from mac */ if (res_type == CM_LINK_STATS_RESTYPE_SUCCESS) { cp_sta_t *sta; sta = cp_sta_mgr_sta_get_from_mac (ctx, req_data.mac); if (sta) { // The station exists get the TEI. tei = cp_sta_get_tei (sta); slab_release (sta); } else res_type = CM_LINK_STATS_RESTYPE_FAILURE; } if (res_type == CM_LINK_STATS_RESTYPE_SUCCESS) { switch (req_data.transmit) { case CM_LINK_STATS_TLFLAG_TRANSMIT: mfs = mac_store_mfs_get(ctx->mac_store, true, false, req_data.mgmt_flag, req_data.lid, tei); if (!mfs) res_type = CM_LINK_STATS_RESTYPE_FAILURE; break; case CM_LINK_STATS_TLFLAG_RECEIVE: mfs = mac_store_mfs_get(ctx->mac_store, false, false, req_data.mgmt_flag, req_data.lid, tei); if (!mfs) res_type = CM_LINK_STATS_RESTYPE_FAILURE; break; default: break; } } tx_mme = cp_msg_cm_link_stats_cnf_send_begin (ctx, &rx_mme->peer, req_data.req_id, res_type); if (res_type == CM_LINK_STATS_RESTYPE_SUCCESS) { switch (req_data.req_type) { case CM_LINK_STATS_REQTYPE_GET_AND_RESET: cp_msg_cm_link_stats_cnf_send (ctx, tx_mme, mfs, req_data.transmit); reset_link_stats(mfs, req_data.transmit); break; case CM_LINK_STATS_REQTYPE_GET: cp_msg_cm_link_stats_cnf_send (ctx, tx_mme, mfs, req_data.transmit); break; case CM_LINK_STATS_REQTYPE_RESET: reset_link_stats(mfs, req_data.transmit); break; default: break; } } /* release the mfs access */ if (mfs) blk_release (mfs); cp_msg_cm_link_stats_cnf_send_end (ctx, tx_mme); } } void cp_sta_action_process_cm_sta_cap_req (cp_t *ctx, cp_mme_rx_t *rx_mme) { dbg_assert (ctx); dbg_assert (rx_mme); cp_msg_cm_sta_cap_cnf_t data; if (cp_msg_cm_sta_cap_req_receive (ctx, rx_mme)) { data.av_version = CP_AV_VERSION; data.mac = cp_sta_own_data_get_mac_address (ctx); data.oui = SPC_OUI; data.auto_connect = CP_ACS_CAP; data.smoothing = CP_SMOOTHING_CAP; data.cco_cap = CP_CCO_LEVEL; data.proxy_cap = CP_PCO_CAP; data.backup_cco_cap = CP_BACKUP_CCO_CAP; data.soft_handover = CP_SOFT_HANDOVER_CAP; data.two_sym_fc = CP_TWO_SYM_FC_CAP; data.max_fl_av = MAC_MAX_FL_MAX_FL; data.homeplug_11_cap = CP_HOMEPLUG_AV11; data.homeplug_101_int = CP_HOMEPLUG_AV101; data.regulatory_cap = CP_REGULATORY_CAP; data.bidir_burst = CP_BIDIRECTIONAL_BURSTING_CAP; data.implementation_version = CP_IMPLEMENTATION_VERSION; cp_msg_cm_sta_cap_cnf_send (ctx, &rx_mme->peer, &data); } } void cp_sta_action_process_cc_discover_list_req (cp_t *ctx, cp_mme_rx_t *rx_mme) { dbg_assert (ctx); dbg_assert (rx_mme); if (cp_msg_cc_discover_list_req_receive (ctx, rx_mme)) { cp_mme_tx_t *tx_mme; cp_msg_cc_discover_list_ctx_t disc_ctx; cp_net_t *our_net = NULL; /* Check if our station is associated in a network. */ if (cp_sta_own_data_get_tei (ctx) != MAC_TEI_UNASSOCIATED) { our_net = cp_sta_mgr_get_our_avln(ctx); } /* Prepare CC_DISCOVER_LIST.CNF */ tx_mme = cp_msg_cc_discover_list_cnf_send_begin (ctx, &rx_mme->peer, cp_sta_mgr_get_num_discovered_stas (ctx), cp_sta_mgr_get_num_discovered_net (ctx), &disc_ctx); /* Send NumStation */ cp_msg_cc_discover_list_cnf_send_stations_begin (ctx, tx_mme, &disc_ctx); /* Get stations informations for each existing network. */ cp_net_t *net; for (net = cp_sta_mgr_get_first_avln (ctx); net; net = cp_sta_mgr_get_next_avln (ctx, net)) { /* Collect info valid for all stations of this net. */ cp_msg_cc_discover_list_sta_t list; list.snid = cp_net_get_snid (ctx, net); list.access = cp_net_get_access (ctx, net); /* For all kind of association states we seek stations. */ cp_net_sta_status_t assoc; for (assoc = 0; assoc < CP_NET_STA_NB; assoc++) { /* Check association. */ if ((assoc == CP_NET_STA_ASSOC) && (net == our_net)) list.same_network = CC_DISCOVER_LIST_NET_SAME_NETWORK; else list.same_network = CC_DISCOVER_LIST_NET_DIFFERENT_NETWORK; cp_sta_t *cp_sta; for (cp_sta = cp_net_sta_get_first (ctx, net, assoc); cp_sta; cp_sta = cp_net_sta_get_next (ctx, net, cp_sta)) { list.mac_addr = cp_sta_get_mac_address (cp_sta); list.tei = cp_sta_get_tei (cp_sta); list.cco_cap = cp_sta->cco_cap; list.proxy_cap = cp_sta->pco_cap; list.backup_cco_cap = cp_sta->backup_cco_cap; list.cco_status = cp_sta_get_cco_status (cp_sta); list.pco_status = cp_sta_get_pco_status (cp_sta); list.backup_cco_status = cp_sta->is_backup_cco; list.signal_level = 0x00; list.average_ble = 0x00; cp_msg_cc_discover_list_cnf_send_station (ctx, tx_mme, &list); } } } /* Send NumNetwork */ cp_msg_cc_discover_list_cnf_send_net_begin (ctx, tx_mme, &disc_ctx); /* For each present network but our network if we have one. */ for (net = cp_sta_mgr_get_first_avln (ctx); net; net = cp_sta_mgr_get_next_avln (ctx, net)) { if (our_net != net) { /* Collect info for all valid net */ cp_msg_cc_discover_list_net_t data; data.nid = cp_net_get_nid (ctx, net); data.snid = cp_net_get_snid (ctx, net); data.access = cp_net_get_access (ctx, net); data.hm = net->hm; data.numslots = net->avln_num_slots; if (net->network_mode == CP_NET_NM_COORDINATED) /* Unsupported. */ data.coordinated_status = CC_DISCOVER_LIST_COORD_COORDINATED_GROUP_UNKNOWN; else data.coordinated_status = CC_DISCOVER_LIST_COORD_NON_COORDINATED; data.offset = 0; cp_msg_cc_discover_list_cnf_send_net (ctx, tx_mme, &data); } } cp_msg_cc_discover_list_cnf_send_end (ctx, tx_mme); } }