home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.sustworks.com 2018
/
ftp.sustworks.com.zip
/
ftp.sustworks.com
/
USBAx8817x_101.dmg
/
src
/
Source
/
AxOutputQueue.cpp
next >
Wrap
C/C++ Source or Header
|
2005-10-08
|
12KB
|
524 lines
/*
*
*/
#include <IOKit/network/IOOutputQueue.h>
#include <IOKit/network/IONetworkStats.h>
#include <IOKit/network/IONetworkController.h>
#include <IOKit/IOLocks.h>
#include "AxOutputQueue.h"
#define CMD_START (1 << 0)
#define CMD_STOP (1 << 1)
#define CMD_SERVICE (1 << 2)
#define CMD_EXITTHREAD (1 << 3)
#define CMD_SETCAPACITY (1 << 4)
#define CMD_FLUSH (1 << 5)
#define Q_RUNNING (1 << 0)
#define Q_STALLED (1 << 1)
#define Q_ACTIVE (1 << 2)
#define Q_THREADALIVE (1 << 3)
#define super IOOutputQueue
OSDefineMetaClassAndStructors( AxOutputQueue, IOOutputQueue )
#define kIOOutputQueueSignature ((void *) 0xadd1add2)
bool AxOutputQueue::init(OSObject * target,
IOOutputAction action,
UInt32 capacity) {
if(!super::init()) {
return false;
}
cmdLock = NULL;
qLock = NULL;
qThread = NULL;
qCmd = 0;
qState = 0;
blockingCount = 0;
stallCount = 0;
outputCount = 0;
dropCount = 0;
retryCount = 0;
cmdActive = false;
_action = action;
_target = target;
inQueue = IONew(IOMbufQueue, 1);
IOMbufQueueInit(inQueue, capacity);
if(inQueue == NULL) {
IOLog("AxOutputQueue: Failed to create input queue.\n");
return false;
}
cmdLock = IOLockAlloc();
if(cmdLock == NULL) {
IOLog("AxOutputQueue: Failed to allocate command lock.\n");
return false;
}
qLock = IOLockAlloc();
if(qLock == NULL) {
IOLog("AxOutputQueue: Failed to allocate queue lock.\n");
IOLockFree(cmdLock);
cmdLock = NULL;
return false;
}
wakeConsumer = (void *)qLock;
qState |= Q_THREADALIVE;
qThread = IOCreateThread(qThreadEntry, this);
if(qThread == NULL) {
IOLog("AxOutputQueue: Failed to start thread.\n");
IOLockFree(cmdLock);
cmdLock = NULL;
IOLockFree(qLock);
qLock = NULL;
return false;
}
return true;;
}
//---------------------------------------------------------------------------
// Factory methods that will construct and initialize an AxOutputQueue
// object.
AxOutputQueue *
AxOutputQueue::withTarget(IONetworkController * target,
UInt32 capacity) {
AxOutputQueue * queue = new AxOutputQueue;
if (queue && !queue->init(target, target->getOutputHandler(), capacity)) {
queue->release();
queue = NULL;
}
return queue;
}
AxOutputQueue *
AxOutputQueue::withTarget(OSObject * target,
IOOutputAction action,
UInt32 capacity) {
AxOutputQueue * queue = new AxOutputQueue;
if (queue && !queue->init(target, action, capacity)) {
queue->release();
queue = 0;
}
return queue;
}
//---------------------------------------------------------------------------
// Release all resources previously allocated before calling super::free().
void AxOutputQueue::free() {
if(qLock && cmdLock) {
IOLockLock(cmdLock);
if(cmdActive) {
IOLog("AxOutputQueue: free: cmdActive == true!\n");
}
cmdActive = true;
IOLockUnlock(cmdLock);
IOLockLock(qLock);
if(qState & Q_THREADALIVE) {
qCmd |= CMD_EXITTHREAD;
while(qCmd & CMD_EXITTHREAD) {
IOLockWakeup(qLock, wakeConsumer, true);
IOLockSleep(qLock, NO_EVENT, THREAD_ABORTSAFE);
}
IOLockWakeup(qLock, wakeConsumer, true);
IOLockUnlock(qLock);
IOLockLock(cmdLock);
while(cmdActive) {
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockSleep(cmdLock, NO_EVENT, THREAD_ABORTSAFE);
}
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockUnlock(cmdLock);
}
IOLockFree(cmdLock);
IOLockFree(qLock);
qLock = NULL;
cmdLock = NULL;
}
if(inQueue) {
IOMbufFree( IOMbufQueueDequeueAll(inQueue) );
IODelete(inQueue, IOMbufQueue, 1);
inQueue = NULL;
}
super::free();
}
//---------------------------------------------------------------------------
// Add a single packet, or a chain of packets, to the queue object.
// This method can support multiple clients threads.
UInt32 AxOutputQueue::enqueue(mbuf_t m, void * param) {
int loopCount;
IOLockLock(qLock);
loopCount = 0;
while(qState & Q_RUNNING) {
if(IOMbufQueueEnqueue(inQueue, m)) {
IOLockWakeup(qLock, wakeConsumer, true);
IOLockUnlock(qLock);
return 0;
}
if( (blockingCount > 0) || (loopCount > 3)) {
++dropCount;
IOLockWakeup(qLock, wakeConsumer, true);
IOLockUnlock(qLock);
IOMbufFree(m);
return ENOBUFS;
}
++blockingCount;
IOLockSleep(qLock, NO_EVENT, THREAD_ABORTSAFE);
--blockingCount;
++loopCount;
}
if(blockingCount == 0) {
IOLockWakeup(qLock, wakeConsumer, true);
} else {
IOLockWakeup(qLock, NO_EVENT, true);
}
++dropCount;
IOLockUnlock(qLock);
IOMbufFree(m);
return ENOBUFS;
}
//---------------------------------------------------------------------------
// Start or enable the queue.
bool AxOutputQueue::start() {
if(!sendCmdSync(CMD_START)) {
IOLog("AxOutputQueue: start: failed.\n");
return false;
}
return true;
}
//---------------------------------------------------------------------------
// Stop or disable the queue.
bool AxOutputQueue::stop() {
if(!sendCmdSync(CMD_STOP)) {
IOLog("AxOutputQueue: stop: failed.\n");
return false;
}
return true;
}
bool AxOutputQueue::sendCmdSync(UInt32 cmd) {
IOLockLock(cmdLock);
if(cmdActive) {
IOLog("AxOutputQueue: cmdActive: cmdActive == true!\n");
IOLockUnlock(cmdLock);
return false;
}
cmdActive = true;
IOLockUnlock(cmdLock);
IOLockLock(qLock);
if(qState & Q_THREADALIVE) {
qCmd |= cmd;
IOLockWakeup(qLock, wakeConsumer, true);
IOLockUnlock(qLock);
IOLockLock(cmdLock);
while(cmdActive) {
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockSleep(cmdLock, NO_EVENT, THREAD_ABORTSAFE);
}
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockUnlock(cmdLock);
} else {
IOLockUnlock(qLock);
IOLog("AxOutputQueue: sendCmdSync: thread not alive.\n");
return false;
}
return true;
}
//---------------------------------------------------------------------------
// If the queue becomes stalled, then service() must be called by the target
// to restart the queue when the target is ready to accept more packets.
bool AxOutputQueue::service(UInt32 options) {
IOLockLock(qLock);
qCmd |= CMD_SERVICE;
IOLockWakeup(qLock, wakeConsumer, true);
IOLockUnlock(qLock);
return true;
}
//---------------------------------------------------------------------------
// Release all packets held by the queue.
UInt32 AxOutputQueue::flush() {
if(!sendCmdSync(CMD_FLUSH)) {
IOLog("AxOutputQueue: flush: failed.\n");
return false;
}
return true;
}
//---------------------------------------------------------------------------
// Change the capacity of the queue.
bool AxOutputQueue::setCapacity(UInt32 capacity) {
IOLockLock(cmdLock);
if(cmdActive) {
IOLog("AxOutputQueue: setCapacity: cmdActive == true!\n");
IOLockUnlock(cmdLock);
return false;
}
cmdActive = true;
IOLockUnlock(cmdLock);
IOLockLock(qLock);
if(qState & Q_THREADALIVE) {
requestedCapacity = capacity;
qCmd |= CMD_SETCAPACITY;
IOLockWakeup(qLock, wakeConsumer, true);
IOLockUnlock(qLock);
IOLockLock(cmdLock);
while(cmdActive) {
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockSleep(cmdLock, NO_EVENT, THREAD_ABORTSAFE);
}
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockUnlock(cmdLock);
} else {
IOLockWakeup(qLock, wakeConsumer, true);
IOLockUnlock(qLock);
IOLog("AxOutputQueue: setCapacity: thread not alive.\n");
return false;
}
return true;
}
//---------------------------------------------------------------------------
// Returns the current queue capacity.
UInt32 AxOutputQueue::getCapacity() const {
UInt32 capacity;
IOLockLock(qLock);
capacity = IOMbufQueueGetCapacity(inQueue);
IOLockUnlock(qLock);
return capacity;
}
UInt32 AxOutputQueue::getSize() const {
UInt32 size;
IOLockLock(qLock);
size = IOMbufQueueGetSize(inQueue);
IOLockUnlock(qLock);
return size;
}
UInt32 AxOutputQueue::getDropCount() {
return dropCount;
}
UInt32 AxOutputQueue::getOutputCount() {
return outputCount;
}
UInt32 AxOutputQueue::getRetryCount() {
return retryCount;
}
UInt32 AxOutputQueue::getStallCount() {
return stallCount;
}
UInt32 AxOutputQueue::getState() const {
UInt32 state;
state = qState;
return state & 0x07;
}
void AxOutputQueue::qThreadEntry(void *ob) {
AxOutputQueue *me;
if(ob) {
if(me = OSDynamicCast(AxOutputQueue, (OSObject *)ob)) {
me->threadGo();
}
} else {
IOLog("AxOutputQueue: ob == NULL!\n");
}
IOExitThread();
}
void AxOutputQueue::threadGo() {
bool didSomething;
mbuf_t pkt;
UInt32 status;
didSomething = false;
IOLockLock(qLock);
while(true) {
while(qCmd) {
didSomething = true;
if(qCmd & CMD_EXITTHREAD) {
qCmd &= ~CMD_EXITTHREAD;
qState &= ~Q_THREADALIVE;
while(blockingCount > 0) {
IOLockWakeup(qLock, NO_EVENT, true);
IOLockSleep(qLock, wakeConsumer, THREAD_ABORTSAFE);
}
IOLockWakeup(qLock, NO_EVENT, true);
IOLockUnlock(qLock);
IOLockLock(cmdLock);
cmdActive = false;
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockUnlock(cmdLock);
return;
}
if(qCmd & CMD_START) {
qCmd &= ~CMD_START;
if(!(qState & Q_RUNNING)) {
qState |= Q_RUNNING;
qState &= ~Q_STALLED;
}
ackCmdSync();
}
if(qCmd & CMD_SERVICE) {
qCmd &= ~CMD_SERVICE;
qState &= ~Q_STALLED;
}
if(qCmd & CMD_FLUSH) {
qCmd &= ~CMD_FLUSH;
dropCount += IOMbufQueueGetSize(inQueue);
pkt = IOMbufQueueDequeueAll(inQueue);
IOLockWakeup(qLock, NO_EVENT, true);
IOLockUnlock(qLock);
IOMbufFree(pkt);
IOLockLock(cmdLock);
cmdActive = false;
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockUnlock(cmdLock);
IOLockLock(qLock);
}
if(qCmd & CMD_STOP) {
qCmd &= ~CMD_STOP;
qState &= ~Q_RUNNING;
while(blockingCount > 0) {
IOLockWakeup(qLock, NO_EVENT, true);
IOLockSleep(qLock, wakeConsumer, THREAD_ABORTSAFE);
}
ackCmdSync();
}
if(qCmd & CMD_SETCAPACITY) {
qCmd &= ~CMD_SETCAPACITY;
IOMbufQueueSetCapacity(inQueue, requestedCapacity);
ackCmdSync();
}
}
while(!qCmd && !(qState & Q_STALLED) && (qState & Q_RUNNING) &&
(pkt = IOMbufQueueDequeue(inQueue)) ) {
qState |= Q_ACTIVE;
qCmd &= ~CMD_SERVICE;
didSomething = true;
IOLockUnlock(qLock);
status = (_target->*_action)( pkt, 0 );
IOLockLock(qLock);
if(status == kIOReturnOutputSuccess) {
// Fast-path the typical code path.
++outputCount;
} else {
// Look at the return status and update statistics counters.
switch (status & kIOOutputStatusMask) {
default:
case kIOOutputStatusAccepted:
++outputCount;
break;
case kIOOutputStatusRetry:
IOMbufQueuePrepend(inQueue, pkt);
++retryCount;
break;
}
// Handle the requested action.
switch (status & kIOOutputCommandMask) {
case kIOOutputCommandStall:
qState |= Q_STALLED;
++stallCount;
break;
default:
break;
}
}
qState &= ~Q_ACTIVE;
if(qCmd & CMD_SERVICE) {
qCmd &= ~CMD_SERVICE;
qState &= ~Q_STALLED;
}
}
IOLockWakeup(qLock, NO_EVENT, true);
if(didSomething == false) {
IOLockSleep(qLock, wakeConsumer, THREAD_ABORTSAFE);
}
didSomething = false;
}
}
void AxOutputQueue::ackCmdSync() {
IOLockWakeup(qLock, NO_EVENT, true);
IOLockUnlock(qLock);
IOLockLock(cmdLock);
cmdActive = false;
IOLockWakeup(cmdLock, NO_EVENT, true);
IOLockUnlock(cmdLock);
IOLockLock(qLock);
}