/** * @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) { status = skb_cow(Packet, LEADER_SIZE); if (status) { 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((struct bcm_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; }