Windows CE USB Function Driver驱动简析(2)-IST函数(基于WinCE5.0 SMDK2410 BSP的UFBFN驱动)
2010-08-10 15:49
267 查看
上一篇我们简单分析了UFN驱动中的Ufn_pdd函数,现在我们来看看IST主线程ISTMain函数及相关处理函数.
1.ISTMain
ISTMain在UfnPdd_Start被创建:
pContext->hIST = CreateThread(NULL, 0, ISTMain, pContext, 0, NULL);
ISTMain在参数检查后,设置优先级后,然后进入while循环,循环条件为fExitIST为FALSE,即不退出IST.
循环内禁止所有端点中断,然后使能USB reset中断,使能端点0中断.然后等待中断事件hevInterrupt发生.发生后调用HandleUSBEvent进行中断处理.当退出线程fExitIST或重启线程fRestartIST为TRUE时,在禁止中断和清除中断状态后退出线程.并通知MDD层DETACH事件.
2.HandleUSBEvent
当UDC中断发生时就会调用HandleUSBEvent来处理.
首先读取INT_REG和USB_INT_REG寄存器分别获取EP中断状态和USB中断状态(RESET or RESUME or SUSPEND)
BYTE bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET);
BYTE bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET);
然后根据具体的中断状态调用相应的处理函数,如USBbus中断调用HandleUSBBusIrq
if (bUSBBusIrqStat) {
RETAILMSG(1, (_T("%s USB_INT_REG = 0x%02x/r/n"),
pszFname, bUSBBusIrqStat));
HandleUSBBusIrq(pContext, bUSBBusIrqStat);
}
EP中断根据是EP0中断还是EP1-4中断分别调用HandleEndpoint0Event和HandleEndpointEvent.
if (bEpIrqStat & EP0_INT_INTR) {
HandleEndpoint0Event(pContext);
}
for (DWORD dwEndpoint = 1; dwEndpoint < ENDPOINT_COUNT; ++dwEndpoint) {
BYTE bEpBit = EpToIrqStatBit(dwEndpoint);
if (bEpIrqStat & bEpBit) {
HandleEndpointEvent(pContext, dwEndpoint, bEpIrqStat);
}
}
然后继续读取INT_REG和USB_INT_REG寄存器获取中断状态,如果还有中断则继续调用上面的流程进行处理.
完整代码如下:
3.HandleUSBBusIrq
HandleUSBBusIrq用来处理USBbus中段(RESET or RESUME or SUSPEND).实际上只处理了RESET中断.
如果是RESET中断,清除中断标志,设置fSpeedReported为false;
如果是UFN_DETACH状态,则通知MDD层UFN_ATTACH事件.
接着设置EP0状态为EP0_STATE_IDLE,并通知MDD层UFN_RESET事件.
4.HandleEndpoint0Event
当EP0中断发生时,被会调用HandleEndpoint0Event.
首先进行参数检查,获取EP0状态结构PEP_STATUS保存到peps.然后清除EP0中断状态.然后设置INDEX_REG为EP0读取EP0_CSR.
当中断状态为SETUP_END配置结束时,写EP0_CSR寄存器清除SETUP_END,将要写的值先保存在bEp0CsrToWrite中,最后一起写入.同时设置EP0状态为IDLE.
接着获取EP0状态结构的PSTransfer,如果存在,调用CompleteTransfer通知MDD UFN_MSG_TRANSFER_COMPLETE事件.
if (bEP0IrqStatus & SETUP_END) {
bEp0CsrToWrite |= SERVICED_SETUP_END;
pContext->Ep0State = EP0_STATE_IDLE;
PSTransfer pTransfer = peps->pTransfer;
// Make MDD think everything is ok.
if(pTransfer) {
pContext->sendDataEnd = FALSE;
CompleteTransfer(pContext, peps, UFN_NO_ERROR);
}
}
如果是SENT_STALL状态,即协议冲突发生时,设置Ep0State为EP0_STATE_IDLE,交由下面的代码继续处理.
if (bEP0IrqStatus & EP0_SENT_STALL) {
pContext->Ep0State = EP0_STATE_IDLE;
}
接着读取OUT_FIFO_CNT1寄存器,该寄存器保存了写出数据的字节数低位.
DWORD cbFifo = ReadIndexedReg(pContext, 0, OUT_FIFO_CNT1_REG_OFFSET);
当Ep0State状态为EP0_STATE_IDLE时,判断bEP0IrqStatus是否是EP0_OUT_PACKET_RDY(写数据包准备好),如果未通知MDD速度则发出FULL SPEED的通知.
然后准备发送配置包,读取要发送包的字节数,获得USB Device Request指针:
const DWORD cbOutFifo = ReadIndexedReg(pContext, 0,
OUT_FIFO_CNT1_REG_OFFSET);
PBYTE pbUdr = (PBYTE) &pContext->udr;
读取FIFO数据(EP0_FIFO_REG)到pbUdr
volatile ULONG *pulFifoReg = _GetDataRegister(0);
DWORD cbBytesRemaining = cbOutFifo;
while (cbBytesRemaining--) {
*pbUdr++ = (BYTE) *pulFifoReg;
}
如果数据大小不匹配,设置bEp0CsrToWrite,清除相应状态标志.
if (cbOutFifo != sizeof(pContext->udr)) {
// Ideally this should not hapen. This is a recovery mechanism if
// we get out of sync somehow.
bEp0CsrToWrite |= (EP0_SEND_STALL | SERVICED_OUT_PKT_RDY |
DATA_END);
}
否则对读到的数据进行解析,如果数据长度大于0,获得传输方向:
if (pContext->udr.bmRequestType & USB_ENDPOINT_DIRECTION_MASK) {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_IN_DATA_PHASE;
}
else {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_OUT_DATA_PHASE;
}
如果数据长度为0,设置sendDataEnd为TRUE表示数据传输完成.
接下来根据上面获得是发出数据还是接收数据状态,调用不同的处理流程.
如果是接收数据(EP0_STATE_OUT_DATA_PHASE),并且OUT_PACKET_READY数据准备好,调用HandleRx:
bEp0CsrToWrite |= HandleRx(pContext, peps, &fCompleted, &dwStatus);
否则设置状态为EP0_STATE_IDLE.
如果是发送数据(EP0_STATE_IN_DATA_PHASE),当OUT_PACKET_READY和cbFifo为0(控制传输结束)或者IN_PACKET_READY,调用HandleTx:
if (bHandleTx) {
bEp0CsrToWrite |= HandleTx(pContext, peps, 0);
}
最后然后清除中断状态,调用CompleteTransfer置pTransfer为NULL,并如果通知MDD UFN_MSG_TRANSFER_COMPLETE事件.
如果向UDR发送数据(fSendUdr为TRUE)通知MDD UFN_MSG_SETUP_PACKET事件.
5.HandleRx
HandleRx用来读取端点数据.
首先获取端点号dwEndpoint,传输结构pTransfer,如果为TRUE,则验证传输方向,然后获得端点FIFO数据地址:
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
然后获取buffer地址pbBuffer,剩余buffer大小,读取数据的大小cbFifo,实际读取数据大小cbRead,接着将pulFifoReg地址的FIFO数据读取到pbBuffer中.
PBYTE pbBuffer = (PBYTE)pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
DWORD cbFifo = ReadIndexedReg(pContext, dwEndpoint, OUT_FIFO_CNT1_REG_OFFSET);
DEBUGCHK(cbFifo <= peps->dwPacketSizeAssigned);
// Read from the FIFO
const DWORD cbRead = min(cbFifo, cbBuffer);
DWORD cbToRead = cbRead;
while (cbToRead--) {
*pbBuffer++ = (BYTE) *pulFifoReg;
}
同时设置已传输数据大小:
pTransfer->cbTransferred += cbRead;
如果是short packet或者buffer已满,则结束传输清除Out Packet Ready,如果是端点0设置DATA_END的返回值.
6.HandleTx
HandleTx用来写数据到端点.
首先获取端点号dwEndpoint,传输结构pTransfer,如果为TRUE,则验证传输方向.
然后获取buffer地址pbBuffer,剩余buffer大小,数据FIFO地址pulFifoReg,写数据大小cbToWrite,读取IN_CSR1_REG寄存器
PBYTE pbBuffer = (PBYTE) pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
DWORD cbWritten = 0;
// Min of input byte count and supported size
DWORD cbToWrite = min(cbBuffer, peps->dwPacketSizeAssigned);
BYTE bRegStatus = ReadIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET);
如果是端点0.将pbBuffer的数据写入FIFO寄存器:
if (dwEndpoint == 0) {
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = *pbBuffer++;
}
如果Buffer已满或者数据长度为0,则标志数据传输完成.设置EP状态为IDLE以及DATA_END的返回值.
如果数据大于0,或者buffer已满,设置EP0_IN_PACKET_RDY
如果数据长度为0,设置EP0_IN_PACKET_RDY,并置pTransfer->pvPddData为0.
如果是其他端点,使能端点中断:
EnableEndpointInterrupt(pContext, dwEndpoint);
写数据到FIFO:
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = (ULONG) *pbBuffer++;
}
如果Buffer已满或者数据长度为0,则标志传输完成.否则设置IN_PACKET_READY,并更新已发送数据大小.
数据传输完成后(fCompleted为TRUE),禁止端点中断,通知MDD传输完成:
DisableEndpointInterrupt(pContext, peps->dwEndpointNumber);
CompleteTransfer(pContext, peps, dwStatus);
SMDK2410的USB Device Driver的PDD层驱动就简单解析到这里,如果错误,欢迎指正.,更多代码请参考sc2410pdd.cpp.要深入了解USB驱动还需要更多的阅读和实践.
1.ISTMain
ISTMain在UfnPdd_Start被创建:
pContext->hIST = CreateThread(NULL, 0, ISTMain, pContext, 0, NULL);
ISTMain在参数检查后,设置优先级后,然后进入while循环,循环条件为fExitIST为FALSE,即不退出IST.
循环内禁止所有端点中断,然后使能USB reset中断,使能端点0中断.然后等待中断事件hevInterrupt发生.发生后调用HandleUSBEvent进行中断处理.当退出线程fExitIST或重启线程fRestartIST为TRUE时,在禁止中断和清除中断状态后退出线程.并通知MDD层DETACH事件.
static DWORD WINAPI ISTMain( LPVOID lpParameter ) { SETFNAME(); FUNCTION_ENTER_MSG(); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) lpParameter; ValidateContext(pContext); CeSetThreadPriority(pContext->hIST, pContext->dwISTPriority); while (!pContext->fExitIST) { pContext->fRestartIST = FALSE; // Enable Suspend Mode in the Power Register // SetClearReg(pContext, PWR_REG_OFFSET, SUSPEND_MODE_ENABLE_CTRL, SET); // Disable All Endpoint interrupts WriteReg(pContext, EP_INT_EN_REG_OFFSET, 0); // Disable All // Enable Device interrupts WriteReg(pContext, USB_INT_EN_REG_OFFSET, (USB_RESET_INTR));// | USB_SUSPEND_INTR)); // Enable Endpoint interrupt 0 EnableEndpointInterrupt(pContext, 0); while (TRUE) { DWORD dwWait = WaitForSingleObject(pContext->hevInterrupt, INFINITE); if (pContext->fExitIST || pContext->fRestartIST) { break; } if (dwWait == WAIT_OBJECT_0) { HandleUSBEvent(pContext); InterruptDone(pContext->dwSysIntr); } else { RETAILMSG(1, (_T("%s WaitForMultipleObjects failed. Exiting IST./r/n"), pszFname)); break; } } // Disable Device interrupts - write Zeros to Disable WriteReg(pContext, USB_INT_EN_REG_OFFSET, 0 ); // Disable endpoint interrupts - write Zeros to Disable WriteReg(pContext, EP_INT_EN_REG_OFFSET, 0); // Clear any outstanding device & endpoint interrupts // USB Device Interrupt Status - Write a '1' to Clear WriteReg(pContext, USB_INT_REG_OFFSET, (USB_RESET_INTR | USB_RESUME_INTR | USB_SUSPEND_INTR)); // End point Interrupt Status - Write a '1' to Clear WriteReg(pContext, EP_INT_REG_OFFSET, CLEAR_ALL_EP_INTRS); // Send detach pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_EVENTS, UFN_DETACH); pContext->fSpeedReported = FALSE; pContext->attachedState = UFN_DETACH; } FUNCTION_LEAVE_MSG(); return 0; }
2.HandleUSBEvent
当UDC中断发生时就会调用HandleUSBEvent来处理.
首先读取INT_REG和USB_INT_REG寄存器分别获取EP中断状态和USB中断状态(RESET or RESUME or SUSPEND)
BYTE bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET);
BYTE bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET);
然后根据具体的中断状态调用相应的处理函数,如USBbus中断调用HandleUSBBusIrq
if (bUSBBusIrqStat) {
RETAILMSG(1, (_T("%s USB_INT_REG = 0x%02x/r/n"),
pszFname, bUSBBusIrqStat));
HandleUSBBusIrq(pContext, bUSBBusIrqStat);
}
EP中断根据是EP0中断还是EP1-4中断分别调用HandleEndpoint0Event和HandleEndpointEvent.
if (bEpIrqStat & EP0_INT_INTR) {
HandleEndpoint0Event(pContext);
}
for (DWORD dwEndpoint = 1; dwEndpoint < ENDPOINT_COUNT; ++dwEndpoint) {
BYTE bEpBit = EpToIrqStatBit(dwEndpoint);
if (bEpIrqStat & bEpBit) {
HandleEndpointEvent(pContext, dwEndpoint, bEpIrqStat);
}
}
然后继续读取INT_REG和USB_INT_REG寄存器获取中断状态,如果还有中断则继续调用上面的流程进行处理.
完整代码如下:
static VOID HandleUSBEvent( PCTRLR_PDD_CONTEXT pContext ) { SETFNAME(); FUNCTION_ENTER_MSG(); ValidateContext(pContext); BYTE bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET); BYTE bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET); while (bEpIrqStat || bUSBBusIrqStat) { if (bUSBBusIrqStat) { RETAILMSG(1, (_T("%s USB_INT_REG = 0x%02x/r/n"), pszFname, bUSBBusIrqStat)); HandleUSBBusIrq(pContext, bUSBBusIrqStat); } if (bEpIrqStat) { RETAILMSG(1, (_T("%s EP_INT_REG = 0x%02x/r/n"), pszFname, bEpIrqStat)); if (bEpIrqStat & EP0_INT_INTR) { HandleEndpoint0Event(pContext); } // Process All Other (besides EP0) Endpoints for (DWORD dwEndpoint = 1; dwEndpoint < ENDPOINT_COUNT; ++dwEndpoint) { // Check the Interrupt Mask // Check the Interrupt Status BYTE bEpBit = EpToIrqStatBit(dwEndpoint); if (bEpIrqStat & bEpBit) { HandleEndpointEvent(pContext, dwEndpoint, bEpIrqStat); } } } bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET); bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET); } FUNCTION_LEAVE_MSG(); }
3.HandleUSBBusIrq
HandleUSBBusIrq用来处理USBbus中段(RESET or RESUME or SUSPEND).实际上只处理了RESET中断.
如果是RESET中断,清除中断标志,设置fSpeedReported为false;
如果是UFN_DETACH状态,则通知MDD层UFN_ATTACH事件.
接着设置EP0状态为EP0_STATE_IDLE,并通知MDD层UFN_RESET事件.
static VOID HandleUSBBusIrq( PCTRLR_PDD_CONTEXT pContext, BYTE bUSBBusIrqStat ) { SETFNAME(); FUNCTION_ENTER_MSG(); if (bUSBBusIrqStat & USB_RESET_INTR) { WriteReg(pContext, USB_INT_REG_OFFSET, USB_RESET_INTR); RETAILMSG(1, (_T("%s Reset/r/n"), pszFname)); pContext->fSpeedReported = FALSE; if (pContext->attachedState == UFN_DETACH){ pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_EVENTS, UFN_ATTACH); pContext->attachedState = UFN_ATTACH; } pContext->Ep0State = EP0_STATE_IDLE; pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_EVENTS, UFN_RESET); } FUNCTION_LEAVE_MSG(); }
4.HandleEndpoint0Event
当EP0中断发生时,被会调用HandleEndpoint0Event.
首先进行参数检查,获取EP0状态结构PEP_STATUS保存到peps.然后清除EP0中断状态.然后设置INDEX_REG为EP0读取EP0_CSR.
当中断状态为SETUP_END配置结束时,写EP0_CSR寄存器清除SETUP_END,将要写的值先保存在bEp0CsrToWrite中,最后一起写入.同时设置EP0状态为IDLE.
接着获取EP0状态结构的PSTransfer,如果存在,调用CompleteTransfer通知MDD UFN_MSG_TRANSFER_COMPLETE事件.
if (bEP0IrqStatus & SETUP_END) {
bEp0CsrToWrite |= SERVICED_SETUP_END;
pContext->Ep0State = EP0_STATE_IDLE;
PSTransfer pTransfer = peps->pTransfer;
// Make MDD think everything is ok.
if(pTransfer) {
pContext->sendDataEnd = FALSE;
CompleteTransfer(pContext, peps, UFN_NO_ERROR);
}
}
如果是SENT_STALL状态,即协议冲突发生时,设置Ep0State为EP0_STATE_IDLE,交由下面的代码继续处理.
if (bEP0IrqStatus & EP0_SENT_STALL) {
pContext->Ep0State = EP0_STATE_IDLE;
}
接着读取OUT_FIFO_CNT1寄存器,该寄存器保存了写出数据的字节数低位.
DWORD cbFifo = ReadIndexedReg(pContext, 0, OUT_FIFO_CNT1_REG_OFFSET);
当Ep0State状态为EP0_STATE_IDLE时,判断bEP0IrqStatus是否是EP0_OUT_PACKET_RDY(写数据包准备好),如果未通知MDD速度则发出FULL SPEED的通知.
然后准备发送配置包,读取要发送包的字节数,获得USB Device Request指针:
const DWORD cbOutFifo = ReadIndexedReg(pContext, 0,
OUT_FIFO_CNT1_REG_OFFSET);
PBYTE pbUdr = (PBYTE) &pContext->udr;
读取FIFO数据(EP0_FIFO_REG)到pbUdr
volatile ULONG *pulFifoReg = _GetDataRegister(0);
DWORD cbBytesRemaining = cbOutFifo;
while (cbBytesRemaining--) {
*pbUdr++ = (BYTE) *pulFifoReg;
}
如果数据大小不匹配,设置bEp0CsrToWrite,清除相应状态标志.
if (cbOutFifo != sizeof(pContext->udr)) {
// Ideally this should not hapen. This is a recovery mechanism if
// we get out of sync somehow.
bEp0CsrToWrite |= (EP0_SEND_STALL | SERVICED_OUT_PKT_RDY |
DATA_END);
}
否则对读到的数据进行解析,如果数据长度大于0,获得传输方向:
if (pContext->udr.bmRequestType & USB_ENDPOINT_DIRECTION_MASK) {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_IN_DATA_PHASE;
}
else {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_OUT_DATA_PHASE;
}
如果数据长度为0,设置sendDataEnd为TRUE表示数据传输完成.
接下来根据上面获得是发出数据还是接收数据状态,调用不同的处理流程.
如果是接收数据(EP0_STATE_OUT_DATA_PHASE),并且OUT_PACKET_READY数据准备好,调用HandleRx:
bEp0CsrToWrite |= HandleRx(pContext, peps, &fCompleted, &dwStatus);
否则设置状态为EP0_STATE_IDLE.
如果是发送数据(EP0_STATE_IN_DATA_PHASE),当OUT_PACKET_READY和cbFifo为0(控制传输结束)或者IN_PACKET_READY,调用HandleTx:
if (bHandleTx) {
bEp0CsrToWrite |= HandleTx(pContext, peps, 0);
}
最后然后清除中断状态,调用CompleteTransfer置pTransfer为NULL,并如果通知MDD UFN_MSG_TRANSFER_COMPLETE事件.
如果向UDR发送数据(fSendUdr为TRUE)通知MDD UFN_MSG_SETUP_PACKET事件.
static VOID HandleEndpoint0Event( PCTRLR_PDD_CONTEXT pContext ) { SETFNAME(); FUNCTION_ENTER_MSG(); ValidateContext(pContext); DEBUGCHK(pContext->fRunning); PEP_STATUS peps = GetEpStatus(pContext, 0); LOCK_ENDPOINT(peps); ClearEndpointInterrupt(pContext, 0); BYTE bEP0IrqStatus = ReadIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET); RETAILMSG(1, (_T("%s EP0_CSR_REG = 0x%02x. Data phase = %u/r/n"), pszFname, bEP0IrqStatus, pContext->Ep0State)); // Write 0 to SEND_STALL and SENT_STALL to clear them, so we need to // leave them unchanged by default. BYTE bEp0CsrToWrite = 0; if (bEP0IrqStatus & SETUP_END) { bEp0CsrToWrite |= SERVICED_SETUP_END; pContext->Ep0State = EP0_STATE_IDLE; PSTransfer pTransfer = peps->pTransfer; // Make MDD think everything is ok. if(pTransfer) { pContext->sendDataEnd = FALSE; CompleteTransfer(pContext, peps, UFN_NO_ERROR); } RETAILMSG(1, (_T("%s Setup End, %x/r/n"), pszFname, bEP0IrqStatus)); } // Set By USB if protocol violation detected if (bEP0IrqStatus & EP0_SENT_STALL) { // Must Clear both Send and Sent Stall pContext->Ep0State = EP0_STATE_IDLE; } BOOL fSendUdr = FALSE; DWORD cbFifo = ReadIndexedReg(pContext, 0, OUT_FIFO_CNT1_REG_OFFSET); BOOL fCompleted = FALSE; DWORD dwStatus; if (pContext->Ep0State == EP0_STATE_IDLE) { if (bEP0IrqStatus & EP0_OUT_PACKET_RDY) { if (pContext->fSpeedReported == FALSE) { // After Every Reset Notify MDD of Speed setting. // This device can only support FULL Speed. pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_SPEED, BS_FULL_SPEED); pContext->fSpeedReported = TRUE; } // New setup packet const DWORD cbOutFifo = ReadIndexedReg(pContext, 0, OUT_FIFO_CNT1_REG_OFFSET); PBYTE pbUdr = (PBYTE) &pContext->udr; volatile ULONG *pulFifoReg = _GetDataRegister(0); DWORD cbBytesRemaining = cbOutFifo; while (cbBytesRemaining--) { *pbUdr++ = (BYTE) *pulFifoReg; } if (cbOutFifo != sizeof(pContext->udr)) { RETAILMSG(1, (_T("%s Setup packet was only %x bytes!/r/n"), pszFname, cbOutFifo)); // Ideally this should not hapen. This is a recovery mechanism if // we get out of sync somehow. bEp0CsrToWrite |= (EP0_SEND_STALL | SERVICED_OUT_PKT_RDY | DATA_END); } else { // Parse the Setup Command this is necessary to Configure the // SW State Machine and to set bits to enable the HW to // ACK/NAK correctly. // Determine if this is a NO Data Packet if (pContext->udr.wLength > 0) { // Determine transfer Direction if (pContext->udr.bmRequestType & USB_ENDPOINT_DIRECTION_MASK) { // Start the SW OUT State Machine pContext->Ep0State = EP0_STATE_IN_DATA_PHASE; } else { // Start the SW OUT State Machine pContext->Ep0State = EP0_STATE_OUT_DATA_PHASE; } pContext->sendDataEnd = FALSE; } else { // udr.wLength == 0 // ClientDriver will issue a SendControlStatusHandshake to // complete the transaction. pContext->sendDataEnd = TRUE; // Nothing left to do... stay in IDLE. DEBUGCHK(pContext->Ep0State == EP0_STATE_IDLE); } fSendUdr = TRUE; } } } else if (pContext->Ep0State == EP0_STATE_OUT_DATA_PHASE) { if (bEP0IrqStatus & OUT_PACKET_READY) { bEp0CsrToWrite |= SERVICED_OUT_PKT_RDY; // Check For out packet read && receive fifo not empty -> out token event if (cbFifo) { RETAILMSG(1, (_T("%s out token packet on endpoint 0 /r/n"), pszFname)); bEp0CsrToWrite |= HandleRx(pContext, peps, &fCompleted, &dwStatus); } // status stage of control transfer; zero-length packet received else { RETAILMSG(1, (_T("%s status stage of control transfer on endpoint 0/r/n"), pszFname)); pContext->Ep0State = EP0_STATE_IDLE; } } } else { RETAILMSG(1, (_T("%s Data Phase In Token/r/n"), pszFname)); DEBUGCHK(pContext->Ep0State == EP0_STATE_IN_DATA_PHASE); BOOL bHandleTx = FALSE; // IN Data Phase and IPR is cleared // Check For status stage - End control transfer; zero-length packet received if ( (bEP0IrqStatus & OUT_PACKET_READY) && (cbFifo == 0) ) { bHandleTx = TRUE; RETAILMSG(1, (_T("%s In - end xfer/r/n"), pszFname)); } else if ((bEP0IrqStatus & IN_PACKET_READY) == 0) { bHandleTx = TRUE; } if (bHandleTx) { bEp0CsrToWrite |= HandleTx(pContext, peps, 0); } } // Clear any interrupts RETAILMSG(1, (_T("%s Writing 0x%02x to EP0_CSR/r/n"), pszFname, bEp0CsrToWrite)); WriteIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET, bEp0CsrToWrite); if (fCompleted) { CompleteTransfer(pContext, peps, dwStatus); } if (fSendUdr) { pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_SETUP_PACKET, (DWORD) &pContext->udr); } FUNCTION_LEAVE_MSG(); UNLOCK_ENDPOINT(peps); }
5.HandleRx
HandleRx用来读取端点数据.
首先获取端点号dwEndpoint,传输结构pTransfer,如果为TRUE,则验证传输方向,然后获得端点FIFO数据地址:
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
然后获取buffer地址pbBuffer,剩余buffer大小,读取数据的大小cbFifo,实际读取数据大小cbRead,接着将pulFifoReg地址的FIFO数据读取到pbBuffer中.
PBYTE pbBuffer = (PBYTE)pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
DWORD cbFifo = ReadIndexedReg(pContext, dwEndpoint, OUT_FIFO_CNT1_REG_OFFSET);
DEBUGCHK(cbFifo <= peps->dwPacketSizeAssigned);
// Read from the FIFO
const DWORD cbRead = min(cbFifo, cbBuffer);
DWORD cbToRead = cbRead;
while (cbToRead--) {
*pbBuffer++ = (BYTE) *pulFifoReg;
}
同时设置已传输数据大小:
pTransfer->cbTransferred += cbRead;
如果是short packet或者buffer已满,则结束传输清除Out Packet Ready,如果是端点0设置DATA_END的返回值.
static BYTE HandleRx( PCTRLR_PDD_CONTEXT pContext, PEP_STATUS peps, PBOOL pfCompleted, PDWORD pdwStatus ) { BOOL fCompleted = FALSE; DWORD dwStatus = ERROR_GEN_FAILURE; DWORD dwEndpoint = peps->dwEndpointNumber; BYTE bRet = 0; SETFNAME(); FUNCTION_ENTER_MSG(); PSTransfer pTransfer = peps->pTransfer; pTransfer = peps->pTransfer; if (pTransfer) { DEBUGCHK(pTransfer->dwFlags == USB_OUT_TRANSFER); DEBUGCHK(pTransfer->dwUsbError == UFN_NOT_COMPLETE_ERROR); ValidateTransferDirection(pContext, peps, pTransfer); DEBUGCHK(peps->fInitialized); DWORD dwCurrentPermissions = GetCurrentPermissions(); SetProcPermissions(pTransfer->dwCallerPermissions); __try { volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint); DEBUGCHK(pulFifoReg != NULL); PBYTE pbBuffer = (PBYTE)pTransfer->pvBuffer + pTransfer->cbTransferred; DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred; DWORD cbFifo = ReadIndexedReg(pContext, dwEndpoint, OUT_FIFO_CNT1_REG_OFFSET); DEBUGCHK(cbFifo <= peps->dwPacketSizeAssigned); // Read from the FIFO const DWORD cbRead = min(cbFifo, cbBuffer); DWORD cbToRead = cbRead; while (cbToRead--) { *pbBuffer++ = (BYTE) *pulFifoReg; } DEBUGCHK(cbRead <= pTransfer->cbBuffer - pTransfer->cbTransferred); pTransfer->cbTransferred += cbRead; if ( (cbRead < peps->dwPacketSizeAssigned) || (pTransfer->cbTransferred == pTransfer->cbBuffer) ) { // Short packet or filled buffer. Complete transfer. fCompleted = TRUE; dwStatus = UFN_NO_ERROR; } if (dwEndpoint == 0) { bRet |= SERVICED_OUT_PKT_RDY; if (fCompleted) { bRet |= DATA_END; pContext->Ep0State = EP0_STATE_IDLE; } } else { // Clear Out Packet Ready - Allow next packet to come in. DEBUGCHK( (bRet & OUT_PACKET_READY) == 0); } } __except(EXCEPTION_EXECUTE_HANDLER) { RETAILMSG(1, (_T("%s Exception!/r/n"), pszFname)); fCompleted = TRUE; dwStatus = UFN_CLIENT_BUFFER_ERROR; } SetProcPermissions(dwCurrentPermissions); RETAILMSG(1, (_T("%s Rx Ep%x BufferSize=%u,Xfrd=%u /r/n"), pszFname, dwEndpoint, pTransfer->cbBuffer, pTransfer->cbTransferred)); if (fCompleted) { RETAILMSG(1, (_T("%s RxDone Ep%x BufferSize=%u, Xfrd=%u/r/n"), pszFname, dwEndpoint,pTransfer->cbBuffer, pTransfer->cbTransferred)); } } *pfCompleted = fCompleted; *pdwStatus = dwStatus; FUNCTION_LEAVE_MSG(); return bRet; }
6.HandleTx
HandleTx用来写数据到端点.
首先获取端点号dwEndpoint,传输结构pTransfer,如果为TRUE,则验证传输方向.
然后获取buffer地址pbBuffer,剩余buffer大小,数据FIFO地址pulFifoReg,写数据大小cbToWrite,读取IN_CSR1_REG寄存器
PBYTE pbBuffer = (PBYTE) pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
DWORD cbWritten = 0;
// Min of input byte count and supported size
DWORD cbToWrite = min(cbBuffer, peps->dwPacketSizeAssigned);
BYTE bRegStatus = ReadIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET);
如果是端点0.将pbBuffer的数据写入FIFO寄存器:
if (dwEndpoint == 0) {
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = *pbBuffer++;
}
如果Buffer已满或者数据长度为0,则标志数据传输完成.设置EP状态为IDLE以及DATA_END的返回值.
如果数据大于0,或者buffer已满,设置EP0_IN_PACKET_RDY
如果数据长度为0,设置EP0_IN_PACKET_RDY,并置pTransfer->pvPddData为0.
如果是其他端点,使能端点中断:
EnableEndpointInterrupt(pContext, dwEndpoint);
写数据到FIFO:
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = (ULONG) *pbBuffer++;
}
如果Buffer已满或者数据长度为0,则标志传输完成.否则设置IN_PACKET_READY,并更新已发送数据大小.
数据传输完成后(fCompleted为TRUE),禁止端点中断,通知MDD传输完成:
DisableEndpointInterrupt(pContext, peps->dwEndpointNumber);
CompleteTransfer(pContext, peps, dwStatus);
static BYTE HandleTx( PCTRLR_PDD_CONTEXT pContext, PEP_STATUS peps, BOOL fEnableInterrupts ) { SETFNAME(); DEBUGCHK(pContext); PREFAST_DEBUGCHK(peps); // This routine can be entered from both ISTMain and MDD/Client threads so // need critical section. FUNCTION_ENTER_MSG(); BYTE bRet = 0; BOOL fCompleted = FALSE; PSTransfer pTransfer = peps->pTransfer; DWORD dwStatus = ERROR_GEN_FAILURE; DEBUGCHK(peps->fInitialized); DWORD dwEndpoint = peps->dwEndpointNumber; pTransfer = peps->pTransfer; if (pTransfer) { ValidateTransferDirection(pContext, peps, pTransfer); DEBUGCHK(pTransfer->dwFlags == USB_IN_TRANSFER); DEBUGCHK(pTransfer->dwUsbError == UFN_NOT_COMPLETE_ERROR); DWORD dwCurrentPermissions = GetCurrentPermissions(); SetProcPermissions(pTransfer->dwCallerPermissions); // Transfer is ready __try { PBYTE pbBuffer = (PBYTE) pTransfer->pvBuffer + pTransfer->cbTransferred; DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred; volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint); DWORD cbWritten = 0; // Min of input byte count and supported size DWORD cbToWrite = min(cbBuffer, peps->dwPacketSizeAssigned); BYTE bRegStatus = ReadIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET); RETAILMSG(1, (_T("%s Tx on EP %u, Bytes = %u/r/n"), pszFname, dwEndpoint, cbToWrite)); if (dwEndpoint == 0) { for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) { *pulFifoReg = *pbBuffer++; } /* We can complete on a packet which is full. We need to wait till * next time and generate a zero length packet, so only complete * if we're at the end and it is not the max packet size. */ pTransfer->cbTransferred += cbWritten; if (pTransfer->cbTransferred == pTransfer->cbBuffer && pTransfer->pvPddData == 0) { dwStatus = UFN_NO_ERROR; fCompleted = TRUE; pContext->Ep0State = EP0_STATE_IDLE; bRet |= DATA_END; } /* Update the register - Set the IPR bit and possibly data end*/ if( (cbWritten > 0) || (pTransfer->cbBuffer == 0) ) { bRet |= EP0_IN_PACKET_RDY; } // Also set IPR if a 0 byte packet needs to go out. else if(pTransfer->pvPddData) { bRet |= EP0_IN_PACKET_RDY; pTransfer->pvPddData = 0; } } else { // Enable Interrupts before writing to the FIFO. This insures // That any interrupts generated because of the write will be // "latched" if (fEnableInterrupts) { DEBUGCHK(dwEndpoint != 0); EnableEndpointInterrupt(pContext, dwEndpoint); } // Write to the FIFO directly to send the bytes. for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) { *pulFifoReg = (ULONG) *pbBuffer++; } // By Placing the check for packet complete here, before // cbTransferred is updated, there is a 1 interrupt cycle delay // That is complete is not declared until the data has actually // been ACKd (TPC set) by the host if ( (pTransfer->cbTransferred == pTransfer->cbBuffer) || (cbWritten == 0) ){ fCompleted = TRUE; dwStatus = UFN_NO_ERROR; } else { /* Set In Packet Ready , this will Tells the HW the FIFO is ready to be transitted to be sent */ bRet |= IN_PACKET_READY; } // Update the Transfered Count pTransfer->cbTransferred += cbWritten; } } __except(EXCEPTION_EXECUTE_HANDLER) { RETAILMSG(1, (_T("%s Exception!/r/n"), pszFname)); fCompleted = TRUE; dwStatus = UFN_CLIENT_BUFFER_ERROR; } SetProcPermissions(dwCurrentPermissions); } else { // It is possible for an interrupt to come in while still in this // function for first pass of transfer. If this happens it is possible // to complete the transfer and have that interrupt be unnecessary // so... just ignore it. goto EXIT; } if (fCompleted) { // Disable transfer interrupts until another transfer is issued. if (peps->dwEndpointNumber != 0) { DisableEndpointInterrupt(pContext, peps->dwEndpointNumber); } RETAILMSG(1, (_T("%s Tx Done Ep%x Status %u/r/n"), pszFname, dwEndpoint, dwStatus)); CompleteTransfer(pContext, peps, dwStatus); } else { RETAILMSG(1, (_T("%s Tx EP%x BufferSize=%u, Xfrd=%u/r/n"), pszFname, dwEndpoint, pTransfer->cbBuffer, pTransfer->cbTransferred)); } EXIT: FUNCTION_LEAVE_MSG(); return bRet; }
SMDK2410的USB Device Driver的PDD层驱动就简单解析到这里,如果错误,欢迎指正.,更多代码请参考sc2410pdd.cpp.要深入了解USB驱动还需要更多的阅读和实践.
相关文章推荐
- Windows CE USB Function Driver驱动简析(1)-驱动架构及UfnPdd函数(基于WinCE5.0 SMDK2410 BSP的UFBFN驱动)
- Windows CE LCD显示驱动简析(1)(基于WinCE5.0 SMDK2410 BSP的LCD显示设备驱动)
- Windows CE SDHC驱动简析(2)-CSDIOControllerBase类(基于WinCE5.0 SMDK2410 BSP的SDHC驱动)
- Windows CE LCD显示驱动简析(2)(基于WinCE5.0 SMDK2410 BSP的LCD显示设备驱动)
- WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(19)-Nand Flash驱动(FMD)及其简析(2)
- Windows CE SDHC驱动简析(3)-CSDIOController类(基于WinCE5.0 SMDK2410 BSP的SDHC驱动)
- Windows CE 触摸屏(TouchPanel)驱动简析(2)-DDSI函数-(基于WinCE5.0 SMDK2410 BSP的TouchPanel驱动)
- Windows CE串口驱动简析(1)-驱动架构(基于WinCE5.0 SMDK2410 BSP的Serial驱动)
- Windows CE串口驱动简析(3)-PDD层实现:RX,MODEM,LINE,IR部分(基于WinCE5.0 SMDK2410 BSP的Serial驱动)
- WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(18)-Nand Flash驱动(FMD)及其简析(1)
- Windows CE串口驱动简析(4)-PDD层实现:CPdd2410Serial1和CPdd2410Serial2(基于WinCE5.0 SMDK2410 BSP的Serial驱动)
- Windows CE SDHC驱动简析(1)-驱动架构(基于WinCE5.0 SMDK2410 BSP的SDHC驱动)
- Windows CE SDHC驱动简析(3)-CSDIOController类(基于WinCE5.0 SMDK2410 BSP的SDHC驱动)
- Windows CE SDHC驱动简析(1)-驱动架构(基于WinCE5.0 SMDK2410 BSP的SDHC驱动)
- Windows CE串口驱动简析(1)-驱动架构(基于WinCE5.0 SMDK2410 BSP的Serial驱动)
- Windows CE串口驱动简析(2)-PDD层实现:CReg2410Uart和TX部分(基于WinCE5.0 SMDK2410 BSP的Serial驱动)
- Windows CE 触摸屏(TouchPanel)驱动简析(1)-原理及驱动架构(基于WinCE5.0 SMDK2410 BSP的TouchPanel驱动)
- WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(2)-基于Nand Flash的Eboot
- WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(4)-LCD驱动
- WinCE5.0 SMDK2410 BSP在GEC2410开发板上的移植(7)-Serial串口驱动