aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/bcm/PHSModule.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/bcm/PHSModule.c')
-rw-r--r--drivers/staging/bcm/PHSModule.c1448
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;
+}