/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <ethernet.h>
#include <e32hal.h>
#include <e32cmn.h>
#include "ethernet_device.h"
// Constants specific to this file
const TInt K1000mSecDelay = 1000;
/////////////////////////////////////////
// Implementation of EthernetDevice class
/////////////////////////////////////////
EthernetDevice::EthernetDevice() : iRxDfc(RxDfc, this)
{
DP("** (PDD) EthernetDevice()");
iInterruptId=-1;
iCreated = FALSE;
iStarted = FALSE;
iRxDfc.SetDfcQ(Kern::DfcQue0());
}
EthernetDevice::~EthernetDevice()
{
DP("** (PDD) ~EthernetDevice()");
DP("** (PDD) ~EthernetDevice Unbind Interrupt");
Interrupt::Unbind(EIntNet0);
}
void EthernetDevice::GetConfig(TEthernetConfigV01 &aConfig) const
{
DP("** (PDD) EthernetDevice::GetConfig");
aConfig = iDefaultConfig;
}
TInt EthernetDevice::ValidateConfig(const TEthernetConfigV01 &aConfig) const
{
DP("** (PDD) EthernetDevice::ValidateConfig");
switch(aConfig.iEthSpeed)
{
case KEthSpeedUnknown:
return KErrNotSupported;
default:
break;
}
switch(aConfig.iEthDuplex)
{
case KEthDuplexUnknown:
return KErrNotSupported;
default:
break;
}
return KErrNone;
}
void EthernetDevice::Caps(TDes8 &aCaps) const
{
DP("** (PDD) EthernetDevice::Caps");
TEthernetCaps capsBuf;
aCaps.FillZ(aCaps.MaxLength());
aCaps=capsBuf.Left(Min(capsBuf.Length(),aCaps.MaxLength()));
}
TDfcQue* EthernetDevice::DfcQ(TInt aUnit)
{
DP("** (PDD) EthernetDevice::DfcQ");
return Kern::DfcQue0();
}
TInt EthernetDevice::DisableIrqs()
{
DP("** (PDD) EthernetDevice::DisableIrqs");
return NKern::DisableInterrupts(1);
}
void EthernetDevice::RestoreIrqs(TInt aLevel)
{
DP("** (PDD) EthernetDevice::RestoreIrqs");
NKern::RestoreInterrupts(aLevel);
}
//
// Enable Ethernet interrupt line, allocate memory for Rx/Tx paths and send QEMU frames for Rx transmission.
//
TInt EthernetDevice::DoCreate(TInt aUnit, const TDesC8* anInfo)
{
DP("** (PDD) Enter: EthernetDevice::DoCreate");
if(iCreated)
return KErrNone;
// Bind to Ethernet interrupt
Interrupt::Bind(EIntNet0,Isr,this);
SetMacAddress();
DP("** (PDD) EthernetDevice::DoCreate - VNET[VIRTIO_STATUS]");
VNET[VIRTIO_STATUS] = VIRTIO_CONFIG_S_ACKNOWLEDGE
| VIRTIO_CONFIG_S_DRIVER;
AllocRings();
VNET[VIRTIO_STATUS] |= VIRTIO_CONFIG_S_DRIVER_OK;
AddRx();
VNET[VIRTIO_INT_ENABLE] = 1;
iCreated = TRUE;
DP("** (PDD) Exit: EthernetDevice::DoCreate");
return KErrNone;
}
TInt EthernetDevice::Start()
{
DP("** (PDD) EthernetDevice::Start()");
if(iStarted)
return KErrNone;
// wait for 1 sec as negotiation is going on... hopefully auto-neg completes by then
NKern::Sleep(K1000mSecDelay);
Interrupt::Enable(EIntNet0);
iStarted = TRUE;
return KErrNone;
}
void EthernetDevice::Stop(TStopMode aMode)
{
DP("** (PDD) EthernetDevice::Stop(TStopMode aMode)");
switch (aMode)
{
case EStopNormal:
case EStopEmergency:
iRxDfc.Cancel();
//Should we disable QEMU interrupts here?
break;
default:
break;
}
iStarted = FALSE;
Interrupt::Disable(EIntNet0);
}
//
// Transmit Tx data to QEMU and increment iTxAvail idx counter to indicate to QEMU that a new frame is available.
//
TInt EthernetDevice::Send(TBuf8<KMaxEthernetPacket+32> &aBuffer)
{
DP("** (PDD) EthernetDevice::Send");
TInt err = KErrNone;
TUint32 length = aBuffer.Length();
iTxBuffer = aBuffer;
DP ("** (PDD) Value of iTxAvail->idx = %d\n", iTxAvail->idx);
TInt ring_slot = iTxAvail->idx & (tx_ring_size - 1);
DP ("** (PDD) Value of ring_slot = %d\n", ring_slot);
memset(&tx_header, 0, sizeof(tx_header));
iTxDesc[0].addr = Epoc::LinearToPhysical((TUint32)&tx_header);
iTxDesc[0].len = sizeof(tx_header);
iTxDesc[0].flags = VRING_DESC_F_NEXT;
iTxDesc[0].next = 1;
iTxDesc[1].addr = Epoc::LinearToPhysical((TUint32) iTxBuffer.Ptr());
iTxDesc[1].len = length;
iTxDesc[1].flags = 0;
iTxAvail->ring[ring_slot] = 0;
iTxAvail->idx++;
DP ("** (PDD)iTxAvail->idx = %x, rx_last_used=%d\n", iTxAvail->idx, rx_last_used);
VNET[VIRTIO_QUEUE_NOTIFY] = TX_QUEUE;
DP ("** (PDD) iTxDesc[0].addr = %x\n", iTxDesc[0].addr);
DP ("** (PDD) iTxDesc[0].len = %d\n", iTxDesc[0].len);
DP ("** (PDD) iTxDesc[0].flags = %d\n", iTxDesc[0].flags);
DP ("** (PDD) iTxDesc[0].next = %d\n", iTxDesc[0].next);
DP ("** (PDD) iTxDesc[1].addr = %x\n", iTxDesc[1].addr);
DP ("** (PDD) iTxDesc[1].len = %d\n", iTxDesc[1].len);
DP ("** (PDD) iTxDesc[1].flags = %d\n", iTxDesc[1].flags);
return err;
}
TInt EthernetDevice::ReceiveFrame(TBuf8<KMaxEthernetPacket+32> &aBuffer,
TBool okToUse)
{
DP("** (PDD) EthernetDevice::ReceiveFrame");
//If no buffer available dump frame
if (!okToUse)
{
DP("** (PDD) EthernetDevice::ReceiveFrame - dumping frame");
return KErrGeneral;
}
aBuffer.Copy(iRxBuffer.Ptr(), ETHERNET_PAYLOAD_SIZE);
AddRx();
return KErrNone;
}
void EthernetDevice::Isr(TAny* aPtr)
{
DP("** (PDD) EthernetDevice::Isr(TAny* aPtr)");
Interrupt::Clear(EIntNet0);
VNET[VIRTIO_INT_STATUS] = 1;
EthernetDevice& d=*(EthernetDevice*)aPtr;
d.iRxDfc.Add();
}
void EthernetDevice::RxDfc(TAny* aPtr)
{
DP("** (PDD) EthernetDevice::RxDfc");
EthernetDevice& d=*(EthernetDevice*)aPtr;
TInt x = VNET[VIRTIO_INT_STATUS];
DP("** (PDD) EthernetDevice::RxDfc - value of x=%d", x);
TInt ring_slot = d.iRxUsed->idx & (d.rx_ring_size - 1);
DP("**(PDD) RxDfc (d.iTxAvail->idx) = %d (d.iRxAvail->idx) = %d", d.iTxAvail->idx, d.iRxAvail->idx);
DP("**(PDD) RxDfc (d.iRxUsed->idx) = %d ", d.iRxUsed->idx);
DP("**(PDD) RxDfc ring_slot=%d, (d.iRxUsed->ring[%d].id) = %d", ring_slot, ring_slot, d.iRxUsed->ring[ring_slot].id);
DP("**(PDD) RxDfc d.iRxDesc[1].next = %d", d.iRxDesc[1].next);
//check to see if this is a Rx or Tx
if (d.iRxUsed->idx != d.rx_last_used)
{
DP("** (PDD) Received Rx Interrupt");
d.rx_last_used++;
DP("** (PDD) Value of rx_last_used=%d", d.rx_last_used);
d.ReceiveIsr();
}
}
void EthernetDevice::MacConfigure(TEthernetConfigV01 &aConfig)
{
DP("** (PDD) EthernetDevice::MacConfigure");
iDefaultConfig.iEthAddress[0] = aConfig.iEthAddress[0];
iDefaultConfig.iEthAddress[1] = aConfig.iEthAddress[1];
iDefaultConfig.iEthAddress[2] = aConfig.iEthAddress[2];
iDefaultConfig.iEthAddress[3] = aConfig.iEthAddress[3];
iDefaultConfig.iEthAddress[4] = aConfig.iEthAddress[4];
iDefaultConfig.iEthAddress[5] = aConfig.iEthAddress[5];
DP ("** (PDD) macaddr %02x:%02x:%02x:%02x:%02x:%02x\n",
iDefaultConfig.iEthAddress[0],
iDefaultConfig.iEthAddress[1],
iDefaultConfig.iEthAddress[2],
iDefaultConfig.iEthAddress[3],
iDefaultConfig.iEthAddress[4],
iDefaultConfig.iEthAddress[5]);
}
//Descriptor list and vring_used must start on a 4k page boundary.
TAny * EthernetDevice::AllocAligned(TUint16 size)
{
DP("** (PDD) Enter: EthernetDevice::alloc_aligned");
DP("** (PDD) size=%d",size);
TAny * p = Kern::Alloc(size + 4095);
DP("** (PDD) BEFORE: p=%x",p);
p = (TAny *)(((TUint32)p + 4095) & ~4095);
DP("** (PDD) AFTER: p=%x",p);
DP("** (PDD) Exit: EthernetDevice::alloc_aligned");
return p;
}
TAny EthernetDevice::AllocRings()
{
DP("** (PDD) Enter: EthernetDevice::AllocRings");
TUint size;
TUint used_offset;
TUint32 p;
VNET[VIRTIO_QUEUE_SEL] = TX_QUEUE;
tx_ring_size = VNET[VIRTIO_QUEUE_NUM];
DP ("** (PDD) tx_ring_size = %d\n", tx_ring_size);
size = (tx_ring_size * (16 + 2)) + 4;
used_offset = (size + 4095) & ~1024;
DP ("** (PDD) used_offset = %d", used_offset);
size = used_offset + 4 + (tx_ring_size * 8);
DP ("** (PDD) size = %d", size);
p = (TUint32)AllocAligned(size);
DP ("** (PDD) p = %x\n", p);
VNET[VIRTIO_QUEUE_BASE] = Epoc::LinearToPhysical(p);
iTxDesc = reinterpret_cast<vring_desc*>(p);
iTxAvail = reinterpret_cast<vring_avail*>(p + tx_ring_size * 16);
iTxUsed = reinterpret_cast<vring_used*>(p + used_offset);
DP ("** (PDD) iTxDesc = %x", iTxDesc);
DP ("** (PDD) iTxAvail = %x", iTxAvail);
DP ("** (PDD) iTxUsed = %x", iTxUsed);
VNET[VIRTIO_QUEUE_SEL] = RX_QUEUE;
rx_ring_size = VNET[VIRTIO_QUEUE_NUM];
size = (rx_ring_size * (16 + 2)) + 4;
used_offset = (size + 4095) & ~4095;
size = used_offset + 4 + (rx_ring_size * 8);
p = (TUint32)AllocAligned(size);
DP ("** (PDD) p = %x\n", p);
VNET[VIRTIO_QUEUE_BASE] = Epoc::LinearToPhysical(p);
iRxDesc = reinterpret_cast<vring_desc*>(p);
iRxAvail = reinterpret_cast<vring_avail*>(p + rx_ring_size * 16);
iRxUsed = reinterpret_cast<vring_used*>(p + used_offset);
DP ("** (PDD) iRxDesc = %x", iRxDesc);
DP ("** (PDD) iRxAvail = %x", iRxAvail);
DP ("** (PDD) iRxUsed = %x", iRxUsed);
DP("** (PDD) Exit: EthernetDevice::AllocRings");
}
void EthernetDevice::AddRx()
{
DP("** (PDD) Enter: EthernetDevice::AddRx");
TInt n = iRxAvail->idx & (rx_ring_size - 1);
memset(&rx_header, 0, sizeof(rx_header));
iRxDesc[0].addr = Epoc::LinearToPhysical((TUint32)&rx_header);
iRxDesc[0].len = sizeof(rx_header);
iRxDesc[0].flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE;
iRxDesc[0].next = 1;
iRxDesc[1].addr = Epoc::LinearToPhysical((TUint32) iRxBuffer.Ptr());
iRxDesc[1].len = ETHERNET_PAYLOAD_SIZE;
iRxDesc[1].flags = VRING_DESC_F_WRITE;
iRxAvail->ring[n] = 0;
iRxAvail->idx++;
VNET[VIRTIO_QUEUE_NOTIFY] = RX_QUEUE;
DP("** (PDD) Exit: EthernetDevice::AddRx");
}
void EthernetDevice::SetMacAddress(void)
{
DP("** (PDD) Enter: EthernetDevice::SetMacAddress");
iDefaultConfig.iEthAddress[0] = VNET_MAC[0];
iDefaultConfig.iEthAddress[1] = VNET_MAC[1];
iDefaultConfig.iEthAddress[2] = VNET_MAC[2];
iDefaultConfig.iEthAddress[3] = VNET_MAC[3];
iDefaultConfig.iEthAddress[4] = VNET_MAC[4];
iDefaultConfig.iEthAddress[5] = VNET_MAC[5];
DP ("** (PDD) macaddr %02x:%02x:%02x:%02x:%02x:%02x\n",
iDefaultConfig.iEthAddress[0],
iDefaultConfig.iEthAddress[1],
iDefaultConfig.iEthAddress[2],
iDefaultConfig.iEthAddress[3],
iDefaultConfig.iEthAddress[4],
iDefaultConfig.iEthAddress[5]);
DP("** (PDD) Exit: EthernetDevice::SetMacAddress");
}
//
// Functions that are just stubs
//
void EthernetDevice::CheckConfig(TEthernetConfigV01& aConfig)
{
DP("** (PDD) EthernetDevice::CheckConfig");
}
TInt EthernetDevice::Configure(TEthernetConfigV01 &aConfig)
{
DP("** (PDD) EthernetDevice::Configure");
return KErrNone;
}