#include "i82557.h"
extern "C" {
#include <sys/param.h>
#include <sys/mbuf.h>
#include <string.h>
}
static inline IOReturn
IOPhysicalFromVirtual(vm_address_t vaddr, IOPhysicalAddress * paddr)
{
*paddr = pmap_extract(kernel_pmap, vaddr);
return (*paddr == 0) ? kIOReturnBadArgument : kIOReturnSuccess;
}
static inline scb_status_t _intrACK(CSR_t * CSR_p)
{
scb_status_t stat_irq = OSReadLE16(&CSR_p->status) & SCB_STATUS_INT_MASK;
if (stat_irq)
OSWriteLE16(&CSR_p->status, stat_irq); return (stat_irq);
}
static inline bool
_waitSCBCommandClear(CSR_t * CSR_p)
{
for (int i = 0; i < SPIN_TIMEOUT; i++) {
if (!OSReadLE8(&CSR_p->command))
return true;
IODelay(SPIN_COUNT);
}
return false; }
static inline bool
_waitCUNonActive(CSR_t * CSR_p)
{
for (int i = 0; i < SPIN_TIMEOUT; i++) {
if (CSR_VALUE(SCB_STATUS_CUS, OSReadLE16(&CSR_p->status)) !=
SCB_CUS_ACTIVE)
return true;
IODelay(SPIN_COUNT);
}
return false;
}
bool Intel82557::_polledCommand(cbHeader_t * hdr_p, IOPhysicalAddress paddr)
{
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: _polledCommand:(%s): _waitSCBCommandClear failed\n",
CUCommandString(CSR_VALUE(CB_CMD, OSReadLE16(&hdr_p->command))),
getName());
return false;
}
if (!_waitCUNonActive(CSR_p)) {
IOLog("%s: _polledCommand:(%s): _waitCUNonActive failed\n",
CUCommandString(CSR_VALUE(CB_CMD, OSReadLE16(&hdr_p->command))),
getName());
return false;
}
OSWriteLE32(&CSR_p->pointer, paddr);
OSWriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_START));
prevCUCommand = SCB_CUC_START;
for (int i = 0; i < SPIN_TIMEOUT; i++) {
if (OSReadLE16(&hdr_p->status) & CB_STATUS_C)
return true;
IODelay(SPIN_COUNT);
}
return false;
}
bool Intel82557::_abortReceive()
{
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: _abortReceive: _waitSCBCommandClear failed\n", getName());
return false;
}
OSWriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_RUC, SCB_RUC_ABORT));
for (int i = 0; i < SPIN_TIMEOUT; i++) {
if (CSR_VALUE(SCB_STATUS_RUS, OSReadLE16(&CSR_p->status)) ==
SCB_RUS_IDLE)
return true;
IODelay(SPIN_COUNT);
}
IOLog("%s: _abortReceive: timeout\n", getName());
return false;
}
bool Intel82557::_startReceive()
{
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: _startReceive: _waitSCBCommandClear failed\n", getName());
return false;
}
OSWriteLE32(&headRfd->rbdAddr, headRfd->_rbd._paddr);
OSWriteLE32(&CSR_p->pointer, headRfd->_paddr);
OSWriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_RUC, SCB_RUC_START));
for (int i = 0; i < SPIN_TIMEOUT; i++) {
if (CSR_VALUE(SCB_STATUS_RUS, OSReadLE16(&CSR_p->status)) ==
SCB_RUS_READY)
return true;
IODelay(SPIN_COUNT);
}
IOLog("%s: _startReceive: timeout\n", getName());
return false;
}
void Intel82557::_resetChip()
{
int i = 0;
sendPortCommand(portSelectiveReset_e, 0);
do {
IOSleep(1);
} while (OSReadLE32(&CSR_p->port) && ++i < 100);
sendPortCommand(portReset_e, 0);
IOSleep(1);
return;
}
void Intel82557::issueReset()
{
IOLog("%s: resetting adapter\n", getName());
etherStats->dot3RxExtraEntry.resets++;
setActivationLevel(kActivationLevel0);
if (!setActivationLevel(currentLevel)) {
IOLog("%s: Reset attempt unsuccessful\n", getName());
}
}
bool Intel82557::updateRFDFromMbuf(rfd_t * rfd_p, struct mbuf * m)
{
struct IOPhysicalSegment vector;
UInt count;
count = rxMbufCursor->getPhysicalSegments(m, &vector, 1);
if (!count)
return false;
rfd_p->_rbd.buffer = vector.location;
rfd_p->_rbd._mbuf = m;
return true;
}
bool Intel82557::_initTcbQ(bool enable = false)
{
int i;
tcbQ.numFree = tcbQ.numTcbs = NUM_TRANSMIT_FRAMES;
tcbQ.activeHead_p = tcbQ.activeTail_p = tcbQ.freeHead_p = tcbList_p;
for (i = 0; i < tcbQ.numTcbs; i++) {
if (tcbList_p[i]._mbuf) {
freePacket(tcbList_p[i]._mbuf);
tcbList_p[i]._mbuf = 0;
}
}
bzero(tcbList_p, sizeof(tcb_t) * tcbQ.numTcbs);
if (!enable)
return true;
for (i = 0; i < tcbQ.numTcbs; i++) {
IOPhysicalAddress paddr;
IOReturn result = IOPhysicalFromVirtual((vm_address_t) &tcbList_p[i],
&tcbList_p[i]._paddr);
if (result != kIOReturnSuccess) {
IOLog("i82557(tcbQ): Invalid TCB address\n");
return false;
}
result = IOPhysicalFromVirtual((vm_address_t) &tcbList_p[i]._tbds,
&paddr);
if (result != kIOReturnSuccess) {
IOLog("i82557(tcbQ): Invalid TBD address\n");
return false;
}
OSWriteLE32(&tcbList_p[i].tbdAddr, paddr);
if (i == (tcbQ.numTcbs - 1))
tcbList_p[i]._next = &tcbList_p[0];
else
tcbList_p[i]._next = &tcbList_p[i + 1];
}
for (i = 0; i < tcbQ.numTcbs; i++)
OSWriteLE32(&tcbList_p[i].link, tcbList_p[i]._next->_paddr);
return true;
}
static void _setupRfd(rfd_t * rfdList_p)
{
for (int i = 0; i < NUM_RECEIVE_FRAMES; i++) {
if (i == (NUM_RECEIVE_FRAMES - 1)) {
OSSetLE16(&rfdList_p[i].command, RFD_COMMAND_EL);
rfdList_p[i]._next = &rfdList_p[0];
OSSetLE32(&rfdList_p[i]._rbd.size, RBD_SIZE_EL);
rfdList_p[i]._rbd._next = &rfdList_p[0]._rbd;
}
else {
rfdList_p[i]._next = &rfdList_p[i + 1];
rfdList_p[i]._rbd._next = &rfdList_p[i + 1]._rbd;
}
OSWriteLE32(&rfdList_p[i].link, rfdList_p[i]._next->_paddr);
OSWriteLE32(&rfdList_p[i].rbdAddr,
(i == 0) ? rfdList_p[0]._rbd._paddr : C_NULL);
OSWriteLE32(&rfdList_p[i]._rbd.link, rfdList_p[i]._rbd._next->_paddr);
OSSetLE32(&rfdList_p[i]._rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE));
}
}
bool Intel82557::_initRfdList(bool enable = false)
{
int i;
IOReturn result;
for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
if (rfdList_p[i]._rbd._mbuf) {
freePacket(rfdList_p[i]._rbd._mbuf);
}
}
bzero(rfdList_p, sizeof(rfd_t) * NUM_RECEIVE_FRAMES);
if (!enable)
return true;
for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
OSSetLE16(&rfdList_p[i].command, RFD_COMMAND_SF);
result = IOPhysicalFromVirtual((vm_address_t) &rfdList_p[i],
&rfdList_p[i]._paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid RFD address\n", getName());
return false;
}
result = IOPhysicalFromVirtual((vm_address_t) &rfdList_p[i]._rbd,
&rfdList_p[i]._rbd._paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid RBD address\n", getName());
return false;
}
}
_setupRfd(rfdList_p);
for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
struct mbuf * m = allocatePacket(MAX_BUF_SIZE);
if (!m)
return false;
if (updateRFDFromMbuf(&rfdList_p[i], m) == false) {
IOLog("%s: updateRFDFromMbuf() error\n", getName());
freePacket(m);
return false;
}
}
headRfd = rfdList_p;
tailRfd = rfdList_p + NUM_RECEIVE_FRAMES - 1;
return true;
}
bool Intel82557::_resetRfdList()
{
int i;
struct _cache {
IOPhysicalAddress rbd_buffer;
struct mbuf * rbd_mbuf;
IOPhysicalAddress rfd_paddr;
IOPhysicalAddress rbd_paddr;
} * cache_p = (struct _cache *) KDB_buf_p;
if ((sizeof(struct _cache) * NUM_RECEIVE_FRAMES) > ETHERMAXPACKET) {
IOLog("%s: no space for cache data\n", getName());
return false;
}
for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
cache_p[i].rbd_mbuf = rfdList_p[i]._rbd._mbuf;
cache_p[i].rbd_buffer = rfdList_p[i]._rbd.buffer;
cache_p[i].rfd_paddr = rfdList_p[i]._paddr;
cache_p[i].rbd_paddr = rfdList_p[i]._rbd._paddr;
}
bzero(rfdList_p, sizeof(rfd_t) * NUM_RECEIVE_FRAMES);
for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
OSSetLE16(&rfdList_p[i].command, RFD_COMMAND_SF);
rfdList_p[i]._paddr = cache_p[i].rfd_paddr;
rfdList_p[i]._rbd._paddr = cache_p[i].rbd_paddr;
}
_setupRfd(rfdList_p);
for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
rfdList_p[i]._rbd.buffer = cache_p[i].rbd_buffer;
rfdList_p[i]._rbd._mbuf = cache_p[i].rbd_mbuf;
}
headRfd = rfdList_p;
tailRfd = rfdList_p + NUM_RECEIVE_FRAMES - 1;
return true;
}
bool
Intel82557::_mdiReadPHY(UInt8 phyAddress, UInt8 regAddress, UInt16 * data_p)
{
mdi_control_t mdi;
mdi = CSR_FIELD(MDI_CONTROL_PHYADDR, phyAddress) |
CSR_FIELD(MDI_CONTROL_REGADDR, regAddress) |
CSR_FIELD(MDI_CONTROL_OPCODE, MDI_CONTROL_OP_READ);
OSWriteLE32(&CSR_p->mdiControl, mdi);
IODelay(20);
bool ready = false;
for (int i = 0; i < SPIN_TIMEOUT; i++) {
if (OSReadLE32(&CSR_p->mdiControl) & MDI_CONTROL_READY) {
ready = true;
break;
}
IODelay(20);
}
if (ready == false) {
IOLog("%s: _mdiReadPHYRegisterSuccess timeout\n", getName());
return false;
}
*data_p = CSR_VALUE(MDI_CONTROL_DATA, OSReadLE32(&CSR_p->mdiControl));
return true;
}
bool Intel82557::_mdiWritePHY(UInt8 phyAddress, UInt8 regAddress, UInt16 data)
{
mdi_control_t mdi;
mdi = CSR_FIELD(MDI_CONTROL_PHYADDR, phyAddress) |
CSR_FIELD(MDI_CONTROL_REGADDR, regAddress) |
CSR_FIELD(MDI_CONTROL_OPCODE, MDI_CONTROL_OP_WRITE) |
CSR_FIELD(MDI_CONTROL_DATA, data);
OSWriteLE32(&CSR_p->mdiControl, mdi);
IODelay(20);
bool ready = false;
for (int i = 0; i < SPIN_TIMEOUT; i++) {
if (OSReadLE32(&CSR_p->mdiControl) & MDI_CONTROL_READY) {
ready = true;
break;
}
IODelay(20);
}
if (ready == false) {
IOLog("%s: _mdiWritePHYRegisterData timeout\n", getName());
return false;
}
return true;
}
bool Intel82557::nop()
{
cbHeader_t * nop_p = &overlay_p->nop;
bzero(nop_p, sizeof(*nop_p));
OSWriteLE16(&nop_p->command, CSR_FIELD(CB_CMD, CB_CMD_NOP) | CB_EL);
OSWriteLE32(&nop_p->link, C_NULL);
return _polledCommand(nop_p, overlay_paddr);
}
bool Intel82557::config()
{
UInt8 * cb_p;
cb_configure_t * cfg_p = &overlay_p->configure;
bzero(cfg_p, sizeof(*cfg_p));
OSWriteLE16(&cfg_p->header.command,
CSR_FIELD(CB_CMD, CB_CMD_CONFIGURE) | CB_EL);
OSWriteLE32(&cfg_p->header.link, C_NULL);
cb_p = cfg_p->byte;
cb_p[0] = CSR_FIELD(CB_CB0_BYTE_COUNT, CB_CONFIG_BYTE_COUNT);
cb_p[1] = CSR_FIELD(CB_CB1_TX_FIFO_LIMIT, CB_CB1_TX_FIFO_0) |
CSR_FIELD(CB_CB1_RX_FIFO_LIMIT, CB_CB1_RX_FIFO_64);
cb_p[3] = CB_CB3_MWI_ENABLE;
cb_p[4] = 0; cb_p[5] = 0;
cb_p[6] = CB_CB6_NON_DIRECT_DMA | CB_CB6_STD_TCB | CB_CB6_STD_STATS;
cb_p[7] = CSR_FIELD(CB_CB7_UNDERRUN_RETRY, CB_CB7_UNDERRUN_RETRY_1) |
CB_CB7_DISC_SHORT_FRAMES;
if ((eeprom->getContents()->controllerType != I82558_CONTROLLER_TYPE) &&
(phyAddr != PHY_ADDRESS_I82503))
cb_p[8] = CB_CB8_CSMA_EN;
cb_p[10] = CSR_FIELD(CB_CB10_PREAMBLE, CB_CB10_PREAMBLE_7_BYTES) |
CB_CB10_NSAI;
cb_p[12] = CSR_FIELD(CB_CB12_IFS, CB_CB12_IFS_96_BIT_TIMES);
cb_p[13] = CSR_FIELD(CB_CB13_FC_TYPE_LSB, CB_CB13_FC_TYPE_LSB_DEF);
cb_p[14] = CSR_FIELD(CB_CB14_FC_TYPE_MSB, CB_CB14_FC_TYPE_MSB_DEF);
cb_p[15] = ((cb_p[8] & CB_CB8_CSMA_EN) ? 0 : CB_CB15_CRS_CDT) |
(promiscuousEnabled ? CB_CB15_PROMISCUOUS : 0);
cb_p[16] = CSR_FIELD(CB_CB16_FC_DELAY_LSB, CB_CB16_FC_DELAY_LSB_DEF);
cb_p[17] = CSR_FIELD(CB_CB17_FC_DELAY_MSB, CB_CB17_FC_DELAY_MSB_DEF);
cb_p[18] = CB_CB18_PADDING | CB_CB18_STRIPPING;
#if 0 // XXX - need to fix this
if (forceFullDuplex || (phyAddr == PHY_ADDRESS_0 && fullDuplexMode))
cb_p[19] = CB_CB19_FORCE_FDX;
#endif
cb_p[19] = CB_CB19_AUTO_FDX;
if (flowControl) {
cb_p[19] |= ( CB_CB19_TX_FC |
CB_CB19_RX_FC_RESTOP |
CB_CB19_RX_FC_RESTART |
CB_CB19_REJECT_FC );
}
cb_p[20] = CSR_FIELD(CB_CB20_FC_ADDR_LSB, CB_CB20_FC_ADDR_LSB_DEF);
IOSync();
return _polledCommand((cbHeader_t *) cfg_p, overlay_paddr);
}
bool Intel82557::iaSetup()
{
cb_iasetup_t * iaSetup_p = &overlay_p->iasetup;
bzero(iaSetup_p, sizeof(*iaSetup_p));
OSWriteLE16(&iaSetup_p->header.command, CSR_FIELD(CB_CMD, CB_CMD_IASETUP) |
CB_EL);
OSWriteLE32(&iaSetup_p->header.link, C_NULL);
iaSetup_p->addr = myAddress;
return _polledCommand((cbHeader_t *) iaSetup_p, overlay_paddr);
}
bool Intel82557::mcSetup(IOEthernetAddress * addrs,
UInt count,
bool fromData = false)
{
cb_mcsetup_t * mcSetup_p;
bool cmdResult;
IOReturn result;
IOPhysicalAddress mcSetup_paddr;
if (fromData) {
addrs = 0;
count = 0;
if (netif) {
OSData * mcData = OSDynamicCast(OSData,
netif->getProperty(kIOMulticastFilterData));
if (mcData) {
addrs = (IOEthernetAddress *) mcData->getBytesNoCopy();
count = mcData->getLength() / sizeof(IOEthernetAddress);
assert(addrs && count);
}
}
}
mcSetup_p = (cb_mcsetup_t *) IOMallocAligned(PAGE_SIZE, PAGE_SIZE);
if (!mcSetup_p) {
IOLog("%s: mcSetup:IOMallocAligned return NULL\n", getName());
return false;
}
reserveDebuggerLock();
do {
cmdResult = false;
OSWriteLE16(&mcSetup_p->header.status, 0);
OSWriteLE16(&mcSetup_p->header.command,
CSR_FIELD(CB_CMD, CB_CMD_MCSETUP) | CB_EL);
OSWriteLE32(&mcSetup_p->header.link, C_NULL);
for (UInt i = 0; i < count; i++)
mcSetup_p->addrs[i] = addrs[i];
OSWriteLE16(&mcSetup_p->count, count * sizeof(IOEthernetAddress));
result = IOPhysicalFromVirtual((vm_address_t) mcSetup_p,
&mcSetup_paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid MC-setup command block address\n", getName());
break;
}
if (!_polledCommand((cbHeader_t *) mcSetup_p, mcSetup_paddr)) {
IOLog("%s: MC-setup command failed 0x%x\n", getName(),
OSReadLE16(&mcSetup_p->header.status));
break;
}
cmdResult = (OSReadLE16(&mcSetup_p->header.status) & CB_STATUS_OK) ?
true : false;
} while (0);
releaseDebuggerLock();
IOFreeAligned(mcSetup_p, PAGE_SIZE);
return cmdResult;
}
bool Intel82557::_selfTest()
{
port_selftest_t * test_p = (port_selftest_t *) overlay_p;
UInt32 results;
OSWriteLE32(&test_p->signature, 0);
OSWriteLE32(&test_p->results, ~0);
sendPortCommand(portSelfTest_e, overlay_paddr);
IOSleep(20);
if (OSReadLE32(&test_p->signature) == 0) {
IOLog("%s: Self test timed out\n", getName());
return false;
}
results = OSReadLE32(&test_p->results);
if (results) {
if (results & PORT_SELFTEST_ROM)
IOLog("%s: Self test reports invalid ROM contents\n",
getName());
if (results & PORT_SELFTEST_REGISTER)
IOLog("%s: Self test reports internal register failure\n",
getName());
if (results & PORT_SELFTEST_DIAGNOSE)
IOLog("%s: Self test reports serial subsystem failure\n",
getName());
if (results & PORT_SELFTEST_GENERAL)
IOLog("%s: Self test failed\n", getName());
return false;
}
return true;
}
void Intel82557::sendPortCommand(port_command_t command, UInt arg)
{
OSWriteLE32(&CSR_p->port, (arg & PORT_ADDRESS_MASK) |
CSR_FIELD(PORT_FUNCTION, command));
return;
}
void Intel82557::enableAdapterInterrupts()
{
UInt8 interruptByte;
interruptByte = SCB_INTERRUPT_ER | SCB_INTERRUPT_FCP;
OSWriteLE8(&CSR_p->interrupt, interruptByte);
interruptEnabled = true;
return;
}
void Intel82557::disableAdapterInterrupts()
{
UInt8 interruptByte;
interruptByte = SCB_INTERRUPT_M;
OSWriteLE8(&CSR_p->interrupt, interruptByte);
interruptEnabled = false;
return;
}
static inline void
_logCounters(errorCounters_t * errorCounters_p)
{
if (errorCounters_p->tx_good_frames)
IOLog("tx_good_frames %ld\n",
OSReadLE32(&errorCounters_p->tx_good_frames));
if (errorCounters_p->tx_maxcol_errors)
IOLog("tx_maxcol_errors %ld\n",
OSReadLE32(&errorCounters_p->tx_maxcol_errors));
if (errorCounters_p->tx_late_collision_errors)
IOLog("tx_late_collision_errors %ld\n",
OSReadLE32(&errorCounters_p->tx_late_collision_errors));
if (errorCounters_p->tx_underrun_errors)
IOLog("tx_underrun_errors %ld\n",
OSReadLE32(&errorCounters_p->tx_underrun_errors));
if (errorCounters_p->tx_lost_carrier_sense_errors)
IOLog("tx_lost_carrier_sense_errors %ld\n",
OSReadLE32(&errorCounters_p->tx_lost_carrier_sense_errors));
if (errorCounters_p->tx_deferred)
IOLog("tx_deferred %ld\n", OSReadLE32(&errorCounters_p->tx_deferred));
if (errorCounters_p->tx_single_collisions)
IOLog("tx_single_collisions %ld\n",
OSReadLE32(&errorCounters_p->tx_single_collisions));
if (errorCounters_p->tx_multiple_collisions)
IOLog("tx_multiple_collisions %ld\n",
OSReadLE32(&errorCounters_p->tx_multiple_collisions));
if (errorCounters_p->tx_total_collisions)
IOLog("tx_total_collisions %ld\n",
OSReadLE32(&errorCounters_p->tx_total_collisions));
if (errorCounters_p->rx_good_frames)
IOLog("rx_good_frames %ld\n",
OSReadLE32(&errorCounters_p->rx_good_frames));
if (errorCounters_p->rx_crc_errors)
IOLog("rx_crc_errors %ld\n",
OSReadLE32(&errorCounters_p->rx_crc_errors));
if (errorCounters_p->rx_alignment_errors)
IOLog("rx_alignment_errors %ld\n",
OSReadLE32(&errorCounters_p->rx_alignment_errors));
if (errorCounters_p->rx_resource_errors)
IOLog("rx_resource_errors %ld\n",
OSReadLE32(&errorCounters_p->rx_resource_errors));
if (errorCounters_p->rx_overrun_errors)
IOLog("rx_overrun_errors %ld\n",
OSReadLE32(&errorCounters_p->rx_overrun_errors));
if (errorCounters_p->rx_collision_detect_errors)
IOLog("rx_collision_detect_errors %ld\n",
OSReadLE32(&errorCounters_p->rx_collision_detect_errors));
if (errorCounters_p->rx_short_frame_errors)
IOLog("rx_short_frame_errors %ld\n",
OSReadLE32(&errorCounters_p->rx_short_frame_errors));
return;
}
bool Intel82557::_dumpStatistics()
{
reserveDebuggerLock();
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: _dumpStatistics: _waitSCBCommandClear failed\n", getName());
return false;
}
OSWriteLE8(&CSR_p->command,
CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_DUMP_RESET_STAT));
prevCUCommand = SCB_CUC_DUMP_RESET_STAT;
releaseDebuggerLock();
return true;
}
void Intel82557::_updateStatistics()
{
if (OSReadLE32(&errorCounters_p->_status) != DUMP_STATUS) {
if (verbose)
_logCounters(errorCounters_p);
etherStats->dot3StatsEntry.singleCollisionFrames +=
OSReadLE32(&errorCounters_p->tx_single_collisions);
etherStats->dot3StatsEntry.multipleCollisionFrames +=
OSReadLE32(&errorCounters_p->tx_multiple_collisions);
etherStats->dot3StatsEntry.lateCollisions +=
OSReadLE32(&errorCounters_p->tx_late_collision_errors);
etherStats->dot3StatsEntry.excessiveCollisions +=
OSReadLE32(&errorCounters_p->tx_maxcol_errors);
etherStats->dot3StatsEntry.deferredTransmissions +=
OSReadLE32(&errorCounters_p->tx_deferred);
etherStats->dot3StatsEntry.carrierSenseErrors +=
OSReadLE32(&errorCounters_p->tx_lost_carrier_sense_errors);
etherStats->dot3TxExtraEntry.underruns +=
OSReadLE32(&errorCounters_p->tx_underrun_errors);
etherStats->dot3StatsEntry.alignmentErrors +=
OSReadLE32(&errorCounters_p->rx_alignment_errors);
etherStats->dot3StatsEntry.fcsErrors +=
OSReadLE32(&errorCounters_p->rx_crc_errors);
etherStats->dot3RxExtraEntry.resourceErrors +=
OSReadLE32(&errorCounters_p->rx_resource_errors);
etherStats->dot3RxExtraEntry.overruns +=
OSReadLE32(&errorCounters_p->rx_overrun_errors);
etherStats->dot3RxExtraEntry.collisionErrors +=
OSReadLE32(&errorCounters_p->rx_collision_detect_errors);
etherStats->dot3RxExtraEntry.frameTooShorts +=
OSReadLE32(&errorCounters_p->rx_short_frame_errors);
netStats->outputErrors =
( etherStats->dot3StatsEntry.lateCollisions
+ etherStats->dot3StatsEntry.excessiveCollisions
+ etherStats->dot3StatsEntry.carrierSenseErrors
+ etherStats->dot3TxExtraEntry.underruns
+ etherStats->dot3TxExtraEntry.resourceErrors);
netStats->inputErrors =
( etherStats->dot3StatsEntry.fcsErrors
+ etherStats->dot3StatsEntry.alignmentErrors
+ etherStats->dot3RxExtraEntry.resourceErrors
+ etherStats->dot3RxExtraEntry.overruns
+ etherStats->dot3RxExtraEntry.collisionErrors
+ etherStats->dot3RxExtraEntry.frameTooShorts);
netStats->collisions +=
OSReadLE32(&errorCounters_p->tx_total_collisions);
OSWriteLE32(&errorCounters_p->_status, DUMP_STATUS);
_dumpStatistics();
}
}
bool Intel82557::_allocateMemPage(pageBlock_t * p)
{
p->memSize = PAGE_SIZE;
p->memPtr = IOMallocAligned(p->memSize, PAGE_SIZE);
if (!p->memPtr)
return false;
bzero(p->memPtr, p->memSize);
p->memAllocPtr = p->memPtr;
p->memAvail = p->memSize;
return true;
}
void Intel82557::_freeMemPage(pageBlock_t * p)
{
IOFreeAligned(p->memPtr, p->memSize);
}
bool Intel82557::hwInit()
{
disableAdapterInterrupts();
_resetChip();
disableAdapterInterrupts();
OSWriteLE8(&CSR_p->earlyRxInterrupt, 0);
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: hwInit: CU _waitSCBCommandClear failed\n", getName());
return false;
}
OSWriteLE32(&CSR_p->pointer, 0);
OSWriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_LOAD_BASE));
prevCUCommand = SCB_CUC_LOAD_BASE;
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: hwInit: RU _waitSCBCommandClear failed\n", getName());
return false;
}
OSWriteLE32(&CSR_p->pointer, 0);
OSWriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_RUC, SCB_RUC_LOAD_BASE));
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: hwInit: before LOAD_DUMP_COUNTERS_ADDRESS:"
" _waitSCBCommandClear failed\n", getName());
return false;
}
OSWriteLE32(&errorCounters_p->_status, DUMP_STATUS);
OSWriteLE32(&CSR_p->pointer, errorCounters_paddr);
OSWriteLE8(&CSR_p->command,
CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_LOAD_DUMP_ADDR));
prevCUCommand = SCB_CUC_LOAD_DUMP_ADDR;
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: hwInit: before intrACK _waitSCBCommandClear failed\n",
getName());
return false;
}
OSWriteLE8(&CSR_p->flowControlThreshold,
CSR_FIELD(FC_THRESHOLD, FC_THRESHOLD_512));
_intrACK(CSR_p);
_phyProbe();
phyID = _phyGetID();
VPRINT("%s: PHY model id is 0x%08lx\n", getName(), phyID);
phyID &= PHY_MODEL_MASK;
if (!config())
return false;
IOSleep(500);
if (!iaSetup())
return false;
_intrACK(CSR_p);
return true;
}
void * Intel82557::_memAllocFrom(pageBlock_t * p, UInt allocSize, UInt align)
{
void * allocPtr;
UInt sizeReal;
if (align == 0)
return 0;
allocPtr =
(void *)((UInt)((UInt) p->memAllocPtr + (align - 1)) & (~(align - 1)));
sizeReal = allocSize + ((UInt) allocPtr - (UInt) p->memAllocPtr);
if (sizeReal > p->memAvail)
return 0;
p->memAllocPtr = (void *)((UInt) p->memAllocPtr + sizeReal);
p->memAvail = p->memSize - ((UInt) p->memAllocPtr - (UInt) p->memPtr);
return allocPtr;
}
bool Intel82557::coldInit()
{
IOReturn result;
IOPhysicalAddress paddr;
disableAdapterInterrupts();
if (!_allocateMemPage(&shared)) {
IOLog("%s: Can't allocate shared memory page\n", getName());
return false;
}
if (!_allocateMemPage(&txRing)) {
IOLog("%s: Can't allocate memory page for TX ring\n", getName());
return false;
}
if (!_allocateMemPage(&rxRing)) {
IOLog("%s: Can't allocate memory page for RX ring\n", getName());
return false;
}
overlay_p = (overlay_t *) _memAllocFrom(&shared, sizeof(overlay_t),
PARAGRAPH_ALIGNMENT);
if (!overlay_p)
return false;
result = IOPhysicalFromVirtual((vm_address_t) overlay_p, &overlay_paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid command block address\n", getName());
return false;
}
tcbList_p = (tcb_t *) _memAllocFrom(&txRing,
sizeof(tcb_t) * NUM_TRANSMIT_FRAMES,
CACHE_ALIGNMENT);
if (!tcbList_p)
return false;
KDB_tcb_p = (tcb_t *) _memAllocFrom(&shared,
sizeof(tcb_t),
CACHE_ALIGNMENT);
if (!KDB_tcb_p)
return false;
result = IOPhysicalFromVirtual((vm_address_t) KDB_tcb_p,
&KDB_tcb_p->_paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid TCB address\n", getName());
return false;
}
result = IOPhysicalFromVirtual((vm_address_t) &KDB_tcb_p->_tbds, &paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid TCB->_TBD address\n", getName());
return false;
}
OSWriteLE32(&KDB_tcb_p->tbdAddr, paddr);
KDB_buf_p = _memAllocFrom(&shared, ETHERMAXPACKET, DWORD_ALIGNMENT);
if (!KDB_buf_p)
return false;
result = IOPhysicalFromVirtual((vm_address_t) KDB_buf_p, &KDB_buf_paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid address\n", getName());
return false;
}
errorCounters_p = (errorCounters_t *) _memAllocFrom(&shared,
sizeof(errorCounters_t),
DWORD_ALIGNMENT);
if (!errorCounters_p)
return false;
result = IOPhysicalFromVirtual((vm_address_t) errorCounters_p,
&errorCounters_paddr);
if (result != kIOReturnSuccess) {
IOLog("%s: Invalid errorCounters address\n", getName());
return false;
}
rfdList_p = (rfd_t *) _memAllocFrom(&rxRing,
sizeof(rfd_t) * NUM_RECEIVE_FRAMES,
CACHE_ALIGNMENT);
if (!rfdList_p)
return false;
if (!_selfTest())
return false;
myAddress = eeprom->getContents()->addr;
return true;
}
bool Intel82557::receiveInterruptOccurred()
{
bool packetsQueued = false;
while (OSReadLE16(&headRfd->status) & RFD_STATUS_C) {
rbd_count_t rbd_count = OSReadLE32(&headRfd->_rbd.count);
UInt rxCount = CSR_VALUE(RBD_COUNT, rbd_count);
#if 0
if (!(rbd_count & RBD_COUNT_EOF)) {
IOLog("%s: more than 1 rbd, frame size %d\n", getName(), rxCount);
IOLog("%s: RFD status: %04x\n", getName(),
OSReadLE16(&headRfd->status));
issueReset();
return;
}
#endif
if ((!(OSReadLE16(&headRfd->status) & RFD_STATUS_OK)) ||
(rxCount < (ETHERMINPACKET - ETHERCRC)) ||
!enabledForNetif) {
;
}
else {
struct mbuf * m = headRfd->_rbd._mbuf;
struct mbuf * m_in = 0; bool replaced;
packetsReceived = true;
m_in = replaceOrCopyPacket(&m, rxCount, &replaced);
if (!m_in) {
etherStats->dot3RxExtraEntry.resourceErrors++;
goto RX_INTR_ABORT;
}
if (replaced && (updateRFDFromMbuf(headRfd, m) == false)) {
freePacket(m); m_in = 0; etherStats->dot3RxExtraEntry.resourceErrors++;
IOLog("%s: updateRFDFromMbuf() error\n", getName());
goto RX_INTR_ABORT;
}
netif->inputPacket(m_in, rxCount, true);
packetsQueued = true;
netStats->inputPackets++;
}
RX_INTR_ABORT:
OSWriteLE16(&headRfd->status, 0);
OSWriteLE16(&headRfd->command, (RFD_COMMAND_SF | RFD_COMMAND_EL));
OSWriteLE32(&headRfd->rbdAddr, C_NULL);
OSWriteLE32(&headRfd->misc, 0);
OSWriteLE32(&headRfd->_rbd.count, 0);
OSWriteLE32(&headRfd->_rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE) |
RBD_SIZE_EL);
OSWriteLE32(&tailRfd->_rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE));
OSWriteLE16(&tailRfd->command, RFD_COMMAND_SF);
tailRfd = headRfd; headRfd = headRfd->_next; }
return packetsQueued;
}
void Intel82557::transmitInterruptOccurred()
{
tcbQ_t * tcbQ_p = &tcbQ;
tcb_t * head;
head = tcbQ_p->activeHead_p;
while (tcbQ_p->numFree < tcbQ_p->numTcbs &&
(OSReadLE16(&head->status) & TCB_STATUS_C))
{
OSWriteLE16(&head->status, 0);
if (head->_mbuf) {
freePacket(head->_mbuf);
head->_mbuf = 0;
}
head = tcbQ_p->activeHead_p = head->_next;
tcbQ_p->numFree++;
}
return;
}
void Intel82557::interruptOccurred(IOInterruptEventSource * src, int )
{
scb_status_t status;
bool flushInputQ = false;
bool doService = false;
reserveDebuggerLock();
if (interruptEnabled == false) {
_intrACK(CSR_p);
releaseDebuggerLock();
IOLog("%s: unexpected interrupt\n", getName());
return;
}
while (1) {
if ((status = _intrACK(CSR_p)) == 0)
break;
if (status & (SCB_STATUS_FR | SCB_STATUS_RNR)) {
flushInputQ = receiveInterruptOccurred() || flushInputQ;
etherStats->dot3RxExtraEntry.interrupts++;
if (status & SCB_STATUS_RNR) {
etherStats->dot3RxExtraEntry.resets++;
_abortReceive();
_resetRfdList();
if (!_startReceive()) {
IOLog("%s: Unable to restart receiver\n", getName());
}
}
}
if (status & (SCB_STATUS_CX | SCB_STATUS_CNA)) {
transmitInterruptOccurred();
etherStats->dot3TxExtraEntry.interrupts++;
doService = true;
}
}
releaseDebuggerLock();
if (enabledForNetif) {
if (flushInputQ)
netif->flushInputQueue();
if (doService)
transmitQueue->service();
}
}
struct mbuf *
Intel82557::updateTCBForMbuf(tcb_t * tcb_p, struct mbuf * m)
{
OSWriteLE16(&tcb_p->status, 0);
if (++txCount == TRANSMIT_INT_DELAY) {
OSWriteLE16(&tcb_p->command, CSR_FIELD(TCB_COMMAND, CB_CMD_TRANSMIT) |
TCB_COMMAND_S |
TCB_COMMAND_SF |
TCB_COMMAND_I);
txCount = 0;
}
else
OSWriteLE16(&tcb_p->command, CSR_FIELD(TCB_COMMAND, CB_CMD_TRANSMIT) |
TCB_COMMAND_S |
TCB_COMMAND_SF);
OSWriteLE8(&tcb_p->threshold, TCB_TX_THRESHOLD);
OSWriteLE16(&tcb_p->count, 0);
UInt segments = txMbufCursor->getPhysicalSegmentsWithCoalesce(m,
(struct IOPhysicalSegment *) &tcb_p->_tbds[0],
TBDS_PER_TCB);
if (!segments) {
IOLog("%s: getPhysicalSegments error, pkt len = %d\n",
getName(), m->m_pkthdr.len);
return 0;
}
OSWriteLE8(&tcb_p->number, segments);
return m;
}
UInt32 Intel82557::outputPacket(struct mbuf * m, void * param)
{
tcb_t * tcb_p;
if (!enabledForNetif) { freePacket(m);
return kIOReturnOutputDropped;
}
reserveDebuggerLock();
if (tcbQ.numFree == 0) { releaseDebuggerLock();
return kIOReturnOutputStall;
}
packetsTransmitted = true;
netStats->outputPackets++;
tcb_p = tcbQ.freeHead_p;
tcb_p->_mbuf = updateTCBForMbuf(tcb_p, m);
if (tcb_p->_mbuf == 0) {
etherStats->dot3TxExtraEntry.resourceErrors++;
goto fail;
}
tcbQ.numFree--;
tcbQ.freeHead_p = tcbQ.freeHead_p->_next;
if (tcbQ.activeTail_p != tcb_p)
OSClearLE16(&tcbQ.activeTail_p->command, TCB_COMMAND_S);
tcbQ.activeTail_p = tcb_p;
if (CSR_VALUE(SCB_STATUS_CUS, OSReadLE16(&CSR_p->status)) == SCB_CUS_IDLE)
{
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: outputPacket: _waitSCBCommandClear error\n", getName());
etherStats->dot3TxExtraEntry.timeouts++;
goto fail;
}
OSWriteLE32(&CSR_p->pointer, tcb_p->_paddr);
OSWriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_START));
prevCUCommand = SCB_CUC_START;
}
else {
if (prevCUCommand != SCB_CUC_RESUME) {
if (!_waitSCBCommandClear(CSR_p)) {
IOLog("%s: outputPacket: _waitSCBCommandClear error\n",
getName());
etherStats->dot3TxExtraEntry.timeouts++;
goto fail;
}
}
OSWriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC,SCB_CUC_RESUME));
prevCUCommand = SCB_CUC_RESUME;
}
releaseDebuggerLock();
return kIOReturnOutputSuccess;
fail:
freePacket(m);
tcb_p->_mbuf = 0;
releaseDebuggerLock();
return kIOReturnOutputDropped;
}
bool Intel82557::_receivePacket(void * pkt, UInt * len, UInt timeout)
{
bool processPacket = true;
bool ret = false;
scb_status_t status;
timeout *= 1000;
while ((OSReadLE16(&headRfd->status) & RFD_STATUS_C) == 0) {
if ((int) timeout <= 0) {
processPacket = false;
break;
}
IODelay(50);
timeout -= 50;
}
if (processPacket) {
if ((OSReadLE16(&headRfd->status) & RFD_STATUS_OK) &&
(OSReadLE32(&headRfd->_rbd.count) & RBD_COUNT_EOF))
{
*len = CSR_VALUE(RBD_COUNT, OSReadLE32(&headRfd->_rbd.count));
*len = MIN(*len, ETHERMAXPACKET);
bcopy(mtod(headRfd->_rbd._mbuf, void *), pkt, *len);
ret = true;
}
OSWriteLE16(&headRfd->status, 0);
OSWriteLE16(&headRfd->command, (RFD_COMMAND_SF | RFD_COMMAND_EL));
OSWriteLE32(&headRfd->rbdAddr, C_NULL);
OSWriteLE32(&headRfd->misc, 0);
OSWriteLE32(&headRfd->_rbd.count, 0);
OSWriteLE32(&headRfd->_rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE) |
RBD_SIZE_EL);
OSWriteLE32(&tailRfd->_rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE));
OSWriteLE16(&tailRfd->command, RFD_COMMAND_SF);
tailRfd = headRfd; headRfd = headRfd->_next; }
status = OSReadLE16(&CSR_p->status) & SCB_STATUS_RNR;
if (status) {
OSWriteLE16(&CSR_p->status, status);
IOLog("Intel82557::%s restarting receiver\n", __FUNCTION__);
IOLog("%s::%s RUS:0x%x Index:%d\n", getName(), __FUNCTION__,
CSR_VALUE(SCB_STATUS_RUS, OSReadLE16(&CSR_p->status)),
tailRfd - rfdList_p);
_abortReceive();
#if 0 // Display RFD/RBD fields
for (int i = 0; i < NUM_RECEIVE_FRAMES; i++) {
IOLog(" %02d: %04x %04x - %08x %08x\n", i,
OSReadLE16(&rfdList_p[i].command),
OSReadLE16(&rfdList_p[i].status),
OSReadLE32(&rfdList_p[i]._rbd.size),
OSReadLE32(&rfdList_p[i].misc));
}
#endif
_resetRfdList();
_startReceive();
}
return ret;
}
bool Intel82557::_sendPacket(void * pkt, UInt len)
{
tbd_t * tbd_p;
OSWriteLE16(&KDB_tcb_p->status, 0);
OSWriteLE32(&KDB_tcb_p->link, C_NULL);
OSWriteLE8(&KDB_tcb_p->threshold, TCB_TX_THRESHOLD);
OSWriteLE16(&KDB_tcb_p->command, CSR_FIELD(TCB_COMMAND, CB_CMD_TRANSMIT) |
TCB_COMMAND_EL |
TCB_COMMAND_SF );
OSWriteLE16(&KDB_tcb_p->count, 0); OSWriteLE8(&KDB_tcb_p->number, 1);
len = MIN(len, ETHERMAXPACKET);
len = MAX(len, ETHERMINPACKET);
bcopy(pkt, KDB_buf_p, len);
tbd_p = &KDB_tcb_p->_tbds[0];
OSWriteLE32(&tbd_p->addr, KDB_buf_paddr);
OSWriteLE32(&tbd_p->size, CSR_FIELD(TBD_SIZE, len));
return _polledCommand((cbHeader_t *) KDB_tcb_p, KDB_tcb_p->_paddr);
}