AppleUSBUHCI_UIM.cpp [plain text]
#include <IOKit/usb/IOUSBLog.h>
#include <kern/clock.h>
#include <machine/limits.h>
#include "AppleUSBUHCI.h"
static char *
USBErrorToString(IOReturn status)
{
switch (status) {
case kIOReturnSuccess:
return "kIOReturnSuccess";
case kIOReturnError:
return "kIOReturnError";
case kIOReturnNotResponding:
return "kIOReturnNotResponding";
case kIOUSBPipeStalled:
return "kIOUSBPipeStalled";
case kIOReturnOverrun:
return "kIOReturnOverrun";
case kIOReturnUnderrun:
return "kIOReturnUnderrun";
case kIOUSBLinkErr:
return "kIOUSBLinkErr";
case kIOUSBCRCErr:
return "kIOUSBCRCErr";
case kIOUSBBitstufErr:
return "kIOUSBBitstufErr";
case kIOUSBTransactionReturned:
return "kIOUSBTransactionReturned";
case kIOReturnAborted:
return "kIOReturnAborted";
case kIOReturnIsoTooNew:
return "kIOReturnIsoTooNew";
case kIOReturnIsoTooOld:
return "kIOReturnIsoTooOld";
case kIOReturnNoDevice:
return "kIOReturnNoDevice";
case kIOReturnBadArgument:
return "kIOReturnBadArgument";
case kIOReturnInternalError:
return "kIOReturnInternalError";
case kIOReturnNoMemory:
return "kIOReturnNoMemory";
case kIOReturnUnsupported:
return "kIOReturnUnsupported";
case kIOReturnNoResources:
return "kIOReturnNoResources";
case kIOReturnNoBandwidth:
return "kIOReturnNoBandwidth";
case kIOReturnIPCError:
return "kIOReturnIPCError";
case kIOReturnTimeout:
return "kIOReturnTimeout";
case kIOReturnBusy:
return "kIOReturnBusy";
case kIOUSBTransactionTimeout:
return "kIOUSBTransactionTimeout";
case kIOUSBNotSent1Err:
return "kIOUSBNotSent1Err";
case kIOUSBNotSent2Err:
return "kIOUSBNotSent2Err";
}
return "Unknown";
}
#pragma mark Control
IOReturn
AppleUSBUHCI::UIMCreateControlEndpoint(
UInt8 functionNumber,
UInt8 endpointNumber,
UInt16 maxPacketSize,
UInt8 speed,
USBDeviceAddress highSpeedHub,
int highSpeedPort)
{
return UIMCreateControlEndpoint(functionNumber, endpointNumber, maxPacketSize, speed);
}
IOReturn
AppleUSBUHCI::UIMCreateControlEndpoint(
UInt8 functionNumber,
UInt8 endpointNumber,
UInt16 maxPacketSize,
UInt8 speed)
{
UHCIEndpoint *ep;
QH *qh;
USBLog(5, "%s[%p]::UIMCreateControlEndpoint (f %d ep %d) max %d spd %d", getName(), this,
functionNumber, endpointNumber, maxPacketSize, speed);
if (functionNumber == _rootFunctionNumber) {
return kIOReturnSuccess;
}
if (maxPacketSize == 0) {
return kIOReturnBadArgument;
}
USBLog(5, "%s[%p]::UIMCreateControlEndpoint allocating endpoint", getName(), this);
ep = AllocEndpoint(functionNumber, endpointNumber, kUSBNone, speed, maxPacketSize, kUSBControl);
if (ep == NULL)
return kIOReturnNoMemory;
ep->head_qh->elink = NULL;
ep->head_qh->qlink = NULL;
ep->head_qh->hw.elink = HostToUSBLong(kUHCI_QH_T);
if (speed == kUSBDeviceSpeedLow) {
qh = _lsControlQHEnd;
} else {
qh = _hsControlQHEnd;
}
USBLog(3, "%s[%p]::UIMCreateControlEndpoint linking qh %p into schedule after %p", getName(), this, ep->head_qh, qh);
ep->head_qh->hlink = qh->hlink;
ep->head_qh->hw.hlink = qh->hw.hlink;
IOSync();
qh->hlink = ep->head_qh;
qh->hw.hlink = HostToUSBLong(ep->head_qh->paddr | kUHCI_QH_Q);
IOSync();
if (speed == kUSBDeviceSpeedLow) {
_lsControlQHEnd = ep->head_qh;
} else {
_hsControlQHEnd = ep->head_qh;
}
USBLog(5, "%s[%p]::UIMCreateControlEndpoint done", getName(), this);
#if DEBUG
DumpFrame();
#endif
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::UIMCreateControlTransfer(
short functionNumber,
short endpointNumber,
IOUSBCommand* command,
IOMemoryDescriptor * CBP,
bool bufferRounding, UInt32 bufferSize,
short direction)
{
UHCIEndpoint *ep;
UHCITransaction *tp;
QH *qh;
TD *td, *last_td;
IOReturn status;
USBLog(7, "%s[%p]::UIMCreateControlTransfer (f %d ep %d dir %d) size %d", getName(), this, functionNumber, endpointNumber, direction, bufferSize);
ep = FindEndpoint(functionNumber, endpointNumber, kUSBAnyDirn);
if (ep == NULL) {
USBLog(4, "%s[%p]: endpoint not found", getName(), this);
return kIOUSBEndpointNotFound;
}
if (ep->stalled) {
USBLog(4, "%s[%p]: Control pipe stalled", getName(), this);
return kIOUSBPipeStalled;
}
tp = (UHCITransaction *)command->GetUIMScratch(0);
USBLog(7, "%s[%p]: scratch TP is %p, stall %d", getName(), this, tp, ep->stalled);
if (tp == NULL) {
tp = AllocTransaction(ep);
if (tp == NULL) {
return kIOReturnNoMemory;
}
USBLog(7, "%s[%p]: creating new transaction %p", getName(), this, tp);
tp->command = command;
tp->buf = CBP;
tp->bufLen = command->GetDataRemaining();
tp->timeout = command->GetCompletionTimeout();
if (tp->timeout == 0) {
tp->timeout = command->GetNoDataTimeout();
}
USBLog(7, "%s[%p]: Data timeout is %d, completion timeout is %d",
getName(), this,
command->GetNoDataTimeout(), command->GetCompletionTimeout());
tp->completion = command->GetUSLCompletion();
qh = AllocQH();
if (qh == NULL) {
return kIOReturnNoMemory;
}
tp->qh = qh;
command->SetUIMScratch(0, (UInt32)tp);
}
tp->nCompletions++;
qh = tp->qh;
USBLog(7, "%s[%p]: allocating TD chain", getName(), this);
status = AllocTDChain(ep, CBP, bufferSize, bufferRounding, direction, &td, &last_td, false, true);
if (status != kIOReturnSuccess) {
return status;
}
if (tp->last_td != NULL) {
tp->last_td->link = td;
tp->last_td->hw.link = HostToUSBLong(td->paddr);
} else {
tp->qh->elink = td;
tp->qh->qlink = NULL;
tp->qh->hw.elink = HostToUSBLong(td->paddr);
tp->first_td = td;
}
IOSync();
tp->last_td = last_td;
if (!command->GetMultiTransferTransaction() ||
command->GetFinalTransferInTransaction()) {
last_td->hw.ctrlStatus |= HostToUSBLong(kUHCI_TD_IOC);
IOSync();
USBLog(7, "%s[%p]: starting transaction %p", getName(), this, tp);
DumpTransaction(tp, 7);
StartTransaction(tp);
}
return kIOReturnSuccess;
}
#pragma mark Bulk
IOReturn
AppleUSBUHCI::UIMCreateBulkEndpoint(
UInt8 functionNumber,
UInt8 endpointNumber,
UInt8 direction,
UInt8 speed,
UInt16 maxPacketSize,
USBDeviceAddress highSpeedHub,
int highSpeedPort)
{
return UIMCreateBulkEndpoint(functionNumber, endpointNumber, direction, speed, maxPacketSize);
}
IOReturn
AppleUSBUHCI::UIMCreateBulkEndpoint(
UInt8 functionNumber,
UInt8 endpointNumber,
UInt8 direction,
UInt8 speed,
UInt8 maxPacketSize)
{
UHCIEndpoint *ep;
TD *td;
QH *qh;
USBLog(5, "%s[%p]::UIMCreateBulkEndpoint (fn %d ep %d dir %d) speed %d mp %d", getName(), this,
functionNumber, endpointNumber, direction, speed, maxPacketSize);
if (maxPacketSize == 0) {
return kIOReturnBadArgument;
}
ep = AllocEndpoint(functionNumber, endpointNumber, direction, speed, maxPacketSize, kUSBBulk);
if (ep == NULL) {
return kIOReturnNoMemory;
}
ep->head_qh->elink = NULL;
ep->head_qh->qlink = NULL;
ep->head_qh->hw.elink = HostToUSBLong(kUHCI_QH_T);
qh = _bulkQHEnd;
ep->head_qh->hlink = qh->hlink;
ep->head_qh->hw.hlink = qh->hw.hlink;
IOSync();
qh->hlink = ep->head_qh;
qh->hw.hlink = HostToUSBLong(ep->head_qh->paddr | kUHCI_QH_Q);
IOSync();
_bulkQHEnd = ep->head_qh;
DumpFrame(ReadFrameNumber() % kUHCI_NVFRAMES, 5 );
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::UIMCreateBulkTransfer(IOUSBCommand* command)
{
UHCIEndpoint *ep;
UHCITransaction *tp;
IOMemoryDescriptor *mp;
IOReturn status;
QH *qh;
USBLog(7, "%s[%p]::UIMCreateBulkTransfer (%d, %d, %d) size %d", getName(), this,
command->GetAddress(), command->GetEndpoint(), command->GetDirection(),
command->GetReqCount());
ep = FindEndpoint(command);
if (ep == NULL) {
USBLog(2, "%s[%p]: endpoint (fn %d, ep %d, dir %d) not found", getName(), this,
command->GetAddress(),
command->GetEndpoint(),
command->GetDirection()
);
return kIOUSBEndpointNotFound;
}
if (ep->stalled) {
USBLog(4, "%s[%p]: Bulk pipe stalled", getName(), this);
return kIOUSBPipeStalled;
}
tp = AllocTransaction(ep);
if (tp == NULL) {
return kIOReturnNoMemory;
}
tp->command = command;
tp->buf = command->GetBuffer();
tp->bufLen = command->GetReqCount();
tp->completion = command->GetUSLCompletion();
tp->timeout = command->GetCompletionTimeout();
if (tp->timeout == 0) {
tp->timeout = command->GetNoDataTimeout();
}
qh = AllocQH();
if (qh == NULL) {
return kIOReturnNoMemory;
}
tp->qh = qh;
tp->nCompletions = 1;
mp = command->GetBuffer();
status = AllocTDChain(ep, mp, command->GetReqCount(), command->GetBufferRounding(), command->GetDirection(), &tp->first_td, &tp->last_td);
if (status != kIOReturnSuccess) {
USBLog(4, "AllocTDChain returns %d", status);
return status;
}
tp->last_td->hw.ctrlStatus |= HostToUSBLong(kUHCI_TD_IOC);
IOSync();
qh->elink = tp->first_td;
qh->qlink = NULL;
qh->hw.elink = HostToUSBLong(tp->first_td->paddr);
IOSync();
USBLog(7, "%s[%p]: dumping bulk transaction %p", getName(), this, tp);
DumpTransaction(tp, 7);
USBLog(7, "%s[%p]: activating bulk transaction %p", getName(), this, tp);
StartTransaction(tp);
return kIOReturnSuccess;
}
#pragma mark Interrupt
IOReturn
AppleUSBUHCI::UIMCreateInterruptEndpoint(
short functionNumber,
short endpointNumber,
UInt8 direction,
short speed,
UInt16 maxPacketSize,
short pollingRate,
USBDeviceAddress highSpeedHub,
int highSpeedPort)
{
return UIMCreateInterruptEndpoint(functionNumber, endpointNumber, direction, speed, maxPacketSize, pollingRate);
}
IOReturn
AppleUSBUHCI::UIMCreateInterruptEndpoint(
short functionNumber,
short endpointNumber,
UInt8 direction,
short speed,
UInt16 maxPacketSize,
short pollingRate)
{
UHCIEndpoint *ep;
QH *qh;
int i;
USBLog(7, "%s[%p]::UIMCreateInterruptEndpoint (fn %d, ep %d, dir %d) spd %d pkt %d rate %d", getName(), this,
functionNumber, endpointNumber, direction, speed,
maxPacketSize, pollingRate );
if (functionNumber == _rootFunctionNumber) {
if (endpointNumber != 0 && endpointNumber != 1) {
return kIOReturnBadArgument;
}
return RHCreateInterruptEndpoint(endpointNumber, direction, speed, maxPacketSize, pollingRate);
}
ep = FindEndpoint(functionNumber, endpointNumber, direction);
if ( ep != NULL )
{
IOReturn ret;
USBLog(3, "%s[%p]: UIMCreateInterruptEndpoint endpoint already existed -- deleting it",getName(), this);
ret = UIMDeleteEndpoint(functionNumber, endpointNumber, direction);
if ( ret != kIOReturnSuccess)
{
USBLog(3, "%s[%p]: UIMCreateInterruptEndpoint deleting endpoint returned %p",getName(), this, ret);
return ret;
}
}
else
USBLog(7, "%s[%p]: UIMCreateInterruptEndpoint endpoint does NOT exist",getName(), this);
ep = AllocEndpoint(functionNumber, endpointNumber,
direction, speed, maxPacketSize, kUSBInterrupt);
if (ep == NULL) {
return kIOReturnNoMemory;
}
ep->pollingRate = pollingRate;
for (i=kUHCI_NINTR_QHS-1; i>=0; i--) {
if ((1 << i) <= pollingRate) {
break;
}
}
if (i<0) {
i = 0;
}
USBLog(5, "%s[%p]: we will use interrupt queue %d, which corresponds to a rate of %d",
getName(), this, i, (1 << i));
ep->intr_queue = i;
ep->head_qh->elink = NULL;
ep->head_qh->qlink = NULL;
ep->head_qh->hw.elink = HostToUSBLong(kUHCI_TD_T);
qh = _intrQH[i];
ep->head_qh->hlink = qh->hlink;
ep->head_qh->hw.hlink = qh->hw.hlink;
IOSync();
qh->hlink = ep->head_qh;
qh->hw.hlink = HostToUSBLong(ep->head_qh->paddr | kUHCI_QH_Q);
IOSync();
USBLog(7, "%s[%p]::UIMCreateInterruptEndpoint done", getName(), this);
#if DEBUG
DumpFrame();
#endif
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::UIMCreateInterruptTransfer(IOUSBCommand* command)
{
UHCIEndpoint *ep;
UHCITransaction *tp;
QH *qh;
IOReturn status;
IOMemoryDescriptor *mp;
IOByteCount len;
USBLog(7, "%s[%p]::UIMCreateInterruptTransfer adr=(%d,%d) len %d rounding %d", getName(), this,
command->GetAddress(), command->GetEndpoint(), command->GetReqCount(),
command->GetBufferRounding());
if (command->GetAddress() == _rootFunctionNumber)
{
return RHCreateInterruptTransfer(command);
}
ep = FindEndpoint(command);
if (ep == NULL) {
return kIOUSBEndpointNotFound;
}
if (ep->stalled) {
USBLog(5, "%s[%p]: Interrupt pipe stalled", getName(), this);
return kIOUSBPipeStalled;
}
tp = AllocTransaction(ep);
if (tp == NULL) {
return kIOReturnNoMemory;
}
tp->command = command;
tp->buf = command->GetBuffer();
tp->bufLen = command->GetReqCount();
tp->completion = command->GetUSLCompletion();
qh = AllocQH();
if (qh == NULL) {
return kIOReturnNoMemory;
}
tp->qh = qh;
tp->nCompletions = 1;
mp = command->GetBuffer();
len = command->GetReqCount();
#define INTERRUPT_TRANSFERS_ONE_PACKET 0
#if INTERRUPT_TRANSFERS_ONE_PACKET
if ((int)len > ep->maxPacketSize) {
len = ep->maxPacketSize;
}
#endif
status = AllocTDChain(ep, mp, len, command->GetBufferRounding(), command->GetDirection(),
&tp->first_td, &tp->last_td);
if (status != kIOReturnSuccess) {
return status;
}
tp->last_td->hw.ctrlStatus |= HostToUSBLong(kUHCI_TD_IOC);
IOSync();
qh->elink = tp->first_td;
qh->qlink = NULL;
qh->hw.elink = HostToUSBLong(tp->first_td->paddr);
IOSync();
StartTransaction(tp);
return kIOReturnSuccess;
}
#pragma mark Isochronous
IOReturn
AppleUSBUHCI::UIMCreateIsochEndpoint(
short functionNumber,
short endpointNumber,
UInt32 maxPacketSize,
UInt8 direction,
USBDeviceAddress highSpeedHub,
int highSpeedPort)
{
return UIMCreateIsochEndpoint(functionNumber, endpointNumber, maxPacketSize, direction);
}
IOReturn
AppleUSBUHCI::UIMCreateIsochEndpoint(
short functionNumber,
short endpointNumber,
UInt32 maxPacketSize,
UInt8 direction)
{
int i, frame;
TD *td, *vtd;
UInt32 token;
UHCIEndpoint *ep;
USBLog(7, "%s[%p]::UIMCreateIsochEndpoint (fn %d, ep %d, dir %d) mp %d", getName(), this,
functionNumber, endpointNumber, direction, maxPacketSize);
ep = FindEndpoint(functionNumber, endpointNumber, direction);
if (ep != NULL) {
USBLog(3, "%s[%p]: the endpoint %p already exists with packet size %d.", getName(), this, ep,
ep->maxPacketSize);
if (maxPacketSize != ep->maxPacketSize) {
if (ep->buffersInUse > 0) {
if (maxPacketSize > ep->maxBufferSize) {
USBLog(3, "%s[%p]: trying to change packet size from %d to %d with %d buffers outstanding",
getName(), this, ep->maxPacketSize, maxPacketSize, ep->buffersInUse);
return kIOReturnNoBandwidth;
}
}
}
if (maxPacketSize > ep->maxPacketSize) {
if ((maxPacketSize - ep->maxPacketSize) >= _isocBandwidth) {
USBLog(3, "%s[%p]: only bandwidth available is %d, returning error",
getName(), this, _isocBandwidth);
return kIOReturnNoBandwidth;
}
_isocBandwidth -= (maxPacketSize - ep->maxPacketSize);
} else {
_isocBandwidth += (ep->maxPacketSize - maxPacketSize);
}
if (ep->buffersInUse == 0) {
if (EndpointFreeAllBuffers(ep) != kIOReturnSuccess) {
USBError(1, "%s[%p]: error attempting to free endpoint alignment buffers",
getName(), this);
return kIOReturnNoMemory;
}
ep->maxBufferSize = maxPacketSize;
}
USBLog(5, "%s[%p]: packet size adjusted from %d to %d",
getName(), this, ep->maxPacketSize, maxPacketSize);
ep->maxPacketSize = maxPacketSize;
return kIOReturnSuccess;
}
if (maxPacketSize > _isocBandwidth) {
USBLog(3, "%s[%p]: requested bandwidth %d greater than available bandwidth %d",
getName(), this, maxPacketSize, _isocBandwidth);
return kIOReturnNoBandwidth;
}
ep = AllocEndpoint(functionNumber, endpointNumber, direction, 0, maxPacketSize, kUSBIsoc);
if (ep == NULL) {
return kIOReturnNoMemory;
}
if (direction == kUSBIn) {
token = kUHCI_TD_PID_IN |
UHCI_TD_SET_MAXLEN(maxPacketSize) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
} else if (direction == kUSBOut) {
token = kUHCI_TD_PID_OUT |
UHCI_TD_SET_MAXLEN(maxPacketSize) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
} else {
USBError(1, "%s[%p]: invalid direction %d in creating isoch endpoint", getName(), this, direction);
FreeEndpoint(ep);
return kIOReturnBadArgument;
}
USBLog(3, "%s[%p]: setting maxpacket %d endpoint %d function %d token = %x",
getName(), this,
maxPacketSize, ep->endpointNumber, ep->functionNumber, token);
_isocBandwidth -= maxPacketSize;
for (i=0; i<kUHCI_NVFRAMES; i++) {
frame = i % kUHCI_NVFRAMES;
td = ep->isoc_tds[i];
if (td == NULL) {
USBError(1, "%s[%p]: NULL td in isoc frame %d", getName(), this, frame);
return kIOReturnNoMemory;
}
USBLog(7, "%s[%p]: inserting isoc td %p in frame %d", getName(), this, td, frame);
td->hw.ctrlStatus = HostToUSBLong(kUHCI_TD_ISO | UHCI_TD_SET_ERRCNT(1) | UHCI_TD_SET_ACTLEN(0));
td->hw.token = HostToUSBLong(token);
IOSync();
vtd = _vframes[frame].td;
td->link = vtd->link;
td->hw.link = vtd->hw.link;
IOSync();
vtd->link = td;
vtd->hw.link = HostToUSBLong(td->paddr);
IOSync();
}
USBLog(3, "%s[%p]: IsochEndpoint successfully created.", getName(), this);
#if DEBUG
USBLog(3, "%s[%p]: --isoc---------Dumping Frame 0:---------------", getName(), this);
DumpFrame();
#endif
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::StartIsochTransfer(
UHCITransaction *tp
)
{
IOUSBIsocFrame *pf = (IOUSBIsocFrame *)tp->isoc_frames;
IOUSBLowLatencyIsocFrame *pllf = (IOUSBLowLatencyIsocFrame *)tp->isoc_frames;
UHCIEndpoint *ep = tp->endpoint;
IOPhysicalAddress paddr;
IOByteCount offset, phys_len;
unsigned int i;
unsigned int skipCount;
UInt32 frameCount = tp->isoc_num_frames;
SInt32 frameOffset;
UInt32 frame;
UInt32 checkFrame;
#if DEBUG
UInt32 debugFrame1, debugFrame2;
#endif
UInt32 requestFrameIndex;
unsigned int len;
UInt32 status, token;
TD *td = NULL;
USBLog(6, "%s[%p]::StartIsochTransfer (tp %p)", getName(), this, tp);
if (ep->stalled) {
USBLog(6, "%s[%p]::CreateIsochTransfer on stalled endpoint", getName(), this);
}
if (ep->maxPacketSize == 0) {
return kIOReturnBadArgument;
}
frame = ReadFrameNumber();
#if DEBUG
debugFrame1 = frame;
#endif
frameOffset = tp->isoc_full_start_frame - GetFrameNumber();
if ((frameOffset + (SInt32)frameCount) <= (kUHCI_MIN_FRAME_OFFSET - (kUHCI_NVFRAMES * 10))) {
IOLog("%s[%p]: requested offset %d is too old, rejecting\n", getName(), this, (int)frameOffset); USBLog(6, "%s[%p]: requested offset %d is too old, rejecting", getName(), this, frameOffset);
return kIOReturnIsoTooOld;
}
if ((frameOffset + (SInt32)frameCount) > kUHCI_NVFRAMES) {
USBLog(6, "%s[%p]: requested offset %d is too new, rejecting", getName(), this, frameOffset);
return kIOReturnIsoTooNew;
}
if (frameOffset < kUHCI_MIN_FRAME_OFFSET) {
USBLog(2, "%s[%p]: warning, requested offset %d will result in dropped frames", getName(), this, frameOffset);
requestFrameIndex = kUHCI_MIN_FRAME_OFFSET - frameOffset;
if (requestFrameIndex > frameCount) {
requestFrameIndex = frameCount;
}
USBLog(2, "%s[%p]: UHCI: Frame offset %d < %d, skipping %d\n", getName(), this, frameOffset, kUHCI_MIN_FRAME_OFFSET, requestFrameIndex);
frameOffset += requestFrameIndex;
frameCount -= requestFrameIndex;
if (tp->isoc_low_latency) {
for (i=0; i<requestFrameIndex; i++) {
pllf[i].frStatus = kIOUSBNotSent2Err;
}
}
} else {
requestFrameIndex = 0;
}
if (frameCount > 0) {
frame = frame + frameOffset;
frame = (frame % kUHCI_NVFRAMES);
#if DEBUG
debugFrame2 = frame;
#endif
checkFrame = frame;
skipCount = 0;
for (i=0; i<frameCount; i++) {
td = ep->isoc_tds[checkFrame];
if (USBToHostLong(td->hw.ctrlStatus) & kUHCI_TD_ACTIVE) {
skipCount = i + 1;
}
if (++checkFrame >= kUHCI_NVFRAMES)
checkFrame = 0;
}
if (skipCount > 0) {
if (tp->isoc_low_latency) {
for (i=0; i < skipCount; i++) {
pllf[i + requestFrameIndex].frStatus = kIOUSBNotSent2Err;
}
}
frame = frame + skipCount;
frame = (frame % kUHCI_NVFRAMES);
frameOffset += skipCount;
frameCount -= skipCount;
}
}
tp->isoc_start_frame = frame;
tp->timeout = frameOffset + frameCount + kUHCI_NVFRAMES;
phys_len = 0;
offset = 0;
paddr = 0;
td = NULL;
tp->first_td = NULL;
for (i=0; i<frameCount; i++, requestFrameIndex++) {
td = ep->isoc_tds[frame];
if (USBToHostLong(td->hw.ctrlStatus) & kUHCI_TD_ACTIVE) {
#if DEBUG
UHCITransaction *tp2;
int count = 0;
USBError(1, "%s[%p]: wrapping %s isoch request for frame %d (%d of %d)", getName(), this, (ep->direction == kUSBIn) ? "IN" : "OUT", frame, i, frameCount);
for (i=0; i<kUHCI_NVFRAMES; i++) {
td = ep->isoc_tds[i];
if (USBToHostLong(td->hw.ctrlStatus) & kUHCI_TD_ACTIVE)
count++;
}
USBError(1, "%s[%p]: the current frame is %d", getName(), this, ReadFrameNumber());
USBError(1, "%s[%p]: there are %d/%d isoc TDs active for this endpoint", getName(), this, count, kUHCI_NVFRAMES);
count = 0;
queue_iterate(&ep->activeTransactions, tp2, UHCITransaction *, endpoint_chain) {
count++;
}
USBError(1, "%s[%p]: there are %d active transactions for this endpoint", getName(), this, count);
DumpTransaction(tp, 2);
USBError(2, "%s[%p]: conflicting transactions:", getName(), this);
queue_iterate(&_activeTransactions, tp2, UHCITransaction *, active_chain) {
if (tp != tp2 && tp2->type == kUSBIsoc && (frame >= tp2->isoc_start_frame && frame < (tp2->isoc_start_frame + tp2->isoc_num_frames))) {
DumpTransaction(tp2, 2);
}
}
#endif
return kIOReturnIsoTooNew;
}
if (tp->first_td == NULL) {
tp->first_td = td;
}
if (tp->isoc_low_latency) {
len = pllf[requestFrameIndex].frReqCount;
pllf[requestFrameIndex].frStatus = kUSBLowLatencyIsochTransferKey;
} else {
len = pf[requestFrameIndex].frReqCount;
}
paddr = tp->buf->getPhysicalSegment(offset, &phys_len);
if (phys_len < len) {
UHCIAlignmentBuffer *bp;
tp->isoc_unaligned = true;
USBLog(2, "%s[%p]: ****** Offset %d physical length %d less than transfer length %d! *****",
getName(), this, offset, (int)phys_len, len);
bp = EndpointAllocBuffer(ep);
if (bp == NULL) {
USBError(1, "%s[%p]: Could not allocate alignment buffer for isoch transaction", getName(), this);
return kIOReturnNoMemory;
}
USBLog(2, "%s[%p]: ****** using alignment buffer %p vaddr %p", getName(), this, bp, bp->vaddr);
td->buffer = bp;
bp->userBuffer = tp->buf;
bp->userOffset = offset;
bp->userAddr = NULL;
paddr = bp->paddr;
if (ep->direction != kUSBIn) {
if (tp->isoc_map == NULL) {
tp->isoc_map = tp->buf->map();
}
bp->userBuffer = NULL;
bp->userOffset = 0;
if (tp->isoc_map == NULL) {
USBLog(2, "%s[%p]: null map on unaligned isoc output buffer", getName(), this);
bzero((void *)bp->vaddr, len);
bp->userAddr = NULL;
bp->userLength = 0;
} else {
bp->userAddr = tp->isoc_map->getVirtualAddress() + offset;
bp->userLength = len;
}
USBLog(2, "%s[%p]: copying %d bytes in", getName(), this, len);
if (i < 2) {
if (bp->userAddr != NULL) {
bcopy((void *)bp->userAddr, (void *)bp->vaddr, bp->userLength);
bp->userAddr = NULL;
}
} else {
unsigned int early_frame;
TD *early_td;
early_frame = (frame - 2); early_frame = early_frame % kUHCI_NVFRAMES;
early_td = ep->isoc_tds[early_frame];
if (early_td) {
USBLog(6, "%s[%p]: setting IOC bit on frame %d", getName(), this, early_frame);
early_td->hw.ctrlStatus |= HostToUSBLong(kUHCI_TD_IOC);
IOSync();
}
}
}
}
td->hw.buffer = HostToUSBLong(paddr);
IOSync();
offset += len;
token = USBToHostLong(td->hw.token);
token &= ~kUHCI_TD_MAXLEN_MASK;
token |= UHCI_TD_SET_MAXLEN(len);
td->hw.token = HostToUSBLong(token);
IOSync();
status = kUHCI_TD_ISO |
kUHCI_TD_ACTIVE |
UHCI_TD_SET_ERRCNT(1) |
UHCI_TD_SET_ACTLEN(0);
if ((i == 0 && tp->isoc_low_latency) || (i == (frameCount - 1))) {
status |= kUHCI_TD_IOC;
USBLog(6, "%s[%p]: setting IOC bit on frame %d",
getName(), this, frame);
if (tp->isoc_low_latency) {
td->fllp = &pllf[requestFrameIndex];
}
}
td->hw.ctrlStatus = HostToUSBLong(status);
IOSync();
USBLog(6, "%s[%p]: inserting isoc td %p frame %d",
getName(), this, td, frame);
frame++;
if (frame >= kUHCI_NVFRAMES) {
frame = 0;
}
}
tp->last_td = td;
#if DEBUG
if (ReadFrameNumber() != debugFrame1) {
USBError(1, "%s[%p]: frame number changed (%d/%d) in StartIsochTransfer", getName(), this, debugFrame1, (UInt32)ReadFrameNumber());
USBError(1, "%s[%p]: started queueing %d, frameCount %d", getName(), this, debugFrame2, frameCount);
}
#endif
clock_get_uptime(&tp->timestamp);
clock_get_uptime(&tp->endpoint->timestamp);
tp->state = kUHCI_TP_STATE_ACTIVE;
queue_enter(&_activeTransactions, tp, UHCITransaction *, active_chain);
queue_enter(&tp->endpoint->activeTransactions, tp, UHCITransaction *, endpoint_chain);
if (frameCount == 0) {
tp->isoc_num_frames = 0;
_interruptSource->signalInterrupt();
}
USBLog(6, "%s[%p]: activated transaction %p", getName(), this, tp);
USBLog(6, "%s[%p]: all isoc TDs activated", getName(), this);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::CreateIsochTransfer(
short functionNumber,
short endpointNumber,
IOUSBIsocCompletion completion,
UInt8 direction,
UInt64 frameStart,
IOMemoryDescriptor * pBuffer,
UInt32 frameCount,
void * pFrames,
UInt32 updateFrequency,
bool isLowLatency)
{
IOUSBIsocFrame *pf = (IOUSBIsocFrame *)pFrames;
IOUSBLowLatencyIsocFrame *pllf = (IOUSBLowLatencyIsocFrame *)pFrames;
UHCIEndpoint *ep;
UHCITransaction *tp;
IOPhysicalAddress paddr;
IOByteCount offset, phys_len;
unsigned int i;
SInt32 frameOffset;
UInt32 frame;
UInt32 requestFrameIndex;
unsigned int len;
UInt32 status, token;
TD *td = NULL;
USBLog(6, "%s[%p]::CreateIsochTransfer (fn %d, ep %d, dir %s) frame %d count %d LL %d", getName(), this,
functionNumber, endpointNumber,
direction == kUSBIn ? "IN" : "OUT",
(int)frameStart, (int)frameCount, isLowLatency);
if (frameCount == 0) {
return kIOReturnBadArgument;
}
ep = FindEndpoint(functionNumber, endpointNumber, direction);
if (ep == NULL) {
return kIOUSBEndpointNotFound;
}
if (ep->stalled) {
USBLog(2, "%s[%p]::CreateIsochTransfer on stalled endpoint", getName(), this);
}
if (ep->maxPacketSize == 0) {
return kIOReturnBadArgument;
}
tp = AllocTransaction(ep);
if (tp == NULL) {
return kIOReturnNoMemory;
}
tp->isoc_num_frames = frameCount;
tp->isoc_frames = pFrames;
tp->isoc_low_latency = isLowLatency;
tp->isoc_map = NULL;
tp->first_td = NULL;
tp->last_td = NULL;
tp->nCompletions = 1;
tp->isoc_completion = completion;
tp->buf = pBuffer;
tp->isoc_full_start_frame = frameStart;
tp->isoc_request_received = GetFrameNumber();
IOReturn result = StartIsochTransfer(tp);
if (result != kIOReturnSuccess) {
USBLog(2, "%s[%p]:StartIsochTransfer failed", getName(), this);
FreeTransaction(tp);
}
return result;
}
IOReturn
AppleUSBUHCI::UIMCreateIsochTransfer(
short functionNumber,
short endpointNumber,
IOUSBIsocCompletion completion,
UInt8 direction,
UInt64 frameStart,
IOMemoryDescriptor * pBuffer,
UInt32 frameCount,
IOUSBIsocFrame *pFrames)
{
return CreateIsochTransfer(functionNumber, endpointNumber, completion, direction,
frameStart, pBuffer, frameCount,
(void *)pFrames, 0, false);
}
IOReturn
AppleUSBUHCI::UIMCreateIsochTransfer(
short functionNumber,
short endpointNumber,
IOUSBIsocCompletion completion,
UInt8 direction,
UInt64 frameStart,
IOMemoryDescriptor * pBuffer,
UInt32 frameCount,
IOUSBLowLatencyIsocFrame *pFrames,
UInt32 updateFrequency)
{
return CreateIsochTransfer(functionNumber, endpointNumber, completion, direction,
frameStart, pBuffer, frameCount,
(void *)pFrames, updateFrequency, true);
}
#pragma mark Endpoints
void
AppleUSBUHCI::StopEndpoint(UHCIEndpoint *ep)
{
ep->head_qh->hw.elink = HostToUSBLong(kUHCI_QH_T);
IOSync();
}
void
AppleUSBUHCI::ReturnEndpointTransactions(
UHCIEndpoint *ep,
IOReturn status)
{
UHCITransaction *tp;
queue_head_t complete;
queue_init(&complete);
StopEndpoint(ep);
IOSleep(2);
while (!queue_empty(&ep->activeTransactions)) {
tp = (UHCITransaction *)queue_first(&ep->activeTransactions);
RemoveTransaction(tp);
tp->state = kUHCI_TP_STATE_ABORTED;
queue_enter(&complete, tp, UHCITransaction *, active_chain);
}
while (!queue_empty(&complete)) {
queue_remove_first(&complete, tp, UHCITransaction *, active_chain);
USBLog(4, "%s[%p]:: returning transaction %p with status %s", getName(), this, tp, USBErrorToString(status));
CompleteTransaction(tp, status);
}
}
IOReturn
AppleUSBUHCI::UIMAbortEndpoint(
short functionNumber,
short endpointNumber,
short direction)
{
UHCIEndpoint *ep;
UHCITransaction *tp;
queue_head_t complete;
USBLog(3, "%s[%p]::UIMAbortEndpoint %d %d %d", getName(), this,
functionNumber, endpointNumber, direction);
if (functionNumber == _rootFunctionNumber) {
if (endpointNumber != 0 && endpointNumber != 1) {
return kIOReturnBadArgument;
}
return RHAbortEndpoint(endpointNumber, direction);
}
ep = FindEndpoint(functionNumber, endpointNumber, direction);
if (ep == NULL) {
return kIOUSBEndpointNotFound;
}
USBLog(5, "%s[%p]: aborting endpoint %p type %d", getName(), this, ep, ep->type);
ReturnEndpointTransactions(ep, (ep->type == kUSBIsoc) ? kIOReturnAborted : kIOUSBTransactionReturned);
ep->stalled = false;
USBLog(5, "%s[%p]: finished aborting endpoint %p", getName(), this, ep);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::UIMDeleteEndpoint(
short functionNumber,
short endpointNumber,
short direction)
{
UHCIEndpoint *ep;
int i;
USBLog(5, "%s[%p]::UIMDeleteEndpoint %d %d %d", getName(), this,
functionNumber, endpointNumber, direction);
#if DEBUG
DumpFrame(0);
#endif
if (functionNumber == _rootFunctionNumber) {
if (endpointNumber != 0 && endpointNumber != 1) {
return kIOReturnBadArgument;
}
return RHDeleteEndpoint(endpointNumber, direction);
}
ep = FindEndpoint(functionNumber, endpointNumber, direction);
if (ep == NULL) {
return kIOUSBEndpointNotFound;
}
StopEndpoint(ep);
USBLog(5, "%s[%p]: deleting endpoint %p", getName(), this, ep);
if (ep->type != kUSBIsoc) {
QH *qh, *prev_qh;
qh = ep->head_qh;
if (ep->type == kUSBInterrupt) {
prev_qh = _intrQH[ep->intr_queue];
} else {
prev_qh = _lsControlQHStart;
}
while (prev_qh != NULL) {
if (prev_qh->hlink == ep->head_qh)
break;
prev_qh = prev_qh->hlink;
}
if (prev_qh != NULL) {
USBLog(5, "%s[%p]::UIMDeleteEndpoint linking %p->hlink to %p", getName(), this, qh, ep->head_qh->hlink);
prev_qh->hlink = ep->head_qh->hlink;
prev_qh->hw.hlink= ep->head_qh->hw.hlink;
IOSync();
if (_lsControlQHEnd == qh)
_lsControlQHEnd = prev_qh;
else if (_hsControlQHEnd == qh)
_hsControlQHEnd = prev_qh;
else if (_bulkQHEnd == qh)
_bulkQHEnd = prev_qh;
} else {
USBError(1, "%s[%p]::UIMDeleteEndpoint couldn't find previous QH for endpoint %p", getName(), this, ep);
}
}
ReturnEndpointTransactions(ep, kIOUSBTransactionReturned);
if (ep->type == kUSBIsoc) {
TD *td, *vtd;
USBLog(3, "%s[%p]: deleting isoc TDs from frame list", getName(), this);
for (i=0; i<kUHCI_NVFRAMES; i++) {
ep->isoc_tds[i]->fllp = NULL;
ep->isoc_tds[i]->hw.ctrlStatus &= HostToUSBLong(~(kUHCI_TD_ACTIVE|kUHCI_TD_IOC));
IOSync();
}
IOSleep(2);
for (i=0; i<kUHCI_NVFRAMES; i++) {
td = ep->isoc_tds[i];
for (vtd = _vframes[i].td; vtd != NULL && vtd->link != td; vtd = vtd->link) {
}
if (vtd == NULL) {
USBError(1, "%s[%p]: frame %d TD %p not found when deleting isoc endpoint %p",
getName(), this, i, td, ep);
#if DEBUG
if (i == 0) {
DumpFrame(0);
}
#endif
continue;
}
USBLog(7, "%s[%p]: removing isoc td %p in frame %d", getName(), this, td, i);
vtd->link = td->link;
vtd->hw.link = td->hw.link;
IOSync();
}
_isocBandwidth += ep->maxPacketSize;
}
queue_remove(&_endpoints, ep, UHCIEndpoint *, chain);
FreeEndpoint(ep);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::UIMClearEndpointStall(
short functionNumber,
short endpointNumber,
short direction)
{
UHCIEndpoint *ep;
USBLog(5, "%s[%p]::UIMClearEndpointStall %d %d %d", getName(), this,
functionNumber, endpointNumber, direction);
ep = FindEndpoint(functionNumber, endpointNumber, direction);
if (ep == NULL) {
return kIOUSBEndpointNotFound;
}
ReturnEndpointTransactions(ep, kIOUSBTransactionReturned);
ep->stalled = false;
ep->lastDBit = true;
#if DEBUG
USBLog(7, "%s[%p]::clearing toggle on all (%d, %d, %d)", getName(), this, functionNumber, endpointNumber, direction);
queue_iterate(&_endpoints, ep, UHCIEndpoint *, chain) {
if (ep->functionNumber == functionNumber) {
USBLog(5, "%s[%p]: endpoint %p (%d, %d, %d) getting its D bit reset", getName(), this, ep,
ep->functionNumber, ep->endpointNumber, ep->direction);
ep->lastDBit = true;
}
}
#endif
USBLog(5, "%s[%p]::UIMClearEndpointStall done ep %p", getName(), this, ep);
return kIOReturnSuccess;
}
UHCIEndpoint *
AppleUSBUHCI::FindEndpoint(IOUSBCommand *command)
{
short functionNumber, endpointNumber;
UInt8 direction;
functionNumber = command->GetAddress();
endpointNumber = command->GetEndpoint();
direction = command->GetDirection();
return FindEndpoint(functionNumber, endpointNumber, direction);
}
UHCIEndpoint *
AppleUSBUHCI::FindEndpoint(short functionNumber,
short endpointNumber,
UInt8 direction)
{
UHCIEndpoint *ep;
USBLog(7, "%s[%p]::FindEndpoint(%d, %d, %d)", getName(), this, functionNumber, endpointNumber, direction);
queue_iterate(&_endpoints, ep, UHCIEndpoint *, chain) {
if (ep->functionNumber == functionNumber
&& ep->endpointNumber == endpointNumber
&& ((direction == kUSBNone && direction != kUSBOut) ||
(direction == kUSBAnyDirn) ||
ep->direction == direction)
) {
USBLog(7, "%s[%p]: endpoint %p found", getName(), this, ep);
return ep;
}
}
USBLog(7, "%s[%p]: endpoint not found", getName(), this);
return NULL;
}
#pragma mark Transaction starting and completing
void AppleUSBUHCI::UIMCheckForTimeouts(void)
{
AbsoluteTime currentTime, t;
UInt64 elapsedTime;
UHCITransaction *tp;
queue_chain_t save;
queue_head_t complete;
UInt64 frameNumber;
UInt16 status;
int completed;
if (isInactive() || _powerLevel != kUHCIPowerLevelRunning) {
return;
}
USBLog(6, "%s[%p]: UIMCheckForTimeouts", getName(), this);
completed = ProcessCompletedTransactions();
if (completed > 0) {
USBLog(2, "%s[%p]: processed %d completed transactions in UIMCheckForTimeouts", getName(), this, completed);
}
clock_get_uptime(¤tTime);
status = ioRead16(kUHCI_STS);
if (status & kUHCI_STS_HCH) {
ioWrite16(kUHCI_STS, kUHCI_STS_HCH);
USBError(1, "%s[%p]: Host controller halted, resetting", getName(), this);
Reset(true);
Run(true);
}
frameNumber = GetFrameNumber();
#if DEBUG
t = currentTime;
SUB_ABSOLUTETIME(&t, &_lastFrameNumberTime);
absolutetime_to_nanoseconds(t, &elapsedTime);
if (frameNumber == _lastTimeoutFrameNumber && elapsedTime > NANOSECOND_TO_MILLISECOND) {
USBError(1, "%s[%p]: host controller frame number halted, resetting", getName(), this);
Reset(true);
Run(true);
}
#endif
_lastTimeoutFrameNumber = frameNumber;
_lastFrameNumberTime = currentTime;
queue_init(&complete);
for (tp = (UHCITransaction *)queue_first(&_activeTransactions), save = tp->active_chain;
!queue_end(&_activeTransactions, (queue_entry_t)tp);
tp = (UHCITransaction *)queue_next(&save), save = tp->active_chain) {
if (tp->timeout == 0) {
continue;
}
if (tp->type != kUSBControl && tp->type != kUSBBulk && tp->type != kUSBIsoc) {
continue;
}
t = currentTime;
SUB_ABSOLUTETIME(&t, &tp->timestamp);
absolutetime_to_nanoseconds(t, &elapsedTime);
elapsedTime /= NANOSECOND_TO_MILLISECOND;
if (elapsedTime > tp->timeout) {
USBLog(4, "%s[%p]: stale transaction %p", getName(), this, tp);
RemoveTransaction(tp);
tp->state = kUHCI_TP_STATE_ABORTED;
queue_enter(&complete, tp, UHCITransaction *, active_chain);
#if DEBUG
if (tp->type == kUSBIsoc) {
static int dump_count = 0;
USBLog(4, "%s[%p]: warning: timing out isoc transaction %p timeout %d", getName(), this, tp, tp->timeout);
USBLog(4, "%s[%p]: tp was queued at %d for frame %d(%d/%d), current frame is %d(%d)", getName(), this,
(UInt32)tp->isoc_request_received,
(UInt32)tp->isoc_full_start_frame, (UInt32)tp->isoc_full_start_frame % kUHCI_NVFRAMES, tp->isoc_start_frame,
(UInt32)GetFrameNumber(), (UInt32)GetFrameNumber() % kUHCI_NVFRAMES);
if (dump_count < 10) {
dump_count++;
DumpFrame(tp->isoc_start_frame, 4);
DumpTransaction(tp, 4);
}
}
#endif
}
}
while (!queue_empty(&complete)) {
queue_remove_first(&complete, tp, UHCITransaction *, active_chain);
if (tp->type == kUSBBulk) {
tp->endpoint->lastDBit = !tp->endpoint->lastDBit;
}
USBLog(4, "%s[%p]: timing out transaction %p", getName(), this, tp);
DumpTransaction(tp, 2);
CompleteTransaction(tp, kIOUSBTransactionTimeout);
}
t = currentTime;
SUB_ABSOLUTETIME(&t, &_lastTime);
absolutetime_to_nanoseconds(t, &elapsedTime);
elapsedTime /= NANOSECOND_TO_MILLISECOND;
if (elapsedTime > kUHCICheckForRootHubConnectionsPeriod) {
_lastTime = currentTime;
if (_powerLevel != kUHCIPowerLevelIdleSuspend) {
USBLog(5, "%s[%p]: checking root hub for connections", getName(), this);
if (RHAreAllPortsDisconnected()) {
t = currentTime;
SUB_ABSOLUTETIME(&t, &_rhChangeTime);
absolutetime_to_nanoseconds(t, &elapsedTime);
elapsedTime /= NANOSECOND_TO_MILLISECOND;
if (elapsedTime >= kUHCICheckForRootHubInactivityPeriod) {
USBLog(5,"%s[%p] Suspending idle root hub", getName(), this);
setPowerState( kUHCIPowerLevelIdleSuspend, this);
}
}
}
}
}
IOReturn
AppleUSBUHCI::TDToUSBError(UInt32 status)
{
IOReturn result;
status &= kUHCI_TD_ERROR_MASK;
if (status == 0) {
result = kIOReturnSuccess;
} else if (status & kUHCI_TD_CRCTO) {
result = kIOReturnNotResponding;
} else if (status & kUHCI_TD_BABBLE) {
result = kIOReturnOverrun;
} else if (status & kUHCI_TD_STALLED) {
result = kIOUSBPipeStalled;
} else if (status & kUHCI_TD_DBUF) {
result = kIOReturnOverrun;
} else if (status & kUHCI_TD_CRCTO) {
result = kIOUSBCRCErr;
} else if (status & kUHCI_TD_BITSTUFF) {
result = kIOUSBBitstufErr;
} else {
result = kIOUSBTransactionReturned;
}
return result;
}
void
AppleUSBUHCI::StartTransaction(UHCITransaction *tp)
{
UHCIEndpoint *ep;
UHCITransaction *last_tp;
USBLog(7, "%s[%p]::StartTransaction %p", getName(), this, tp);
ep = tp->endpoint;
assert(ep != NULL);
if (queue_empty(&ep->activeTransactions)) {
last_tp = NULL;
USBLog(7, "%s[%p]::StartTransaction queue is empty", getName(), this);
} else {
last_tp = (UHCITransaction *)queue_last(&ep->activeTransactions);
USBLog(7, "%s[%p]::StartTransaction queue has transactions, last_tp = %p", getName(), this, last_tp);
}
tp->qh->hlink = ep->head_qh->hlink;
tp->qh->hw.hlink = ep->head_qh->hw.hlink;
IOSync();
tp->last_td->link = NULL;
tp->last_td->hw.link = HostToUSBLong(kUHCI_TD_T);
IOSync();
if (last_tp == NULL) {
ep->head_qh->qlink = tp->qh;
ep->head_qh->elink = NULL;
ep->head_qh->hw.elink = HostToUSBLong(tp->qh->paddr | kUHCI_QH_Q);
IOSync();
} else {
last_tp->last_td->hw.link = HostToUSBLong(tp->qh->paddr | kUHCI_TD_Q);
IOSync();
#if 0 // XXX depend on transaction completion to take care of this
if (USBToHostLong(last_tp->qh->hw.elink) & kUHCI_QH_T) {
USBLog(7, "%s[%p]::StartTransaction XXX restarting previous qh %p", getName(), this, last_tp->qh);
last_tp->qh->hw.elink = HostToUSBLong(tp->qh->paddr | kUHCI_TD_Q);
IOSync();
}
#endif
}
clock_get_uptime(&tp->timestamp);
clock_get_uptime(&tp->endpoint->timestamp);
tp->state = kUHCI_TP_STATE_ACTIVE;
queue_enter(&_activeTransactions, tp, UHCITransaction *, active_chain);
queue_enter(&tp->endpoint->activeTransactions, tp, UHCITransaction *, endpoint_chain);
DumpEndpoint(ep, 7);
USBLog(7, "%s[%p]::StartTransaction %p done", getName(), this, tp);
}
void
AppleUSBUHCI::RemoveTransaction(UHCITransaction *tp)
{
UHCIEndpoint *ep;
UHCITransaction *last_tp, *next_tp;
TD *td;
USBLog(7, "%s[%p]::RemoveTransaction %p", getName(), this, tp);
ep = tp->endpoint;
DumpEndpoint(ep, 7);
if (tp == (UHCITransaction *)queue_first(&ep->activeTransactions)) {
last_tp = NULL;
assert(tp == (UHCITransaction *)queue_prev(&tp->endpoint_chain));
} else {
last_tp = (UHCITransaction *)queue_prev(&tp->endpoint_chain);
}
next_tp = (UHCITransaction *)queue_next(&tp->endpoint_chain);
if (next_tp == tp) {
next_tp = NULL;
}
USBLog(7, "%s[%p]::RemoveTransaction last_tp = %p", getName(), this, last_tp);
queue_remove(&ep->activeTransactions, tp, UHCITransaction *, endpoint_chain);
queue_remove(&_activeTransactions, tp, UHCITransaction *, active_chain);
if (tp->type == kUSBIsoc) {
TD **isoc_tds;
unsigned int i, frame;
USBLog(7, "%s[%p]: disabling ACTIVE on isoc tp %p", getName(), this, tp);
isoc_tds = ep->isoc_tds;
frame = tp->isoc_start_frame;
for (i=0; i < tp->isoc_num_frames; i++) {
td = isoc_tds[frame];
td->fllp = NULL;
td->hw.ctrlStatus &= HostToUSBLong(~(kUHCI_TD_IOC | kUHCI_TD_ACTIVE));
IOSync();
frame++;
if (frame >= kUHCI_NVFRAMES) {
frame = 0;
}
}
} else {
if (last_tp == NULL) {
USBLog(7, "%s[%p]:: setting qlink %p, elink %p, hw.elink %p", getName(), this,
tp->qh->qlink, tp->qh->elink, tp->last_td->hw.link);
ep->head_qh->elink = NULL;
ep->head_qh->hw.elink = tp->last_td->hw.link;
} else {
last_tp->last_td->hw.link = tp->last_td->hw.link;
IOSync();
}
if (next_tp == NULL) {
ep->head_qh->hw.elink = HostToUSBLong(kUHCI_QH_T);
} else {
if (ep->head_qh->hw.elink == HostToUSBLong(tp->qh->paddr)) {
ep->head_qh->hw.elink = tp->last_td->hw.link;
}
}
IOSync();
tp->qh->hw.elink = HostToUSBLong(kUHCI_QH_T);
assert(tp->last_td != NULL);
tp->last_td->hw.link = HostToUSBLong(kUHCI_TD_T);
IOSync();
}
DumpEndpoint(ep, 7);
USBLog(7, "%s[%p]::RemoveTransaction %p done", getName(), this, tp);
}
void
AppleUSBUHCI::CompleteIsoc(IOUSBIsocCompletion completion,
IOReturn status,
void * pFrames)
{
IOUSBIsocCompletionAction action;
USBLog(6, "%s[%p]::CompleteIsoc status %d (%s) parameter %x pframes %p", getName(), this,
status, USBErrorToString(status), completion.parameter, pFrames);
if (completion.action) {
action = completion.action;
completion.action = NULL;
(*action)(completion.target,
completion.parameter,
status,
(IOUSBIsocFrame *)pFrames);
} else {
USBLog(2, "%s[%p]:CompleteIsoc has no action!!", getName(), this);
}
}
void
AppleUSBUHCI::CompleteTransaction(UHCITransaction *tp, IOReturn returnCode )
{
UHCIEndpoint *ep;
IOReturn result, isoc_result;
unsigned int i, count, frame;
UInt32 length, total_length, req_length;
AbsoluteTime currentTime, delta;
TD *td;
UInt32 status, td_status, token, pid;
IOUSBIsocFrame *fp = (IOUSBIsocFrame *)tp->isoc_frames;
IOUSBLowLatencyIsocFrame *fllp = (IOUSBLowLatencyIsocFrame *)tp->isoc_frames;
bool lowLatency = tp->isoc_low_latency;
UHCIAlignmentBuffer *bp;
bool needsReset = false;
USBLog(7, "%s[%p]::CompleteTransaction(%p, %d)", getName(), this, tp, returnCode);
DumpTransaction(tp, 7);
clock_get_uptime (¤tTime);
tp->endpoint->timestamp = currentTime;
ep = tp->endpoint;
isoc_result = kIOReturnSuccess;
total_length = 0;
status = 0;
i = 0;
if (tp->type == kUSBIsoc) {
TD **isoc_tds = ep->isoc_tds;
nanoseconds_to_absolutetime(NANOSECOND_TO_MILLISECOND, &delta);
for (i=0, frame = tp->isoc_start_frame; i<tp->isoc_num_frames; i++) {
td = isoc_tds[frame];
status = USBToHostLong(td->hw.ctrlStatus);
token = USBToHostLong(td->hw.token);
length = UHCI_TD_GET_ACTLEN(status);
USBLog(6, "%s[%p]: first isoc td %p frame %d length %d", getName(), this, td, frame, length);
if (lowLatency) {
req_length = fllp[i].frReqCount;
} else {
req_length = fp[i].frReqCount;
}
assert(req_length > 0 && td->hw.buffer != 0);
pid = UHCI_TD_GET_PID(token);
bp = td->buffer;
if (bp != NULL && bp->userBuffer != NULL && pid == kUHCI_TD_PID_IN) {
USBLog(6, "%s[%p]: writing %d bytes to alignment buffer at %p", getName(), this, length, bp->vaddr);
bp->userBuffer->writeBytes(bp->userOffset, (const void *)bp->vaddr, length);
}
#if DEBUG
if (bp != NULL && pid == kUHCI_TD_PID_OUT) {
if (bp->userAddr != 0) {
USBLog(2, "%s[%p]: isoc data out for frame %d wasn't copied at interrupt time", getName(), this, frame);
}
}
#endif
if (bp != NULL) {
EndpointFreeBuffer(ep, bp);
td->buffer = NULL;
}
result = TDToUSBError(status);
if (result == kIOReturnSuccess) {
if ((status & kUHCI_TD_ACTIVE) || (length == 0)) {
result = kIOUSBNotSent2Err;
} else if ((ep->direction == kUSBIn) && (length < req_length)) {
result = kIOReturnUnderrun;
}
}
if (lowLatency) {
fllp[i].frStatus = result;
if (result == kIOReturnSuccess) {
fllp[i].frActCount = fllp[i].frReqCount;
} else {
fllp[i].frActCount = length;
}
if (i == 0) {
if (fllp[i].frStatus == kUSBLowLatencyIsochTransferKey) {
USBLog(2, "%s[%p]: XXX timestamp missing on first frame (%d) of %s isoc trans %p",
getName(), this, frame,
(pid == kUHCI_TD_PID_IN ? "IN" : "OUT"), tp);
UInt64 t;
absolutetime_to_nanoseconds(currentTime, &t);
t -= (NANOSECOND_TO_MILLISECOND * (tp->isoc_num_frames - 1));
nanoseconds_to_absolutetime(t, ¤tTime);
fllp[i].frTimeStamp = currentTime;
} else {
delta = currentTime;
SUB_ABSOLUTETIME(&delta, &fllp[i].frTimeStamp);
if (tp->isoc_num_frames > 2) {
AbsoluteTime_to_scalar(&delta) = AbsoluteTime_to_scalar(&delta) / (UInt64)(tp->isoc_num_frames - 1);
}
USBLog(6, "%s[%p]: %d microseconds between frames",
getName(), this, (UInt32)(AbsoluteTime_to_scalar(&delta) / 1000ULL));
currentTime = fllp[i].frTimeStamp;
}
} else {
fllp[i].frTimeStamp = currentTime;
}
ADD_ABSOLUTETIME(¤tTime, &delta);
} else {
fp[i].frStatus = result;
fp[i].frActCount = length;
}
if (result != kIOReturnSuccess) {
if (result != kIOReturnUnderrun) {
isoc_result = result;
} else if (isoc_result == kIOReturnSuccess) {
isoc_result = kIOReturnUnderrun;
}
}
USBLog(6, "%s[%p]: isoc td %p length %d, req_length %d, result %d", getName(), this,
td, length, req_length, result);
if (result != kIOReturnSuccess && result != kIOReturnUnderrun) {
DumpTD(td, 6);
}
frame++;
if (frame >= kUHCI_NVFRAMES) {
frame = 0;
}
}
if (tp->isoc_map != NULL) {
tp->isoc_map->release();
tp->isoc_map = NULL;
}
} else {
TD *last_dbit_td = NULL;
bool d_bit_of_last_td;
for (td = tp->first_td; td; td = td->link) {
td_status = USBToHostLong(td->hw.ctrlStatus);
token = USBToHostLong(td->hw.token);
USBLog(7, "Checking TD %p", td);
bp = td->buffer;
if ((_errataBits & kUHCIResetAfterBabble) != 0 && (td_status & kUHCI_TD_BABBLE) != 0)
needsReset = true;
assert((UHCI_TD_GET_MAXLEN(td_status) == 0 && td->hw.buffer == 0) ||
(UHCI_TD_GET_MAXLEN(td_status) > 0 && td->hw.buffer != 0));
if (td_status & kUHCI_TD_ACTIVE) {
} else {
status = td_status;
pid = UHCI_TD_GET_PID(token);
if (pid != kUHCI_TD_PID_SETUP) {
length = UHCI_TD_GET_ACTLEN(status);
if (bp != NULL && bp->userBuffer != NULL && pid == kUHCI_TD_PID_IN) {
USBLog(5, "%s[%p]: writing to alignment buffer at %p", getName(), this, bp->vaddr);
bp->userBuffer->writeBytes(bp->userOffset, (const void *)bp->vaddr, length);
}
if ((status & kUHCI_TD_ERROR_MASK) == 0 &&
UHCI_TD_GET_MAXLEN(token) > 0) {
last_dbit_td = td;
}
} else {
length = 0;
}
total_length += length;
}
if (bp != NULL) {
EndpointFreeBuffer(ep, bp);
td->buffer = NULL;
}
if (td == tp->last_td) {
break;
}
}
if (last_dbit_td && last_dbit_td != tp->last_td) {
ep->lastDBit = (USBToHostLong(last_dbit_td->hw.token) & kUHCI_TD_D) ? true : false;
FixEndpointDBits(ep);
}
}
if (returnCode != kIOReturnSuccess) {
result = returnCode;
} else if (tp->type == kUSBIsoc) {
result = isoc_result;
} else {
result = TDToUSBError(status);
}
if (status & kUHCI_TD_STALLED) {
USBLog(5, "%s[%p]: tp %p result makes endpoint stalled", getName(), this, tp);
tp->endpoint->stalled = true;
}
USBLog(7, "%s[%p]: tp %p final result %d (%s), bufLen %d, length %d", getName(), this,
tp, result, USBErrorToString(result), tp->bufLen, total_length);
if (result != kIOReturnSuccess && result != kIOReturnUnderrun) {
DumpTransaction(tp, 6);
}
count = tp->nCompletions;
for (i=0; i<count; i++) {
if (tp->type == kUSBIsoc) {
CompleteIsoc(tp->isoc_completion, result, tp->isoc_frames);
} else {
USBLog(6, "%s[%p]: Calling %d Complete(x, 0x%x (%s), %d) tp %p", getName(), this,
i, result, USBErrorToString(result), (tp->bufLen - total_length), tp);
Complete(tp->completion, result, tp->bufLen - total_length);
#if DEBUG
DumpFrame(ReadFrameNumber() % kUHCI_NVFRAMES );
#endif
}
}
FreeTransaction(tp);
if (needsReset) {
USBError(1, "%s[%p]: Resetting controller due to errors in transaction", getName(), this);
Reset(true);
Run(true);
}
}
void
AppleUSBUHCI::FixEndpointDBits(UHCIEndpoint *ep)
{
bool d_bit;
UHCITransaction *tp;
TD *td;
UInt32 token;
if (ep->type != kUSBBulk && ep->type != kUSBInterrupt) {
return;
}
d_bit = !ep->lastDBit;
queue_iterate(&ep->activeTransactions, tp, UHCITransaction *, endpoint_chain) {
for (td = tp->first_td; td != NULL; td = td->link) {
token = td->hw.token;
if (d_bit)
token |= HostToUSBLong(kUHCI_TD_D);
else
token &= ~HostToUSBLong(kUHCI_TD_D);
td->hw.token = token;
d_bit = !d_bit;
}
IOSync();
}
ep->lastDBit = !d_bit;
}
#pragma mark Transaction descriptors
IOReturn
AppleUSBUHCI::AllocTDChain(UHCIEndpoint *ep,
IOMemoryDescriptor *mp,
IOByteCount len,
bool shortOK,
short direction,
TD **start_p, TD **end_p,
bool setVFlag,
bool isControlTransfer)
{
TD *td = NULL, *prev_td;
UInt32 status;
IOByteCount pkt_len;
IOPhysicalAddress paddr;
IOByteCount offset, phys_len;
bool d_bit;
UInt32 token;
USBLog(7, "%s[%p]::AllocTDChain mp %p len %d short %d dir %d pktsize %d", getName(), this,
mp, len, shortOK, direction, ep->maxPacketSize);
d_bit = ep->lastDBit;
status = kUHCI_TD_ACTIVE | UHCI_TD_SET_ERRCNT(3) | UHCI_TD_SET_ACTLEN(0);
if (ep->speed == kUSBDeviceSpeedLow) {
status |= kUHCI_TD_LS;
}
if (shortOK) {
}
prev_td = NULL;
offset = 0;
if (isControlTransfer && len == 0) {
pkt_len = 0;
td = AllocTD();
if (td == NULL) {
return kIOReturnNoMemory;
}
td->hw.ctrlStatus = HostToUSBLong(status);
IOSync();
if (direction == kUSBIn) {
token = kUHCI_TD_PID_IN |
UHCI_TD_SET_MAXLEN(pkt_len) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
token |= kUHCI_TD_D;
} else if (direction == kUSBOut) {
token = kUHCI_TD_PID_OUT |
UHCI_TD_SET_MAXLEN(pkt_len) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
token |= kUHCI_TD_D;
} else {
token = kUHCI_TD_PID_SETUP |
UHCI_TD_SET_MAXLEN(pkt_len) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
d_bit = 1;
}
td->hw.token = HostToUSBLong(token);
td->hw.buffer = 0;
IOSync();
*start_p = td;
} else {
pkt_len = ep->maxPacketSize;
do {
if (len > 0) {
paddr = mp->getPhysicalSegment(offset, &phys_len);
if (paddr == 0 || phys_len == 0) {
USBError(1, "%s[%p]: bad physical memory when allocating TDs", getName(), this);
return kIOReturnNoMemory;
}
USBLog(7, "%s[%p]: physical segment at offset %d = %p len %d",
getName(), this, offset, paddr, phys_len);
if (phys_len > len) {
phys_len = len;
}
} else {
paddr = 0;
phys_len = 0;
}
if (len < pkt_len) {
pkt_len = len;
}
td = AllocTD();
if (td == NULL) {
return kIOReturnNoMemory;
}
if (len > 0 && (phys_len < len)) {
UHCIAlignmentBuffer *bp;
bp = EndpointAllocBuffer(ep);
USBLog(7, "%s[%p]: using alignment buffer %p at paddr %p instead of %p", getName(), this, bp->vaddr, bp->paddr, paddr);
td->buffer = bp;
td->hw.buffer = HostToUSBLong(bp->paddr);
if (direction != kUSBIn) {
mp->readBytes(offset, (void *)bp->vaddr, pkt_len);
}
bp->userBuffer = mp;
bp->userOffset = offset;
bp->userAddr = NULL;
} else {
td->hw.buffer = HostToUSBLong(paddr);
}
IOSync();
if (prev_td == NULL) {
*start_p = td;
} else {
if (setVFlag) {
prev_td->hw.link = HostToUSBLong(td->paddr | kUHCI_TD_VF);
} else {
prev_td->hw.link = HostToUSBLong(td->paddr);
}
IOSync();
prev_td->link = td;
}
if (direction == kUSBIn) {
if (ep->type == kUSBIsoc) {
d_bit = false;
} else {
d_bit = !d_bit;
}
token = kUHCI_TD_PID_IN |
UHCI_TD_SET_MAXLEN(pkt_len) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
status |= HostToUSBLong(kUHCI_TD_SPD);
if (d_bit) {
token |= kUHCI_TD_D;
}
} else if (direction == kUSBOut) {
if (ep->type == kUSBIsoc) {
d_bit = false;
} else {
d_bit = !d_bit;
}
token = kUHCI_TD_PID_OUT |
UHCI_TD_SET_MAXLEN(pkt_len) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
if (d_bit) {
token |= kUHCI_TD_D;
}
} else {
token = kUHCI_TD_PID_SETUP |
UHCI_TD_SET_MAXLEN(pkt_len) |
UHCI_TD_SET_ENDPT(ep->endpointNumber) |
UHCI_TD_SET_ADDR(ep->functionNumber);
d_bit = 0;
}
td->hw.token = HostToUSBLong(token);
td->hw.ctrlStatus = HostToUSBLong(status);
USBLog(7, "%s[%p]: TD paddr %p len 0x%x", getName(), this, paddr, pkt_len);
prev_td = td;
paddr += pkt_len;
phys_len -= pkt_len;
len -= pkt_len;
offset += pkt_len;
} while (len > 0);
}
*end_p = td;
td->hw.link = HostToUSBLong(kUHCI_TD_T);
IOSync();
td->link = NULL;
ep->lastDBit = d_bit;
USBLog(7, "%s[%p]: AllocTDChain finished", getName(), this);
return kIOReturnSuccess;
}
void
AppleUSBUHCI::FreeTDChain(TD *td)
{
TD *next;
while (td != NULL) {
next = td->link;
FreeTD(td);
td = next;
}
}