499 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			499 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /****************************************************************************
 | |
| 
 | |
| Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED.
 | |
| 
 | |
| This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT
 | |
| be copied by any method or incorporated into another program without
 | |
| the express written consent of Aerospace C.Power. This Information or any portion
 | |
| thereof remains the property of Aerospace C.Power. The Information contained herein
 | |
| is believed to be accurate and Aerospace C.Power assumes no responsibility or
 | |
| liability for its use in any way and conveys no license or title under
 | |
| any patent or copyright and makes no representation or warranty that this
 | |
| Information is free from patent or copyright infringement.
 | |
| 
 | |
| ****************************************************************************/
 | |
| #include "mac_stream.h"
 | |
| #include "mac_desc_engine.h"
 | |
| #include "iot_errno.h"
 | |
| #include "hash_table.h"
 | |
| #include "sw_sched.h"
 | |
| #include "iot_io.h"
 | |
| #include "iot_dbglog_api.h"
 | |
| #include "iot_dbglog_parser.h"
 | |
| #include "os_utils_api.h"
 | |
| 
 | |
| typedef struct _mac_stream_vdev_dump_info {
 | |
|     uint32_t peer_cnt               : 16,
 | |
|         proxy_peer_cnt              : 16;
 | |
|     uint32_t stream_cnt             : 16,
 | |
|         rx_stream_cnt               : 16;
 | |
|     uint32_t stream_overflow_cnt    : 16,
 | |
|         rsvd                        : 16;
 | |
| } mac_stream_vdev_dump_info_t;
 | |
| 
 | |
| typedef struct _mac_stream_exhausted_info {
 | |
|     uint32_t cur_ts;
 | |
|     mac_stream_vdev_dump_info_t vdev_info[MAX_VDEV_NUM];
 | |
|     mac_peer_t peer;
 | |
| } mac_stream_exhausted_info_t;
 | |
| 
 | |
| mac_stream_t *g_stream_hash_tbl[stream_tbl_sz] = { 0 };
 | |
| 
 | |
| uint32_t stream_hash_tbl_get_hsahv(nid_t nid, tei_t tei, \
 | |
|     lid_t lid, uint8_t is_tx, uint8_t is_rf)
 | |
| {
 | |
|     stream_hash_src_t hash_src = { 0 };
 | |
|     hash_src.nid = nid;
 | |
|     hash_src.tei = tei;
 | |
|     hash_src.lid = lid;
 | |
|     hash_src.is_tx= is_tx;
 | |
|     hash_src.is_rf = is_rf;
 | |
|     uint8_t *hash_str = (uint8_t*)&hash_src;
 | |
|     return ((stream_tbl_sz - 1) \
 | |
|         & iot_getcrc32(hash_str, sizeof(stream_hash_src_t)));
 | |
| }
 | |
| 
 | |
| /* @return stream address if found
 | |
| *            NULL if not found
 | |
| */
 | |
| mac_stream_t * find_stream(mac_peer_t *peer_in, \
 | |
|     nid_t nid, tei_t tei, lid_t lid, uint8_t is_tx, uint8_t is_rf)
 | |
| {
 | |
|     nid_t peer_nid = (nid_t)PLC_NID_INVALID;
 | |
| 
 | |
|     if (!nid && !peer_in) {
 | |
|         /* if nid = 0, peer_in must valid */
 | |
|         return NULL;
 | |
|     }
 | |
|     mac_vdev_t *vdev = get_vdev_ptr(peer_in->pdev_id, peer_in->vdev_id);
 | |
|     if (ERR_OK != vdev_get_nid(vdev, &peer_nid)) {
 | |
|         peer_nid = (nid_t)PLC_NID_INVALID;
 | |
|     }
 | |
|     if (peer_in->is_self_peer) {
 | |
|         if (PLC_NID_INVALID == peer_nid) {
 | |
|             /* not assoc yet, use the peer's vdev's nid instead */
 | |
|             nid = (nid_t)PLC_NID_INVALID;
 | |
|             tei = vdev_get_tei(vdev);
 | |
|         }
 | |
|     } else if (peer_in->is_bcast_peer) {
 | |
|         /* else if bcast peer, and not assoc yet then
 | |
|          * ignore the nid of the packet, use
 | |
|          * the peer's vdev's nid instead
 | |
|          */
 | |
|         IOT_ASSERT(peer_in->tei == PLC_TEI_BCAST);
 | |
|         if (PLC_NID_INVALID == peer_nid) {
 | |
|             /* not assoc yet, use the peer's vdev's nid instead */
 | |
|             nid = (nid_t)PLC_NID_INVALID;
 | |
|         }
 | |
|         tei = (tei_t)peer_in->tei;
 | |
|     }
 | |
| 
 | |
|     /* should be the correct peer */
 | |
|     IOT_ASSERT(peer_in->tei == tei);
 | |
| 
 | |
|     uint32_t hash_code = stream_hash_tbl_get_hsahv( \
 | |
|         nid, \
 | |
|         tei, \
 | |
|         lid,\
 | |
|         is_tx,\
 | |
|         is_rf);
 | |
|     mac_stream_t *tmp = \
 | |
|         g_stream_hash_tbl[hash_code];
 | |
| 
 | |
|     mac_peer_t *peer;
 | |
|     /* look every node for the hash entry */
 | |
|     for (; tmp; tmp = tmp->next) {
 | |
|         peer = tmp->peer;
 | |
|         if (peer) {
 | |
|             vdev = get_vdev_ptr(peer->pdev_id, peer->vdev_id);
 | |
|             if (!vdev) {
 | |
|                 continue;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             continue;
 | |
|         }
 | |
|         if ((tmp->lid == lid) \
 | |
|             && (peer->tei == tei) \
 | |
|             && (peer_nid == nid) \
 | |
|             && (tmp->is_tx == is_tx) \
 | |
|             && (tmp->is_rf == is_rf)) {
 | |
|             return tmp;
 | |
|         }
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /* @return - 0 for successful
 | |
| *           other for failed
 | |
| */
 | |
| uint32_t stream_hash_tbl_add(mac_stream_t *stream)
 | |
| {
 | |
|     if (!stream) {
 | |
|         return ERR_INVAL;
 | |
|     }
 | |
|     lid_t lid = stream->lid;
 | |
|     mac_peer_t *peer = stream->peer;
 | |
|     if (!peer) {
 | |
|         return ERR_INVAL;
 | |
|     }
 | |
|     tei_t tei = (tei_t)peer->tei;
 | |
|     mac_vdev_t *vdev = get_vdev_ptr(peer->pdev_id, peer->vdev_id);
 | |
|     if (!vdev) {
 | |
|         return ERR_INVAL;
 | |
|     }
 | |
|     nid_t nid;
 | |
|     vdev_get_nid(vdev, &nid);
 | |
| 
 | |
|     uint8_t is_tx = (uint8_t)stream->is_tx;
 | |
|     uint8_t is_rf = (uint8_t)stream->is_rf;
 | |
| 
 | |
|     if (find_stream(peer, nid, tei, lid, is_tx, is_rf)) {
 | |
|         return ERR_EXIST;
 | |
|     }
 | |
| 
 | |
|     mac_stream_t **tmp = \
 | |
|         &g_stream_hash_tbl[stream_hash_tbl_get_hsahv(nid, \
 | |
|         tei, lid, is_tx, is_rf)];
 | |
|     stream->next = *tmp;
 | |
|     *tmp = stream;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| uint32_t stream_hash_tbl_del(mac_stream_t *stream)
 | |
| {
 | |
|     mac_stream_t *str = stream;
 | |
|     mac_stream_t **hash_table_entry;
 | |
|     mac_stream_t *entry = NULL;
 | |
|     tei_t tei;
 | |
|     nid_t nid;
 | |
|     if (str->peer) {
 | |
|         tei = (tei_t) str->peer->tei;
 | |
|         vdev_get_nid(get_vdev_ptr(str->peer->pdev_id, str->peer->vdev_id),
 | |
|             &nid); //TODO: check ret value
 | |
|     } else {
 | |
|         iot_printf("str lid=%d without peer\n", str->lid);
 | |
|         IOT_ASSERT(0);
 | |
|         tei = 0;
 | |
|         nid = 0;
 | |
|         return ERR_FAIL;
 | |
|     }
 | |
| 
 | |
|     /* for normal case */
 | |
|     hash_table_entry = &g_stream_hash_tbl[stream_hash_tbl_get_hsahv(nid,
 | |
|             tei, str->lid, (uint8_t)str->is_tx,
 | |
|             (uint8_t)str->is_rf)];
 | |
| 
 | |
|     for(mac_stream_t **cur = hash_table_entry; *cur;){
 | |
|         entry = *cur;
 | |
|         if(str == entry){
 | |
|             *cur = entry->next;
 | |
|             break;
 | |
|         }
 | |
|         else{
 | |
|            cur = &entry->next;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* for nid valid but tei is 0 */
 | |
|     if(str != entry){
 | |
|         hash_table_entry = &g_stream_hash_tbl[stream_hash_tbl_get_hsahv( \
 | |
|             nid, 0, str->lid, (uint8_t)str->is_tx,
 | |
|             (uint8_t)str->is_rf)];
 | |
| 
 | |
|         for(mac_stream_t **cur = hash_table_entry; *cur;){
 | |
|             entry = *cur;
 | |
|             if(str == entry){
 | |
|                 *cur = entry->next;
 | |
|                 break;
 | |
|             }
 | |
|             else{
 | |
|                 cur = &entry->next;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* for nid invalid and tei invalid */
 | |
|     if(str != entry){
 | |
|         hash_table_entry = &g_stream_hash_tbl[stream_hash_tbl_get_hsahv( \
 | |
|             0, 0, str->lid, (uint8_t)str->is_tx,
 | |
|             (uint8_t)str->is_rf)];
 | |
| 
 | |
|         for(mac_stream_t **cur = hash_table_entry; *cur;){
 | |
|             entry = *cur;
 | |
|             if(str == entry){
 | |
|                 *cur = entry->next;
 | |
|                 break;
 | |
|             }
 | |
|             else{
 | |
|                 cur = &entry->next;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* for bcast when nid invalid */
 | |
|     if(str != entry){
 | |
|         hash_table_entry = &g_stream_hash_tbl[stream_hash_tbl_get_hsahv(0, tei,
 | |
|                 str->lid, (uint8_t)str->is_tx,
 | |
|                 (uint8_t)str->is_rf)];
 | |
| 
 | |
|         for(mac_stream_t **cur = hash_table_entry; *cur;){
 | |
|             entry = *cur;
 | |
|             if(str == entry){
 | |
|                 *cur = entry->next;
 | |
|                 break;
 | |
|             }
 | |
|             else{
 | |
|                 cur = &entry->next;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (str != entry) {
 | |
|         /* if not bcast peer for this stream
 | |
|          * we need check where is gone
 | |
|          */
 | |
|         iot_printf("str lid%d tei:%d nid 0x%x tx:%d is_rf:%d not found.\n",
 | |
|             str->lid, str->peer->tei, nid, str->is_tx, str->is_rf);
 | |
| 
 | |
| #if DEBUG_FREE_STREAM_FAIL
 | |
|         mac_debug_stream_t debug_str;
 | |
|         debug_str.nid = (uint32_t)nid;
 | |
|         debug_str.lid = (uint32_t)str->lid;
 | |
|         debug_str.tei = (uint32_t)str->peer->tei;
 | |
|         debug_str.is_tx = (uint32_t)str->is_tx;
 | |
|         debug_str.is_rf = (uint32_t)str->is_rf;
 | |
|         IOT_ASSERT_DUMP(0, (uint32_t *)&debug_str, \
 | |
|             sizeof(mac_debug_stream_t)/sizeof(uint32_t));
 | |
| #endif
 | |
|     }
 | |
|     return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| uint32_t mac_tx_stream_ctxt_init(mac_tx_stream_ctxt_t *tx, \
 | |
|     mac_stream_t *stream, uint32_t need_free, iot_pkt_t *iot_pkt)
 | |
| {
 | |
|     IOT_ASSERT(tx);
 | |
|     tx->mac_frame_list = tx->last_mac_frame = NULL;
 | |
|     tx->mac_frame_num = 0;
 | |
|     tx->cur_tx_msdu = NULL;
 | |
|     iot_list_init(&tx->swq_node);
 | |
|     (void)stream;
 | |
|     (void)need_free;
 | |
|     (void)iot_pkt;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| uint32_t mac_tx_stream_ctxt_deinit(mac_stream_t* stream)
 | |
| {
 | |
|     mac_tx_stream_ctxt_t * tx = &stream->msdu.tx;
 | |
|     mac_msdu_frame_t *tmp = tx->mac_frame_list;
 | |
|     if (tx->cur_tx_msdu) {
 | |
|         /* free the msdu if not freed yet */
 | |
|         if (!tx->cur_tx_msdu->in_hwq) {
 | |
|             mac_msdu_deinit(tx->cur_tx_msdu);
 | |
|         } else {
 | |
|             tx->cur_tx_msdu->ref_mac_stream = NULL;
 | |
|         }
 | |
|         tx->cur_tx_msdu = NULL;
 | |
|     }
 | |
|     uint32_t bcn_region_type = \
 | |
|             mac_get_bcn_region_type(stream->lid);
 | |
| 
 | |
|     if(tx->mac_frame_num == 0){
 | |
|         IOT_ASSERT(tx->last_mac_frame == NULL && \
 | |
|             tx->mac_frame_list == NULL);
 | |
|     }
 | |
|     else{
 | |
|         IOT_ASSERT(tx->last_mac_frame != NULL && \
 | |
|             tx->mac_frame_list != NULL);
 | |
|         for(uint32_t i = 0; i < (PLC_MAC_MSDU_FRAME_POOL_SIZE
 | |
|             + RF_MAC_MSDU_FRAME_POOL_SIZE); i++){
 | |
|             if(tmp == NULL){
 | |
|                 break;
 | |
|             }
 | |
|             tx->mac_frame_list = tmp->next;
 | |
|             IOT_ASSERT(tmp->buf);
 | |
|             iot_pkt_free_tx_done(tmp->buf, IOT_PKT_STATE_TX_FAIL);
 | |
|             mac_token_free(bcn_region_type, stream->lid, stream->is_rf);
 | |
|             mac_desc_free(&g_mac_desc_eng, \
 | |
|             PLC_MAC_MSDU_FRAME_POOL, tmp);
 | |
|             tmp = tx->mac_frame_list;
 | |
|             tx->mac_frame_num -= 1;
 | |
|         }
 | |
|         IOT_ASSERT(tx->mac_frame_num == 0 && tmp == NULL);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| uint32_t mac_stream_alloc(mac_peer_t *peer, uint32_t is_tx, \
 | |
|     lid_t lid, uint8_t is_rf, mac_stream_t **stream_ptr)
 | |
| {
 | |
|     mac_stream_t *stream;
 | |
|     if (!peer) {
 | |
|         return ERR_INVAL;
 | |
|     }
 | |
|     mac_vdev_t *vdev = get_vdev_ptr(peer->pdev_id, peer->vdev_id);
 | |
|     IOT_ASSERT(vdev);
 | |
| #if 0
 | |
|     /* double check if the stream already exist
 | |
|     * can be omit to make faster */
 | |
|     mac_vdev_t *vdev = get_vdev_ptr(peer->pdev_id, peer->vdev_id);
 | |
|     nid_t nid;
 | |
|     nid = vdev_get_nid(vdev);
 | |
|     if (nid) {
 | |
|         /* if vdev's nid is known */
 | |
|         stream = find_stream(peer, nid, \
 | |
|             peer->tei, lid);
 | |
|         if (stream) {
 | |
|             /* if found the stream already */
 | |
|             *stream_ptr = stream;
 | |
|             return ERR_EXIST;
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         /* if vdev's nid is not set, use self peer */
 | |
|         mac_peer_t *self_peer = (mac_peer_t *)vdev->self_peer;
 | |
|         stream = find_stream(peer, nid, \
 | |
|             self_peer->tei, lid);
 | |
|         if (stream) {
 | |
|             /* if found the stream already */
 | |
|             *stream_ptr = stream;
 | |
|             return ERR_EXIST;
 | |
|         }
 | |
|     }
 | |
| #endif
 | |
|     /* create the stream */
 | |
|     uint32_t ret = mac_desc_get(&g_mac_desc_eng, \
 | |
|         PLC_MAC_STREAM_POOL, (void**)&stream);
 | |
|     if (ret) {
 | |
|         /* if the first get stream fail.
 | |
|          * del the oldest stream. and get stream again.
 | |
|          */
 | |
|         mac_vdev_del_timeout_stream_internal(vdev, false);
 | |
|         vdev->stream_overflow_cnt++;
 | |
|         /* get stream again */
 | |
|         ret = mac_desc_get(&g_mac_desc_eng, \
 | |
|             PLC_MAC_STREAM_POOL, (void**)&stream);
 | |
|     }
 | |
| 
 | |
|     if (ret) {
 | |
|         mac_peer_stream_overflow_cnt_dbglog();
 | |
|         return ERR_NOMEM;
 | |
|     }
 | |
|     else {
 | |
|         if (is_tx) {
 | |
|             /* for tx usage */
 | |
|             mac_tx_stream_ctxt_init(&stream->msdu.tx, stream, 1, NULL);
 | |
|         }
 | |
|         else {
 | |
|             /* for rx usage */
 | |
|             reorder_buf_init(&stream->msdu.rx.reorder_buf, \
 | |
|                 stream);
 | |
|         }
 | |
|         iot_single_list_init(&stream->peer_node);
 | |
|         stream->is_tx = (uint8_t)is_tx;
 | |
|         stream->in_swq = 0;
 | |
|         stream->lid = lid;
 | |
|         stream->peer = peer;
 | |
|         stream->phase_in_swq = PLC_PHASE_ALL;
 | |
|         stream->is_rf = is_rf;
 | |
|         /* add into peer */
 | |
|         peer_add_stream(peer, stream);
 | |
|         /* add into hash table */
 | |
|         stream_hash_tbl_add(stream);
 | |
|         /* stream cnt ++*/
 | |
|         vdev->stream_cnt++;
 | |
|         if (!stream->is_tx) {
 | |
|             vdev->rx_stream_cnt++;
 | |
|         }
 | |
|         *stream_ptr = stream;
 | |
|     }
 | |
| #if PLC_SUPPORT_CCO_ROLE
 | |
|     nid_t nid;
 | |
|     vdev_get_nid(get_vdev_ptr(peer->pdev_id, peer->vdev_id), &nid);
 | |
|     iot_printf("%s,vid:%d, str:0x%x, nid: 0x%x, tei:%d, "
 | |
|         "lid:%d, is_tx:%d, is_rf:%d\n", __FUNCTION__, peer->vdev_id,
 | |
|         stream, nid, peer->tei, lid, is_tx, is_rf);
 | |
| #endif
 | |
|     return ERR_OK;
 | |
| }
 | |
| 
 | |
| uint32_t mac_stream_free(mac_peer_t *peer, mac_stream_t *stream)
 | |
| {
 | |
|     IOT_ASSERT(peer);
 | |
|     mac_vdev_t *vdev = get_vdev_ptr(peer->pdev_id, peer->vdev_id);
 | |
|     IOT_ASSERT(vdev);
 | |
|     mac_stream_t *str = stream;
 | |
|     IOT_ASSERT(str != NULL);
 | |
|     /* delete this node from peer */
 | |
|     peer_del_stream(peer, str);
 | |
|     /* delete this node from hash table streamlist */
 | |
|     stream_hash_tbl_del(str);
 | |
|     /* delete from swq streamlist */
 | |
|     mac_swsch_del_stream(str);
 | |
| 
 | |
| #if PLC_SUPPORT_CCO_ROLE
 | |
|     nid_t nid;
 | |
|     vdev_get_nid(get_vdev_ptr(peer->pdev_id, peer->vdev_id), &nid);
 | |
|     iot_printf("%s,vid:%d, str:0x%x, nid: 0x%x, tei:%d, lid:%d, "
 | |
|         "is_tx:%d, is_rf:%d\n", __FUNCTION__, peer->vdev_id,
 | |
|         str, nid, peer->tei, stream->lid, stream->is_tx, stream->is_rf);
 | |
| #endif
 | |
|     /* destroy stream */
 | |
|     if (stream->is_tx) {
 | |
|         mac_tx_stream_ctxt_deinit(str);
 | |
|     } else {
 | |
|         reorder_buf_free(&stream->msdu.rx.reorder_buf);
 | |
|     }
 | |
|     /* stream cnt--*/
 | |
|     vdev->stream_cnt--;
 | |
|     if (!stream->is_tx) {
 | |
|         vdev->rx_stream_cnt--;
 | |
|     }
 | |
|     mac_desc_free(&g_mac_desc_eng, PLC_MAC_STREAM_POOL, stream);
 | |
|     return ERR_OK;
 | |
| }
 | |
| 
 | |
| uint32_t is_stream_empty(mac_stream_t *stream)
 | |
| {
 | |
|     if (stream->is_tx == 0) {
 | |
|         /* is RX stream */
 | |
|         if (stream->msdu.rx.reorder_buf.pb_rdy_cnt != 0) {
 | |
|             return 0;
 | |
|         }
 | |
|     } else {
 | |
|        /* is TX stream */
 | |
|         if (stream->msdu.tx.mac_frame_num != 0 || \
 | |
|             (stream->msdu.tx.cur_tx_msdu != NULL && \
 | |
|             stream->msdu.tx.cur_tx_msdu->in_hwq != 0)) {
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| void mac_stream_exhausted_dump(mac_peer_t *peer)
 | |
| {
 | |
|     uint8_t idx;
 | |
|     mac_vdev_t *vdev;
 | |
|     mac_stream_exhausted_info_t str_dump = {0};
 | |
| 
 | |
|     str_dump.cur_ts = os_boot_time32();
 | |
|     os_mem_cpy(&str_dump.peer, peer, sizeof(*peer));
 | |
| 
 | |
|     for (idx = 0; idx < MAX_VDEV_NUM; idx++) {
 | |
|         vdev = get_vdev_ptr(0, idx);
 | |
|         str_dump.vdev_info[idx].peer_cnt = vdev->peer_cnt;
 | |
|         str_dump.vdev_info[idx].proxy_peer_cnt = vdev->proxy_peer_cnt;
 | |
|         str_dump.vdev_info[idx].stream_cnt = vdev->stream_cnt;
 | |
|         str_dump.vdev_info[idx].rx_stream_cnt = vdev->rx_stream_cnt;
 | |
|         str_dump.vdev_info[idx].stream_overflow_cnt = vdev->stream_overflow_cnt;
 | |
|     }
 | |
| 
 | |
|     IOT_ASSERT_DUMP(0, (uint32_t *)&str_dump,
 | |
|         sizeof(str_dump) / sizeof(uint32_t));
 | |
| }
 |