blob: 5e603ce76fea5a401030315b4310be7cfa3979f7 [file] [log] [blame]
/**
@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;
}