[Feature][Modem]Update MTK MODEM V1.6 baseline version: MOLY.NR15.R3.MD700.IVT.MP1MR3.MP.V1.6

MTK modem version: MT2735_IVT_MOLY.NR15.R3.MD700.IVT.MP1MR3.MP.V1.6.tar.gz
RF  modem version: NA

Change-Id: I45a4c2752fa9d1a618beacd5d40737fb39ab64fb
diff --git a/mcu/interface/service/hif/cccidev_qbm.h b/mcu/interface/service/hif/cccidev_qbm.h
new file mode 100644
index 0000000..604ab33
--- /dev/null
+++ b/mcu/interface/service/hif/cccidev_qbm.h
@@ -0,0 +1,1080 @@
+/*****************************************************************************
+*  Copyright Statement:
+*  --------------------
+*  This software is protected by Copyright and the information contained
+*  herein is confidential. The software may not be copied and the information
+*  contained herein may not be used or disclosed except with the written
+*  permission of MediaTek Inc. (C) 2012
+*
+*  BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+*  THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+*  RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON
+*  AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+*  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+*  NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+*  SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+*  SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH
+*  THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO
+*  NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S
+*  SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM.
+*
+*  BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE
+*  LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+*  AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+*  OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO
+*  MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+*
+*  THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE
+*  WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF
+*  LAWS PRINCIPLES.  ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND
+*  RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER
+*  THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC).
+*
+*****************************************************************************/
+
+/*******************************************************************************
+ * Filename:
+ * ---------
+ *   cccidev_qbm.h
+ *
+ * Project:
+ * --------
+ *   MOLY
+ *
+ * Description:
+ * ------------
+ *   Defines the common qmu_bm macros or inline functions for CCCI devices
+ *
+ * Author:
+ * -------
+ * -------
+ *
+ * ==========================================================================
+ * $Log$
+ *
+ * 09 18 2020 li-cheng.tsai
+ * [MOLY00569647] [MP7.PRECHECKIN.DEV][Code sync] sync code from T700
+ * [R3.MP][OA][CCCI]code sync from T700
+ *
+ * 09 15 2020 li-cheng.tsai
+ * [MOLY00569647] [MP7.PRECHECKIN.DEV][Code sync] sync code from T700
+ * [MP7.PRECHECKIN.DEV][OA][CCCI]code sync from T700
+ *
+ * 08 04 2020 actory.ou
+ * [MOLY00554534] [Colgin][Code sync] sync code from MT6880.MP
+ * [T700][OA][CCCI] sync from MT6880
+ *
+ * 07 07 2020 actory.ou
+ * [MOLY00543186] [Colgin] code sync to NR15.R3.MT6880.MP
+ * [R3.MT6880.MP][OA][CCCI] sync from COLGIN.SB.SMT.DEV
+ *
+ * 05 19 2020 actory.ou
+ * [MOLY00525599] code sync for Colgin
+ * [NR15.R3.COLGIN.SB.SMT.DEV][OA][CCCI] code sync from 19NOV
+ *
+ * 12 18 2019 actory.ou
+ * [MOLY00465742] [Gen97] add L5 channels and change CCIF SHM layout
+ * [19NOV.DEV][OA] add L5 channels
+ *
+ * 09 15 2017 chien-hui.lu
+ * [MOLY00278317] [CCCI][LHIFCORE][CCISMCORE] Performance improvement of INT_QueryExceptionStatus
+ * [CCCI][LHIFCORE][CCISMCORE] Performance improvement of INT_QueryExceptionStatus.
+ *
+ * 08 01 2017 chien-hui.lu
+ * [MOLY00260287] MD CCCI add cache API enhance to LR12 trunk
+ * [CCCI] cache API enhancement and aggregation, porting from UMOLY.
+ *
+ * 11 08 2016 cs.huang
+ * [MOLY00204430] [CCCI] MT6293 change
+ * [CCCI] Fast CCCI header problem
+ *
+ * 08 15 2016 cs.huang
+ * [MOLY00197453] [CCCI] Fast CCCI header change SPD header size
+ * [CCCI] Fast CCCI header problem
+ *
+ * 06 07 2016 cs.huang
+ * [MOLY00183140] [Coverity Scanned Code Defect]CID:138824 code defect happened in /mcu/common/interface/service/hif/cccidev_qbm.h
+ * [CCCI] Fix coverity warning
+ * 06 01 2016 cs.huang
+ * [MOLY00182647] [CCCI] Fast CCCI header
+ * [CCCI] Fast CCCI header
+ *
+ * 06 01 2016 cs.huang
+ * [MOLY00182647] [CCCI] Fast CCCI header
+ * [CCCI] Fast CCCI header
+ *
+ * 12 28 2015 cs.huang
+ * [MOLY00155074] [CCCI] Fix CCCI header length for TTY and fix IT program
+ * [CCCI] Fix CCCI header length for TTY and fix IT program
+ *
+ * 12 28 2015 cs.huang
+ * [MOLY00155074] [CCCI] Fix CCCI header length for TTY and fix IT program
+ * [CCCI] Fix CCCI header length for TTY and fix IT program
+ * 08 07 2015 cs.huang
+ * [MOLY00136043] cccidev_qbm.h assertion
+ * [CCMNI] Fix SPD EOL problem
+ *
+ *
+ * 08 07 2015 cs.huang
+ * [MOLY00136043] cccidev_qbm.h assertion
+ * [CCMNI] Fix SPD EOL problem
+ *
+ * 08 06 2015 cs.huang
+ * [MOLY00135464] [CCCI] New CCCI handshake flow
+ * [CCCI] New CCCI handshake flow
+ *
+ * 08 06 2015 cs.huang
+ * [MOLY00135464] [CCCI] New CCCI handshake flow
+ * [CCCI] New CCCI handshake flow
+ * 07 28 2015 cs.huang
+ * [MOLY00131268] [CCMNI] Add SPD trace
+ * [CCMNI] Add SPD trace
+ *
+ * 07 28 2015 cs.huang
+ * [MOLY00131268] [CCMNI] Add SPD trace
+ * [CCMNI] Add SPD trace
+ *
+ * 07 24 2015 cs.huang
+ * [MOLY00131268] [CCMNI] Add SPD trace
+ * [CCMNI] Add SPD trace timing
+ *
+ * 07 23 2015 cs.huang
+ * [MOLY00131268] [CCMNI] Add SPD trace
+ * [CCMNI] Add SPD trace
+ *
+ * 05 08 2015 cs.huang
+ * [MOLY00112001] [CCCI] Change CCCI sequence definition in CCCI header.
+ * [CCCI] Change CCCI sequence definition in CCCI header.
+ *
+ * 05 05 2015 cs.huang
+ * [MOLY00111097] [CCCI] Modify RMPU buffer definition
+ * [CCCI] Modify polling buffer defination for RMPU
+ *
+ * 04 15 2015 i-wei.tsai
+ * [MOLY00107626] [TK6291][CCCITTY] enable sequence check and exception SPD support
+ * 	.
+ *
+ * 03 27 2015 ap.wang
+ * [MOLY00100246] [UMOLY] Fix SPD type3 re-layout error
+ * Add CCCI Len for SPD DL v2
+ *
+ * 03 24 2015 ap.wang
+ * [MOLY00100246] [UMOLY] Fix SPD type3 re-layout error
+ * .
+ *
+ * 01 23 2015 ap.wang
+ * [MOLY00092900] [UMOLY] L2 Copro zero len packet on SPD
+ * .
+ *
+ * 12 10 2014 cs.huang
+ * [MOLY00080351] [MT6291][CCCI] Add L1Core CCCI service (CCCI SYSMSG/RPC/IPC)
+ * [CCCI] PCore/L1Core CCCI enhancement
+ *
+ * 11 13 2014 cs.huang
+ * [MOLY00084049] [CCCI] Merging P2P CCCI related change
+ * Merging
+ * 	
+ * 	//UMOLY/DEV/MT6291_DEV/mcu/pcore/...
+ * 	
+ * 	to //UMOLY/TRUNK/UMOLY/mcu/pcore/...
+ *
+ * 11 13 2014 cs.huang
+ * [MOLY00081425] [TK6291_DEV] Add SPD sw solution
+ * Merging
+ * 	
+ * 	//UMOLY/DEV/MT6291_DEV/mcu/pcore/...
+ * 	
+ * 	to //UMOLY/TRUNK/UMOLY/mcu/pcore/...
+ *
+ * 11 13 2014 cs.huang
+ * [MOLY00075481] Add CCCI SPD DL support
+ * Merging
+ * 	
+ * 	//UMOLY/DEV/MT6291_DEV/mcu/pcore/...
+ * 	
+ * 	to //UMOLY/TRUNK/UMOLY/mcu/pcore/...
+ *
+ *
+ *
+ * 08 14 2014 ap.wang
+ * [MOLY00075481] Add CCCI SPD DL support
+ * CCCIDEV SPD DL common process
+ * 07 22 2014 cs.huang
+ * [MOLY00071952] [CCCI] Add new CCCI debug mechanism (1. CCCI seq check 2. AP polling MD status)
+ * [CCCI] ccci new debug mechanism
+ *
+ * 07 22 2014 cs.huang
+ * [MOLY00071952] [CCCI] Add new CCCI debug mechanism (1. CCCI seq check 2. AP polling MD status)
+ * [CCCI] CCCI new debug mechanism
+ * 07 09 2014 cs.huang
+ * [MOLY00071904] [WW FT][4G Gemini][FT][HK][Offical][CSL+PCCW]Externel (EE),0,0,99,/data/core/,1,modem,md0:[ASSERT] file:ccci_rpc_data.c line:587
+ * [CCCI RPC] Fix ccci rpc coding defect in ccci_rpc_receive_cb
+ *
+ * 07 09 2014 cs.huang
+ * [MOLY00071904] [WW FT][4G Gemini][FT][HK][Offical][CSL+PCCW]Externel (EE),0,0,99,/data/core/,1,modem,md0:[ASSERT] file:ccci_rpc_data.c line:587
+ * [CCCI RPC] Fix ccci rpc coding defect in ccci_rpc_receive_cb
+ *
+ * 09 06 2013 ap.wang
+ * [MOLY00036761] [CCCI] Add L2 trace log	[CCCI] Add L2 trace log
+ *
+ * 06 25 2013 ian.cheng
+ * [MOLY00027392] [CCCI]CCCIDEV_GET_QBM_DATALEN should return GPD.len for DHL
+ * [MOLY][CCCI] CCCIDEV_GET_QBM_DATALEN return GPD length even when there's BD
+ *
+ * 03 14 2013 ap.wang
+ * [MOLY00011922] [CCCIDEV] Add GPD cache flush after set checksum
+ * [CCCIDEV] Add GPD cache flush after set checksum
+ *
+ * 02 21 2013 i-wei.tsai
+ * [MOLY00010624] CCCITTY integartion test code revise
+ * remove internal _reset_ccci_common
+ *
+ * 02 04 2013 i-wei.tsai
+ * [MOLY00009892] Rename as MT6290
+ * Rename project name as MT6290
+ *
+ * 01 08 2013 i-wei.tsai
+ * [MOLY00008347] [MT6290] [CCCI] CCCI re-Architecture
+ * sync latest version of new features
+ *
+ * 12 06 2012 ian.cheng
+ * [MOLY00007169] [CCCI_SDIO] MOLY phase in
+ * [MOLY][CCCI_SDIO] 1st version of MT6290 CCCI feature
+ *
+ * 12 06 2012 ian.cheng
+ * [MOLY00007169] [CCCI_SDIO] MOLY phase in
+ * [MOLY][CCCI_SDIO] 1st version of MT6290 CCCI feature
+ ****************************************************************************/
+#ifndef _CCCIDEV_QBM_H
+#define _CCCIDEV_QBM_H
+#include "kal_public_api.h"
+#include "qmu_bm.h"
+#include "qmu_bm_util.h"
+#include "ccci_if.h"          /* ccci_io_request_t */
+#include "hif_spd_ext.h"
+
+#define CCCI_TTY_DEV_NUM (uart_port_ccci_end-uart_port_ccci_start+2) //+2 for uart_port_dhl_sp_expt and uart_port_dhl_ctrl_sp_expt
+
+/*!
+ * @function        [INLINE] CCCICOMM_SET_QBM_DATALEN
+ * @brief           Set data length for QBM_TYPE_CCCI_COMM by QBM_DES_SET_DATALEN
+ *
+ * @param gpd       [IN] pointer to the GPD
+ *
+ * @return          void
+ */
+static __inline void CCCICOMM_SET_QBM_DATALEN(void* gpd, kal_uint32 data_len)
+{
+    void* bd = NULL;
+
+    EXT_ASSERT(NULL != gpd, (kal_uint32)gpd, data_len, 0);
+    /*QBM_TYPE_CCCI_COMM specific function*/
+    EXT_ASSERT(QBM_TYPE_CCCI_COMM == QBM_GET_TYPE(gpd), (kal_uint32)QBM_GET_TYPE(gpd), (kal_uint32)gpd, data_len);
+    /*Must have BD*/
+    EXT_ASSERT(0 != QBM_DES_GET_BDP(gpd), (kal_uint32)QBM_DES_GET_BDP(gpd), (kal_uint32)gpd, data_len);
+
+    bd = QBM_DES_GET_DATAPTR(gpd);
+    EXT_ASSERT(NULL!=bd, (kal_uint32)gpd, (kal_uint32)bd, data_len);
+    QBM_DES_SET_DATALEN(bd, data_len);
+    qbm_cal_set_checksum((kal_uint8 *)bd);
+    
+    QBM_DES_SET_DATALEN(gpd, data_len);
+    qbm_cal_set_checksum((kal_uint8 *)gpd);
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_GET_QBM_DATALEN
+ * @brief           Obtain the data length of first BD in GPD list. 
+ *                  Possible application is used during inserting layer headers ex. CCCI headers.
+ *
+ * @param gpd       [IN] pointer to the GPD
+ *
+ * @return          Return gpd->1st_bd->data_len / gpd->data_len
+ */
+static __inline kal_uint32 CCCIDEV_GET_QBM_DATALEN(void* gpd)
+{
+    kal_uint32 data_len = 0;
+
+    EXT_ASSERT(NULL!=gpd, (kal_uint32) gpd,0 ,0);
+	/* user should put length in gpd.len 
+	   gpd.len = bd.ext_len + bd.len */
+	data_len = QBM_DES_GET_DATALEN(gpd);
+    return data_len;
+}
+
+#include <ex_public.h>
+/*!
+ * @function        [INLINE] CCCIDEV_GET_QBM_DATAPTR
+ * @brief           Obtain the pointer of data. 
+ *
+ * @param gpd       [IN] pointer to the GPD
+ *
+ * @return          Return gpd->1st_bd->p_data_tbd / gpd->p_data_tbd
+ */
+
+static __inline void* CCCIDEV_GET_QBM_DATAPTR(void* gpd)
+{
+    void* bd = NULL;
+    void* data_ptr = NULL;
+    
+    EXT_ASSERT(NULL!=gpd, (kal_uint32) gpd, 0, 0);
+    if(0 != QBM_DES_GET_BDP(gpd)){
+        //4 <case 1> GPD->BD->BUFF
+        bd = QBM_DES_GET_DATAPTR(gpd);
+        EXT_ASSERT(NULL!=bd, (kal_uint32) bd, (kal_uint32) gpd, 0);
+        data_ptr = QBM_DES_GET_DATAPTR(bd);
+    }else{
+        //4 <case 2> GPD->BUFF        
+        data_ptr = QBM_DES_GET_DATAPTR(gpd);
+    }
+
+    //Note: This API is also used in exception mode
+    //In exception mode, DHL may directly dump memory address 0x0 (use 0x0 as data buffer address)
+    if(INT_QueryExceptionStatus() == KAL_FALSE)
+    {
+        EXT_ASSERT(NULL!=data_ptr, (kal_uint32) data_ptr, (kal_uint32) bd, (kal_uint32) gpd);
+    }
+    return data_ptr;
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_GET_GPD_LIST_SIZE
+ * @brief           Traverse the GPD chain to obtain the number of GPDs in GPD chain. 
+ *
+ * @param head      [IN] pointer to the head of the GPD chain
+ * @param tail      [IN] pointer to the tail of the GPD chain
+ *
+ * @return          Number of the GPDs in GPD chain. 
+ */
+static __inline kal_uint32 CCCIDEV_GET_GPD_LIST_SIZE(qbm_gpd *head, qbm_gpd *tail)
+{
+    kal_uint32          cnt = 0;
+
+    if (tail) {
+        while (head) {
+            cnt++;
+            if (head != tail) {
+                head = QBM_DES_GET_NEXT(head);
+            } else {
+                break;
+            }
+        }
+    }
+
+    return cnt;
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_GET_NONBPS_GPD_LIST_SIZE
+ * @brief           Traverse the GPD chain to obtain the number of NON-bypass GPDs in GPD chain. 
+ *
+ * @param head      [IN] pointer to the head of the GPD chain
+ * @param tail      [IN] pointer to the tail of the GPD chain
+ *
+ * @return          Number of the non-bypass GPDs in GPD chain. 
+ */
+static __inline kal_uint32 CCCIDEV_GET_NONBPS_GPD_LIST_SIZE(qbm_gpd *head, qbm_gpd *tail)
+{
+    kal_uint32          cnt = 0;
+
+    if (tail) {
+        while (head) {
+            cnt += (0 == QBM_DES_GET_BPS(head));
+            if (head != tail) {
+                head = QBM_DES_GET_NEXT(head);
+            } else {
+                break;
+            }
+        }
+    }
+
+    return cnt;
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_PUSH_QBM_DATAHEAD
+ * @brief           Push the Tx GPD/BD data pointer. i.e. increase the header room
+ *                  1. move the GPD->1st_BD->data / GPD->data pointer back "offset"
+ *                  2. increase the BD length
+ *                  3. DO NOT flush the Tx BD header -> QMU enqueue should do it
+ *                     Please refer to the mail 20120606 from YiLun
+ *                     >  Folder:  0WCP\0. important announce\programming related
+ *                     >  [Note] CACHE op convention : upper user no need to take TX GPD/BD cache flush(clean)
+ *                  4. set gpd data length
+ *                  5. DO NOT flush the Tx GPD header -> QMU enqueue should do it
+ *                  reference : ETHC_CORE_PUSH_QBM_DATAHEAD
+ *
+ * @param gpd       [IN] pointer to the GPD need modification
+ * @param offset    [IN] move the data_ptr by offset
+ *
+ * @return          void
+ */
+static __inline void CCCIDEV_PUSH_QBM_DATAHEAD(void* gpd, kal_uint32 offset)
+{
+    void* bd = NULL;
+    kal_uint8* data_ptr = NULL;
+    kal_uint32 data_len = 0;
+
+    if(0 != QBM_DES_GET_BDP(gpd)){
+        //4 <case 1> GPD->BD->BUFF
+        /* set bd data ptr */
+        bd = QBM_DES_GET_DATAPTR(gpd);
+        data_ptr =  (kal_uint8*)QBM_DES_GET_DATAPTR(bd);
+        QBM_DES_SET_DATAPTR(bd, data_ptr-offset);
+        /* set bd data len */
+        data_len = QBM_DES_GET_DATALEN(bd);
+        QBM_DES_SET_DATALEN(bd, data_len+offset);
+        /* set bd checksum */
+        qbm_cal_set_checksum(bd);
+
+        /* set gpd data len */
+        data_len = QBM_DES_GET_DATALEN(gpd);
+        QBM_DES_SET_DATALEN(gpd, data_len+offset);
+        /* set gpd checksum */
+        //qbm_cal_set_checksum(gpd);
+    }else{
+        //4 <case 2> GPD->BUFF  
+        /* set gpd data ptr */
+        data_ptr =  (kal_uint8*)QBM_DES_GET_DATAPTR(gpd);
+        QBM_DES_SET_DATAPTR(gpd, data_ptr-offset);
+        /* set gpd data len */
+        data_len = QBM_DES_GET_DATALEN(gpd);
+        QBM_DES_SET_DATALEN(gpd, data_len+offset);
+        /* set gpd checksum */
+        //qbm_cal_set_checksum(gpd);
+    }
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_PULL_QBM_DATAHEAD
+ * @brief           Pull the Tx GPD/BD data pointer. i.e. decrease the header room
+ *
+ *                  <Used in non-network GPD->BD->BUFF >
+ *
+ * @param gpd       [IN] pointer to the GPD need modification
+ * @param offset    [IN] move the data_ptr by offset
+ *
+ * @return          void
+ */
+static __inline void CCCIDEV_PULL_QBM_DATAHEAD(void* gpd, kal_uint32 offset)
+{
+    void* bd = NULL;
+    kal_uint8* data_ptr = NULL;
+    kal_uint32 data_len = 0;
+
+    if(0 != QBM_DES_GET_BDP(gpd)){
+        //4 <case 1> GPD->BD->BUFF
+        /* set bd data ptr */
+        bd = QBM_DES_GET_DATAPTR(gpd);
+        data_ptr =  (kal_uint8*)QBM_DES_GET_DATAPTR(bd);
+        QBM_DES_SET_DATAPTR(bd, data_ptr+offset);
+        /* set bd data len */
+        data_len = QBM_DES_GET_DATALEN(bd);
+        QBM_DES_SET_DATALEN(bd, data_len-offset);
+        /* set bd checksum */
+        qbm_cal_set_checksum(bd);
+        QBM_CACHE_FLUSH_NO_DSR(bd, sizeof(qbm_gpd));
+
+        /* set gpd data len */
+        data_len = QBM_DES_GET_DATALEN(gpd);
+        QBM_DES_SET_DATALEN(gpd, data_len-offset);
+        /* set gpd checksum */
+        qbm_cal_set_checksum(gpd);
+        QBM_CACHE_FLUSH_NO_DSR(gpd, sizeof(qbm_gpd));
+    }else{
+        //4 <case 2> GPD->BUFF  
+        /* set gpd data ptr */
+        data_ptr =  (kal_uint8*)QBM_DES_GET_DATAPTR(gpd);
+        QBM_DES_SET_DATAPTR(gpd, data_ptr+offset);
+        /* set gpd data len */
+        data_len = QBM_DES_GET_DATALEN(gpd);
+        QBM_DES_SET_DATALEN(gpd, data_len-offset);
+        /* set bd checksum */
+        qbm_cal_set_checksum(gpd);
+        QBM_CACHE_FLUSH_NO_DSR(gpd, sizeof(qbm_gpd));
+    }
+    QBM_DSR();
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_QBM_ENQ
+ * @brief           Enqueue p_new_head/p_new_tail to pp_orig_head/pp_orig_tail
+ *
+ * @param p_new_head    [IN] head of new gpd chain
+ * @param p_new_tail    [IN] tail of new gpd chain
+ * @param pp_orig_head  [IN/OUT] head of original gpd chain
+ * @param pp_orig_tail  [IN/OUT] tail of original gpd chain
+ *
+ * @return          void
+ */
+static __inline void CCCIDEV_QBM_ENQ(void *p_new_head, void *p_new_tail, void **pp_orig_head, void **pp_orig_tail)
+{
+    kal_uint8 *p_ori_tail;
+    p_ori_tail = *pp_orig_tail;
+
+    if(*pp_orig_head == NULL){
+        *pp_orig_tail = p_new_tail;
+        *pp_orig_head = p_new_head;
+    }else{
+        /* link new queue */
+        QBM_DES_SET_NEXT(p_ori_tail, p_new_head);
+        // change tail to new tail
+        *pp_orig_tail = p_new_tail;
+    }
+    QBM_DES_SET_NEXT(*pp_orig_tail, NULL);
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_QBM_DEQ
+ * @brief           dequeue n GPD from pp_src_head/pp_src_tail to p_new_head/p_new_tail
+ *                  a accelerate version of qbmt_de_q_n
+ *
+ * @param pp_src_head   [IN/OUT] head of src gpd chain
+ * @param pp_src_tail   [IN/OUT] tail of src gpd chain
+ * @param n             [IN] dequeue count
+ * @param pp_new_head   [OUT] head of dest gpd chain
+ * @param pp_new_tail   [OUT] tail of dest gpd chain
+ *
+ * @return          void
+ */
+static __inline kal_uint32 CCCIDEV_QBM_DEQ(
+		void **pp_src_head, 
+		void **pp_src_tail, 
+		kal_uint32 n,
+		void **pp_new_head,
+		void **pp_new_tail
+		)
+{
+    kal_uint32 deq_num = 0;
+
+    if(0 == n){
+        *pp_new_head = NULL;
+        *pp_new_tail = NULL;
+        return 0;
+    }
+
+    if(NULL == *pp_src_head){
+        EXT_ASSERT(NULL == *pp_src_tail, (kal_uint32)*pp_src_head, (kal_uint32) *pp_src_tail, 0);
+        *pp_new_head = NULL;
+        *pp_new_tail = NULL;
+        return 0;
+    }
+
+    *pp_new_head = *pp_src_head;
+
+
+    //for(deq_num = 0; deq_num < n; deq_num++){
+    do{ 
+        *pp_new_tail = *pp_src_head;
+        deq_num++;
+        *pp_src_head = QBM_DES_GET_NEXT(*pp_src_head);
+    }while((deq_num < n) && (NULL != *pp_src_head));
+
+    if(NULL == *pp_src_head){
+        *pp_src_tail = NULL;
+    }
+    
+    return deq_num;
+}
+
+/*!
+ * @function        [INLINE] CCCIDEV_FIX_IOR_NULL_LAST
+ * @brief           if the last gpd of ior is == NULL, traverse the GPD chain to fill the last_gpd info
+ *
+ * @param ior       [IN] input ior
+ *
+ * @return          void
+ */
+static __inline void CCCIDEV_FIX_IOR_NULL_LAST(ccci_io_request_t* ior)
+{
+    qbm_gpd             *last_gpd = ior->last_gpd; 
+    qbm_gpd             *first_gpd = ior->first_gpd;
+    qbm_gpd             *current_gpd;
+    
+    /*  fix the last_gpd == NULL case */
+    if (last_gpd == NULL)
+    {
+        current_gpd = first_gpd;
+        while ( current_gpd->p_next != NULL )
+        {
+            current_gpd = current_gpd->p_next;
+        }
+        ior->last_gpd = current_gpd;
+    }
+}
+
+
+/*!
+ * @function        [INLINE] CCCIDEV_RM_CCCI_HEADERS 
+ * @brief           Tool function to remove the CCCI header in GPD->BD->BUFF / GPD->BUFF
+ *
+ * @param channel   [IN] channel number for this CCCI header
+ * @param gpd       [IN] pointer to the gpd
+ *
+ * @return          KAL_TRUE: success, KAL_FALSE: channel number not matched
+ */
+static __inline kal_bool CCCIDEV_RM_CCCI_HEADERS(CCCI_CHANNEL_T channel, qbm_gpd *gpd)
+{
+    CCCI_BUFF_T         *pdata;
+
+    //4 <1> check gpd->bd->buff->channel = channel
+    pdata = CCCIDEV_GET_QBM_DATAPTR(gpd);
+    EXT_ASSERT(pdata, (kal_uint32)pdata, (kal_uint32)gpd, (kal_uint32)channel);
+
+    /* treat channel not match and size = 0 as invalid GPD*/
+    if(pdata->channel != channel || pdata->data[1] == sizeof(CCCI_BUFF_T)){
+        //hif_trace_error(CCCIDEV_TR_UL_CCCI_CH_ERR, pdata->channel, channel);
+        return KAL_FALSE;
+    }else{
+        //4 <2> move data pointer to raw data
+        CCCIDEV_PULL_QBM_DATAHEAD(gpd, sizeof(CCCI_BUFF_T));
+    }
+
+    return KAL_TRUE;
+}
+
+/*!
+ * @function        [INLINE] ccci_dest_ior
+ * @brief           Traverse ior chain to free the linked ior/GPD/BD/Buff 
+ *
+ * @param ior       [IN] pointer to the ior chain
+ *
+ * @return          void
+ */
+static __inline void ccci_dest_ior(ccci_io_request_t *ior){
+    ccci_io_request_t    *next_ior;
+    qbm_gpd*            current_gpd;
+
+    EXT_ASSERT(ior, (kal_uint32)ior, 0, 0);
+    for (; ior; ior = next_ior) {
+        next_ior = ior->next_request;
+        EXT_ASSERT(ior->first_gpd, (kal_uint32)ior->first_gpd, (kal_uint32)ior,  (kal_uint32)next_ior);
+
+        /*  fix the last_gpd == NULL case */
+        if (ior->last_gpd == NULL)
+        {
+            current_gpd = ior->first_gpd;
+            while ( current_gpd->p_next != NULL )
+            {
+                current_gpd = current_gpd->p_next;
+            }
+            ior->last_gpd = current_gpd;
+        }
+        
+        EXT_ASSERT(ior->first_gpd && ior->last_gpd, (kal_uint32)ior->first_gpd, (kal_uint32)ior->last_gpd, (kal_uint32)ior);
+        qbmt_dest_q(ior->first_gpd, ior->last_gpd);
+    }
+}
+
+/* Get address by a offset of the PD */
+#define CCCIDEV_QBM_DES_GET_ADDR_BY_OFFSET(_p, _ofst)  \
+          (void*)((kal_uint8*)(_p) + (_ofst))
+
+/*  Used for buffer and descriptor are in continus memory address */
+#define CCCIDEV_QBM_DES_SET_DATA_BY_OFFSET(_p, _ofst)  \
+          (QBM_GET_GPD_PTR(_p)->p_data_tbd = CCCIDEV_QBM_DES_GET_ADDR_BY_OFFSET(_p, _ofst))
+
+#define CCCI_COMM_BD_OFST    (64) /* p_data_tbd = 64 */
+#define CCCI_COMM_GET_BD(_p) CCCIDEV_QBM_DES_GET_ADDR_BY_OFFSET(_p, CCCI_COMM_BD_OFST)
+
+/* this code will set datalen and extlen to 0 */
+/* Because in descriptor, they are in the 12 bytes */
+#define CCCI_COMM_RESET_DATALEN_EXTLEN(_p) *(kal_uint32*)((kal_uint8*)(_p) + 12) = 0
+
+/*!
+ * @function        [INLINE] CCCIDEV_RST_CCCI_COMM_GPD_LIST
+ * @brief           Reset the GPD list to the default value, type has to be QBM_TYPE_CCCI_COMM
+ *
+ * @param first_gpd [IN] pointer to the first GPD in the GPD chain
+ * @param last_gpd  [IN] pointer to the last GPD in the GPD chain
+ *
+ * @return          void
+ */
+static __inline kal_uint32 CCCIDEV_RST_CCCI_COMM_GPD_LIST(qbm_gpd* first_gpd, qbm_gpd* last_gpd)
+{
+    qbm_gpd*            current_gpd = NULL;
+    qbm_gpd*            next_gpd = NULL;
+    kal_uint32          num_gpd = 0;
+    
+    EXT_ASSERT(first_gpd && last_gpd, (kal_uint32)first_gpd, (kal_uint32)last_gpd, 0);
+    current_gpd = first_gpd;
+
+    do {
+        next_gpd = QBM_DES_GET_NEXT(current_gpd);
+        qbm_reset_pd(QBM_TYPE_CCCI_COMM, (void*)current_gpd);
+        qbm_cal_set_checksum((kal_uint8 *)current_gpd);
+        QBM_CACHE_FLUSH_NO_DSR(current_gpd, sizeof(qbm_gpd));
+        num_gpd ++;
+        if ( current_gpd == last_gpd )
+        {
+            break;
+        }        
+        current_gpd = next_gpd;
+    } while ( current_gpd != NULL );
+    QBM_DSR();
+    return num_gpd;
+}
+
+/*!
+ * @function        [Prototype] cccidev_dl_header_handle_cb
+ * @brief           Prototype of function to handle each packet's CCCI Header
+ *
+ * @param pDevice     [IN] pointer to the CCCIDEV device
+ * @param p_ccci_head [IN] pointer to ccci header buffer
+ * @param kal_uint8   [IN] pointer to the data buffer
+ *
+ * @return          0, unused 
+ */
+typedef kal_uint32 (*cccidev_dl_header_handle_cb)(void* p_device, CCCI_BUFF_T* p_ccci_head, kal_uint8* pdata, kal_uint32 data_len);
+
+/*!
+ * @function        [static] CCCIDEV_SPD_PI_RELAYOUT
+ * @brief           Traverse the input GPD list and insert the CCCI header on the first BD->data 
+ *
+ * @param spd       [IN] pointer to the SPD in the GPD chain
+ *
+ * @return          relayout success
+ */
+static __inline kal_uint32 CCCIDEV_SPD_PI_RELAYOUT(qbm_gpd* p_spd){
+    hif_spd_ext_header          *p_spd_ext = (hif_spd_ext_header*)QBM_SPD_GET_EXT((qbm_spd*)p_spd);
+    hif_spd_packet_header       *p_spd_ph;
+    qbm_spd_pie                 *p_spd_pie;
+    qbm_spd_pi                  *p_spd_pi = QBM_SPD_GET_PI((qbm_spd*)p_spd); 
+    kal_uint16                  i, pkt_num = 0, header_len = sizeof(CCCI_BUFF_T);
+    
+    EXT_ASSERT(QBM_DES_GET_PDT(p_spd) == DES_FLAG_BIT_SPD3,QBM_DES_GET_PDT(p_spd), (kal_uint32)p_spd, 0);
+    p_spd_pie = QBM_SPD_PI_GET_FIRST_PIE(p_spd_pi);
+    p_spd_ph = HIF_SPD_EXT_GET_FIRST_PH(p_spd_ext);
+    pkt_num = QBM_SPD_PI_GET_PKTNUM(p_spd_pi);
+    HIF_SPD_EXT_SET_PKTNUM(p_spd_ext, pkt_num);
+    HIF_SPD_EXT_SET_SPD1_HEADERLEN(p_spd_ext , (kal_uint8)header_len);
+#ifdef CCCIDEV_SPD_RELAYOUT_SET_PKTNUM_TRACE
+    if(KAL_TRUE != kal_query_systemInit() && KAL_FALSE == INT_QueryExceptionStatus())
+        CCCIDEV_SPD_RELAYOUT_SET_PKTNUM_TRACE(pkt_num, (kal_uint32) p_spd_pi, (kal_uint32) p_spd_ext);
+#endif 
+    QBM_DES_SET_SPD1(p_spd);
+    for(i = 1; i <=pkt_num; i++){
+        kal_mem_cpy(p_spd_ph, p_spd_pie, sizeof(qbm_spd_pie));
+		// @This is the last packet : check if EPDCP set the EOL correctly, if this assert happend, please contact EPDCP owner
+        if (i == pkt_num){
+            EXT_ASSERT(HIF_SPD_PH_GET_EOL(p_spd_ph), \
+                (kal_uint32)p_spd, pkt_num, (kal_uint32)p_spd_ph);
+        }
+		// @This is an EOL packet : if get EOL but not last packet, should break, no need to handle remaining packet
+        else if (HIF_SPD_PH_GET_EOL(p_spd_ph)){
+#ifdef CCCIDEV_SPD_RELAYOUT_GET_EOL_BREAK_TRACE
+            if(KAL_TRUE != kal_query_systemInit() && KAL_FALSE == INT_QueryExceptionStatus())
+                CCCIDEV_SPD_RELAYOUT_GET_EOL_BREAK_TRACE(pkt_num, i, (kal_uint32) p_spd_ph);
+#endif           
+			break;
+        }
+		// @This is a normal packet : if get not last GPD, move to next ph
+		else{
+            p_spd_ph = HIF_SPD_PH_NEXT(p_spd_ph, (kal_uint8)header_len);
+            p_spd_pie = QBM_SPD_PIE_NEXT(p_spd_pie);
+        }
+    }
+    return 0;
+}
+
+
+/*!
+ * @function        [static] CCCIDEV_PROCESS_DL_GPD_LIST
+ * @brief           Traverse the input GPD list and insert the CCCI header on the first BD->data 
+ *
+ * @param pDevice   [IN] pointer to the CCCIDEV device
+ * @param first_gpd [IN] pointer to the first GPD in the GPD chain
+ * @param last_gpd  [IN] pointer to the last GPD in the GPD chain
+ * @param cccidev_dl_header_handle_cb  [IN] pointer of the callback function to handle each packet's CCCI Header
+ *
+ * @return          number of gpd/spd in between first_gpd and last_gpd
+ */
+static __inline kal_uint32 CCCIDEV_PROCESS_DL_GPD_LIST(void* pDevice, qbm_gpd* first_gpd, qbm_gpd* last_gpd, cccidev_dl_header_handle_cb cb)
+{  /*process_tx_gpd_list*/
+    qbm_gpd*            p_gpd = NULL;
+    kal_uint32          gpd_cnt = 0;
+    CCCI_BUFF_T         *p_ccci_head;
+    kal_uint8           current_pdt;
+    hif_spd_packet_header  *p_spd_ph, *p_spd_ph_next=NULL;  //spd packet head
+    kal_uint8           *p_payload, *p_payload_next=NULL; //spd packet payload
+    hif_spd_ext_header  *p_spd_ext; //spd extention
+    kal_uint16          pkt_num;
+    kal_uint32          i, payload_len, header_len=0;
+    kal_uint32          data_len = 0;
+    EXT_ASSERT(first_gpd && last_gpd, (kal_uint32)first_gpd, (kal_uint32)last_gpd, (kal_uint32)pDevice);
+  
+    p_gpd = first_gpd;
+    do {
+        current_pdt = QBM_DES_GET_PDT(p_gpd);
+        if(current_pdt == DES_FLAG_BIT_SPD3){
+            CCCIDEV_SPD_PI_RELAYOUT(p_gpd);
+            current_pdt = QBM_DES_GET_PDT(p_gpd);
+        }
+        
+        EXT_ASSERT((( current_pdt == DES_FLAG_BIT_SPD1) || (current_pdt == DES_FLAG_BIT_GPD)), \
+            (kal_uint32)current_pdt, (kal_uint32)pDevice, (kal_uint32)p_gpd);
+
+        if(current_pdt == DES_FLAG_BIT_SPD1){
+        //4 <1> SPD1 handling
+            p_spd_ext = (hif_spd_ext_header*)QBM_SPD_GET_EXT((qbm_spd*)p_gpd);
+            p_spd_ph = HIF_SPD_EXT_GET_FIRST_PH((hif_spd_ext_header*)p_spd_ext);
+            p_payload = QBM_DES_GET_DATAPTR((qbm_spd*)p_gpd);
+            pkt_num = HIF_SPD_EXT_GET_PKTNUM((hif_spd_ext_header*)p_spd_ext);
+            //[tk6291_mw_hif_spd_design_doc_v1.0: The packet number should be larger than 1 
+            EXT_ASSERT(pkt_num >= 1, pkt_num, (kal_uint32)p_spd_ext, (kal_uint32)(qbm_spd*)p_gpd);
+            
+            //4 <1.1> set spd1_header_len = sizeof(CCCI_BUFF_T);
+            //Header length fill by first user
+            header_len = HIF_SPD_EXT_GET_SPD1_HEADERLEN((hif_spd_ext_header*)p_spd_ext);
+            p_spd_ph_next = HIF_SPD_PH_NEXT(p_spd_ph, header_len); // initial
+#ifdef CCCIDEV_PROCESS_DL_SPD_TRACE
+            if(KAL_TRUE != kal_query_systemInit() && KAL_FALSE == INT_QueryExceptionStatus())
+                CCCIDEV_PROCESS_DL_SPD_TRACE(pkt_num, (kal_uint32)p_gpd);
+#endif
+            //4 <1.2> Set IGR for 1st pkt, for MBIM use only, and move to 2nd PH   
+            //iterate from the 1st ph
+            for (i=1; i<=pkt_num; i++){
+                payload_len = HIF_SPD_PH_GET_PAYLOAD_LEN(p_spd_ph);
+                if (i != pkt_num){
+                    p_spd_ph_next = HIF_SPD_PH_NEXT(p_spd_ph, header_len);
+                    p_payload_next = QBM_SPD_PAYLOAD_NEXT(p_payload, payload_len);
+                }    
+                if(0 == HIF_SPD_PH_GET_IGR(p_spd_ph)){ // if IGR bit is not set
+                    //4 <1.3> append CCCI header CCCI_BUFF_T
+                    p_ccci_head = (CCCI_BUFF_T *)HIF_SPD_PH_GET_HEADER(p_spd_ph);
+                    // call CCCI Device CCCI header handle 
+                    cb(pDevice, p_ccci_head, p_payload, payload_len);
+                    // payload length check, if this assert happend, please contact EPDCP owner
+                    EXT_ASSERT(payload_len, \
+                        (kal_uint32)p_gpd, pkt_num, (kal_uint32)p_spd_ph);
+                }
+                else{ // if IGR bit is set
+#ifdef CCCIDEV_PROCESS_DL_SPD_SET_IGR_TRACE
+                    if(KAL_TRUE != kal_query_systemInit() && KAL_FALSE == INT_QueryExceptionStatus())
+                        CCCIDEV_PROCESS_DL_SPD_SET_IGR_TRACE(HIF_SPD_PH_GET_IGR(p_spd_ph), i, (kal_uint32)p_spd_ph);
+#endif
+                }
+				// @This is the last packet : check if EPDCP set the EOL correctly, if this assert happend, please contact EPDCP owner
+                if (i == pkt_num)
+                {
+                    EXT_ASSERT(HIF_SPD_PH_GET_EOL(p_spd_ph), \
+                        (kal_uint32)p_gpd, pkt_num, (kal_uint32)p_spd_ph);
+                    QBM_CACHE_FLUSH(p_spd_ext, sizeof(qbm_spd_ext));
+                }       
+				// @This is an EOL packet :if get EOL but not last packet, should breakm, no need to handle remaining packet
+                else if (HIF_SPD_PH_GET_EOL(p_spd_ph)){
+#ifdef CCCIDEV_SPD_RELAYOUT_GET_EOL_BREAK_TRACE
+                    if(KAL_TRUE != kal_query_systemInit() && KAL_FALSE == INT_QueryExceptionStatus())
+                        CCCIDEV_SPD_RELAYOUT_GET_EOL_BREAK_TRACE(pkt_num, i, (kal_uint32) p_spd_ph);
+#endif           
+                    QBM_CACHE_FLUSH(p_spd_ext, sizeof(qbm_spd_ext));
+			        break;
+                }
+				// @This is a normal packet : if get not last GPD, move to next ph
+                else 
+                {
+                    p_spd_ph = p_spd_ph_next;
+                    p_payload = p_payload_next;
+                }            
+            }
+        }else
+        {
+        //4 <2> GPD handling
+            /* ASSERT if not GPD type, if this assert happens meaning DL is neither SPD type1 nor GPD
+               please seek for EPDCP owner's help*/
+            EXT_ASSERT((current_pdt == DES_FLAG_BIT_GPD), \
+            (kal_uint32)current_pdt, (kal_uint32)pDevice, (kal_uint32)p_gpd);
+        
+            //4 <2.1> append CCCI header CCCI_BUFF_T
+            // CCCIDEV append ccci header in BD EXT(only for CCMNI with first empty BD) or GPD EXT
+            if((0 != QBM_DES_GET_BDP(p_gpd)) && (0 == QBM_DES_GET_DATALEN (QBM_DES_GET_DATAPTR(p_gpd)))){
+                //4 <case 1> GPD->BD->BUFF
+                /* Store the DL CCCI header in the BD extention part */
+                void* bd = QBM_DES_GET_DATAPTR(p_gpd);
+                QBM_DES_SET_EXTLEN(bd, sizeof(CCCI_BUFF_T));
+                p_ccci_head = (CCCI_BUFF_T *)QBM_DES_GET_EXT(bd);
+                qbm_cal_set_checksum(bd);
+                QBM_CACHE_FLUSH(bd, sizeof(qbm_gpd));
+                QBM_DES_SET_DATALEN(p_gpd, CCCIDEV_GET_QBM_DATALEN(p_gpd)+sizeof(CCCI_BUFF_T));
+                data_len = CCCIDEV_GET_QBM_DATALEN(p_gpd);
+            }
+            else{
+                //4 <case 2> GPD->BUFF
+                /* Store the DL CCCI header in the GPD extention part */
+                QBM_DES_SET_EXTLEN(p_gpd, sizeof(CCCI_BUFF_T));
+                p_ccci_head = (CCCI_BUFF_T *)QBM_DES_GET_EXT(p_gpd);
+                data_len = CCCIDEV_GET_QBM_DATALEN(p_gpd) + sizeof(CCCI_BUFF_T);
+            }     
+            p_payload = CCCIDEV_GET_QBM_DATAPTR(p_gpd);
+            // call CCCI Device CCCI header handle 
+            cb(pDevice, p_ccci_head, p_payload, data_len);
+
+        }        
+        gpd_cnt++;
+        if ( p_gpd == last_gpd )
+        {
+            break;
+        }
+        //make sure there is no invalid GPD in the list
+        EXT_ASSERT(QBM_DES_GET_NEXT(p_gpd) != NULL, (kal_uint32)p_gpd, (kal_uint32)first_gpd, (kal_uint32)last_gpd);
+        p_gpd = QBM_DES_GET_NEXT(p_gpd);
+    } while ( p_gpd != NULL );
+    return gpd_cnt;
+}
+
+/*!
+ * @function        [Prototype] cccidev_dl_header_handle_cb
+ * @brief           Prototype of function to handle each packet's CCCI Header
+ *
+ * @param pDevice     [IN] pointer to the CCCIDEV device
+ * @param p_ccci_head [IN] pointer to ccci header buffer
+ * @param kal_uint8   [IN] pointer to the data buffer
+ *
+ * @return          0, unused 
+ */
+typedef kal_uint32 (*cccidev_dl_header_handle_only_first_cb)(void* p_device, CCCI_BUFF_T* p_ccci_head, kal_uint8* pdata, kal_uint32 data_len, kal_uint16 rem_gpd);
+
+/*!
+ * @function        [static] CCCIDEV_PROCESS_DL_GPD_LIST
+ * @brief           Traverse the input GPD list and insert the CCCI header on the first BD->data 
+ *
+ * @param pDevice   [IN] pointer to the CCCIDEV device
+ * @param first_gpd [IN] pointer to the first GPD in the GPD chain
+ * @param last_gpd  [IN] pointer to the last GPD in the GPD chain
+ * @param cccidev_dl_header_handle_cb  [IN] pointer of the callback function to handle each packet's CCCI Header
+ *
+ * @return          number of gpd/spd in between first_gpd and last_gpd
+ */
+static __inline kal_uint32 CCCIDEV_PROCESS_DL_GPD_LIST_ONLY_FIRST(void* pDevice, qbm_gpd* first_gpd, qbm_gpd* last_gpd, cccidev_dl_header_handle_only_first_cb cb, kal_uint16 rem_gpd)
+{  /*process_tx_gpd_list*/
+    qbm_gpd*            p_gpd = NULL;
+    kal_uint32          gpd_cnt = 0;
+    CCCI_BUFF_T         *p_ccci_head;
+    kal_uint8           current_pdt;
+    hif_spd_packet_header  *p_spd_ph, *p_spd_ph_next=NULL;  //spd packet head
+    kal_uint8           *p_payload, *p_payload_next=NULL; //spd packet payload
+    hif_spd_ext_header  *p_spd_ext; //spd extention
+    kal_uint16          pkt_num;
+    kal_uint32          i, payload_len, header_len =0;
+    kal_bool            is_first_SPD = KAL_TRUE;
+
+    EXT_ASSERT(first_gpd && last_gpd, (kal_uint32)first_gpd, (kal_uint32)last_gpd, (kal_uint32)pDevice);
+  
+    p_gpd = first_gpd;
+    do {
+        current_pdt = QBM_DES_GET_PDT(p_gpd);
+        if(current_pdt == DES_FLAG_BIT_SPD3){
+            CCCIDEV_SPD_PI_RELAYOUT(p_gpd);
+            current_pdt = QBM_DES_GET_PDT(p_gpd);
+        }
+
+	EXT_ASSERT((( current_pdt == DES_FLAG_BIT_SPD1) || (current_pdt == DES_FLAG_BIT_GPD)), \
+            (kal_uint32)current_pdt, (kal_uint32)pDevice, (kal_uint32)p_gpd);
+
+        if(current_pdt == DES_FLAG_BIT_SPD1)	
+	{
+            p_spd_ext = (hif_spd_ext_header*)QBM_SPD_GET_EXT((qbm_spd*)p_gpd);
+            p_spd_ph = HIF_SPD_EXT_GET_FIRST_PH((hif_spd_ext_header*)p_spd_ext);
+	    p_spd_ph_next = HIF_SPD_PH_NEXT(p_spd_ph, 0); //initial
+            p_payload = QBM_DES_GET_DATAPTR((qbm_spd*)p_gpd);
+            pkt_num = HIF_SPD_EXT_GET_PKTNUM((hif_spd_ext_header*)p_spd_ext);
+            //[tk6291_mw_hif_spd_design_doc_v1.0: The packet number should be larger than 1 
+            EXT_ASSERT(pkt_num >= 1, pkt_num, (kal_uint32)p_spd_ext, (kal_uint32)(qbm_spd*)p_gpd);
+            
+            //4 <1.1> set spd1_header_len = sizeof(CCCI_BUFF_T);
+            //Header length fill by first user
+            header_len = HIF_SPD_EXT_GET_SPD1_HEADERLEN((hif_spd_ext_header*)p_spd_ext);
+#ifdef CCCIDEV_PROCESS_DL_SPD_TRACE
+            if(KAL_TRUE != kal_query_systemInit() && KAL_FALSE == INT_QueryExceptionStatus())
+                CCCIDEV_PROCESS_DL_SPD_TRACE(pkt_num, (kal_uint32)p_gpd);
+#endif
+            //4 <1.2> Set IGR for 1st pkt, for MBIM use only, and move to 2nd PH   
+            //iterate from the 1st ph
+	    if(is_first_SPD == KAL_TRUE){ // only add CCCI header to first SPD
+	        is_first_SPD = KAL_FALSE;
+		for (i=1; i<=pkt_num; i++){
+		    payload_len = HIF_SPD_PH_GET_PAYLOAD_LEN(p_spd_ph);
+		    if (i != pkt_num){
+			p_spd_ph_next = HIF_SPD_PH_NEXT(p_spd_ph, header_len); 
+			p_payload_next = QBM_SPD_PAYLOAD_NEXT(p_payload, payload_len);
+		    }    
+		    if(0 == HIF_SPD_PH_GET_IGR(p_spd_ph)){ // if IGR bit is not set
+			//4 <1.3> append CCCI header CCCI_BUFF_T
+			p_ccci_head = (CCCI_BUFF_T *)HIF_SPD_PH_GET_HEADER(p_spd_ph);
+			// call CCCI Device CCCI header handle 
+			cb(pDevice, p_ccci_head, p_payload, payload_len, rem_gpd);
+			// This is SPD mode
+			if(rem_gpd > 0)
+			    p_ccci_head->data[1] = p_ccci_head->data[1] | (1 << 31);
+			// payload length check, if this assert happend, please contact EPDCP owner
+			EXT_ASSERT(payload_len, \
+			    (kal_uint32)p_gpd, pkt_num, (kal_uint32)p_spd_ph);
+			break;//only handle first header
+		    }
+		    else{ // if IGR bit is set
+#ifdef CCCIDEV_PROCESS_DL_SPD_SET_IGR_TRACE
+			if(KAL_TRUE != kal_query_systemInit() && KAL_FALSE == INT_QueryExceptionStatus())
+			    CCCIDEV_PROCESS_DL_SPD_SET_IGR_TRACE(HIF_SPD_PH_GET_IGR(p_spd_ph), i, (kal_uint32)p_spd_ph);
+#endif
+		    }
+		    // @This is a normal packet : if get not last GPD, move to next ph
+		    //else 
+		    {
+			p_spd_ph = p_spd_ph_next;
+			p_payload = p_payload_next;
+		    }    
+		}
+	    }
+	    // Flush all spd ext
+            QBM_CACHE_FLUSH(p_spd_ext, sizeof(qbm_spd_ext));
+	}
+	else
+        {
+        //4 <2> GPD handling
+            /* ASSERT if not GPD type, if this assert happens meaning DL is neither SPD type1 nor GPD
+               please seek for EPDCP owner's help*/
+            EXT_ASSERT((current_pdt == DES_FLAG_BIT_GPD), \
+            (kal_uint32)current_pdt, (kal_uint32)pDevice, (kal_uint32)p_gpd);
+        
+            //4 <2.1> append CCCI header CCCI_BUFF_T
+            // CCCIDEV append ccci header in BD EXT(only for CCMNI with first empty BD) or GPD EXT
+            if((0 != QBM_DES_GET_BDP(p_gpd)) && (0 == QBM_DES_GET_DATALEN (QBM_DES_GET_DATAPTR(p_gpd)))){
+                //4 <case 1> GPD->BD->BUFF
+                /* Store the DL CCCI header in the BD extention part */
+                void* bd = QBM_DES_GET_DATAPTR(p_gpd);
+                QBM_DES_SET_EXTLEN(bd, sizeof(CCCI_BUFF_T));
+                p_ccci_head = (CCCI_BUFF_T *)QBM_DES_GET_EXT(bd);
+                qbm_cal_set_checksum(bd);
+                QBM_CACHE_FLUSH(bd, sizeof(qbm_gpd));
+                QBM_DES_SET_DATALEN(p_gpd, CCCIDEV_GET_QBM_DATALEN(p_gpd)+sizeof(CCCI_BUFF_T));
+                CCCI_STREAM_SET_LEN(p_ccci_head, CCCIDEV_GET_QBM_DATALEN(p_gpd));
+                /* hifsdioq_set_gpd will set HWO and set checksum */
+                //qbm_cal_set_checksum(p_gpd);
+                //QBM_CACHE_FLUSH(p_gpd, sizeof(qbm_gpd));
+            }
+            else{
+                //4 <case 2> GPD->BUFF
+                /* Store the DL CCCI header in the GPD extention part */
+                QBM_DES_SET_EXTLEN(p_gpd, sizeof(CCCI_BUFF_T));
+                p_ccci_head = (CCCI_BUFF_T *)QBM_DES_GET_EXT(p_gpd);
+                CCCI_STREAM_SET_LEN(p_ccci_head, CCCIDEV_GET_QBM_DATALEN(p_gpd)+sizeof(CCCI_BUFF_T));
+            }     
+            p_payload = CCCIDEV_GET_QBM_DATAPTR(p_gpd);
+            // call CCCI Device CCCI header handle 
+            cb(pDevice, p_ccci_head, p_payload, CCCIDEV_GET_QBM_DATALEN(p_gpd), rem_gpd);
+            break; // GPD mode only processes first gpd
+        }        
+        gpd_cnt++;
+        if ( p_gpd == last_gpd )
+        {
+            break;
+        }
+        //make sure there is no invalid GPD in the list
+        EXT_ASSERT(QBM_DES_GET_NEXT(p_gpd) != NULL, (kal_uint32)p_gpd, (kal_uint32)first_gpd, (kal_uint32)last_gpd);
+        p_gpd = QBM_DES_GET_NEXT(p_gpd);
+    } while ( p_gpd != NULL );
+    return gpd_cnt;
+}
+
+#endif //#ifndef _CCCIDEV_QBM_H