diff options
Diffstat (limited to 'drivers/staging/bcm/PHSModule.c')
| -rw-r--r-- | drivers/staging/bcm/PHSModule.c | 1448 |
1 files changed, 1448 insertions, 0 deletions
diff --git a/drivers/staging/bcm/PHSModule.c b/drivers/staging/bcm/PHSModule.c new file mode 100644 index 00000000000..07c5a0bae1e --- /dev/null +++ b/drivers/staging/bcm/PHSModule.c @@ -0,0 +1,1448 @@ +#include "headers.h" + +static UINT CreateSFToClassifierRuleMapping(B_UINT16 uiVcid, B_UINT16 uiClsId, struct bcm_phs_table *psServiceFlowTable, struct bcm_phs_rule *psPhsRule, B_UINT8 u8AssociatedPHSI); + +static UINT CreateClassiferToPHSRuleMapping(B_UINT16 uiVcid, B_UINT16 uiClsId, struct bcm_phs_entry *pstServiceFlowEntry, struct bcm_phs_rule *psPhsRule, B_UINT8 u8AssociatedPHSI); + +static UINT CreateClassifierPHSRule(B_UINT16 uiClsId, struct bcm_phs_classifier_table *psaClassifiertable, struct bcm_phs_rule *psPhsRule, enum bcm_phs_classifier_context eClsContext, B_UINT8 u8AssociatedPHSI); + +static UINT UpdateClassifierPHSRule(B_UINT16 uiClsId, struct bcm_phs_classifier_entry *pstClassifierEntry, struct bcm_phs_classifier_table *psaClassifiertable, struct bcm_phs_rule *psPhsRule, B_UINT8 u8AssociatedPHSI); + +static bool ValidatePHSRuleComplete(struct bcm_phs_rule *psPhsRule); + +static bool DerefPhsRule(B_UINT16 uiClsId, struct bcm_phs_classifier_table *psaClassifiertable, struct bcm_phs_rule *pstPhsRule); + +static UINT GetClassifierEntry(struct bcm_phs_classifier_table *pstClassifierTable, B_UINT32 uiClsid, enum bcm_phs_classifier_context eClsContext, struct bcm_phs_classifier_entry **ppstClassifierEntry); + +static UINT GetPhsRuleEntry(struct bcm_phs_classifier_table *pstClassifierTable, B_UINT32 uiPHSI, enum bcm_phs_classifier_context eClsContext, struct bcm_phs_rule **ppstPhsRule); + +static void free_phs_serviceflow_rules(struct bcm_phs_table *psServiceFlowRulesTable); + +static int phs_compress(struct bcm_phs_rule *phs_members, unsigned char *in_buf, + unsigned char *out_buf, unsigned int *header_size, UINT *new_header_size); + +static int verify_suppress_phsf(unsigned char *in_buffer, unsigned char *out_buffer, + unsigned char *phsf, unsigned char *phsm, unsigned int phss, unsigned int phsv, UINT *new_header_size); + +static int phs_decompress(unsigned char *in_buf, unsigned char *out_buf, + struct bcm_phs_rule *phs_rules, UINT *header_size); + +static ULONG PhsCompress(void *pvContext, + B_UINT16 uiVcid, + B_UINT16 uiClsId, + void *pvInputBuffer, + void *pvOutputBuffer, + UINT *pOldHeaderSize, + UINT *pNewHeaderSize); + +static ULONG PhsDeCompress(void *pvContext, + B_UINT16 uiVcid, + void *pvInputBuffer, + void *pvOutputBuffer, + UINT *pInHeaderSize, + UINT *pOutHeaderSize); + +#define IN +#define OUT + +/* + * Function: PHSTransmit + * Description: This routine handle PHS(Payload Header Suppression for Tx path. + * It extracts a fragment of the NDIS_PACKET containing the header + * to be suppressed. It then suppresses the header by invoking PHS exported compress routine. + * The header data after suppression is copied back to the NDIS_PACKET. + * + * Input parameters: IN struct bcm_mini_adapter *Adapter - Miniport Adapter Context + * IN Packet - NDIS packet containing data to be transmitted + * IN USHORT Vcid - vcid pertaining to connection on which the packet is being sent.Used to + * identify PHS rule to be applied. + * B_UINT16 uiClassifierRuleID - Classifier Rule ID + * BOOLEAN bHeaderSuppressionEnabled - indicates if header suprression is enabled for SF. + * + * Return: STATUS_SUCCESS - If the send was successful. + * Other - If an error occurred. + */ + +int PHSTransmit(struct bcm_mini_adapter *Adapter, + struct sk_buff **pPacket, + USHORT Vcid, + B_UINT16 uiClassifierRuleID, + bool bHeaderSuppressionEnabled, + UINT *PacketLen, + UCHAR bEthCSSupport) +{ + /* PHS Sepcific */ + UINT unPHSPktHdrBytesCopied = 0; + UINT unPhsOldHdrSize = 0; + UINT unPHSNewPktHeaderLen = 0; + /* Pointer to PHS IN Hdr Buffer */ + PUCHAR pucPHSPktHdrInBuf = Adapter->stPhsTxContextInfo.ucaHdrSuppressionInBuf; + /* Pointer to PHS OUT Hdr Buffer */ + PUCHAR pucPHSPktHdrOutBuf = Adapter->stPhsTxContextInfo.ucaHdrSuppressionOutBuf; + UINT usPacketType; + UINT BytesToRemove = 0; + bool bPHSI = 0; + LONG ulPhsStatus = 0; + UINT numBytesCompressed = 0; + struct sk_buff *newPacket = NULL; + struct sk_buff *Packet = *pPacket; + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "In PHSTransmit"); + + if (!bEthCSSupport) + BytesToRemove = ETH_HLEN; + /* + * Accumulate the header upto the size we support suppression + * from NDIS packet + */ + + usPacketType = ((struct ethhdr *)(Packet->data))->h_proto; + + pucPHSPktHdrInBuf = Packet->data + BytesToRemove; + /* considering data after ethernet header */ + if ((*PacketLen - BytesToRemove) < MAX_PHS_LENGTHS) + unPHSPktHdrBytesCopied = (*PacketLen - BytesToRemove); + else + unPHSPktHdrBytesCopied = MAX_PHS_LENGTHS; + + if ((unPHSPktHdrBytesCopied > 0) && + (unPHSPktHdrBytesCopied <= MAX_PHS_LENGTHS)) { + + /* + * Step 2 Suppress Header using PHS and fill into intermediate ucaPHSPktHdrOutBuf. + * Suppress only if IP Header and PHS Enabled For the Service Flow + */ + if (((usPacketType == ETHERNET_FRAMETYPE_IPV4) || + (usPacketType == ETHERNET_FRAMETYPE_IPV6)) && + (bHeaderSuppressionEnabled)) { + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nTrying to PHS Compress Using Classifier rule 0x%X", uiClassifierRuleID); + unPHSNewPktHeaderLen = unPHSPktHdrBytesCopied; + ulPhsStatus = PhsCompress(&Adapter->stBCMPhsContext, + Vcid, + uiClassifierRuleID, + pucPHSPktHdrInBuf, + pucPHSPktHdrOutBuf, + &unPhsOldHdrSize, + &unPHSNewPktHeaderLen); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nPHS Old header Size : %d New Header Size %d\n", unPhsOldHdrSize, unPHSNewPktHeaderLen); + + if (unPHSNewPktHeaderLen == unPhsOldHdrSize) { + + if (ulPhsStatus == STATUS_PHS_COMPRESSED) + bPHSI = *pucPHSPktHdrOutBuf; + + ulPhsStatus = STATUS_PHS_NOCOMPRESSION; + } + + if (ulPhsStatus == STATUS_PHS_COMPRESSED) { + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "PHS Sending packet Compressed"); + + if (skb_cloned(Packet)) { + newPacket = skb_copy(Packet, GFP_ATOMIC); + + if (newPacket == NULL) + return STATUS_FAILURE; + + dev_kfree_skb(Packet); + *pPacket = Packet = newPacket; + pucPHSPktHdrInBuf = Packet->data + BytesToRemove; + } + + numBytesCompressed = unPhsOldHdrSize - (unPHSNewPktHeaderLen + PHSI_LEN); + + memcpy(pucPHSPktHdrInBuf + numBytesCompressed, pucPHSPktHdrOutBuf, unPHSNewPktHeaderLen + PHSI_LEN); + memcpy(Packet->data + numBytesCompressed, Packet->data, BytesToRemove); + skb_pull(Packet, numBytesCompressed); + + return STATUS_SUCCESS; + } else { + /* if one byte headroom is not available, increase it through skb_cow */ + if (!(skb_headroom(Packet) > 0)) { + + if (skb_cow(Packet, 1)) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "SKB Cow Failed\n"); + return STATUS_FAILURE; + } + } + skb_push(Packet, 1); + + /* + * CAUTION: The MAC Header is getting corrupted + * here for IP CS - can be saved by copying 14 + * Bytes. not needed .... hence corrupting it. + */ + *(Packet->data + BytesToRemove) = bPHSI; + return STATUS_SUCCESS; + } + } else { + + if (!bHeaderSuppressionEnabled) + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nHeader Suppression Disabled For SF: No PHS\n"); + + return STATUS_SUCCESS; + } + } + + /* BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL,"PHSTransmit : Dumping data packet After PHS"); */ + return STATUS_SUCCESS; +} + +int PHSReceive(struct bcm_mini_adapter *Adapter, + USHORT usVcid, + struct sk_buff *packet, + UINT *punPacketLen, + UCHAR *pucEthernetHdr, + UINT bHeaderSuppressionEnabled) +{ + u32 nStandardPktHdrLen = 0; + u32 nTotalsuppressedPktHdrBytes = 0; + int ulPhsStatus = 0; + PUCHAR pucInBuff = NULL; + UINT TotalBytesAdded = 0; + + if (!bHeaderSuppressionEnabled) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "\nPhs Disabled for incoming packet"); + return ulPhsStatus; + } + + pucInBuff = packet->data; + + /* Restore PHS suppressed header */ + nStandardPktHdrLen = packet->len; + ulPhsStatus = PhsDeCompress(&Adapter->stBCMPhsContext, + usVcid, + pucInBuff, + Adapter->ucaPHSPktRestoreBuf, + &nTotalsuppressedPktHdrBytes, + &nStandardPktHdrLen); + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "\nSuppressed PktHdrLen : 0x%x Restored PktHdrLen : 0x%x", + nTotalsuppressedPktHdrBytes, nStandardPktHdrLen); + + if (ulPhsStatus != STATUS_PHS_COMPRESSED) { + skb_pull(packet, 1); + return STATUS_SUCCESS; + } else { + TotalBytesAdded = nStandardPktHdrLen - nTotalsuppressedPktHdrBytes - PHSI_LEN; + + if (TotalBytesAdded) { + if (skb_headroom(packet) >= (SKB_RESERVE_ETHERNET_HEADER + TotalBytesAdded)) + skb_push(packet, TotalBytesAdded); + else { + if (skb_cow(packet, skb_headroom(packet) + TotalBytesAdded)) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "cow failed in receive\n"); + return STATUS_FAILURE; + } + + skb_push(packet, TotalBytesAdded); + } + } + + memcpy(packet->data, Adapter->ucaPHSPktRestoreBuf, nStandardPktHdrLen); + } + + return STATUS_SUCCESS; +} + +void DumpFullPacket(UCHAR *pBuf, UINT nPktLen) +{ + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, IPV4_DBG, DBG_LVL_ALL, "Dumping Data Packet"); + BCM_DEBUG_PRINT_BUFFER(Adapter, DBG_TYPE_TX, IPV4_DBG, DBG_LVL_ALL, pBuf, nPktLen); +} + +/* + * Procedure: phs_init + * + * Description: This routine is responsible for allocating memory for classifier and + * PHS rules. + * + * Arguments: + * pPhsdeviceExtension - ptr to Device extension containing PHS Classifier rules and PHS Rules , RX, TX buffer etc + * + * Returns: + * TRUE(1) -If allocation of memory was successful. + * FALSE -If allocation of memory fails. + */ +int phs_init(struct bcm_phs_extension *pPhsdeviceExtension, struct bcm_mini_adapter *Adapter) +{ + int i; + struct bcm_phs_table *pstServiceFlowTable; + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nPHS:phs_init function"); + + if (pPhsdeviceExtension->pstServiceFlowPhsRulesTable) + return -EINVAL; + + pPhsdeviceExtension->pstServiceFlowPhsRulesTable = kzalloc(sizeof(struct bcm_phs_table), GFP_KERNEL); + + if (!pPhsdeviceExtension->pstServiceFlowPhsRulesTable) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nAllocation ServiceFlowPhsRulesTable failed"); + return -ENOMEM; + } + + pstServiceFlowTable = pPhsdeviceExtension->pstServiceFlowPhsRulesTable; + for (i = 0; i < MAX_SERVICEFLOWS; i++) { + struct bcm_phs_entry sServiceFlow = pstServiceFlowTable->stSFList[i]; + sServiceFlow.pstClassifierTable = kzalloc(sizeof(struct bcm_phs_classifier_table), GFP_KERNEL); + if (!sServiceFlow.pstClassifierTable) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nAllocation failed"); + free_phs_serviceflow_rules(pPhsdeviceExtension->pstServiceFlowPhsRulesTable); + pPhsdeviceExtension->pstServiceFlowPhsRulesTable = NULL; + return -ENOMEM; + } + } + + pPhsdeviceExtension->CompressedTxBuffer = kmalloc(PHS_BUFFER_SIZE, GFP_KERNEL); + if (pPhsdeviceExtension->CompressedTxBuffer == NULL) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nAllocation failed"); + free_phs_serviceflow_rules(pPhsdeviceExtension->pstServiceFlowPhsRulesTable); + pPhsdeviceExtension->pstServiceFlowPhsRulesTable = NULL; + return -ENOMEM; + } + + pPhsdeviceExtension->UnCompressedRxBuffer = kmalloc(PHS_BUFFER_SIZE, GFP_KERNEL); + if (pPhsdeviceExtension->UnCompressedRxBuffer == NULL) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nAllocation failed"); + kfree(pPhsdeviceExtension->CompressedTxBuffer); + free_phs_serviceflow_rules(pPhsdeviceExtension->pstServiceFlowPhsRulesTable); + pPhsdeviceExtension->pstServiceFlowPhsRulesTable = NULL; + return -ENOMEM; + } + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\n phs_init Successful"); + return STATUS_SUCCESS; +} + +int PhsCleanup(IN struct bcm_phs_extension *pPHSDeviceExt) +{ + if (pPHSDeviceExt->pstServiceFlowPhsRulesTable) { + free_phs_serviceflow_rules(pPHSDeviceExt->pstServiceFlowPhsRulesTable); + pPHSDeviceExt->pstServiceFlowPhsRulesTable = NULL; + } + + kfree(pPHSDeviceExt->CompressedTxBuffer); + pPHSDeviceExt->CompressedTxBuffer = NULL; + + kfree(pPHSDeviceExt->UnCompressedRxBuffer); + pPHSDeviceExt->UnCompressedRxBuffer = NULL; + + return 0; +} + +/* + * PHS functions + * PhsUpdateClassifierRule + * + * Routine Description: + * Exported function to add or modify a PHS Rule. + * + * Arguments: + * IN void* pvContext - PHS Driver Specific Context + * IN B_UINT16 uiVcid - The Service Flow ID for which the PHS rule applies + * IN B_UINT16 uiClsId - The Classifier ID within the Service Flow for which the PHS rule applies. + * IN struct bcm_phs_rule *psPhsRule - The PHS Rule strcuture to be added to the PHS Rule table. + * + * Return Value: + * + * 0 if successful, + * >0 Error. + */ +ULONG PhsUpdateClassifierRule(IN void *pvContext, + IN B_UINT16 uiVcid , + IN B_UINT16 uiClsId , + IN struct bcm_phs_rule *psPhsRule, + IN B_UINT8 u8AssociatedPHSI) +{ + ULONG lStatus = 0; + UINT nSFIndex = 0; + struct bcm_phs_entry *pstServiceFlowEntry = NULL; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + struct bcm_phs_extension *pDeviceExtension = (struct bcm_phs_extension *)pvContext; + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "PHS With Corr2 Changes\n"); + + if (pDeviceExtension == NULL) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "Invalid Device Extension\n"); + return ERR_PHS_INVALID_DEVICE_EXETENSION; + } + + if (u8AssociatedPHSI == 0) + return ERR_PHS_INVALID_PHS_RULE; + + /* Retrieve the SFID Entry Index for requested Service Flow */ + nSFIndex = GetServiceFlowEntry(pDeviceExtension->pstServiceFlowPhsRulesTable, + uiVcid, &pstServiceFlowEntry); + + if (nSFIndex == PHS_INVALID_TABLE_INDEX) { + /* This is a new SF. Create a mapping entry for this */ + lStatus = CreateSFToClassifierRuleMapping(uiVcid, uiClsId, + pDeviceExtension->pstServiceFlowPhsRulesTable, psPhsRule, u8AssociatedPHSI); + return lStatus; + } + + /* SF already Exists Add PHS Rule to existing SF */ + lStatus = CreateClassiferToPHSRuleMapping(uiVcid, uiClsId, + pstServiceFlowEntry, psPhsRule, u8AssociatedPHSI); + + return lStatus; +} + +/* + * PhsDeletePHSRule + * + * Routine Description: + * Deletes the specified phs Rule within Vcid + * + * Arguments: + * IN void* pvContext - PHS Driver Specific Context + * IN B_UINT16 uiVcid - The Service Flow ID for which the PHS rule applies + * IN B_UINT8 u8PHSI - the PHS Index identifying PHS rule to be deleted. + * + * Return Value: + * + * 0 if successful, + * >0 Error. + */ +ULONG PhsDeletePHSRule(IN void *pvContext, IN B_UINT16 uiVcid, IN B_UINT8 u8PHSI) +{ + UINT nSFIndex = 0, nClsidIndex = 0; + struct bcm_phs_entry *pstServiceFlowEntry = NULL; + struct bcm_phs_classifier_table *pstClassifierRulesTable = NULL; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + struct bcm_phs_extension *pDeviceExtension = (struct bcm_phs_extension *)pvContext; + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "======>\n"); + + if (pDeviceExtension) { + /* Retrieve the SFID Entry Index for requested Service Flow */ + nSFIndex = GetServiceFlowEntry(pDeviceExtension->pstServiceFlowPhsRulesTable, uiVcid, &pstServiceFlowEntry); + + if (nSFIndex == PHS_INVALID_TABLE_INDEX) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "SFID Match Failed\n"); + return ERR_SF_MATCH_FAIL; + } + + pstClassifierRulesTable = pstServiceFlowEntry->pstClassifierTable; + if (pstClassifierRulesTable) { + for (nClsidIndex = 0; nClsidIndex < MAX_PHSRULE_PER_SF; nClsidIndex++) { + if (pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].bUsed && pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule) { + if (pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule->u8PHSI == u8PHSI) { + + if (pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt) + pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt--; + + if (0 == pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt) + kfree(pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule); + + memset(&pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex], 0, + sizeof(struct bcm_phs_classifier_entry)); + } + } + } + } + } + return 0; +} + +/* + * PhsDeleteClassifierRule + * + * Routine Description: + * Exported function to Delete a PHS Rule for the SFID,CLSID Pair. + * + * Arguments: + * IN void* pvContext - PHS Driver Specific Context + * IN B_UINT16 uiVcid - The Service Flow ID for which the PHS rule applies + * IN B_UINT16 uiClsId - The Classifier ID within the Service Flow for which the PHS rule applies. + * + * Return Value: + * + * 0 if successful, + * >0 Error. + */ +ULONG PhsDeleteClassifierRule(IN void *pvContext, IN B_UINT16 uiVcid, IN B_UINT16 uiClsId) +{ + UINT nSFIndex = 0, nClsidIndex = 0; + struct bcm_phs_entry *pstServiceFlowEntry = NULL; + struct bcm_phs_classifier_entry *pstClassifierEntry = NULL; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + struct bcm_phs_extension *pDeviceExtension = (struct bcm_phs_extension *)pvContext; + + if (pDeviceExtension) { + /* Retrieve the SFID Entry Index for requested Service Flow */ + nSFIndex = GetServiceFlowEntry(pDeviceExtension->pstServiceFlowPhsRulesTable, uiVcid, &pstServiceFlowEntry); + if (nSFIndex == PHS_INVALID_TABLE_INDEX) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "SFID Match Failed\n"); + return ERR_SF_MATCH_FAIL; + } + + nClsidIndex = GetClassifierEntry(pstServiceFlowEntry->pstClassifierTable, + uiClsId, eActiveClassifierRuleContext, &pstClassifierEntry); + + if ((nClsidIndex != PHS_INVALID_TABLE_INDEX) && (!pstClassifierEntry->bUnclassifiedPHSRule)) { + if (pstClassifierEntry->pstPhsRule) { + if (pstClassifierEntry->pstPhsRule->u8RefCnt) + pstClassifierEntry->pstPhsRule->u8RefCnt--; + + if (0 == pstClassifierEntry->pstPhsRule->u8RefCnt) + kfree(pstClassifierEntry->pstPhsRule); + } + memset(pstClassifierEntry, 0, sizeof(struct bcm_phs_classifier_entry)); + } + + nClsidIndex = GetClassifierEntry(pstServiceFlowEntry->pstClassifierTable, + uiClsId, eOldClassifierRuleContext, &pstClassifierEntry); + + if ((nClsidIndex != PHS_INVALID_TABLE_INDEX) && (!pstClassifierEntry->bUnclassifiedPHSRule)) { + kfree(pstClassifierEntry->pstPhsRule); + memset(pstClassifierEntry, 0, sizeof(struct bcm_phs_classifier_entry)); + } + } + return 0; +} + +/* + * PhsDeleteSFRules + * + * Routine Description: + * Exported function to Delete a all PHS Rules for the SFID. + * + * Arguments: + * IN void* pvContext - PHS Driver Specific Context + * IN B_UINT16 uiVcid - The Service Flow ID for which the PHS rules need to be deleted + * + * Return Value: + * + * 0 if successful, + * >0 Error. + */ +ULONG PhsDeleteSFRules(IN void *pvContext, IN B_UINT16 uiVcid) +{ + UINT nSFIndex = 0, nClsidIndex = 0; + struct bcm_phs_entry *pstServiceFlowEntry = NULL; + struct bcm_phs_classifier_table *pstClassifierRulesTable = NULL; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + struct bcm_phs_extension *pDeviceExtension = (struct bcm_phs_extension *)pvContext; + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "====>\n"); + + if (pDeviceExtension) { + /* Retrieve the SFID Entry Index for requested Service Flow */ + nSFIndex = GetServiceFlowEntry(pDeviceExtension->pstServiceFlowPhsRulesTable, + uiVcid, &pstServiceFlowEntry); + if (nSFIndex == PHS_INVALID_TABLE_INDEX) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "SFID Match Failed\n"); + return ERR_SF_MATCH_FAIL; + } + + pstClassifierRulesTable = pstServiceFlowEntry->pstClassifierTable; + if (pstClassifierRulesTable) { + for (nClsidIndex = 0; nClsidIndex < MAX_PHSRULE_PER_SF; nClsidIndex++) { + if (pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule) { + + if (pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt) + pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt--; + + if (0 == pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt) + kfree(pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule); + + pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex].pstPhsRule = NULL; + } + memset(&pstClassifierRulesTable->stActivePhsRulesList[nClsidIndex], 0, sizeof(struct bcm_phs_classifier_entry)); + if (pstClassifierRulesTable->stOldPhsRulesList[nClsidIndex].pstPhsRule) { + + if (pstClassifierRulesTable->stOldPhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt) + pstClassifierRulesTable->stOldPhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt--; + + if (0 == pstClassifierRulesTable->stOldPhsRulesList[nClsidIndex].pstPhsRule->u8RefCnt) + kfree(pstClassifierRulesTable->stOldPhsRulesList[nClsidIndex].pstPhsRule); + + pstClassifierRulesTable->stOldPhsRulesList[nClsidIndex].pstPhsRule = NULL; + } + memset(&pstClassifierRulesTable->stOldPhsRulesList[nClsidIndex], 0, sizeof(struct bcm_phs_classifier_entry)); + } + } + pstServiceFlowEntry->bUsed = false; + pstServiceFlowEntry->uiVcid = 0; + } + + return 0; +} + +/* + * PhsCompress + * + * Routine Description: + * Exported function to compress the data using PHS. + * + * Arguments: + * IN void* pvContext - PHS Driver Specific Context. + * IN B_UINT16 uiVcid - The Service Flow ID to which current packet header compression applies. + * IN UINT uiClsId - The Classifier ID to which current packet header compression applies. + * IN void *pvInputBuffer - The Input buffer containg packet header data + * IN void *pvOutputBuffer - The output buffer returned by this function after PHS + * IN UINT *pOldHeaderSize - The actual size of the header before PHS + * IN UINT *pNewHeaderSize - The new size of the header after applying PHS + * + * Return Value: + * + * 0 if successful, + * >0 Error. + */ +static ULONG PhsCompress(IN void *pvContext, + IN B_UINT16 uiVcid, + IN B_UINT16 uiClsId, + IN void *pvInputBuffer, + OUT void *pvOutputBuffer, + OUT UINT *pOldHeaderSize, + OUT UINT *pNewHeaderSize) +{ + UINT nSFIndex = 0, nClsidIndex = 0; + struct bcm_phs_entry *pstServiceFlowEntry = NULL; + struct bcm_phs_classifier_entry *pstClassifierEntry = NULL; + struct bcm_phs_rule *pstPhsRule = NULL; + ULONG lStatus = 0; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + struct bcm_phs_extension *pDeviceExtension = (struct bcm_phs_extension *)pvContext; + + if (pDeviceExtension == NULL) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "Invalid Device Extension\n"); + lStatus = STATUS_PHS_NOCOMPRESSION; + return lStatus; + } + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "Suppressing header\n"); + + /* Retrieve the SFID Entry Index for requested Service Flow */ + nSFIndex = GetServiceFlowEntry(pDeviceExtension->pstServiceFlowPhsRulesTable, + uiVcid, &pstServiceFlowEntry); + if (nSFIndex == PHS_INVALID_TABLE_INDEX) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "SFID Match Failed\n"); + lStatus = STATUS_PHS_NOCOMPRESSION; + return lStatus; + } + + nClsidIndex = GetClassifierEntry(pstServiceFlowEntry->pstClassifierTable, + uiClsId, eActiveClassifierRuleContext, &pstClassifierEntry); + + if (nClsidIndex == PHS_INVALID_TABLE_INDEX) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "No PHS Rule Defined For Classifier\n"); + lStatus = STATUS_PHS_NOCOMPRESSION; + return lStatus; + } + + /* get rule from SF id,Cls ID pair and proceed */ + pstPhsRule = pstClassifierEntry->pstPhsRule; + if (!ValidatePHSRuleComplete(pstPhsRule)) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "PHS Rule Defined For Classifier But Not Complete\n"); + lStatus = STATUS_PHS_NOCOMPRESSION; + return lStatus; + } + + /* Compress Packet */ + lStatus = phs_compress(pstPhsRule, (PUCHAR)pvInputBuffer, + (PUCHAR)pvOutputBuffer, pOldHeaderSize, pNewHeaderSize); + + if (lStatus == STATUS_PHS_COMPRESSED) { + pstPhsRule->PHSModifiedBytes += *pOldHeaderSize - *pNewHeaderSize - 1; + pstPhsRule->PHSModifiedNumPackets++; + } else + pstPhsRule->PHSErrorNumPackets++; + + return lStatus; +} + +/* + * PhsDeCompress + * + * Routine Description: + * Exported function to restore the packet header in Rx path. + * + * Arguments: + * IN void* pvContext - PHS Driver Specific Context. + * IN B_UINT16 uiVcid - The Service Flow ID to which current packet header restoration applies. + * IN void *pvInputBuffer - The Input buffer containg suppressed packet header data + * OUT void *pvOutputBuffer - The output buffer returned by this function after restoration + * OUT UINT *pHeaderSize - The packet header size after restoration is returned in this parameter. + * + * Return Value: + * + * 0 if successful, + * >0 Error. + */ +static ULONG PhsDeCompress(IN void *pvContext, + IN B_UINT16 uiVcid, + IN void *pvInputBuffer, + OUT void *pvOutputBuffer, + OUT UINT *pInHeaderSize, + OUT UINT *pOutHeaderSize) +{ + UINT nSFIndex = 0, nPhsRuleIndex = 0; + struct bcm_phs_entry *pstServiceFlowEntry = NULL; + struct bcm_phs_rule *pstPhsRule = NULL; + UINT phsi; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + struct bcm_phs_extension *pDeviceExtension = (struct bcm_phs_extension *)pvContext; + + *pInHeaderSize = 0; + if (pDeviceExtension == NULL) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "Invalid Device Extension\n"); + return ERR_PHS_INVALID_DEVICE_EXETENSION; + } + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "Restoring header\n"); + + phsi = *((unsigned char *)(pvInputBuffer)); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "PHSI To Be Used For restore : %x\n", phsi); + if (phsi == UNCOMPRESSED_PACKET) + return STATUS_PHS_NOCOMPRESSION; + + /* Retrieve the SFID Entry Index for requested Service Flow */ + nSFIndex = GetServiceFlowEntry(pDeviceExtension->pstServiceFlowPhsRulesTable, + uiVcid, &pstServiceFlowEntry); + if (nSFIndex == PHS_INVALID_TABLE_INDEX) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "SFID Match Failed During Lookup\n"); + return ERR_SF_MATCH_FAIL; + } + + nPhsRuleIndex = GetPhsRuleEntry(pstServiceFlowEntry->pstClassifierTable, phsi, + eActiveClassifierRuleContext, &pstPhsRule); + if (nPhsRuleIndex == PHS_INVALID_TABLE_INDEX) { + /* Phs Rule does not exist in active rules table. Lets try in the old rules table. */ + nPhsRuleIndex = GetPhsRuleEntry(pstServiceFlowEntry->pstClassifierTable, + phsi, eOldClassifierRuleContext, &pstPhsRule); + if (nPhsRuleIndex == PHS_INVALID_TABLE_INDEX) + return ERR_PHSRULE_MATCH_FAIL; + } + + *pInHeaderSize = phs_decompress((PUCHAR)pvInputBuffer, + (PUCHAR)pvOutputBuffer, pstPhsRule, pOutHeaderSize); + + pstPhsRule->PHSModifiedBytes += *pOutHeaderSize - *pInHeaderSize - 1; + + pstPhsRule->PHSModifiedNumPackets++; + return STATUS_PHS_COMPRESSED; +} + +/* + * Procedure: free_phs_serviceflow_rules + * + * Description: This routine is responsible for freeing memory allocated for PHS rules. + * + * Arguments: + * rules - ptr to S_SERVICEFLOW_TABLE structure. + * + * Returns: + * Does not return any value. + */ +static void free_phs_serviceflow_rules(struct bcm_phs_table *psServiceFlowRulesTable) +{ + int i, j; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "=======>\n"); + + if (psServiceFlowRulesTable) { + for (i = 0; i < MAX_SERVICEFLOWS; i++) { + struct bcm_phs_entry stServiceFlowEntry = psServiceFlowRulesTable->stSFList[i]; + struct bcm_phs_classifier_table *pstClassifierRulesTable = stServiceFlowEntry.pstClassifierTable; + + if (pstClassifierRulesTable) { + for (j = 0; j < MAX_PHSRULE_PER_SF; j++) { + if (pstClassifierRulesTable->stActivePhsRulesList[j].pstPhsRule) { + + if (pstClassifierRulesTable->stActivePhsRulesList[j].pstPhsRule->u8RefCnt) + pstClassifierRulesTable->stActivePhsRulesList[j].pstPhsRule->u8RefCnt--; + + if (0 == pstClassifierRulesTable->stActivePhsRulesList[j].pstPhsRule->u8RefCnt) + kfree(pstClassifierRulesTable->stActivePhsRulesList[j].pstPhsRule); + + pstClassifierRulesTable->stActivePhsRulesList[j].pstPhsRule = NULL; + } + + if (pstClassifierRulesTable->stOldPhsRulesList[j].pstPhsRule) { + + if (pstClassifierRulesTable->stOldPhsRulesList[j].pstPhsRule->u8RefCnt) + pstClassifierRulesTable->stOldPhsRulesList[j].pstPhsRule->u8RefCnt--; + + if (0 == pstClassifierRulesTable->stOldPhsRulesList[j].pstPhsRule->u8RefCnt) + kfree(pstClassifierRulesTable->stOldPhsRulesList[j].pstPhsRule); + + pstClassifierRulesTable->stOldPhsRulesList[j].pstPhsRule = NULL; + } + } + kfree(pstClassifierRulesTable); + stServiceFlowEntry.pstClassifierTable = pstClassifierRulesTable = NULL; + } + } + } + + kfree(psServiceFlowRulesTable); + psServiceFlowRulesTable = NULL; +} + +static bool ValidatePHSRuleComplete(IN struct bcm_phs_rule *psPhsRule) +{ + if (psPhsRule) { + if (!psPhsRule->u8PHSI) { + /* PHSI is not valid */ + return false; + } + + if (!psPhsRule->u8PHSS) { + /* PHSS Is Undefined */ + return false; + } + + /* Check if PHSF is defines for the PHS Rule */ + if (!psPhsRule->u8PHSFLength) /* If any part of PHSF is valid then Rule contains valid PHSF */ + return false; + + return TRUE; + } else + return false; +} + +UINT GetServiceFlowEntry(IN struct bcm_phs_table *psServiceFlowTable, + IN B_UINT16 uiVcid, + struct bcm_phs_entry **ppstServiceFlowEntry) +{ + int i; + + for (i = 0; i < MAX_SERVICEFLOWS; i++) { + if (psServiceFlowTable->stSFList[i].bUsed) { + if (psServiceFlowTable->stSFList[i].uiVcid == uiVcid) { + *ppstServiceFlowEntry = &psServiceFlowTable->stSFList[i]; + return i; + } + } + } + + *ppstServiceFlowEntry = NULL; + return PHS_INVALID_TABLE_INDEX; +} + +static UINT GetClassifierEntry(IN struct bcm_phs_classifier_table *pstClassifierTable, + IN B_UINT32 uiClsid, enum bcm_phs_classifier_context eClsContext, + OUT struct bcm_phs_classifier_entry **ppstClassifierEntry) +{ + int i; + struct bcm_phs_classifier_entry *psClassifierRules = NULL; + + for (i = 0; i < MAX_PHSRULE_PER_SF; i++) { + + if (eClsContext == eActiveClassifierRuleContext) + psClassifierRules = &pstClassifierTable->stActivePhsRulesList[i]; + else + psClassifierRules = &pstClassifierTable->stOldPhsRulesList[i]; + + if (psClassifierRules->bUsed) { + if (psClassifierRules->uiClassifierRuleId == uiClsid) { + *ppstClassifierEntry = psClassifierRules; + return i; + } + } + } + + *ppstClassifierEntry = NULL; + return PHS_INVALID_TABLE_INDEX; +} + +static UINT GetPhsRuleEntry(IN struct bcm_phs_classifier_table *pstClassifierTable, + IN B_UINT32 uiPHSI, enum bcm_phs_classifier_context eClsContext, + OUT struct bcm_phs_rule **ppstPhsRule) +{ + int i; + struct bcm_phs_classifier_entry *pstClassifierRule = NULL; + + for (i = 0; i < MAX_PHSRULE_PER_SF; i++) { + if (eClsContext == eActiveClassifierRuleContext) + pstClassifierRule = &pstClassifierTable->stActivePhsRulesList[i]; + else + pstClassifierRule = &pstClassifierTable->stOldPhsRulesList[i]; + + if (pstClassifierRule->bUsed) { + if (pstClassifierRule->u8PHSI == uiPHSI) { + *ppstPhsRule = pstClassifierRule->pstPhsRule; + return i; + } + } + } + + *ppstPhsRule = NULL; + return PHS_INVALID_TABLE_INDEX; +} + +static UINT CreateSFToClassifierRuleMapping(IN B_UINT16 uiVcid, IN B_UINT16 uiClsId, + IN struct bcm_phs_table *psServiceFlowTable, + struct bcm_phs_rule *psPhsRule, + B_UINT8 u8AssociatedPHSI) +{ + struct bcm_phs_classifier_table *psaClassifiertable = NULL; + UINT uiStatus = 0; + int iSfIndex; + bool bFreeEntryFound = false; + + /* Check for a free entry in SFID table */ + for (iSfIndex = 0; iSfIndex < MAX_SERVICEFLOWS; iSfIndex++) { + if (!psServiceFlowTable->stSFList[iSfIndex].bUsed) { + bFreeEntryFound = TRUE; + break; + } + } + + if (!bFreeEntryFound) + return ERR_SFTABLE_FULL; + + psaClassifiertable = psServiceFlowTable->stSFList[iSfIndex].pstClassifierTable; + uiStatus = CreateClassifierPHSRule(uiClsId, psaClassifiertable, psPhsRule, + eActiveClassifierRuleContext, u8AssociatedPHSI); + if (uiStatus == PHS_SUCCESS) { + /* Add entry at free index to the SF */ + psServiceFlowTable->stSFList[iSfIndex].bUsed = TRUE; + psServiceFlowTable->stSFList[iSfIndex].uiVcid = uiVcid; + } + + return uiStatus; +} + +static UINT CreateClassiferToPHSRuleMapping(IN B_UINT16 uiVcid, + IN B_UINT16 uiClsId, + IN struct bcm_phs_entry *pstServiceFlowEntry, + struct bcm_phs_rule *psPhsRule, + B_UINT8 u8AssociatedPHSI) +{ + struct bcm_phs_classifier_entry *pstClassifierEntry = NULL; + UINT uiStatus = PHS_SUCCESS; + UINT nClassifierIndex = 0; + struct bcm_phs_classifier_table *psaClassifiertable = NULL; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + psaClassifiertable = pstServiceFlowEntry->pstClassifierTable; + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "==>"); + + /* Check if the supplied Classifier already exists */ + nClassifierIndex = GetClassifierEntry( + pstServiceFlowEntry->pstClassifierTable, + uiClsId, + eActiveClassifierRuleContext, + &pstClassifierEntry); + + if (nClassifierIndex == PHS_INVALID_TABLE_INDEX) { + /* + * The Classifier doesn't exist. So its a new classifier being added. + * Add new entry to associate PHS Rule to the Classifier + */ + + uiStatus = CreateClassifierPHSRule(uiClsId, psaClassifiertable, + psPhsRule, + eActiveClassifierRuleContext, + u8AssociatedPHSI); + return uiStatus; + } + + /* + * The Classifier exists.The PHS Rule for this classifier + * is being modified + */ + + if (pstClassifierEntry->u8PHSI == psPhsRule->u8PHSI) { + if (pstClassifierEntry->pstPhsRule == NULL) + return ERR_PHS_INVALID_PHS_RULE; + + /* + * This rule already exists if any fields are changed for this PHS + * rule update them. + */ + /* If any part of PHSF is valid then we update PHSF */ + if (psPhsRule->u8PHSFLength) { + /* update PHSF */ + memcpy(pstClassifierEntry->pstPhsRule->u8PHSF, + psPhsRule->u8PHSF, MAX_PHS_LENGTHS); + } + + if (psPhsRule->u8PHSFLength) { + /* update PHSFLen */ + pstClassifierEntry->pstPhsRule->u8PHSFLength = psPhsRule->u8PHSFLength; + } + + if (psPhsRule->u8PHSMLength) { + /* update PHSM */ + memcpy(pstClassifierEntry->pstPhsRule->u8PHSM, + psPhsRule->u8PHSM, MAX_PHS_LENGTHS); + } + + if (psPhsRule->u8PHSMLength) { + /* update PHSM Len */ + pstClassifierEntry->pstPhsRule->u8PHSMLength = + psPhsRule->u8PHSMLength; + } + + if (psPhsRule->u8PHSS) { + /* update PHSS */ + pstClassifierEntry->pstPhsRule->u8PHSS = psPhsRule->u8PHSS; + } + + /* update PHSV */ + pstClassifierEntry->pstPhsRule->u8PHSV = psPhsRule->u8PHSV; + } else { + /* A new rule is being set for this classifier. */ + uiStatus = UpdateClassifierPHSRule(uiClsId, pstClassifierEntry, + psaClassifiertable, psPhsRule, u8AssociatedPHSI); + } + + return uiStatus; +} + +static UINT CreateClassifierPHSRule(IN B_UINT16 uiClsId, + struct bcm_phs_classifier_table *psaClassifiertable, + struct bcm_phs_rule *psPhsRule, + enum bcm_phs_classifier_context eClsContext, + B_UINT8 u8AssociatedPHSI) +{ + UINT iClassifierIndex = 0; + bool bFreeEntryFound = false; + struct bcm_phs_classifier_entry *psClassifierRules = NULL; + UINT nStatus = PHS_SUCCESS; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "Inside CreateClassifierPHSRule"); + + if (psaClassifiertable == NULL) + return ERR_INVALID_CLASSIFIERTABLE_FOR_SF; + + if (eClsContext == eOldClassifierRuleContext) { + /* + * If An Old Entry for this classifier ID already exists in the + * old rules table replace it. + */ + + iClassifierIndex = + GetClassifierEntry(psaClassifiertable, uiClsId, + eClsContext, &psClassifierRules); + + if (iClassifierIndex != PHS_INVALID_TABLE_INDEX) { + /* + * The Classifier already exists in the old rules table + * Lets replace the old classifier with the new one. + */ + bFreeEntryFound = TRUE; + } + } + + if (!bFreeEntryFound) { + /* Continue to search for a free location to add the rule */ + for (iClassifierIndex = 0; iClassifierIndex < + MAX_PHSRULE_PER_SF; iClassifierIndex++) { + if (eClsContext == eActiveClassifierRuleContext) + psClassifierRules = &psaClassifiertable->stActivePhsRulesList[iClassifierIndex]; + else + psClassifierRules = &psaClassifiertable->stOldPhsRulesList[iClassifierIndex]; + + if (!psClassifierRules->bUsed) { + bFreeEntryFound = TRUE; + break; + } + } + } + + if (!bFreeEntryFound) { + + if (eClsContext == eActiveClassifierRuleContext) + return ERR_CLSASSIFIER_TABLE_FULL; + else { + /* Lets replace the oldest rule if we are looking in old Rule table */ + if (psaClassifiertable->uiOldestPhsRuleIndex >= MAX_PHSRULE_PER_SF) + psaClassifiertable->uiOldestPhsRuleIndex = 0; + + iClassifierIndex = psaClassifiertable->uiOldestPhsRuleIndex; + psClassifierRules = &psaClassifiertable->stOldPhsRulesList[iClassifierIndex]; + + (psaClassifiertable->uiOldestPhsRuleIndex)++; + } + } + + if (eClsContext == eOldClassifierRuleContext) { + + if (psClassifierRules->pstPhsRule == NULL) { + + psClassifierRules->pstPhsRule = kmalloc(sizeof(struct bcm_phs_rule), GFP_KERNEL); + + if (NULL == psClassifierRules->pstPhsRule) + return ERR_PHSRULE_MEMALLOC_FAIL; + } + + psClassifierRules->bUsed = TRUE; + psClassifierRules->uiClassifierRuleId = uiClsId; + psClassifierRules->u8PHSI = psPhsRule->u8PHSI; + psClassifierRules->bUnclassifiedPHSRule = psPhsRule->bUnclassifiedPHSRule; + + /* Update The PHS rule */ + memcpy(psClassifierRules->pstPhsRule, psPhsRule, sizeof(struct bcm_phs_rule)); + } else + nStatus = UpdateClassifierPHSRule(uiClsId, psClassifierRules, + psaClassifiertable, psPhsRule, u8AssociatedPHSI); + + return nStatus; +} + +static UINT UpdateClassifierPHSRule(IN B_UINT16 uiClsId, + IN struct bcm_phs_classifier_entry *pstClassifierEntry, + struct bcm_phs_classifier_table *psaClassifiertable, + struct bcm_phs_rule *psPhsRule, + B_UINT8 u8AssociatedPHSI) +{ + struct bcm_phs_rule *pstAddPhsRule = NULL; + UINT nPhsRuleIndex = 0; + bool bPHSRuleOrphaned = false; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + psPhsRule->u8RefCnt = 0; + + /* Step 1 Deref Any Exisiting PHS Rule in this classifier Entry */ + bPHSRuleOrphaned = DerefPhsRule(uiClsId, psaClassifiertable, + pstClassifierEntry->pstPhsRule); + + /* Step 2 Search if there is a PHS Rule with u8AssociatedPHSI in Classifier table for this SF */ + nPhsRuleIndex = GetPhsRuleEntry(psaClassifiertable, u8AssociatedPHSI, + eActiveClassifierRuleContext, &pstAddPhsRule); + if (PHS_INVALID_TABLE_INDEX == nPhsRuleIndex) { + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nAdding New PHSRuleEntry For Classifier"); + + if (psPhsRule->u8PHSI == 0) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nError PHSI is Zero\n"); + return ERR_PHS_INVALID_PHS_RULE; + } + + /* Step 2.a PHS Rule Does Not Exist .Create New PHS Rule for uiClsId */ + if (false == bPHSRuleOrphaned) { + + pstClassifierEntry->pstPhsRule = kmalloc(sizeof(struct bcm_phs_rule), GFP_KERNEL); + if (NULL == pstClassifierEntry->pstPhsRule) + return ERR_PHSRULE_MEMALLOC_FAIL; + } + memcpy(pstClassifierEntry->pstPhsRule, psPhsRule, sizeof(struct bcm_phs_rule)); + } else { + /* Step 2.b PHS Rule Exists Tie uiClsId with the existing PHS Rule */ + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\nTying Classifier to Existing PHS Rule"); + if (bPHSRuleOrphaned) { + kfree(pstClassifierEntry->pstPhsRule); + pstClassifierEntry->pstPhsRule = NULL; + } + pstClassifierEntry->pstPhsRule = pstAddPhsRule; + } + + pstClassifierEntry->bUsed = TRUE; + pstClassifierEntry->u8PHSI = pstClassifierEntry->pstPhsRule->u8PHSI; + pstClassifierEntry->uiClassifierRuleId = uiClsId; + pstClassifierEntry->pstPhsRule->u8RefCnt++; + pstClassifierEntry->bUnclassifiedPHSRule = pstClassifierEntry->pstPhsRule->bUnclassifiedPHSRule; + + return PHS_SUCCESS; +} + +static bool DerefPhsRule(IN B_UINT16 uiClsId, struct bcm_phs_classifier_table *psaClassifiertable, struct bcm_phs_rule *pstPhsRule) +{ + if (pstPhsRule == NULL) + return false; + + if (pstPhsRule->u8RefCnt) + pstPhsRule->u8RefCnt--; + + if (0 == pstPhsRule->u8RefCnt) { + /* + * if(pstPhsRule->u8PHSI) + * Store the currently active rule into the old rules list + * CreateClassifierPHSRule(uiClsId,psaClassifiertable,pstPhsRule,eOldClassifierRuleContext,pstPhsRule->u8PHSI); + */ + return TRUE; + } else + return false; +} + +void DumpPhsRules(struct bcm_phs_extension *pDeviceExtension) +{ + int i, j, k, l; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "\n Dumping PHS Rules :\n"); + + for (i = 0; i < MAX_SERVICEFLOWS; i++) { + + struct bcm_phs_entry stServFlowEntry = + pDeviceExtension->pstServiceFlowPhsRulesTable->stSFList[i]; + if (stServFlowEntry.bUsed) { + + for (j = 0; j < MAX_PHSRULE_PER_SF; j++) { + + for (l = 0; l < 2; l++) { + struct bcm_phs_classifier_entry stClsEntry; + + if (l == 0) { + stClsEntry = stServFlowEntry.pstClassifierTable->stActivePhsRulesList[j]; + if (stClsEntry.bUsed) + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n Active PHS Rule :\n"); + } else { + stClsEntry = stServFlowEntry.pstClassifierTable->stOldPhsRulesList[j]; + if (stClsEntry.bUsed) + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n Old PHS Rule :\n"); + } + + if (stClsEntry.bUsed) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "\n VCID : %#X", stServFlowEntry.uiVcid); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n ClassifierID : %#X", stClsEntry.uiClassifierRuleId); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSRuleID : %#X", stClsEntry.u8PHSI); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n****************PHS Rule********************\n"); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSI : %#X", stClsEntry.pstPhsRule->u8PHSI); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSFLength : %#X ", stClsEntry.pstPhsRule->u8PHSFLength); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSF : "); + + for (k = 0 ; k < stClsEntry.pstPhsRule->u8PHSFLength; k++) + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "%#X ", stClsEntry.pstPhsRule->u8PHSF[k]); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSMLength : %#X", stClsEntry.pstPhsRule->u8PHSMLength); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSM :"); + + for (k = 0; k < stClsEntry.pstPhsRule->u8PHSMLength; k++) + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "%#X ", stClsEntry.pstPhsRule->u8PHSM[k]); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSS : %#X ", stClsEntry.pstPhsRule->u8PHSS); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, (DBG_LVL_ALL|DBG_NO_FUNC_PRINT), "\n PHSV : %#X", stClsEntry.pstPhsRule->u8PHSV); + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "\n********************************************\n"); + } + } + } + } + } +} + +/* + * Procedure: phs_decompress + * + * Description: This routine restores the static fields within the packet. + * + * Arguments: + * in_buf - ptr to incoming packet buffer. + * out_buf - ptr to output buffer where the suppressed header is copied. + * decomp_phs_rules - ptr to PHS rule. + * header_size - ptr to field which holds the phss or phsf_length. + * + * Returns: + * size -The number of bytes of dynamic fields present with in the incoming packet + * header. + * 0 -If PHS rule is NULL.If PHSI is 0 indicateing packet as uncompressed. + */ +static int phs_decompress(unsigned char *in_buf, + unsigned char *out_buf, + struct bcm_phs_rule *decomp_phs_rules, + UINT *header_size) +{ + int phss, size = 0; + struct bcm_phs_rule *tmp_memb; + int bit, i = 0; + unsigned char *phsf, *phsm; + int in_buf_len = *header_size - 1; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + in_buf++; + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "====>\n"); + *header_size = 0; + + if ((decomp_phs_rules == NULL)) + return 0; + + tmp_memb = decomp_phs_rules; + /* + * BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, PHS_RECEIVE,DBG_LVL_ALL,"\nDECOMP:In phs_decompress PHSI 1 %d",phsi)); + * header_size = tmp_memb->u8PHSFLength; + */ + phss = tmp_memb->u8PHSS; + phsf = tmp_memb->u8PHSF; + phsm = tmp_memb->u8PHSM; + + if (phss > MAX_PHS_LENGTHS) + phss = MAX_PHS_LENGTHS; + + /* + * BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, PHS_RECEIVE,DBG_LVL_ALL,"\nDECOMP: + * In phs_decompress PHSI %d phss %d index %d",phsi,phss,index)); + */ + while ((phss > 0) && (size < in_buf_len)) { + bit = ((*phsm << i) & SUPPRESS); + + if (bit == SUPPRESS) { + *out_buf = *phsf; + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "\nDECOMP:In phss %d phsf %d output %d", + phss, *phsf, *out_buf); + } else { + *out_buf = *in_buf; + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_RECEIVE, DBG_LVL_ALL, "\nDECOMP:In phss %d input %d output %d", + phss, *in_buf, *out_buf); + in_buf++; + size++; + } + out_buf++; + phsf++; + phss--; + i++; + *header_size = *header_size + 1; + + if (i > MAX_NO_BIT) { + i = 0; + phsm++; + } + } + + return size; +} + +/* + * Procedure: phs_compress + * + * Description: This routine suppresses the static fields within the packet.Before + * that it will verify the fields to be suppressed with the corresponding fields in the + * phsf. For verification it checks the phsv field of PHS rule. If set and verification + * succeeds it suppresses the field.If any one static field is found different none of + * the static fields are suppressed then the packet is sent as uncompressed packet with + * phsi=0. + * + * Arguments: + * phs_rule - ptr to PHS rule. + * in_buf - ptr to incoming packet buffer. + * out_buf - ptr to output buffer where the suppressed header is copied. + * header_size - ptr to field which holds the phss. + * + * Returns: + * size-The number of bytes copied into the output buffer i.e dynamic fields + * 0 -If PHS rule is NULL.If PHSV field is not set.If the verification fails. + */ +static int phs_compress(struct bcm_phs_rule *phs_rule, + unsigned char *in_buf, + unsigned char *out_buf, + UINT *header_size, + UINT *new_header_size) +{ + unsigned char *old_addr = out_buf; + int suppress = 0; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + if (phs_rule == NULL) { + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nphs_compress(): phs_rule null!"); + *out_buf = ZERO_PHSI; + return STATUS_PHS_NOCOMPRESSION; + } + + if (phs_rule->u8PHSS <= *new_header_size) + *header_size = phs_rule->u8PHSS; + else + *header_size = *new_header_size; + + /* To copy PHSI */ + out_buf++; + suppress = verify_suppress_phsf(in_buf, out_buf, phs_rule->u8PHSF, + phs_rule->u8PHSM, phs_rule->u8PHSS, + phs_rule->u8PHSV, new_header_size); + + if (suppress == STATUS_PHS_COMPRESSED) { + *old_addr = (unsigned char)phs_rule->u8PHSI; + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nCOMP:In phs_compress phsi %d", phs_rule->u8PHSI); + } else { + *old_addr = ZERO_PHSI; + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nCOMP:In phs_compress PHSV Verification failed"); + } + + return suppress; +} + +/* + * Procedure: verify_suppress_phsf + * + * Description: This routine verifies the fields of the packet and if all the + * static fields are equal it adds the phsi of that PHS rule.If any static + * field differs it woun't suppress any field. + * + * Arguments: + * rules_set - ptr to classifier_rules. + * in_buffer - ptr to incoming packet buffer. + * out_buffer - ptr to output buffer where the suppressed header is copied. + * phsf - ptr to phsf. + * phsm - ptr to phsm. + * phss - variable holding phss. + * + * Returns: + * size-The number of bytes copied into the output buffer i.e dynamic fields. + * 0 -Packet has failed the verification. + */ +static int verify_suppress_phsf(unsigned char *in_buffer, + unsigned char *out_buffer, + unsigned char *phsf, + unsigned char *phsm, + unsigned int phss, + unsigned int phsv, + UINT *new_header_size) +{ + unsigned int size = 0; + int bit, i = 0; + struct bcm_mini_adapter *Adapter = GET_BCM_ADAPTER(gblpnetdev); + + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nCOMP:In verify_phsf PHSM - 0x%X", *phsm); + + if (phss > (*new_header_size)) + phss = *new_header_size; + + while (phss > 0) { + bit = ((*phsm << i) & SUPPRESS); + if (bit == SUPPRESS) { + if (*in_buffer != *phsf) { + if (phsv == VERIFY) { + BCM_DEBUG_PRINT(Adapter, + DBG_TYPE_OTHERS, + PHS_SEND, + DBG_LVL_ALL, + "\nCOMP:In verify_phsf failed for field %d buf %d phsf %d", + phss, + *in_buffer, + *phsf); + return STATUS_PHS_NOCOMPRESSION; + } + } else + BCM_DEBUG_PRINT(Adapter, + DBG_TYPE_OTHERS, + PHS_SEND, + DBG_LVL_ALL, + "\nCOMP:In verify_phsf success for field %d buf %d phsf %d", + phss, + *in_buffer, + *phsf); + } else { + *out_buffer = *in_buffer; + BCM_DEBUG_PRINT(Adapter, + DBG_TYPE_OTHERS, + PHS_SEND, + DBG_LVL_ALL, + "\nCOMP:In copying_header input %d out %d", + *in_buffer, + *out_buffer); + out_buffer++; + size++; + } + + in_buffer++; + phsf++; + phss--; + i++; + + if (i > MAX_NO_BIT) { + i = 0; + phsm++; + } + } + BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, PHS_SEND, DBG_LVL_ALL, "\nCOMP:In verify_phsf success"); + *new_header_size = size; + return STATUS_PHS_COMPRESSED; +} |
