/** @file Transmit.c @defgroup tx_functions Transmission @section Queueing @dot digraph transmit1 { node[shape=box] edge[weight=5;color=red] bcm_transmit->reply_to_arp_request[label="ARP"] bcm_transmit->GetPacketQueueIndex[label="IP Packet"] GetPacketQueueIndex->IpVersion4[label="IPV4"] GetPacketQueueIndex->IpVersion6[label="IPV6"] } @enddot @section De-Queueing @dot digraph transmit2 { node[shape=box] edge[weight=5;color=red] interrupt_service_thread->transmit_packets tx_pkt_hdler->transmit_packets transmit_packets->CheckAndSendPacketFromIndex transmit_packets->UpdateTokenCount CheckAndSendPacketFromIndex->PruneQueue CheckAndSendPacketFromIndex->IsPacketAllowedForFlow CheckAndSendPacketFromIndex->SendControlPacket[label="control pkt"] SendControlPacket->bcm_cmd53 CheckAndSendPacketFromIndex->SendPacketFromQueue[label="data pkt"] SendPacketFromQueue->SetupNextSend->bcm_cmd53 } @enddot */ #include "headers.h" /******************************************************************* * Function - bcm_transmit() * * Description - This is the main transmit function for our virtual * interface(veth0). It handles the ARP packets. It * clones this packet and then Queue it to a suitable * Queue. Then calls the transmit_packet(). * * Parameter - skb - Pointer to the socket buffer structure * dev - Pointer to the virtual net device structure * * Returns - zero (success) or -ve value (failure) * *********************************************************************/ INT bcm_transmit(struct sk_buff *skb, /**< skb */ struct net_device *dev /**< net device pointer */ ) { PMINI_ADAPTER Adapter = NULL; USHORT qindex=0; struct timeval tv; UINT pkt_type = 0; UINT calltransmit = 0; BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL, "\n%s====>\n",__FUNCTION__); memset(&tv, 0, sizeof(tv)); /* Check for valid parameters */ if(skb == NULL || dev==NULL) { BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX,TX_OSAL_DBG, DBG_LVL_ALL, "Got NULL skb or dev\n"); return -EINVAL; } Adapter = GET_BCM_ADAPTER(dev); if(!Adapter) { BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL, "Got Invalid Adapter\n"); return -EINVAL; } if(Adapter->device_removed == TRUE || !Adapter->LinkUpStatus) { if(!netif_queue_stopped(dev)) { netif_carrier_off(dev); netif_stop_queue(dev); } return STATUS_FAILURE; } BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL, "Packet size : %d\n", skb->len); /*Add Ethernet CS check here*/ if(Adapter->TransferMode == IP_PACKET_ONLY_MODE ) { pkt_type = ntohs(*(PUSHORT)(skb->data + 12)); /* Get the queue index where the packet is to be queued */ BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL, "Getting the Queue Index....."); qindex = GetPacketQueueIndex(Adapter,skb); if((SHORT)INVALID_QUEUE_INDEX==(SHORT)qindex) { if(pkt_type == ETH_ARP_FRAME) { /* Reply directly to ARP request packet ARP Spoofing only if NO ETH CS rule matches for it */ BCM_DEBUG_PRINT (Adapter,DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL,"ARP OPCODE = %02x", (*(PUCHAR)(skb->data + 21))); reply_to_arp_request(skb); BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX,TX_OSAL_DBG, DBG_LVL_ALL,"After reply_to_arp_request \n"); } else { BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL, "Invalid queue index, dropping pkt\n"); bcm_kfree_skb(skb); } return STATUS_SUCCESS; } if(Adapter->PackInfo[qindex].uiCurrentPacketsOnHost >= SF_MAX_ALLOWED_PACKETS_TO_BACKUP) { atomic_inc(&Adapter->TxDroppedPacketCount); bcm_kfree_skb(skb); return STATUS_SUCCESS; } /* Now Enqueue the packet */ BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "bcm_transmit Enqueueing the Packet To Queue %d",qindex); spin_lock(&Adapter->PackInfo[qindex].SFQueueLock); Adapter->PackInfo[qindex].uiCurrentBytesOnHost += skb->len; Adapter->PackInfo[qindex].uiCurrentPacketsOnHost++; *((B_UINT32 *)skb->cb + SKB_CB_LATENCY_OFFSET ) = jiffies; ENQUEUEPACKET(Adapter->PackInfo[qindex].FirstTxQueue, Adapter->PackInfo[qindex].LastTxQueue, skb); atomic_inc(&Adapter->TotalPacketCount); spin_unlock(&Adapter->PackInfo[qindex].SFQueueLock); do_gettimeofday(&tv); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL,"ENQ: \n"); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL, "Pkt Len = %d, sec: %ld, usec: %ld\n", (skb->len-ETH_HLEN), tv.tv_sec, tv.tv_usec); #ifdef BCM_SHM_INTERFACE spin_lock(&Adapter->txtransmitlock); if(Adapter->txtransmit_running == 0) { Adapter->txtransmit_running = 1; calltransmit = 1; } else calltransmit = 0; spin_unlock(&Adapter->txtransmitlock); #endif if(calltransmit == 1) transmit_packets(Adapter); else { if(!atomic_read(&Adapter->TxPktAvail)) { atomic_set(&Adapter->TxPktAvail, 1); #ifdef BCM_SHM_INTERFACE virtual_mail_box_interrupt(); #endif wake_up(&Adapter->tx_packet_wait_queue); } } BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_OSAL_DBG, DBG_LVL_ALL, "<===="); } else bcm_kfree_skb(skb); return STATUS_SUCCESS; } /** @ingroup ctrl_pkt_functions This function dispatches control packet to the h/w interface @return zero(success) or -ve value(failure) */ INT SendControlPacket(PMINI_ADAPTER Adapter, /**"); PLeader=(PLEADER)pControlPacket; BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Tx"); if(!pControlPacket || !Adapter) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Got NULL Control Packet or Adapter"); return STATUS_FAILURE; } if((atomic_read( &Adapter->CurrNumFreeTxDesc ) < ((PLeader->PLength-1)/MAX_DEVICE_DESC_SIZE)+1)) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "NO FREE DESCRIPTORS TO SEND CONTROL PACKET"); if(Adapter->bcm_jiffies == 0) { Adapter->bcm_jiffies = jiffies; BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "UPDATED TIME(hex): %lu", Adapter->bcm_jiffies); } return STATUS_FAILURE; } /* Update the netdevice statistics */ /* Dump Packet */ BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Status: %x", PLeader->Status); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader VCID: %x",PLeader->Vcid); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Length: %x",PLeader->PLength); if(Adapter->device_removed) return 0; #ifndef BCM_SHM_INTERFACE Adapter->interface_transmit(Adapter->pvInterfaceAdapter, pControlPacket, (PLeader->PLength + LEADER_SIZE)); #else tx_pkts_to_firmware(pControlPacket,(PLeader->PLength + LEADER_SIZE),1); if(PLeader->Status==IDLE_MESSAGE) { if(((CONTROL_MESSAGE*)PLeader)->szData[0] == GO_TO_IDLE_MODE_PAYLOAD && ((CONTROL_MESSAGE*)PLeader)->szData[1] == TARGET_CAN_GO_TO_IDLE_MODE) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Idle Mode Ack Sent to the Device\n"); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Host Entering into Idle Mode\n"); do_gettimeofday(&tv); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "IdleMode Msg sent to f/w at time :%ld ms", tv.tv_sec *1000 + tv.tv_usec /1000); if(Adapter->bDoSuspend != TRUE) { Adapter->IdleMode = TRUE; Adapter->bPreparingForLowPowerMode = FALSE ; } } } if((PLeader->Status == LINK_UP_CONTROL_REQ) && ((PUCHAR)pControlPacket)[sizeof(LEADER)] == LINK_UP_ACK && ((PUCHAR)pControlPacket)[sizeof(LEADER)+1] == LINK_SHUTDOWN_REQ_FROM_FIRMWARE && ((PUCHAR)pControlPacket)[sizeof(LEADER)+2] == SHUTDOWN_ACK_FROM_DRIVER) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Shut Down ACK Sent and Host entering Shut State \n"); if(Adapter->bDoSuspend != TRUE) { Adapter->bShutStatus = TRUE; Adapter->bPreparingForLowPowerMode = FALSE; Adapter->bTriedToWakeUpFromlowPowerMode = FALSE; } } #endif ((PLINUX_DEP_DATA)Adapter->pvOsDepData)->netstats.tx_packets++; ((PLINUX_DEP_DATA)Adapter->pvOsDepData)->netstats.tx_bytes+= PLeader->PLength; atomic_dec(&Adapter->CurrNumFreeTxDesc); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "<========="); return STATUS_SUCCESS; } static LEADER Leader={0}; /** @ingroup tx_functions This function despatches the IP packets with the given vcid to the target via the host h/w interface. @return zero(success) or -ve value(failure) */ INT SetupNextSend(PMINI_ADAPTER Adapter, /**len > MAX_DEVICE_DESC_SIZE) { status = STATUS_FAILURE; goto errExit; } /* Get the Classifier Rule ID */ uiClassifierRuleID = *((UINT32*) (Packet->cb)+SKB_CB_CLASSIFICATION_OFFSET); QueueIndex = SearchVcid( Adapter,Vcid); if(QueueIndex < NO_OF_QUEUES) { bHeaderSupressionEnabled = Adapter->PackInfo[QueueIndex].bHeaderSuppressionEnabled; bHeaderSupressionEnabled = bHeaderSupressionEnabled & Adapter->bPHSEnabled; } if(Adapter->device_removed) { status = STATUS_FAILURE; goto errExit; } status = PHSTransmit(Adapter, &Packet, Vcid, uiClassifierRuleID, bHeaderSupressionEnabled, (UINT *)&Packet->len, Adapter->PackInfo[QueueIndex].bEthCSSupport); if(status != STATUS_SUCCESS) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "PHS Transmit failed..\n"); goto errExit; } Leader.Vcid = Vcid; if(TCP_ACK == *((UINT32*) (Packet->cb) + SKB_CB_TCPACK_OFFSET )) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "Sending TCP ACK\n"); Leader.Status = LEADER_STATUS_TCP_ACK; } else { Leader.Status = LEADER_STATUS; } if(Adapter->PackInfo[QueueIndex].bEthCSSupport) { Leader.PLength = Packet->len; if(skb_headroom(Packet) < LEADER_SIZE) { if((status = skb_cow(Packet,LEADER_SIZE))) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL,"bcm_transmit : Failed To Increase headRoom\n"); goto errExit; } } skb_push(Packet, LEADER_SIZE); memcpy(Packet->data, &Leader, LEADER_SIZE); } else { Leader.PLength = Packet->len - ETH_HLEN; memcpy((LEADER*)skb_pull(Packet, (ETH_HLEN - LEADER_SIZE)), &Leader, LEADER_SIZE); } BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "Packet->len = %d", Packet->len); BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "Vcid = %d", Vcid); #ifndef BCM_SHM_INTERFACE status = Adapter->interface_transmit(Adapter->pvInterfaceAdapter, Packet->data, (Leader.PLength + LEADER_SIZE)); #else status = tx_pkts_to_firmware(Packet,Packet->len,0); #endif if(status) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "Tx Failed..\n"); } else { Adapter->PackInfo[QueueIndex].uiTotalTxBytes += Leader.PLength; atomic_add(Leader.PLength, &Adapter->GoodTxByteCount); atomic_inc(&Adapter->TxTotalPacketCount); #ifdef GDMA_INTERFACE dontfree = 1; #endif } atomic_dec(&Adapter->CurrNumFreeTxDesc); errExit: if(STATUS_SUCCESS == status) { Adapter->PackInfo[QueueIndex].uiCurrentTokenCount -= Leader.PLength << 3; Adapter->PackInfo[QueueIndex].uiSentBytes += (Packet->len); Adapter->PackInfo[QueueIndex].uiSentPackets++; Adapter->PackInfo[QueueIndex].NumOfPacketsSent++; atomic_dec(&Adapter->PackInfo[QueueIndex].uiPerSFTxResourceCount); #ifdef BCM_SHM_INTERFACE if(atomic_read(&Adapter->PackInfo[QueueIndex].uiPerSFTxResourceCount) < 0) { atomic_set(&Adapter->PackInfo[QueueIndex].uiPerSFTxResourceCount, 0); } #endif Adapter->PackInfo[QueueIndex].uiThisPeriodSentBytes += Leader.PLength; } #ifdef GDMA_INTERFACE if(!dontfree){ bcm_kfree_skb(Packet); } #else bcm_kfree_skb(Packet); #endif return status; } /** @ingroup tx_functions Transmit thread */ int tx_pkt_handler(PMINI_ADAPTER Adapter /**< pointer to adapter object*/ ) { #ifndef BCM_SHM_INTERFACE int status = 0; #endif UINT calltransmit = 1; BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Entring to wait for signal from the interrupt service thread!Adapter = %p",Adapter); while(1) { if(Adapter->LinkUpStatus){ wait_event_timeout(Adapter->tx_packet_wait_queue, ((atomic_read(&Adapter->TxPktAvail) && (MINIMUM_PENDING_DESCRIPTORS < atomic_read(&Adapter->CurrNumFreeTxDesc)) && (Adapter->device_removed == FALSE))) || (1 == Adapter->downloadDDR) || kthread_should_stop() #ifndef BCM_SHM_INTERFACE || (TRUE == Adapter->bEndPointHalted) #endif , msecs_to_jiffies(10)); } else{ wait_event(Adapter->tx_packet_wait_queue, ((atomic_read(&Adapter->TxPktAvail) && (MINIMUM_PENDING_DESCRIPTORS < atomic_read(&Adapter->CurrNumFreeTxDesc)) && (Adapter->device_removed == FALSE))) || (1 == Adapter->downloadDDR) || kthread_should_stop() #ifndef BCM_SHM_INTERFACE || (TRUE == Adapter->bEndPointHalted) #endif ); } if(kthread_should_stop() || Adapter->device_removed) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Exiting the tx thread..\n"); Adapter->transmit_packet_thread = NULL; return 0; } #ifndef BCM_SHM_INTERFACE if(Adapter->downloadDDR == 1) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Downloading DDR Settings\n"); Adapter->downloadDDR +=1; status = download_ddr_settings(Adapter); if(status) BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "DDR DOWNLOAD FAILED!\n"); continue; } //Check end point for halt/stall. if(Adapter->bEndPointHalted == TRUE) { Bcm_clear_halt_of_endpoints(Adapter); Adapter->bEndPointHalted = FALSE; StartInterruptUrb((PS_INTERFACE_ADAPTER)(Adapter->pvInterfaceAdapter)); } if(Adapter->LinkUpStatus && !Adapter->IdleMode) { if(atomic_read(&Adapter->TotalPacketCount)) { update_per_sf_desc_cnts(Adapter); } } #endif if( atomic_read(&Adapter->CurrNumFreeTxDesc) && Adapter->LinkStatus == SYNC_UP_REQUEST && !Adapter->bSyncUpRequestSent) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Calling LinkMessage"); LinkMessage(Adapter); } if((Adapter->IdleMode || Adapter->bShutStatus) && atomic_read(&Adapter->TotalPacketCount)) { BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Device in Low Power mode...waking up"); Adapter->usIdleModePattern = ABORT_IDLE_MODE; Adapter->bWakeUpDevice = TRUE; wake_up(&Adapter->process_rx_cntrlpkt); } #ifdef BCM_SHM_INTERFACE spin_lock_bh(&Adapter->txtransmitlock); if(Adapter->txtransmit_running == 0) { Adapter->txtransmit_running = 1; calltransmit = 1; } else calltransmit = 0; spin_unlock_bh(&Adapter->txtransmitlock); #endif if(calltransmit) transmit_packets(Adapter); atomic_set(&Adapter->TxPktAvail, 0); } return 0; } #ifdef BCM_SHM_INTERFACE extern PMINI_ADAPTER psAdaptertest; void virtual_mail_box_interrupt(void) { #ifndef GDMA_INTERFACE PUINT ptr = (PUINT)CPE_VIRTUAL_MAILBOX_REG; UINT intval = (UINT)((*ptr & 0xFF00) >> 8); if (intval != 0) { atomic_set(&psAdaptertest->CurrNumFreeTxDesc, intval); atomic_set (&psAdaptertest->uiMBupdate, TRUE); //make it to 0 *ptr = *ptr & 0xffff00ff; } #endif } unsigned int total_tx_pkts_pending(void) { return atomic_read(&psAdaptertest->TotalPacketCount); } #endif