| /** |
| @file Transmit.c |
| @defgroup tx_functions Transmission |
| @section Queueing |
| @dot |
| digraph transmit1 { |
| node[shape=box] |
| edge[weight=5;color=red] |
| |
| 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" |
| |
| |
| /** |
| @ingroup ctrl_pkt_functions |
| This function dispatches control packet to the h/w interface |
| @return zero(success) or -ve value(failure) |
| */ |
| INT SendControlPacket(struct bcm_mini_adapter *Adapter, char *pControlPacket) |
| { |
| struct bcm_leader *PLeader = (struct bcm_leader *)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"); |
| 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; |
| |
| if (netif_msg_pktdata(Adapter)) |
| print_hex_dump(KERN_DEBUG, PFX "tx control: ", DUMP_PREFIX_NONE, |
| 16, 1, pControlPacket, PLeader->PLength + LEADER_SIZE, 0); |
| |
| Adapter->interface_transmit(Adapter->pvInterfaceAdapter, |
| pControlPacket, (PLeader->PLength + LEADER_SIZE)); |
| |
| atomic_dec(&Adapter->CurrNumFreeTxDesc); |
| BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "<========="); |
| return STATUS_SUCCESS; |
| } |
| |
| /** |
| @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(struct bcm_mini_adapter *Adapter, struct sk_buff *Packet, USHORT Vcid) |
| { |
| int status=0; |
| BOOLEAN bHeaderSupressionEnabled = FALSE; |
| B_UINT16 uiClassifierRuleID; |
| u16 QueueIndex = skb_get_queue_mapping(Packet); |
| struct bcm_leader Leader={0}; |
| |
| if(Packet->len > MAX_DEVICE_DESC_SIZE) |
| { |
| status = STATUS_FAILURE; |
| goto errExit; |
| } |
| |
| /* Get the Classifier Rule ID */ |
| uiClassifierRuleID = *((UINT32*) (Packet->cb)+SKB_CB_CLASSIFICATION_OFFSET); |
| |
| bHeaderSupressionEnabled = Adapter->PackInfo[QueueIndex].bHeaderSuppressionEnabled |
| & 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 )) |
| 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((struct bcm_leader *)skb_pull(Packet, (ETH_HLEN - LEADER_SIZE)), &Leader, LEADER_SIZE); |
| } |
| |
| status = Adapter->interface_transmit(Adapter->pvInterfaceAdapter, |
| Packet->data, (Leader.PLength + LEADER_SIZE)); |
| if(status) |
| { |
| ++Adapter->dev->stats.tx_errors; |
| if (netif_msg_tx_err(Adapter)) |
| pr_info(PFX "%s: transmit error %d\n", Adapter->dev->name, |
| status); |
| } |
| else |
| { |
| struct net_device_stats *netstats = &Adapter->dev->stats; |
| Adapter->PackInfo[QueueIndex].uiTotalTxBytes += Leader.PLength; |
| |
| netstats->tx_bytes += Leader.PLength; |
| ++netstats->tx_packets; |
| |
| 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); |
| Adapter->PackInfo[QueueIndex].uiThisPeriodSentBytes += Leader.PLength; |
| } |
| |
| atomic_dec(&Adapter->CurrNumFreeTxDesc); |
| |
| errExit: |
| |
| dev_kfree_skb(Packet); |
| return status; |
| } |
| |
| static int tx_pending(struct bcm_mini_adapter *Adapter) |
| { |
| return (atomic_read(&Adapter->TxPktAvail) |
| && MINIMUM_PENDING_DESCRIPTORS < atomic_read(&Adapter->CurrNumFreeTxDesc)) |
| || Adapter->device_removed || (1 == Adapter->downloadDDR); |
| } |
| |
| /** |
| @ingroup tx_functions |
| Transmit thread |
| */ |
| int tx_pkt_handler(struct bcm_mini_adapter *Adapter /**< pointer to adapter object*/ |
| ) |
| { |
| int status = 0; |
| |
| while(! kthread_should_stop()) { |
| /* FIXME - the timeout looks like workaround for racey usage of TxPktAvail */ |
| if(Adapter->LinkUpStatus) |
| wait_event_timeout(Adapter->tx_packet_wait_queue, |
| tx_pending(Adapter), msecs_to_jiffies(10)); |
| else |
| wait_event_interruptible(Adapter->tx_packet_wait_queue, |
| tx_pending(Adapter)); |
| |
| if (Adapter->device_removed) |
| break; |
| |
| if(Adapter->downloadDDR == 1) |
| { |
| Adapter->downloadDDR +=1; |
| status = download_ddr_settings(Adapter); |
| if(status) |
| pr_err(PFX "DDR DOWNLOAD FAILED! %d\n", status); |
| 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); |
| } |
| } |
| |
| 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); |
| } |
| |
| transmit_packets(Adapter); |
| |
| atomic_set(&Adapter->TxPktAvail, 0); |
| } |
| |
| BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Exiting the tx thread..\n"); |
| Adapter->transmit_packet_thread = NULL; |
| return 0; |
| } |