home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.sustworks.com 2018
/
ftp.sustworks.com.zip
/
ftp.sustworks.com
/
USBAx8817x_101.dmg
/
src
/
Source
/
USBAx8817x.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2005-10-08
|
94KB
|
3,103 lines
/*
File: USBAx8817x.cpp
Description: Driver for USBAx8817 USB-To-Ethernet devices
Acknowledgements: This driver started out as a sample CDC
USB-To-Ethernet driver, provided by Apple, and
was slowly transformed into the Pegasus driver.
It is partly based upon the linux Pegausus driver
source. Some constants from the linux header
file were copied directly. Also, a CRC function
was copied almost directly from aue.c, the BSD
Pegasus driver.
Copyright: Copyright 2005, Daniel Sumorok and Peter Sichel
Disclaimer: This program is free software; you can redistribute it
and/or modify it under the terms of the GNU
General Public License as published by the Free
Software Foundation; either version 2, or (at
your option) any later version.
This program is distributed in the hope that it
will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU
General along with this program; if not, write to
the Free Software Foundation, 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA. Change
History (least recent first):
<1> 03/25/05 USBPegasusEthernet source
*/
/*
* Copyright (c) 1997, 1998, 1999, 2000-2003
* Bill Paul <wpaul@windriver.com>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Bill Paul.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*/
#include "USBAx8817x_private.h"
#include "USBAx8817x.h"
#include "USBAx8817xEthernetInterface.h"
static IOPMPowerState ourPowerStates[kNumPowerStates] = {
{1,0,0,0,0,0,0,0,0,0,0,0},
{1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0}
};
static struct MediumTable {
UInt32 type;
UInt32 speed;
}
mediumTable[] = {
{kIOMediumEthernetNone, 0},
{kIOMediumEthernetAuto, 0},
{kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex, 10},
{kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex, 10},
{kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex, 100},
{kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex, 100}
};
#define super IOEthernetController
#define POSTEVENT(x) postEvent(this, (void *)(int)x, (void *)(int)true)
OSDefineMetaClassAndStructors(USBAx8817x, IOEthernetController);
#pragma mark -- Device Control --
/*****************************************************************************/
//
// Method: USBAx8817x::axCmd
//
// Inputs: cmd - AX Command (see USBAx8817x.h)
// index -
// val -
// buf -
//
// Outputs: Return code - true means success
// false means failure
//
// Desc: Reads or writes a to a register. The
// cmd input encapsulates whether the
// operation is a read or a write, and
// the register being accessed.
//
/****************************************************************************/
bool USBAx8817x::axCmd(UInt16 cmd, UInt16 index, UInt16 val, void *buf) {
IOUSBDevRequest request;
IOReturn ior;
if(fCntlPipe == NULL) {
IOLog("USBAx8817x::axCmd: fCntlPipe is NULL\n");
return false;
}
if(AXE_CMD_DIR(cmd)) {
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor,
kUSBDevice);
} else {
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor,
kUSBDevice);
}
request.bRequest = AXE_CMD_CMD(cmd);
request.wValue = val;
request.wIndex = index;
request.wLength = AXE_CMD_LEN(cmd);
request.pData = buf;
request.wLenDone = 0;
ior = fCntlPipe->ControlRequest(&request, 0);
if(ior != kIOReturnSuccess) {
IOLog("USBAx8817x::axCmd: Control request failed.\n");
return false;
}
if((int)request.wLenDone != AXE_CMD_LEN(cmd)) {
IOLog("USBAx8817x::axCmd: Control request failed! size = %d, wLenDone = %d\n",
(int)AXE_CMD_LEN(cmd), (int)request.wLenDone);
return false;
}
return true;
}
/*****************************************************************************/
//
// Method: USBAx8817x::axCmdAsync
//
// Inputs: cmd - AX Command (see USBAx8817x.h)
// index -
// val -
// buf -
//
// Outputs: Return code - true means success
// false means failure
//
// Desc: Asynchonously Reads or writes a to a
// register. The cmd input encapsulates
// whether the operation is a read or a
// write, and the register being
// accessed. The callback sends an event
// to the setState() method.
//
/****************************************************************************/
IOReturn USBAx8817x::axCmdAsync(UInt16 cmd, UInt16 index, UInt16 val) {
IOReturn ior;
if(AXE_CMD_DIR(cmd)) {
fCtrlRead.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor,
kUSBDevice);
} else {
fCtrlRead.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor,
kUSBDevice);
}
fCtrlRead.bRequest = AXE_CMD_CMD(cmd);
fCtrlRead.wValue = val;
fCtrlRead.wIndex = index;
fCtrlRead.wLength = AXE_CMD_LEN(cmd);
ior = fCntlPipe->ControlRequest(&fCtrlRead, &fCtrlCompletionInfo);
return ior;
} /* end axCmdAsync */
/*****************************************************************************/
//
// Method: USBAx8817x::readMiiWord
//
// Inputs: phy - The phy address
// inx - The MII register number
// regd - The value to be written
//
// Outputs: Return code - true means success
// false means failure
//
// Desc: Reads a media independent interface
// register.
//
/****************************************************************************/
bool USBAx8817x::readMiiWord(UInt8 phyIn, UInt8 indx, UInt16 *regd) {
if(!axCmd(AXE_CMD_MII_OPMODE_SW, 0, 0, NULL)) {
IOLog("USBAx8817x: readMiiWord: Failed to set SW MII mode.\n");
return false;
}
if(!axCmd(AXE_CMD_MII_READ_REG, indx, phyIn, regd)) {
IOLog("USBAx8817x: readMiiWord: Failed to read MII register.\n");
return false;
}
if(!axCmd(AXE_CMD_MII_OPMODE_HW, 0, 0, NULL)) {
IOLog("USBAx8817x: readMiiWord: Failed to set HW MII mode.\n");
return false;
}
*regd = USBToHostWord(*regd);
return true;
} /* end readMiiWord */
/*****************************************************************************/
//
// Method: USBAx8817x::writeMiiWord
//
// Inputs: phy - The phy address
// inx - The MII register number
// regd - The value to be written
//
// Outputs: Return code - true means success
// false means failure
//
// Desc: Writes a media independent interface
// register.
//
/****************************************************************************/
bool USBAx8817x::writeMiiWord(UInt8 phyIn, UInt8 indx, UInt16 regd) {
UInt16 data16;
if(!axCmd(AXE_CMD_MII_OPMODE_SW, 0, 0, NULL)) {
IOLog("USBAx8817x: readMiiWord: Failed to set SW MII mode.\n");
return false;
}
data16 = HostToUSBWord(regd);
if(!axCmd(AXE_CMD_MII_WRITE_REG, indx, phyIn, &data16)) {
IOLog("USBAx8817x: readMiiWord: Failed to read MII register.\n");
return false;
}
if(!axCmd(AXE_CMD_MII_OPMODE_HW, 0, 0, NULL)) {
IOLog("USBAx8817x: readMiiWord: Failed to set HW MII mode.\n");
return false;
}
return true;
} /* end writeMiiWord */
/*****************************************************************************/
//
// Method: USBAx8817x::readMiiWordAsync
//
// Inputs: inx - The MII register number
//
// Outputs: Return code - IOReturnSuccess on success
// failure otherwise
//
// Desc: Reads a media independent interface
// register asynchonously.
//
/****************************************************************************/
IOReturn USBAx8817x::readMiiWordAsync(UInt8 indx) {
IOReturn ior;
ior = axCmdAsync(AXE_CMD_MII_OPMODE_SW, 0, 0);
if(ior == kIOReturnSuccess) {
miiState = miiStateRead;
miiIndx = indx;
}
return ior;
} /* end readMiiWordAsync */
/*****************************************************************************/
//
// Method: USBAx8817x::writeMiiWordAsync
//
// Inputs: indx - The MII register number
// data - The value to be written
//
// Outputs: Return code - IOReturnSuccess on success
// failure otherwise
//
// Desc: Writes a media independent interface
// register.
//
/****************************************************************************/
IOReturn USBAx8817x::writeMiiWordAsync(UInt8 indx, UInt16 data) {
IOReturn ior;
ior = axCmdAsync(AXE_CMD_MII_OPMODE_SW, 0, 0);
if(ior == kIOReturnSuccess) {
miiState = miiStateWrite;
miiIndx = indx;
miiData = USBToHostWord(data);
}
return ior;
} /* end writeMiiWordAsync */
#pragma mark -- Initialization --
/*****************************************************************************/
//
// Method: USBAx8817x::init
//
// Inputs: properties - data (keys and values) used to match
//
// Outputs: Return code - true (init successful), false
// (init failed)
//
// Desc: Initialize the driver.
//
/*****************************************************************************/
bool USBAx8817x::init(OSDictionary *properties) {
OSNumber *numberTest;
int i;
if (super::init(properties) == false) {
IOLog("USBAx8817x::init: initialize super failed");
return false;
}
numberTest = OSDynamicCast(OSNumber, getProperty("gpioBits"));
if(numberTest) {
gpioBits = numberTest->unsigned32BitValue();
} else {
gpioBits = 0x00130103;
}
for(i=0; i<TX_RING_SIZE; ++i) {
txRing[i].mdp = NULL;
}
for(i=0; i<NUM_RX_BUFFERS; ++i) {
rxMDPList[i] = NULL;
}
readActive = false;
supports1000BaseT = false;
txQueueStopped = false;
readThreadCmd = 0;
writePipeStalled = false;
writeStallCleared = false;
pktQueueLen = 0;
debugFlags = 0;
txInProgress = 0;
txQueueLock = NULL;
txLock = NULL;
rxQueueLock = NULL;
rxThread = NULL;
txSpots = TX_RING_SIZE;
txPkt = NULL;
recursionLevel = 0;
started = false;
awake = false;
callbackPending = false;
checkLinkPending = false;
miiPending = false;
interruptPending = false;
readingInterrupt = false;
fNetworkInterface = NULL;
fDataInterface = NULL;
fpDevice = NULL;
fMediumDict = NULL;
fTransmitQueue = NULL;
fTimerSource = NULL;
timerPending = false;
fTerminate = false;
fNetifEnabled = false;
fNetifRequestEnabled = false;
fInPipe = NULL;
fOutPipe = NULL;
fCntlPipe = NULL;
fInterruptPipe = NULL;
fPipeInterruptMDP = NULL;
modelStr[0] = '\0';
vendorStr[0] = '\0';
revisionStr[0] = '\0';
ourPolicyMaker = NULL;
powerUpThread = NULL;
powerDownThread = NULL;
fpEtherStats = NULL;
fpNetStats = NULL;
promiscMode = false;
promiscRequest = false;
fMulticastEnable = false;
ethCtrl0Val = 0x08;
ethCtrl1Val = 0x00;
ethCtrl2Val = 0x03;
rxCtrlVal = 0x00;
fLinkUp = false;
rcvdLinkStatus = false;
mcBytesToWrite = 0;
setMc = false;
txDone = false;
suspendPending = false;
resumePending = false;
ackPowerPending = false;
cState = ctrlStateSuspend;
miiState = miiStateIdle;
selectedMedium = kIOMediumEthernetNone;
requestedMedium = kIOMediumEthernetAuto;
IOLog("USBAx8817x: This product includes software developed by Bill Paul.\n");
return true;
} /* end init */
/*****************************************************************************/
//
// Method: USBAx8817x::probe
//
// Inputs: provider - my provider
// score - a score indicating how well
// this driver is matched to
// the device.
//
// Outputs: this (I think I can drive this device),
// NULL (I don't recognize the device)
//
// Desc: This is called to let the driver probe
// the device to see if the driver is
// compatible with the device.
//
/****************************************************************************/
IOService * USBAx8817x::probe(IOService *provider, SInt32 *score ) {
if(!super::probe(provider, score)) {
return NULL;
}
return this;
} /* end probe */
/*****************************************************************************/
//
// Method: USBAx8817x::start
//
// Inputs: provider - my provider
//
// Outputs: Return code - true (it's me),
// false (sorry it probably was me, but I can't
// configure it)
//
// Desc: This is called once it has beed determined I'm
// probably the best driver for this device.
//
/****************************************************************************/
bool USBAx8817x::start(IOService *provider) {
if(!super::start(provider)) {
IOLog("USBAx8817x::start: Failed to start provider.\n");
return false;
}
if(openUSB(provider) == false) {
stop(provider);
return false;
}
if(allocateBuffers() == false) {
stop(provider);
return false;
}
if(resetMAC() == false) {
stop(provider);
return false;
}
if(createNetworkInterface() == false) {
stop(provider);
return false;
}
if(createMediumTables() == false) {
stop(provider);
return false;
}
if(startIO() == false) {
stop(provider);
return false;
}
postEvent(this, (void *)(int)ctrlEventStart);
return true;
} /* end start */
/*****************************************************************************/
//
// Method: USBAx8817x::openUSB
//
// Inputs: None
//
// Outputs: true if successful, false otherwise
//
// Desc: Opens the USB device and all the points.
//
/*****************************************************************************/
bool USBAx8817x::openUSB(IOService *provider) {
IOUSBFindInterfaceRequest req; // device request
IOReturn ior = kIOReturnSuccess;
UInt16 numends;
bool goodCall;
IOUSBFindEndpointRequest epReq; // endPoint request struct
const IOUSBEndpointDescriptor *epDesc;
UInt8 strIndex, config;
const IOUSBConfigurationDescriptor *cd = NULL; // configuration descriptor
// Get my USB device provider - the device
fpDevice = OSDynamicCast(IOUSBDevice, provider);
if(!fpDevice) {
IOLog("USBAx8817x::openUSB: Unable to get provider device.\n");
stop(provider);
return false;
}
if(!fpDevice->open(this)) {
IOLog("USBAx8817x::openUSB: failed to open provider.\n");
fpDevice = NULL;
return false;
}
fpDevice->retain();
/* The USB-Ethernet device only has one configuraion (as far as I
know). If this device has more configurations, I assume it is a
different type of device.*/
if(fpDevice->GetNumConfigurations() != 1) {
IOLog("USBAx8817x::openUSB: Wrong number of configurations.\n");
return false;
}
/* Open USB Pipes */
cd = fpDevice->GetFullConfigurationDescriptor(0);
if (!cd) {
IOLog("USBAx8817x::openUSB: GetFullConfigurationDescriptor() failed.\n");
return false;
}
config = cd->bConfigurationValue;
ior = fpDevice->SetConfiguration(this, config);
if (ior != kIOReturnSuccess) {
IOLog("USBAx8817x::openUSB: SetConfiguratoin() failed.\n");
return false;
}
strIndex = fpDevice->GetManufacturerStringIndex();
if(strIndex != 0) {
fpDevice->GetStringDescriptor(strIndex, vendorStr, 32);
}
strIndex = fpDevice->GetProductStringIndex();
if(strIndex != 0) {
fpDevice->GetStringDescriptor(strIndex, modelStr, 32);
}
strIndex = fpDevice->GetSerialNumberStringIndex();
if(strIndex != 0) {
fpDevice->GetStringDescriptor(strIndex, revisionStr, 32);
}
req.bInterfaceClass = kIOUSBFindInterfaceDontCare;
req.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
req.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
/* Hopefully this will find interface zero... */
fDataInterface = fpDevice->FindNextInterface(NULL, &req);
if (fDataInterface == NULL) {
IOLog("USBAx8817x::openUSB: FindNextInterfaceDescriptor() failed.\n");
return false;
}
goodCall = fDataInterface->open(this);
if (!goodCall) {
fDataInterface = NULL;
IOLog("USBAx8817x::openUSB: Open Data Interface failed.\n");
return false;
}
fDataInterface->retain();
numends = fDataInterface->GetNumEndpoints();
if (numends != 3) {
IOLog("USBAx8817x::openUSB: Number of endpoints (%d) is not 3.\n", numends);
return false;
}
// Open all the end points
fCntlPipe = fpDevice->GetPipeZero();
if(fCntlPipe == NULL) {
IOLog("USBAx8817x::openUSB: Failed to find control pipe.\n");
return false;
}
epReq.type = kUSBInterrupt;
epReq.direction = kUSBIn;
epReq.maxPacketSize = 0;
epReq.interval = 0;
fInterruptPipe = fDataInterface->FindNextPipe(NULL, &epReq);
if(fInterruptPipe == NULL) {
IOLog("USBAx8817x::openUSB: Failed to find interrupt pipe.\n");
return false;
}
epReq.type = kUSBBulk;
epReq.direction = kUSBIn;
epReq.maxPacketSize = 0;
epReq.interval = 0;
fInPipe = fDataInterface->FindNextPipe(NULL, &epReq);
if(fInPipe == NULL) {
IOLog("USBAx8817x::openUSB: Failed to find bulk incoming pipe.\n");
return false;
}
epDesc = fInPipe->GetEndpointDescriptor();
epReq.direction = kUSBOut;
fOutPipe = fDataInterface->FindNextPipe(NULL, &epReq);
if(fOutPipe == NULL) {
IOLog("USBAx8817x::openUSB: Failed to find bulk outgoing pipe.\n");
return false;
}
maxPacketSize = USBToHostWord(epDesc->wMaxPacketSize);
return true;
} /* end openUSB */
/*****************************************************************************/
//
// Method: USBAx8817x::allocateBuffers
//
// Inputs: provider - none
//
// Outputs: Return code - true means success
// false meand failure
//
// Desc: Allocates the buffers for asynchronous I/O.
//
/****************************************************************************/
bool USBAx8817x::allocateBuffers() {
int i;
for(i=0; i<NUM_RX_BUFFERS; ++i) {
rxMDPList[i] =
IOBufferMemoryDescriptor::withCapacity(BUFSIZE, kIODirectionIn);
if(!rxMDPList[i]) {
IOLog("USBAx8817x::allocateBuffers: Failed to allocate input buffer %d.\n",
i);
return false;
}
rxMDPList[i]->setLength(BUFSIZE);
}
fPipeInterruptMDP =
IOBufferMemoryDescriptor::withCapacity(8, kIODirectionIn);
if(!fPipeInterruptMDP) {
IOLog("USBAx8817x::allocateBuffers: Failed to allocate interrupt buffer.\n");
return false;
}
fPipeInterruptMDP->setLength(8);
fInterruptPipeBuffer = (UInt8 *)fPipeInterruptMDP->getBytesNoCopy();
fPipeCtrlMDP = IOBufferMemoryDescriptor::withCapacity(8, kIODirectionIn);
if(!fPipeCtrlMDP) {
IOLog("USBAx8817x::allocateBuffers: Failed to allocate control buffer.\n");
return false;
}
fPipeCtrlMDP->setLength(8);
fCtrlPipeBuffer = (UInt8 *)fPipeCtrlMDP->getBytesNoCopy();
/* Allocate Trsnsmit Ring buffers. */
for(i=0; i<TX_RING_SIZE; ++i) {
txRing[i].mdp = IOBufferMemoryDescriptor::withCapacity(BUFSIZE,
kIODirectionOut);
if(!txRing[i].mdp) {
IOLog("USBAx8817x::allocateBuffers: Failed to allocate output buffer %d.\n",
i);
return false;
}
txRing[i].buffer = (UInt8 *)txRing[i].mdp->getBytesNoCopy();
txRing[i].packet = NULL;
txRing[i].flags = 0;
}
/* Connect Transmit Ring. */
for(i=0; i<(TX_RING_SIZE-1); ++i) {
txRing[i].next = &txRing[i+1];
}
txRing[i].next = &txRing[0];
/* Connect Receive Ring. */
for(i=0; i<(NUM_RX_BUFFERS-1); ++i) {
readComp[i].next = &readComp[i+1];
}
readComp[i].next = &readComp[0];
txProducePtr = &txRing[0];
txConsumePtr = &txRing[0];
rxRing = &readComp[0];
return true;
} /* end allocateBuffers */
/*****************************************************************************/
//
// Method: USBAx8817x::resetMAC
//
// Inputs: None
//
// Outputs: true if successful, false otherwise
//
// Desc: Tries to reset the MAC and the phy, and read
// the ethernet address.
//
/*****************************************************************************/
bool USBAx8817x::resetMAC() {
UInt8 ipgs[4];
UInt16 data16;
int i;
bool ret;
IOLog("USBAx8817x: Version 1.0.1.\n");
/* Toggle the GPIOs in a manufacturer/model specific way */
/* Taken from usbnet.c linux driver.*/
for (i = 2; i >= 0; i--) {
if(!axCmd(AXE_CMD_WRITE_GPIO, 0,(gpioBits >> (i * 8)) & 0xff, NULL)) {
IOLog("USBAx8817x: resetMAC: Failed to write GPIO bits.\n");
return false;
}
IOSleep(5);
}
/*
* Get station address.
*/
bzero(fEaddr, 6);
if(!axCmd(AXE_CMD_READ_NODEID, 0, 0, &fEaddr)) {
IOLog("USBAx8817x: resetMAC: Failed to read Node ID.\n");
return false;
}
/* Disable flow control */
mediumStatus = 0;
if(!axCmd(AXE_CMD_WRITE_MEDIA, 0, mediumStatus, NULL)) {
IOLog("USBAx8817x: resetMAC: Failed to write medium status.\n");
return false;
}
rxCtrlVal = AXE_RXCMD_BROADCAST | AXE_RXCMD_UNICAST |
AXE_RXCMD_MULTICAST;
if(!axCmd(AXE_CMD_RXCTL_WRITE, 0, rxCtrlVal, NULL)) {
IOLog("USBAx8817x: resetMAC: Failed to write rx control.\n");
return false;
}
axCmd(AXE_CMD_READ_IPG012, 0, 0, ipgs);
if(!axCmd(AXE_CMD_WRITE_IPG0, 0, ipgs[0], NULL)) {
IOLog("USBAx8817x: resetMAC: Failed to write IPG Register 0.\n");
return false;
}
if(!axCmd(AXE_CMD_WRITE_IPG1, 0, ipgs[1], NULL)) {
IOLog("USBAx8817x: resetMAC: Failed to write IPG Register 1.\n");
return false;
}
if(!axCmd(AXE_CMD_WRITE_IPG2, 0, ipgs[2], NULL)) {
IOLog("USBAx8817x: resetMAC: Failed to write IPG Register 2.\n");
return false;
}
phy = -1;
for(i=0; i<32; ++i) {
data16 = 0;
ret = readMiiWord(i, 1, &data16);
if(ret && (data16 != 0xffff) && (data16 & 0x7800) ) {
phy = i;
break;
}
}
if(phy == -1) {
IOLog("USBAx8817x: resetMAC: Could not located the ethernet phy.\n");
return false;
}
if(data16 & 0x0100) {
supports1000BaseT = true;
IOLog("USBAx8817x: Found 1000BaseT capable phy.\n");
readMiiWord(phy, 15, &data16);
IOLog("USBAx8817x: MII register 15 = 0x%X.\n", data16);
}
data16=0;
if(axCmd(AXE_CMD_SROM_READ, 0, 0x10, &data16)) {
deviceFlag = USBToHostWord(data16);
IOLog("USBAx8817x: Max packet size is = %d.\n", deviceFlag);
}
data16=0;
if(axCmd(AXE_CMD_SROM_READ, 0, 0x01, &data16)) {
data16 = USBToHostWord(data16);
IOLog("USBAx8817x: flags = 0x%X.\n", data16);
}
return true;
} /* end resetMAC */
/***************************************************************************/
//
// Method: USBAx8817x::registerWithPolicyMaker
//
// Inputs: policyMaker - Power Policy Maker object
//
// Outputs: IOReturnSuccess if succesful
//
// Desc: This function is based on the registerWithPolicyMaker()
// in i82557PM.c from the Darwin source code. Register
// with the power management policy maker.
//
/****************************************************************************/
IOReturn USBAx8817x::registerWithPolicyMaker(IOService *policyMaker) {
currentPowerState = kUSBEthernetOffState;
ourPolicyMaker = policyMaker;
powerDownThread =
thread_call_allocate((thread_call_func_t) handleSetPowerStateOff,
(thread_call_param_t) this );
powerUpThread =
thread_call_allocate((thread_call_func_t) handleSetPowerStateOn,
(thread_call_param_t) this );
return policyMaker->registerPowerDriver(this, ourPowerStates,
kNumPowerStates);
} /* end registerWithPolicyMaker */
/*****************************************************************************/
//
// Method: USBAx8817x::createNetworkInterface
//
// Inputs:
//
// Outputs: return Code - true (created and initialilzed ok),
// false (it failed)
//
// Desc: Creates and initializes the network interface
//
/*****************************************************************************/
bool USBAx8817x::createNetworkInterface() {
fTransmitQueue = (AxOutputQueue *)getOutputQueue();
//fTransmitQueue = (IOBasicOutputQueue *)getOutputQueue();
if (!fTransmitQueue) {
IOLog("USBAx8817x::createNetworkInterface: Failed to get output queue.\n");
return false;
}
fTransmitQueue->retain();
fWorkLoop = getWorkLoop();
if(!fWorkLoop) {
IOLog("USBAx8817x::createNetworkInterface: Failed to get work loop.\n");
return false;
}
fTimerSource = IOTimerEventSource::timerEventSource(this, timerFired);
if (fTimerSource == NULL) {
IOLog("USBAx8817x::createNetworkInterface: Failed to allocate timer.\n");
return false;
}
fTimerSource->retain();
if (fWorkLoop->addEventSource(fTimerSource) != kIOReturnSuccess) {
IOLog("USBAx8817x::createNetworkInterface: Failed to add timer event.\n");
return false;
}
if (!attachInterface((IONetworkInterface **)&fNetworkInterface, true)) {
IOLog("USBAx8817x::createNetworkInterface: Failed to attach network interface.\n");
return false;
}
//retain();
return true;
}/* end createNetworkInterface */
/****************************************************************************/
//
// Method: USBAx8817x::createMediumTables
//
// Inputs:
//
// Outputs: Return code - true (tables created), false (not
// created)
//
// Desc: Creates the medium tables
//
/****************************************************************************/
bool USBAx8817x::createMediumTables() {
IONetworkMedium *medium;
UInt32 i;
fMediumDict = OSDictionary::withCapacity(sizeof(mediumTable) /
sizeof(mediumTable[0]));
if(fMediumDict == NULL) {
IOLog("USBAx8817x::createMediumTables: Failed to create dictionary.\n");
return false;
}
for(i = 0; i < sizeof(mediumTable) / sizeof(mediumTable[0]); i++ ) {
medium = IONetworkMedium::medium(mediumTable[i].type,
mediumTable[i].speed);
IONetworkMedium::addMedium(fMediumDict, medium);
medium->release();
}
if(supports1000BaseT) {
medium = IONetworkMedium::medium(kIOMediumEthernet1000BaseTX |
kIOMediumOptionFullDuplex, 1000);
IONetworkMedium::addMedium(fMediumDict, medium);
medium->release();
medium = IONetworkMedium::medium(kIOMediumEthernet1000BaseTX |
kIOMediumOptionHalfDuplex, 1000);
IONetworkMedium::addMedium(fMediumDict, medium);
medium->release();
}
if(publishMediumDictionary(fMediumDict) != true) {
IOLog("USBAx8817x::createMediumTables: Failed to publish medium dictionary.\n");
return false;
}
medium =
IONetworkMedium::getMediumWithType(fMediumDict, kIOMediumEthernetAuto);
setSelectedMedium(medium);
medium =
IONetworkMedium::getMediumWithType(fMediumDict, kIOMediumEthernetNone);
setLinkStatus(kIONetworkLinkValid, medium);
fLinkUp = false;
return true;
} /* end createMediumTables */
/****************************************************************************/
//
// Method: USBAx8817x::createMediumTables
//
// Inputs: none
//
// Outputs: Return code - true (success), false (failure)
//
// Desc: Initializes asychronous I/O
//
/****************************************************************************/
bool USBAx8817x::startIO() {
//IOReturn ior;
int i;
fCtrlRead.pData = fPipeCtrlMDP;
fCtrlWrite.pData = fPipeCtrlMDP;
fCtrlCompletionInfo.target = this;
fCtrlCompletionInfo.action = ctrlComplete;
fCtrlCompletionInfo.parameter = NULL;
fInterruptCompletionInfo.target = this;
fInterruptCompletionInfo.action = interruptComplete;
fInterruptCompletionInfo.parameter = NULL;
for(i=0; i<NUM_RX_BUFFERS; ++i) {
fReadCompletionInfo[i].target = this;
fReadCompletionInfo[i].action = dataReadComplete;
fReadCompletionInfo[i].parameter = (void *)&readComp[i];
readComp[i].completion = &fReadCompletionInfo[i];
readComp[i].mdp = rxMDPList[i];
readComp[i].flags = 0;
readComp[i].packetLength = 0;
}
for(i=0; i<TX_RING_SIZE; ++i) {
txRing[i].writeCompletionInfo.target = this;
txRing[i].writeCompletionInfo.action = dataWriteComplete;
txRing[i].writeCompletionInfo.parameter = &txRing[i];
txRing[i].writeZeroCompletionInfo.target = this;
txRing[i].writeZeroCompletionInfo.action = dataWriteZeroComplete;
txRing[i].writeZeroCompletionInfo.parameter = NULL;
}
txLock = IOLockAlloc();
txQueueLock = IOLockAlloc();
if(txQueueLock == NULL) {
IOLog("USBAx8817x: startIO: Failed to allocate txQueueLock.\n");
return false;
}
rxQueueLock = IOLockAlloc();
if(rxQueueLock == NULL) {
IOLog("USBAx8817x: startIO: Failed to allocate rxQueueLock.\n");
return false;
}
rxThread = IOCreateThread(rxThreadEntry, this);
if(rxThread == NULL) {
IOLog("USBAx8817x: startIO: Failed to start rx thread.\n");
return false;
}
return true;
} /* end startIO */
/*****************************************************************************/
//
// Method: USBAx8817x::createOutputQueue
//
// Inputs:
//
// Outputs: Return code - the output queue
//
// Desc: Creates the output queue
//
/*****************************************************************************/
IOOutputQueue* USBAx8817x::createOutputQueue() {
return AxOutputQueue::withTarget(this, TRANSMIT_QUEUE_SIZE);
//return IOBasicOutputQueue::withTarget(this, TRANSMIT_QUEUE_SIZE);
} /* end createOutputQueue */
/*
* configureInterface() - Ripped off from UniNEnet.cpp
*
*/
bool USBAx8817x::configureInterface( IONetworkInterface *netif ) {
IONetworkData *nd;
if ( super::configureInterface( netif ) == false ) {
return false;
}
/* Grab a pointer to the statistics structure in the interface: */
nd = netif->getNetworkData( kIONetworkStatsKey );
if ( !nd || !(fpNetStats = (IONetworkStats*)nd->getBuffer()) ) {
IOLog( "USBAx8817x::configureInterface invalid network statistics.\n" );
return false;
}
fpNetStats->outputPackets = 0;
fpNetStats->inputPackets = 0;
// Get the Ethernet statistics structure:
nd = netif->getParameter( kIOEthernetStatsKey );
if ( !nd || !(fpEtherStats = (IOEthernetStats*)nd->getBuffer()) ) {
IOLog( "USBAx8817x::configureInterface - invalid ethernet statistics\n" );
return false;
}
return true;
} /* end configureInterface */
/****************************************************************************/
//
// Method: USBAx8817x::createInterface
//
// Inputs: none
//
// Outputs: Return Value - Pointer to IONetworkInterface
//
// Desc: Creates the network interface object. A custom
// network interface object is created that
// removes the IFF_RUNNING flag when it is
// destroyed. This seems to help destroy the bsd
// network interface when the device is unplugged.
//
/*****************************************************************************/
IONetworkInterface * USBAx8817x::createInterface() {
USBAx8817xEthernetInterface * netif = new USBAx8817xEthernetInterface;
if(netif && (netif->init(this) == false) ) {
netif->release();
netif = NULL;
}
return netif;
} /* end createInterface */
/*****************************************************************************/
//
// Method: USBAx8817x::getPacketFilters
//
// Inputs: group - the filter group
//
// Outputs: Return code - kIOReturnSuccess and others
// filters - the capability
//
// Desc: Set the filter capability for the driver
//
/*****************************************************************************/
IOReturn USBAx8817x::getPacketFilters(const OSSymbol *group,
UInt32 *filters) const {
IOReturn rtn = kIOReturnSuccess;
if (group == gIONetworkFilterGroup) {
*filters = kIOPacketFilterUnicast | kIOPacketFilterBroadcast |
kIOPacketFilterPromiscuous | kIOPacketFilterMulticast;
} else {
rtn = super::getPacketFilters(group, filters);
}
if (rtn != kIOReturnSuccess) {
IOLog("USBAx8817x::getPacketFilters: Failed to get packet filters\n");
}
return rtn;
} /* end getPacketFilters */
void USBAx8817x::rxThreadEntry(void *ob) {
USBAx8817x *me;
me = (USBAx8817x *)ob;
if(me != NULL) {
me->rxLoop();
}
IOExitThread();
} /* end rxThreadEntry */
#pragma mark -- Upper Layer Events --
/****************************************************************************/
//
// Method: USBAx8817x::enable
//
// Inputs: netif - the interface being enabled
//
// Outputs: Return code - kIOReturnSuccess or kIOReturnIOError
//
// Desc: Called by IOEthernetInterface client to enable the
// controller.
//
// This method is always called while running on the
// default workloop thread.
//
/*****************************************************************************/
IOReturn USBAx8817x::enable(IONetworkInterface *netif) {
fTransmitQueue->setCapacity(TRANSMIT_QUEUE_SIZE);
fTransmitQueue->start();
//POSTEVENT(ctrlEventEnable);
IOLockLock(rxQueueLock);
readThreadCmd &= ~RXTHREAD_CLOSE;
readThreadCmd |= RXTHREAD_OPEN;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockUnlock(rxQueueLock);
return kIOReturnSuccess;
} /* end enable */
/*****************************************************************************/
//
// Method: USBAx8817x::disable
//
// Inputs: netif - the interface being disabled
//
// Outputs: Return code - kIOReturnSuccess
//
// Desc: Called by IOEthernetInterface client to disable the
// controller.
//
// This method is always called while running on the
// default workloop thread.
//
/****************************************************************************/
IOReturn USBAx8817x::disable(IONetworkInterface * /*netif*/) {
fTransmitQueue->stop();
fTransmitQueue->setCapacity(0);
fTransmitQueue->flush();
IOLockLock(rxQueueLock);
readThreadCmd &= ~RXTHREAD_OPEN;
readThreadCmd |= RXTHREAD_CLOSE;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockUnlock(rxQueueLock);
return kIOReturnSuccess;
} /* end disable */
/*****************************************************************************/
//
// Method: USBAx8817x::outputPacket
//
// Inputs: mbuf - the packet
// param - optional parameter
//
// Outputs: Return code - kIOReturnOutputSuccess or
// kIOReturnOutputStall
//
// Desc: Packet transmission. The BSD mbuf needs to be
// formatted correctly and transmitted
//
//
/*****************************************************************************/
UInt32 USBAx8817x::outputPacket(mbuf_t pkt, void *param) {
mbuf_t m;
int total_pkt_length;
UInt8 *pipeBuffer;
int txCount;
int queueLen;
IOBufferMemoryDescriptor *pipeMDP;
if(!pkt) {
return kIOReturnSuccess;
}
IOLockLock(txQueueLock);
queueLen = --txSpots;
if(queueLen == 0) {
txQueueStopped = true;
}
IOLockUnlock(txQueueLock);
if(queueLen < 0) {
IOLockLock(txQueueLock);
++txSpots;
IOLockUnlock(txQueueLock);
IOLog("USBAx8817x: outputPacket: No buffers. Requeuing packet.\n");
return kIOOutputStatusRetry | kIOOutputCommandStall;
}
m = pkt;
total_pkt_length = 0;
while(m) {
total_pkt_length += mbuf_len(m);
m = mbuf_next(m);
}
if(total_pkt_length > kIOEthernetMaxPacketSize) {
total_pkt_length = kIOEthernetMaxPacketSize;
}
txProducePtr = txProducePtr->next;
pipeBuffer = txProducePtr->buffer;
pipeMDP = txProducePtr->mdp;
#if 0
m = pkt;
txCount = 0;
while(m) {
if(mbuf_len(m) > 0) {
bcopy(mbuf_data(m), &pipeBuffer[txCount], mbuf_len(m));
txCount += mbuf_len(m);
}
m = mbuf_next(m);
}
/* Minimum packet is 60 bytes */
if(txCount < 60) {
bzero(&pipeBuffer[txCount], 60 - txCount);
txCount = 60;
}
#endif
txCount = copyPktToBuffer(pkt, pipeBuffer, total_pkt_length);
pipeMDP->setLength(txCount);
postEvent(this, (void *)ctrlEventTransmit);
freePacket(pkt);
if(queueLen > 0) {
return kIOOutputStatusAccepted | kIOOutputCommandNone;
} else {
return kIOOutputStatusAccepted | kIOOutputCommandStall;
}
} /* end outputPacket */
int USBAx8817x::copyPktToBuffer(mbuf_t m, UInt8 *buf, int len) {
while(m) {
if(mbuf_len(m) > 0) {
bcopy(mbuf_data(m), buf, mbuf_len(m));
buf += mbuf_len(m);
}
m = mbuf_next(m);
}
/* Minimum packet is 60 bytes */
if(len < 60) {
bzero(buf, 60 - len);
len = 60;
}
return len;
}
/*****************************************************************************/
//
// Method: USBAx8817x::timerFired
//
// Inputs:
//
// Outputs:
//
// Desc: Static member function called when a timer event fires.
//
/****************************************************************************/
void USBAx8817x::timerFired(OSObject *owner,
IOTimerEventSource *sender) {
if(owner) {
USBAx8817x* target = OSDynamicCast(USBAx8817x, owner);
if(target) {
postEvent(owner, (void *)(int)ctrlEventTimer);
}
}
} /* end timerFired */
/*****************************************************************************/
//
// Method: USBAx8817x::selectMedium
//
// Inputs:
//
// Outputs:
//
// Desc: Lets us know if someone is playing with ifconfig
//
/*****************************************************************************/
IOReturn USBAx8817x::selectMedium(const IONetworkMedium *medium) {
if(!medium) {
return kIOReturnError;
}
switch(medium->getType()) {
case kIOMediumEthernet1000BaseTX | kIOMediumOptionHalfDuplex:
case kIOMediumEthernet1000BaseTX | kIOMediumOptionFullDuplex:
if(!supports1000BaseT) {
IOLog("USBAx8817x: selectMedium: 1000BaseTx mode is not supported.\n");
return kIOReturnError;
}
case kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex:
case kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex:
case kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex:
case kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex:
case kIOMediumEthernetAuto:
requestedMedium = medium->getType();
break;
case kIOMediumEthernetNone:
requestedMedium = kIOMediumEthernetAuto;
break;
default:
IOLog("USBAx8817x: selectMedium: Medium 0x%X does not exist.\n",
(int)medium->getType());
return kIOReturnError;
}
POSTEVENT(ctrlEventNudge);
return kIOReturnSuccess;
} /* end selectMedium */
/*****************************************************************************/
//
// Method: USBAx8817x::getHardwareAddress
//
// Inputs:
//
// Outputs: Return code - kIOReturnSuccess or kIOReturnError
// ea - the address
//
// Desc: Get the ethernet address from the hardware (actually
// the descriptor)
//
/*****************************************************************************/
IOReturn USBAx8817x::getHardwareAddress(IOEthernetAddress *ea) {
UInt32 i;
for (i=0; i<6; i++) {
ea->bytes[i] = fEaddr[i];
}
return kIOReturnSuccess;
} /* end getHardwareAddress */
/*****************************************************************************/
//
// Method: USBAx8817x::newVendorString
//
// Inputs:
//
// Outputs: Return code - the vendor string
//
// Desc: Identifies the hardware vendor
//
/****************************************************************************/
const OSString* USBAx8817x::newVendorString() const {
return OSString::withCString(vendorStr);
} /* end newVendorString */
/*****************************************************************************/
//
// Method: USBAx8817x::newModelString
//
// Inputs:
//
// Outputs: Return code - the model string
//
// Desc: Identifies the hardware model
//
/****************************************************************************/
const OSString* USBAx8817x::newModelString() const {
return OSString::withCString(modelStr);
} /* end newModelString */
/****************************************************************************/
//
// Method: USBAx8817x::newRevisionString
//
// Inputs:
//
// Outputs: Return code - the revision string
//
// Desc: Identifies the hardware revision
//
/*****************************************************************************/
const OSString* USBAx8817x::newRevisionString() const {
return OSString::withCString(revisionStr);
} /* end newRevisionString */
/*****************************************************************************/
//
// Method: USBAx8817x::setMulticastMode
//
// Inputs: active - true (set it), false (don't)
//
// Outputs: Return code - kIOReturnSuccess
//
// Desc: Sets multicast mode
//
/*****************************************************************************/
IOReturn USBAx8817x::setMulticastMode(bool active) {
if(active) {
fMulticastEnable = true;
} else {
setMulticastList(NULL, 0);
fMulticastEnable = false;
}
return kIOReturnSuccess;
} /* end setMulticastMode */
/* The following was taked from crc32.h, in the linux source code. */
static unsigned const ethernet_polynomial = 0x04c11db7U;
static inline UInt32 ether_crc(int length, unsigned char *data) {
int crc = -1;
while (--length >= 0) {
unsigned char current_octet = *data++;
int bit;
for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
crc = (crc << 1) ^
((crc < 0) ^ (current_octet & 1) ?
ethernet_polynomial : 0);
}
}
return crc;
}
/*****************************************************************************/
//
// Method: USBAx8817x::setMulticastList
//
// Inputs: addrs - list of addresses
// count - number in the list
//
// Outputs: Return code - kIOReturnSuccess or kIOReturnIOError
//
// Desc: Sets multicast list
//
/*****************************************************************************/
IOReturn USBAx8817x::setMulticastList(IOEthernetAddress *addrs,
UInt32 count) {
UInt32 i;
UInt32 index, byteIndex, bitIndex;
UInt32 hash;
if(fMulticastEnable == false) {
return kIOReturnSuccess;
}
for(i=0; i<8; ++i) {
mHashTable[i] = 0;
}
if(addrs != NULL) {
for(i=0; i<count; ++i) {
hash = ether_crc(6, addrs->bytes);
index = hash >> 26;
byteIndex = index >> 3;
bitIndex = index & 0x07;
mHashTable[byteIndex] |= (1 << bitIndex);
++addrs;
}
}
POSTEVENT(ctrlEventSetMulticast);
return kIOReturnSuccess;
} /* end setMulticastList */
/*****************************************************************************/
//
// Method: USBAx8817x::setPromiscuousMode
//
// Inputs: active - true (set it), false (don't)
//
// Outputs: Return code - kIOReturnSuccess
//
// Desc: Sets promiscuous mode
//
/*****************************************************************************/
IOReturn USBAx8817x::setPromiscuousMode(bool active) {
if(active) {
promiscRequest = true;
} else {
promiscRequest = false;
}
POSTEVENT(ctrlEventNudge);
return kIOReturnSuccess;
} /* end setPromiscuousMode */
/***************************************************************************/
//
// Method: USBAx8817x::setPowerState
//
// Inputs: powerStateOrdinal - Power state to change to.
// whatdevice - not used
//
// Outputs: IOReturnSuccess if succesful
//
// Desc: Change our current power state. Either sleep (off) or
// wake up (on).
//
/****************************************************************************/
IOReturn USBAx8817x::setPowerState(unsigned long powerStateOrdinal,
IOService *whatDevice) {
IOReturn result = IOPMAckImplied;
if(currentPowerState == powerStateOrdinal) {
return result;
}
currentPowerState = powerStateOrdinal;
switch(powerStateOrdinal) {
case kUSBEthernetOffState:
if(powerDownThread) {
retain();
if( thread_call_enter(powerDownThread) == TRUE ) {
release();
}
result = 3 * 1000 * 1000; /* 3 seconds */
}
break;
case kUSBEthernetOnState:
if( powerUpThread ) {
retain();
if ( thread_call_enter(powerUpThread) == TRUE ) {
release();
}
result = 3 * 1000 * 1000; /* 3 seconds */
}
break;
}
return result;
} /* end setPowerState */
void USBAx8817x::rxLoop() {
IOLockLock(rxQueueLock);
while(true) {
/* Check for event */
while(rxRing->flags & RX_FLAG_FULL) {
rxRing->flags &= ~RX_FLAG_FULL;
IOLockUnlock(rxQueueLock);
/* Send packet up! */
receivePacket(rxRing->packetLength, rxRing->mdp, rxRing->completion);
rxRing = rxRing->next;
IOLockLock(rxQueueLock);
if(readThreadCmd) {
if(handleRxThreadCmd()) {
return;
}
}
}
IOLockUnlock(rxQueueLock);
fNetworkInterface->flushInputQueue();
IOLockLock(rxQueueLock);
if(readThreadCmd) {
if(handleRxThreadCmd()) {
return;
}
} else {
IOLockSleep(rxQueueLock, NO_EVENT, THREAD_ABORTSAFE);
if(readThreadCmd) {
if(handleRxThreadCmd()) {
return;
}
}
}
}
} /* end rxThreadEntry */
bool USBAx8817x::handleRxThreadCmd() {
IOReturn ior;
int i;
if(readThreadCmd & RXTHREAD_STOP) {
IOLockUnlock(rxQueueLock);
if(fNetworkInterface) {
detachInterface(fNetworkInterface, true);
fNetworkInterface->release();
fNetworkInterface = NULL;
}
if(fpDevice) {
fpDevice->close(this);
fpDevice->release();
fpDevice = NULL;
}
IOLockLock(rxQueueLock);
readThreadCmd |= RXTHREAD_STOPPED;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockUnlock(rxQueueLock);
return true;
}
if(readThreadCmd & RXTHREAD_CLOSE) {
readActive = false;
readThreadCmd &= ~RXTHREAD_CLOSE;
readThreadCmd &= ~RXTHREAD_READSTALLED;
IOLockUnlock(rxQueueLock);
fInPipe->ClearPipeStall(true);
fInterruptPipe->ClearPipeStall(true);
postEvent(this, (void *)(int)ctrlEventDisable);
IOLockLock(rxQueueLock);
}
if(readThreadCmd & RXTHREAD_OPEN) {
readThreadCmd &= ~RXTHREAD_OPEN;
readThreadCmd &= ~RXTHREAD_READSTALLED;
IOLockUnlock(rxQueueLock);
fInPipe->ClearPipeStall(true);
fInterruptPipe->ClearPipeStall(true);
for(i=0; i<NUM_RX_BUFFERS; ++i) {
rxRing->flags &= ~RX_FLAG_FULL;
ior = fInPipe->Read(rxRing->mdp, 0, 0, BUFSIZE,
rxRing->completion, NULL);
if(ior != kIOReturnSuccess) {
IOLog("USBAx8817x::handleRxThreadCmd: Read %d failed.\n", i);
fInPipe->ClearPipeStall(true);
IOLockLock(rxQueueLock);
return false;
}
rxRing = rxRing->next;
}
ior = fInterruptPipe->Read(fPipeInterruptMDP, 0, 0, 8,
&fInterruptCompletionInfo, NULL);
if(ior != kIOReturnSuccess) {
IOLog("USBAx8817x::handleRxThreadCmd: Interrupt read failed.\n");
}
postEvent(this, (void *)(int)ctrlEventEnable);
readActive = true;
IOLockLock(rxQueueLock);
}
if(readThreadCmd & RXTHREAD_READSTALLED) {
readThreadCmd &= ~RXTHREAD_READSTALLED;
IOLockUnlock(rxQueueLock);
fInPipe->ClearPipeStall(true);
if(readActive == true) {
for(i=0; i<NUM_RX_BUFFERS; ++i) {
rxRing->flags &= ~RX_FLAG_FULL;
ior = fInPipe->Read(rxRing->mdp, 0, 0, BUFSIZE,
rxRing->completion, NULL);
if(ior != kIOReturnSuccess) {
IOLog("USBAx8817x::handleRxThreadCmd: Read %d failed.\n", i);
fInPipe->ClearPipeStall(true);
IOLockLock(rxQueueLock);
return false;
}
rxRing = rxRing->next;
}
}
IOLockLock(rxQueueLock);
}
if(readThreadCmd & RXTHREAD_WRITESTALLED) {
readThreadCmd &= ~RXTHREAD_WRITESTALLED;
IOLockUnlock(rxQueueLock);
IOLog("USBAx8817x: Clearing Write Pipe.\n");
fOutPipe->ClearPipeStall(true);
postEvent(this, (void *)(int)ctrlEventClearedWriteStall);
IOLockLock(rxQueueLock);
}
if(readThreadCmd & RXTHREAD_INTRSTALLED) {
readThreadCmd &= ~RXTHREAD_INTRSTALLED;
IOLockUnlock(rxQueueLock);
IOLog("USBAx8817x: Interrupt Pipe stalled!\n");
ior = fInterruptPipe->ClearPipeStall(true);
if(ior == kIOReturnSuccess) {
if(readActive) {
IOLog("USBAx8817x: Interrupt Pipe stall cleared.\n");
ior = fInterruptPipe->Read(fPipeInterruptMDP, 0, 0, 8,
&fInterruptCompletionInfo, NULL);
if(ior != kIOReturnSuccess) {
IOLog("USBAx8817x: interrupt read failed. error = 0x%X.\n",
ior);
}
}
} else {
IOLog("USBAx8817x: Failed to clear interrupt pipe.\n");
}
IOLockLock(rxQueueLock);
}
if(readThreadCmd & RXTHREAD_CTRLSTALLED) {
readThreadCmd &= ~RXTHREAD_CTRLSTALLED;
IOLockUnlock(rxQueueLock);
IOLog("USBAx8817x: Control pipe stalled.\n");
ior = fCntlPipe->ClearPipeStall(true);
if(ior == kIOReturnSuccess) {
IOLog("USBAx8817x: Control pipe stall cleared.\n");
ior = fCntlPipe->ControlRequest(&fCtrlRead, &fCtrlCompletionInfo);
if(ior != kIOReturnSuccess) {
IOLog("USBAx8817x: control request failed. error = 0x%X.\n",
ior);
}
} else {
IOLog("USBAx8817x: ctrlComplete: failed to clear control pipe.\n");
}
IOLockLock(rxQueueLock);
}
return false;
}
#pragma mark -- Lower Layer Events --
/***************************************************************************/
//
// Method: USBAx8817x::dataReadComplete
//
// Inputs: obj - me
// param - unused
// rc - return code
// remaining - what's left
//
// Outputs: None
//
// Desc: BulkIn pipe (Data interface) read completion
// routine. This function get called after a
// paket was transmitted. If all is good,
// recievePacket() is called. Otherwise, the
// network interface is released.
//
//
/****************************************************************************/
void USBAx8817x::dataReadComplete(void *obj, void *param, IOReturn rc,
UInt32 remaining) {
USBAx8817x *me = (USBAx8817x*)obj;
ax8817xCompletion_t *comp;
IOBufferMemoryDescriptor *inMDP;
IOUSBCompletion *completion;
if(param == NULL) {
IOLog("USBAx8817x::dataReadComplete: param == NULL.\n");
return;
}
comp = (ax8817xCompletion_t *)param;
completion = comp->completion;
inMDP = comp->mdp;
if(!me) {
IOLog("USBAx8817x::dataReadComplete: me is NULL.\n");
return;
}
if(rc == kIOReturnSuccess) {
comp->packetLength = BUFSIZE - remaining;
IOLockLock(me->rxQueueLock);
if(!(me->readThreadCmd & RXTHREAD_STOP)) {
comp->flags |= RX_FLAG_FULL;
IOLockWakeup(me->rxQueueLock, NO_EVENT, true);
}
IOLockUnlock(me->rxQueueLock);
} else if (rc != kIOReturnAborted){
if((me->fInPipe->GetPipeStatus()) == kIOUSBPipeStalled) {
IOLog("USBAx8817x::dataReadComplete: return code = 0x%x.\n", rc);
IOLockLock(me->rxQueueLock);
me->readThreadCmd |= RXTHREAD_READSTALLED;
IOLockWakeup(me->rxQueueLock, NO_EVENT, true);
IOLockUnlock(me->rxQueueLock);
}
}
} /* end dataReadComplete */
/***************************************************************************/
//
// Method: USBAx8817x::receivePacket
//
// Inputs: completion - pointer to read completion routine
// inMDP - MDP where received data is
// remaining - BUFSIZE minus length of received packet
//
// Outputs: None
//
// Desc: Receives a packet. Allocates a buffer, copies
// the received packet to the buffer, and sends
// the buffer up the network stack. Then another
// read command is issued. If the read command
// fails, the network interface is freed.
//
//
/****************************************************************************/
void USBAx8817x::receivePacket(UInt32 packetLength,
IOBufferMemoryDescriptor *inMDP,
IOUSBCompletion *completion) {
UInt8 *dataInPtr;
UInt16 length;
mbuf_t rcvd;
IOReturn ior;
if(packetLength > (14 + 6) ) {
dataInPtr = (UInt8 *)inMDP->getBytesNoCopy();
if(deviceFlag & 0x0040) {
length = packetLength;
} else {
length = packetLength - 4; /* Device appended CRC to frame. */
}
rcvd = allocatePacket(length);
if(!rcvd) {
IOLog("USBAx8817x::receivePacket: Failed to allocate packet.\n");
return;
}
bcopy(dataInPtr, mbuf_data(rcvd), length);
fNetworkInterface->inputPacket(rcvd, length, 0x01);
if(fpNetStats) {
++fpNetStats->inputPackets;
}
} else {
IOLog("USBAx8817x::receivePacket: Packet length (%d) is too short.\n",
(int)packetLength);
}
/* Read next packet. */
inMDP->setLength(BUFSIZE);
ior = fInPipe->Read(inMDP, 0, 0, BUFSIZE, completion, NULL);
if(ior != kIOReturnSuccess) {
if((fInPipe->GetPipeStatus()) == kIOUSBPipeStalled) {
IOLog("USBAx8817x::receivePacket: return code = 0x%x.\n", ior);
IOLockLock(rxQueueLock);
readThreadCmd |= RXTHREAD_READSTALLED;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockUnlock(rxQueueLock);
} else {
IOLog("USBAx8817x::receivePacket: Failed to read next packet.\n");
}
}
} /* end receivePacket */
/****************************************************************************/
//
// Method: USBAx8817x::dataWriteComplete
//
// Inputs: obj - me (this)
// param - parameter block(the mbuf)
// rc - return code
// remaining - what's left
//
// Outputs: None
//
// Desc: BulkOut pipe (Data interface) write completion
// routine. If the write length was a multiple
// of 64, a zero length packet is written.
// Otherwise, "transmit complete" event is posted.
//
/****************************************************************************/
void USBAx8817x::dataWriteZeroComplete(void *obj, void *param,
IOReturn rc, UInt32 remaining) {
}
void USBAx8817x::dataWriteComplete(void *obj, void *param,
IOReturn rc, UInt32 remaining) {
int txQueueLen;
USBAx8817x *me;
ax8817xTxRing_t *transmitInfo;
bool restartQueue = false;
bool writeStalled = false;
me = (USBAx8817x *)obj;
if(!me) {
return;
}
transmitInfo = (ax8817xTxRing_t *)param;
IOLockLock(me->txQueueLock);
txQueueLen = ++me->txSpots;
if(me->txQueueStopped && (txQueueLen == TX_QUEUE_RESTART)) {
restartQueue = true;
me->txQueueStopped = false;
}
IOLockUnlock(me->txQueueLock);
if(rc == kIOReturnSuccess) {
++me->fpNetStats->outputPackets;
} else if(rc != kIOReturnAborted) {
IOLog("USBAx8817x::dataWriteComplete: write failed: 0x%X\n", rc);
if((me->fOutPipe->GetPipeStatus()) == kIOUSBPipeStalled) {
writeStalled = true;
}
}
if(writeStalled) {
IOLog("USBAxx8817x: dataWriteComplete: Write pipe stalled!\n");
postEvent(me, (void *)(int)ctrlEventWriteStalled);
}
if(restartQueue) {
postEvent(me, (void *)(int)ctrlEventServiceQueue);
}
if(transmitInfo->packet) {
me->freePacket(transmitInfo->packet);
transmitInfo->packet = NULL;
}
postEvent(me, (void *)(int)ctrlEventTxDone);
} /* end dataWriteComplete */
/****************************************************************************/
//
// Method: USBAx8817x::ctrlComplete
//
// Inputs: obj - me (this)
// param - not used
// rc - return code
// remaining - not used
//
// Outputs: None
//
// Desc: Completion routine for reads and writes to
// the control pipe. This function simply posts
// a "control I/O done" event.
//
/****************************************************************************/
void USBAx8817x::ctrlComplete(void *obj, void *param, IOReturn rc,
UInt32 remaining) {
USBAx8817x *me;
me = (USBAx8817x *)obj;
if(me) {
if(rc == kIOReturnSuccess) {
postEvent(me, (void *)(int)ctrlEventCallback);
} else if((me->fCntlPipe->GetPipeStatus()) == kIOUSBPipeStalled) {
IOLockLock(me->rxQueueLock);
me->readThreadCmd |= RXTHREAD_INTRSTALLED;
IOLockWakeup(me->rxQueueLock, NO_EVENT, true);
IOLockUnlock(me->rxQueueLock);
} else if(rc != kIOReturnAborted) {
IOLog("USBAx8817x: ctrlComplete: error = 0x%X.\n", rc);
}
}
} /* end ctrlComplete */
/****************************************************************************/
//
// Method: USBAx8817x::interruptComplete
//
// Inputs: obj - me (this)
// param - not used
// rc - return code
// remaining - not used
//
// Outputs: None
//
// Desc: Comlpletion routine for reads and writes to
// the interrupt pipe. This function simply posts
// an "interrupt is pending" event.
//
/****************************************************************************/
void USBAx8817x::interruptComplete(void *obj, void *param,
IOReturn rc, UInt32 remaining) {
USBAx8817x *me;
me = (USBAx8817x *)obj;
if(me) {
if(rc == kIOReturnSuccess) {
if(me->rcvdLinkStatus == true) {
if(!(me->fInterruptPipeBuffer[2] & 0x03)) {
me->rcvdLinkStatus = false;
postEvent(me, (void *)(int)ctrlEventLinkDown);
}
} else {
if(me->fInterruptPipeBuffer[2] & 0x03) {
me->rcvdLinkStatus = true;
postEvent(me, (void *)(int)ctrlEventLinkUp);
}
}
me->fInterruptPipe->Read(me->fPipeInterruptMDP, 0, 0, 8,
&me->fInterruptCompletionInfo, NULL);
} else if((me->fInterruptPipe->GetPipeStatus()) == kIOUSBPipeStalled) {
IOLockLock(me->rxQueueLock);
me->readThreadCmd |= RXTHREAD_INTRSTALLED;
IOLockWakeup(me->rxQueueLock, NO_EVENT, true);
IOLockUnlock(me->rxQueueLock);
} else if(rc != kIOReturnAborted) {
IOLog("USBAx8817x::interruptComplete: Interrupt read failed.");
IOLog(" Error code = 0x%X.\n", rc);
}
}
} /* end interruptComplete */
/****************************************************************************/
//
// Method: USBAx8817x::willTerminate
//
// Inputs: provider - Pointer to my provider.
// IOOptionsBits - ignored
//
// Outputs: None
//
// Desc: A message from below indicating that the lower
// layer is about to terminate. This is usually
// due to the device being unplugged. It is
// important to close the provider at this
// point. Shortly after we close the driver, our
// stop() method will be called, and then this
// driver will be unloaded.
//
/****************************************************************************/
bool USBAx8817x::willTerminate(IOService * provider,
IOOptionBits options) {
setLinkStatus(kIONetworkLinkValid, 0);
setLinkStatus(0, 0);
postEvent(this, (void *)(int)ctrlEventTerminate);
if(rxQueueLock != NULL) {
IOLockLock(rxQueueLock);
readThreadCmd |= RXTHREAD_STOP;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockUnlock(rxQueueLock);
return true;
while(!(readThreadCmd & RXTHREAD_STOPPED)) {
readThreadCmd |= RXTHREAD_STOP;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockSleep(rxQueueLock, NO_EVENT, THREAD_ABORTSAFE);
}
IOLockUnlock(rxQueueLock);
IOLockFree(rxQueueLock);
rxQueueLock = NULL;
}
if(fNetworkInterface) {
IOLog("USBAx8817x: willTerminate: Detaching network interface.\n");
detachInterface(fNetworkInterface, true);
fNetworkInterface->release();
fNetworkInterface = NULL;
}
if(fCntlPipe) {
fCntlPipe = NULL;
}
if(fInPipe) {
fInPipe = NULL;
}
if(fOutPipe) {
fOutPipe = NULL;
}
if(fInterruptPipe) {
//fInterruptPipe->release();
fInterruptPipe = NULL;
}
if(fDataInterface) {
fDataInterface->close(this);
fDataInterface->release();
fDataInterface = NULL;
}
if(fpDevice) {
fpDevice->close(this);
fpDevice->release();
fpDevice = NULL;
}
return true;
} /* end willTerminate */
#pragma mark -- Event Handling --
/****************************************************************************/
//
// Method: USBAx8817x::postEvent
//
// Inputs: target - me (this)
// evt - Event to be posted
// result - not used
// gotLock - true if we have the workloop thread.
//
// Outputs: kIOReturnSuccess (always)
//
// Desc: Post an event. This method calls itself through
// the workloop, and then calls setState().
//
/****************************************************************************/
IOReturn USBAx8817x::postEvent(OSObject *target, void *evt, void *result,
void *gotLock, void *arg3) {
USBAx8817x *me;
IOWorkLoop *workLoop;
if(target == NULL) {
return kIOReturnSuccess;
}
me = (USBAx8817x *)target;
if( gotLock == (void *)false) {
workLoop = me->getWorkLoop();
if(workLoop) {
return workLoop->runAction(postEvent, target, evt,
(void *)result,
(void *)true);
} else {
IOLog("USBAx8817x::postEvent: Failed to get work loop.\n");
return kIOReturnSuccess;
}
}
++me->recursionLevel;
me->setState((ctrlEvent_t) (int)evt);
--me->recursionLevel;
return kIOReturnSuccess;
} /* end postEvent */
/****************************************************************************/
//
// Method: USBAx8817x::setState
//
// Inputs: target - me (this)
// evt - Event to be posted
//
// Outputs: none
//
// Desc: Posts an event, and makes asynchronous I/O
// calls. This method must always by synchronized
// by the work loop. Almost all asynchonous I/O
// calls, particularly control calls, are made
// from this method. This records the event, and
// implements three state machines. The first
// one, miiState, controls access to the Media
// Independent Interface (MII) registers. This
// state machine runs at highest priority if it
// is not idle. The next state machine,
// ethState, writes to ethernet control registers
// 0, 1 and 2. Finally, the third state machine,
// ctrlState, handles other control calls, such
// as checking if the cable was unplugged,
// setting promiscuous mode, multicast filters,
// etc.
//
/****************************************************************************/
void USBAx8817x::setState(ctrlEvent_t event) {
IOReturn ior;
UInt16 data16;
IONetworkMedium *medium;
int txLen;
if(fTerminate == true) {
return;
}
/* First, record the event. */
switch(event) {
case ctrlEventServiceQueue:
fTransmitQueue->service();
break;
case ctrlEventTimer:
timerPending = true;
break;
case ctrlEventEnable:
fNetifRequestEnabled = true;
break;
case ctrlEventDisable:
fNetifRequestEnabled = false;
break;
case ctrlEventTxDone:
--txInProgress;
break;
case ctrlEventTransmit:
++pktQueueLen;
break;
case ctrlEventSetMulticast:
setMc = true;
break;
case ctrlEventSuspend:
suspendPending = true;
break;
case ctrlEventResume:
awake = true;
resumePending = true;
break;
case ctrlEventStart:
started = true;
resumePending = true;
break;
case ctrlEventCallback:
callbackPending = true;
break;
case ctrlEventTerminate:
if(fTransmitQueue) {
fTransmitQueue->stop();
fTransmitQueue->setCapacity(0);
fTransmitQueue->flush();
}
fTerminate = true;
return;
case ctrlEventLinkUp:
checkLinkPending = true;
fLinkUp = false; /* This is intentionally reversed. */
break;
case ctrlEventLinkDown:
checkLinkPending = true;
fLinkUp = true; /* This is intentionally reversed. */
break;
case ctrlEventWriteStalled:
writePipeStalled = true;
break;
case ctrlEventClearedWriteStall:
writeStallCleared = true;
break;
default:
break;
}
/* If this method has called itself recursively with a new event,
exit now that the event has been recorded. */
if(recursionLevel > 1) {
return;
}
/* Run the mii state mathine. */
switch(miiState) {
case miiStateRead:
if(callbackPending == true) {
callbackPending = false;
ior = axCmdAsync(AXE_CMD_MII_READ_REG, miiIndx, phy);
if(ior == kIOReturnSuccess) {
miiState = miiStateFinish1;
} else {
IOLog("USBAx8817x: miiStateRead1: axCmdAsync failed!\n");
}
}
return;
case miiStateWrite:
if(callbackPending == true) {
callbackPending = false;
*( (UInt16 *)fCtrlPipeBuffer) = miiData;
ior = axCmdAsync(AXE_CMD_MII_WRITE_REG, miiIndx, phy);
if(ior == kIOReturnSuccess) {
miiState = miiStateFinish1;
}
}
return;
case miiStateFinish1:
if(callbackPending == true) {
callbackPending = false;
miiData = USBToHostWord( *( (UInt16 *)fCtrlPipeBuffer) );
ior = axCmdAsync(AXE_CMD_MII_OPMODE_HW, 0, 0);
if(ior == kIOReturnSuccess) {
miiState = miiStateFinish2;
} else {
IOLog("USBAx8817x: miiStateRead2: axCmdAsync failed!\n");
}
}
return;
case miiStateFinish2:
if(callbackPending == true) {
callbackPending = false;
miiPending = true;
break;
}
return;
default:
break;
} /* switch(miiState) */
miiState = miiStateIdle;
/* Run the control state mathine. */
switch(cState) {
case ctrlStateLinkStatus:
if(miiPending == true) {
miiPending = false;
if(fLinkUp == false) {
if(selectedMedium == kIOMediumEthernetAuto) {
if( (miiData & 0x0004) == 0x0004) { /* Link went up */
ior = readMiiWordAsync(6);
/* Does partner support autonegotiation? */
if(ior == kIOReturnSuccess) {
cState = ctrlStateAutoDone1;
return;
}
}
} else if(miiData & 0x0004) { /* User selected medium type */
/* Link is up! */
medium = IONetworkMedium::getMediumWithType(fMediumDict,
selectedMedium);
setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, medium);
fLinkUp = true;
}
} else { /* Linkup == true */
if( !(miiData & 0x0004) ) { /* link went down */
if(requestedMedium == kIOMediumEthernetAuto) {
medium = /* Autonegotion */
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernetNone);
} else {
medium = /* User picked medium */
IONetworkMedium::getMediumWithType(fMediumDict,
selectedMedium);
}
setLinkStatus(kIONetworkLinkValid, medium);
fLinkUp = false;
}
}
break;
}
return;
case ctrlStateAutoDone1:
if(miiPending == true) {
miiPending = false;
if(miiData & 0x01) {
/* Autonegotiation succeeded! */
/* Figure out medium settings. */
if(supports1000BaseT) {
/* Check for 1000BaseT connection */
ior = readMiiWordAsync(10);
if(ior == kIOReturnSuccess) {
cState = ctrlStateCheckGiga;
}
return;
} else {
ior = readMiiWordAsync(5);
if(ior == kIOReturnSuccess) {
cState = ctrlStateAutoDone2;
return;
}
}
} else {
/* Autonegotiation failed. */
/* Assume medium is 10BaseT, half-duplex. */
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
10 * 1000000);
fLinkUp = true;
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
ior = axCmdAsync(AXE_CMD_WRITE_MEDIA, 0, mediumStatus);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitEth;
}
return;
}
}
return;
case ctrlStateCheckGiga:
if(miiPending == true) {
miiPending = false;
IOLog("USBAx8817x: mii register 10 = 0x%X.\n", miiData);
if(miiData & 0x0800) {
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet1000BaseTX |
kIOMediumOptionFullDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
1000 * 1000000);
//mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet1000BaseTX |
kIOMediumOptionFullDuplex);
fLinkUp = true;
ior = axCmdAsync(AXE_CMD_WRITE_MEDIA, 0, mediumStatus);
if(ior == kIOReturnSuccess) {
}
cState = ctrlStateWaitEth;
} else if(miiData & 0x0400) {
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet1000BaseTX |
kIOMediumOptionHalfDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
1000 * 1000000);
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet1000BaseTX |
kIOMediumOptionHalfDuplex);
fLinkUp = true;
ior = axCmdAsync(AXE_CMD_WRITE_MEDIA, 0, mediumStatus);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitEth;
}
} else {
IOLog("USBAx8817x: Link partner is NOT 1000BaseT HD capbable.\n");
ior = readMiiWordAsync(5);
if(ior == kIOReturnSuccess) {
cState = ctrlStateAutoDone2;
}
}
}
return;
case ctrlStateAutoDone2:
if(miiPending == true) {
miiPending = false;
if( !(miiData & 0x001f) == 0x0001 ) {
/* Autonegotiation failed. Set link to 10BaseT, Half Duplex */
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
10 * 1000000);
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
} else if(miiData & 0x0100) {
/* 100BaseT, Full Duplex */
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet100BaseTX |
kIOMediumOptionFullDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
100 * 1000000);
//mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet100BaseTX |
kIOMediumOptionFullDuplex);
} else if(miiData & 0x0080) {
/* 100BaseT, Half Duplex */
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet100BaseTX |
kIOMediumOptionHalfDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
100 * 1000000);
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet100BaseTX |
kIOMediumOptionHalfDuplex);
} else if(miiData & 0x0040) {
/* 10BaseT, Full Duplex */
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet10BaseT |
kIOMediumOptionFullDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
10 * 1000000);
//mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet10BaseT |
kIOMediumOptionFullDuplex);
} else if(miiData & 0x0020) {
/* 10BaseT, Half Duplex */
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
10 * 1000000);
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
} else {
/* Default: set to 10BaseT, Half duplex */
medium =
IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium,
10 * 1000000);
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
}
fLinkUp = true;
ior = axCmdAsync(AXE_CMD_WRITE_MEDIA, 0, mediumStatus);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitEth;
}
return;
}
return;
case ctrlStateWaitEth:
if(callbackPending == true) {
callbackPending = false;
break;
}
return;
case ctrlStateSetMedium:
if(miiPending == true) {
miiPending = false;
ior = axCmdAsync(AXE_CMD_WRITE_MEDIA, 0, mediumStatus);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitEth;
}
}
return;
case ctrlStateWaitMii:
if(miiPending == true) {
miiPending = false;
break;
}
return;
case ctrlStateDisable:
if(callbackPending == true) {
callbackPending = false;
/* This will cause the phy to be set to requestedMedium on resume. */
selectedMedium = kIOMediumEthernetNone;
medium =
IONetworkMedium::getMediumWithType(fMediumDict, selectedMedium);
if(medium != NULL) {
if(requestedMedium != kIOMediumEthernetAuto) {
setSelectedMedium(medium);
}
setLinkStatus(kIONetworkLinkValid, medium);
fLinkUp = false;
}
/* Turn off phy. */
ior = writeMiiWordAsync(0, 0x0800);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitMii;
}
}
return;
case ctrlStateSuspend:
if(resumePending == true) {
resumePending = false;
if( (awake == true) && (started == true) ) {
break;
}
}
return;
case ctrlStateClearWriteStall:
if(writeStallCleared == true) {
writeStallCleared = false;
break;
}
return;
case ctrlStateIdle:
break;
} /* switch(cState) */
cState = ctrlStateIdle;
if(checkLinkPending == true) {
if(txInProgress > 0) {
return;
}
checkLinkPending = false;
if(fNetifEnabled == true) {
ior = readMiiWordAsync(1);
if(ior == kIOReturnSuccess) {
cState = ctrlStateLinkStatus;
return;
}
}
}
if((suspendPending == true) && (fNetifEnabled == false)) {
if(txInProgress > 0) {
return;
}
suspendPending = false;
cState = ctrlStateSuspend;
ourPolicyMaker->acknowledgeSetPowerState();
return;
}
if( (fNetifRequestEnabled == true) && (fNetifEnabled == false) ) {
if(txInProgress > 0) {
return;
}
fNetifEnabled = true;
/* Walk past any transmit requests that didn't make it out before
the driver was last disabled. */
while(pktQueueLen > 0) {
--pktQueueLen;
if(txConsumePtr->packet) {
freePacket(txConsumePtr->packet);
txConsumePtr->packet = NULL;
}
txConsumePtr->flags = 0;
txConsumePtr = txConsumePtr->next;
}
txInProgress = 0;
rxCtrlVal |= AXE_RXCMD_ENABLE;
ior = axCmdAsync(AXE_CMD_RXCTL_WRITE, 0, rxCtrlVal);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitEth;
return;
}
}
if(fNetifEnabled == false) {
return;
}
if(fNetifRequestEnabled == false) {
if(txInProgress > 0) {
return;
}
fNetifEnabled = false;
rxCtrlVal &= ~AXE_RXCMD_ENABLE;
ior = axCmdAsync(AXE_CMD_RXCTL_WRITE, 0, rxCtrlVal);
if(ior == kIOReturnSuccess) {
cState = ctrlStateDisable;
}
return;
}
if(selectedMedium != requestedMedium) {
if(txInProgress > 0) {
return;
}
selectedMedium = requestedMedium;
medium =
IONetworkMedium::getMediumWithType(fMediumDict, selectedMedium);
if(medium) {
setSelectedMedium(medium);
if(selectedMedium == kIOMediumEthernetAuto) {
medium = IONetworkMedium::getMediumWithType(fMediumDict,
kIOMediumEthernetNone);
}
setLinkStatus(kIONetworkLinkValid, medium);
fLinkUp = false;
}
checkLinkPending = true;
switch(selectedMedium) {
case kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex:
data16 = 0x0000;
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(selectedMedium);
break;
case kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex:
data16 = 0x0100;
//mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(selectedMedium);
break;
case kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex:
data16 = 0x2000;
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(selectedMedium);
break;
case kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex:
data16 = 0x2100;
//mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(selectedMedium);
break;
case kIOMediumEthernet1000BaseTX | kIOMediumOptionHalfDuplex:
data16 = 0x0040;
//mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(selectedMedium);
break;
case kIOMediumEthernet1000BaseTX | kIOMediumOptionFullDuplex:
data16 = 0x0140;
//mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(selectedMedium);
break;
case kIOMediumEthernetAuto:
data16 = 0x1200;
ior = writeMiiWordAsync(0, data16);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitMii;
}
return;
default:
data16 = 0x0000;
//mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
setMediumStatus(kIOMediumEthernet10BaseT |
kIOMediumOptionHalfDuplex);
break;
}
ior = writeMiiWordAsync(0, data16);
if(ior == kIOReturnSuccess) {
cState = ctrlStateSetMedium;
}
return;
}
if(promiscMode != promiscRequest) {
if(txInProgress > 0) {
return;
}
promiscMode = promiscRequest;
if(promiscMode) {
rxCtrlVal |= AXE_RXCMD_PROMISC;
} else {
rxCtrlVal &= ~AXE_RXCMD_PROMISC;
}
ior = axCmdAsync(AXE_CMD_RXCTL_WRITE, 0, rxCtrlVal);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitEth;
}
return;
}
if(setMc == true) {
if(txInProgress > 0) {
return;
}
setMc = false;
mcBytesToWrite = 0;
/* The following two lines just copy 8 bytes from mHashTable to
fCtrlPipeBuffer.*/
*( (UInt32 *)fCtrlPipeBuffer) = *( (UInt32 *)mHashTable);
*( ((UInt32 *)fCtrlPipeBuffer) + 1) = *(((UInt32 *)mHashTable) + 1);
ior = axCmdAsync(AXE_CMD_WRITE_MCAST, 0, 0);
if(ior == kIOReturnSuccess) {
cState = ctrlStateWaitEth;
}
return;
}
while((writePipeStalled == false) && (pktQueueLen > 0)) {
#if ONE_TX_AT_A_TIME
if(txInProgress > 0) {
return;
}
#endif
--pktQueueLen;
txConsumePtr = txConsumePtr->next;
txLen = txConsumePtr->mdp->getLength();
if( txLen && !(txLen & (maxPacketSize - 1)) ) {
ior = fOutPipe->Write(txConsumePtr->mdp, 0, 0, txLen,
&txConsumePtr->writeZeroCompletionInfo);
if(ior == kIOReturnSuccess) {
/* Written packet was a multiple of maxPacketSize */
ior = fOutPipe->Write(txConsumePtr->mdp, 0, 0, 0,
&txConsumePtr->writeCompletionInfo);
}
} else {
ior = fOutPipe->Write(txConsumePtr->mdp, 0, 0, txLen,
&txConsumePtr->writeCompletionInfo);
}
if(ior == kIOReturnSuccess) {
++txInProgress;
} else {
if((fOutPipe->GetPipeStatus()) == kIOUSBPipeStalled) {
writePipeStalled = true;
IOLog("USBAxx8817x: setState: Write pipe stalled!\n");
} else {
IOLog("USBAx8817x::setState: write return code = 0x%x.\n", ior);
}
}
}
if((txInProgress == 0) && (writePipeStalled == true) ) {
writePipeStalled = false;
IOLog("USBAxx8817x: setState: Clearing write stall!\n");
IOLockLock(rxQueueLock);
readThreadCmd |= RXTHREAD_WRITESTALLED;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockUnlock(rxQueueLock);
cState = ctrlStateClearWriteStall;
}
} /* End setState */
/*****************************************************************************/
//
// Method: USBAx8817x::setMediumStatus
//
// Inputs: selectedMedium - Medium to select
//
// Outputs: Return code - none
//
// Desc: The mediumStatus tells the MAC what medium is
// currently selected.
//
/****************************************************************************/
void USBAx8817x::setMediumStatus(UInt32 selectedMedium) {
if(mediumStatus & kIOMediumOptionFullDuplex) {
mediumStatus |= AXE_MEDIA_FULL_DUPLEX;
} else {
mediumStatus &= ~AXE_MEDIA_FULL_DUPLEX;
}
}
/*****************************************************************************/
//
// Method: USBAx8817x::handleSetPowerStateOn
//
// Inputs: param0 - pointer to driver object
// param1 - not used
//
// Outputs: Return code - none
//
// Desc: Called to indicate a power "OFF" event.
//
/****************************************************************************/
void USBAx8817x::handleSetPowerStateOff(thread_call_param_t param0,
thread_call_param_t param1 ) {
USBAx8817x *me;
me = (USBAx8817x *)param0;
if(!me) {
IOLog("USBAx8817x::handleSetPowerStateOff: me == NULL!\n");
return;
}
postEvent(me, (void *)(int)ctrlEventSuspend);
if(me->ourPolicyMaker) {
//me->ourPolicyMaker->acknowledgeSetPowerState();
}
me->release();
} /* end handleSetPowerStateOff */
/*****************************************************************************/
//
// Method: USBAx8817x::handleSetPowerStateOn
//
// Inputs: param0 - pointer to driver object
// param1 - not used
//
// Outputs: Return code - none
//
// Desc: Called to indicate a power "ON" event.
//
/****************************************************************************/
void USBAx8817x::handleSetPowerStateOn(thread_call_param_t param0,
thread_call_param_t param1 ) {
USBAx8817x *me;
me = (USBAx8817x *)param0;
if(!me) {
IOLog("USBAx8817x::handleSetPowerStateOn: me == NULL!\n");
return;
}
postEvent(me, (void *)(int)ctrlEventResume);
if(me->ourPolicyMaker) {
me->ourPolicyMaker->acknowledgeSetPowerState();
}
me->release();
} /* end handleSetPowerStateOn */
#pragma mark -- Shutdown --
/*****************************************************************************/
//
// Method: USBAx8817x::cleanUp
//
// Inputs: None
//
// Outputs: None
//
// Desc: Releases all allocated resources.
//
/*****************************************************************************/
void USBAx8817x::cleanUp() {
int i;
if(rxQueueLock != NULL) {
IOLockLock(rxQueueLock);
while(!(readThreadCmd & RXTHREAD_STOPPED)) {
readThreadCmd |= RXTHREAD_STOP;
IOLockWakeup(rxQueueLock, NO_EVENT, true);
IOLockSleep(rxQueueLock, NO_EVENT, THREAD_ABORTSAFE);
}
IOLockUnlock(rxQueueLock);
IOLockFree(rxQueueLock);
rxQueueLock = NULL;
}
if(fNetworkInterface) {
detachInterface(fNetworkInterface, false);
fNetworkInterface->release();
fNetworkInterface = NULL;
}
if(fTransmitQueue) {
fTransmitQueue->stop();
fTransmitQueue->setCapacity(0);
fTransmitQueue->flush();
fTransmitQueue->release();
fTransmitQueue = NULL;
}
if(fTimerSource) {
fTimerSource->cancelTimeout();
fTimerSource->release();
fTimerSource = NULL;
}
if(fPipeInterruptMDP) {
fPipeInterruptMDP->release();
fPipeInterruptMDP = NULL;
}
if(fPipeCtrlMDP) {
fPipeCtrlMDP->release();
fPipeCtrlMDP = NULL;
}
for(i=0; i<TX_RING_SIZE; ++i) {
if(txRing[i].mdp) {
txRing[i].mdp->release();
txRing[i].mdp = NULL;
}
if(txRing[i].packet) {
freePacket(txRing[i].packet);
txRing[i].packet = NULL;
}
}
for(i=0; i<NUM_RX_BUFFERS; ++i) {
if(rxMDPList[i]) {
rxMDPList[i]->release();
rxMDPList[i] = NULL;
}
}
if(fInterruptPipe) {
//fInterruptPipe->release();
fInterruptPipe = NULL;
}
if(fCntlPipe) {
//fCntlPipe->release();
fCntlPipe = NULL;
}
if(fInPipe) {
//fInPipe->release();
fInPipe = NULL;
}
if(fOutPipe) {
//fOutPipe->release();
fOutPipe = NULL;
}
if(fDataInterface) {
fDataInterface->close(this);
fDataInterface->release();
fDataInterface = NULL;
}
if(fpDevice) {
fpDevice->close(this);
fpDevice->release();
fpDevice = NULL;
}
if(fMediumDict) {
fMediumDict->release();
fMediumDict = NULL;
}
if(powerUpThread) {
thread_call_free(powerUpThread);
powerUpThread = NULL;
}
if(powerDownThread) {
thread_call_free(powerDownThread);
powerDownThread = NULL;
}
if(txQueueLock) {
IOLockFree(txQueueLock);
txQueueLock = NULL;
}
if(txLock) {
IOLockFree(txLock);
txLock = NULL;
}
} /* end cleanUp */
/*****************************************************************************/
//
// Method: USBAx8817x::stop
//
// Inputs: provider - my provider
//
// Outputs: None
//
// Desc: Stops
//
/*****************************************************************************/
void USBAx8817x::stop(IOService *provider) {
cleanUp();
super::stop(provider);
IOLog("USBAx8817x: Stopped.\n");
return;
} /* end stop */