/************************************************************************************************************************** * DIAGS.C SigmaTel STIR4200 diagnostic module ************************************************************************************************************************** * (C) Unpublished Copyright of Sigmatel, Inc. All Rights Reserved. * * * Created: 04/27/2000 * Version 0.92 * Edited: 05/12/2000 * Version 0.94 * Edited: 05/19/2000 * Version 0.95 * Edited: 05/24/2000 * Version 0.96 * Edited: 10/09/2000 * Version 1.10 * * **************************************************************************************************************************/ #if defined(DIAGS) #define DOBREAKS // enable debug breaks #include #include // defines OID's #include #include #include "debug.h" #include "ircommon.h" #include "irndis.h" #include "irusb.h" #include "stir4200.h" #include "diags.h" /***************************************************************************** * * Function: Diags_BufferToFirPacket * * Synopsis: convert a buffer to a Fir IR packet * * Write the IR packet into the provided buffer and report * its actual size. * * Arguments: pIrDev - pointer to device instance * pIrPacketBuf - output buffer * IrPacketBufLen - output buffer size * pContigPacketBuf - temporary staging buffer (input buffer) * ContigPacketLen - input buffer size * pIrPacketLen - lenght of the converted data * * Returns: TRUE - on success * FALSE - on failure * * *****************************************************************************/ BOOLEAN Diags_BufferToFirPacket( IN PIR_DEVICE pIrDev, OUT PUCHAR pIrPacketBuf, ULONG IrPacketBufLen, IN PUCHAR pContigPacketBuf, ULONG ContigPacketLen, OUT PULONG pIrPacketLen ) { ULONG I_fieldBytes; FAST_IR_FCS_TYPE fcs, *pfcs; ULONG i, TotalBytes, EscSize; PSTIR4200_FRAME_HEADER pFrameHeader = (PSTIR4200_FRAME_HEADER)pIrPacketBuf; PUCHAR pIrPacketBufFrame = pIrPacketBuf + sizeof(STIR4200_FRAME_HEADER); /***********************************************/ /* Make sure that the packet is big enough */ /* to be legal. It consists of an A, C, and */ /* variable-length I field. */ /***********************************************/ if( ContigPacketLen < IRDA_A_C_TOTAL_SIZE ) { DEBUGMSG(DBG_ERR, (" Diags_BufferToFirPacket(): Packet is too small\n")); return FALSE; } else { I_fieldBytes = ContigPacketLen - IRDA_A_C_TOTAL_SIZE; } /***********************************************/ /* Make sure that we won't overwrite our */ /* contiguous buffer */ /***********************************************/ if( (ContigPacketLen > MAX_TOTAL_SIZE_WITH_ALL_HEADERS) || (MAX_POSSIBLE_IR_PACKET_SIZE_FOR_DATA(I_fieldBytes) > IrPacketBufLen) ) { /***********************************************/ /* The packet is too large. Tell the caller */ /* to retry with a packet size large enough */ /* to get past this stage next time. */ /***********************************************/ DEBUGMSG(DBG_ERR, (" Diags_BufferToFirPacket(): Packet is too big\n")); return FALSE; } /***********************************************/ /* Compute the FCS on the packet BEFORE */ /* applying transparency fixups. The FCS */ /* also must be sent using ESC-char */ /* transparency. */ /***********************************************/ fcs = ComputeFCS32( pContigPacketBuf, ContigPacketLen ); /***********************************************/ /* Add FCS to packet... */ /***********************************************/ pfcs = (FAST_IR_FCS_TYPE *)&pContigPacketBuf[ContigPacketLen]; *pfcs = fcs; /***********************************************/ /* Build the STIr4200 FIR frame. */ /***********************************************/ /***********************************************/ /* Add preamble... */ /***********************************************/ memset( pIrPacketBufFrame, STIR4200_FIR_PREAMBLE, STIR4200_FIR_PREAMBLE_SIZ ); /***********************************************/ /* Add BOF's... */ /***********************************************/ memset( &pIrPacketBufFrame[STIR4200_FIR_PREAMBLE_SIZ], STIR4200_FIR_BOF, STIR4200_FIR_BOF_SIZ ); /***********************************************/ /* Escape A, C, I & CRC fields of packet... */ /***********************************************/ EscSize = ContigPacketLen + FAST_IR_FCS_SIZE; for( i = 0, TotalBytes = STIR4200_FIR_PREAMBLE_SIZ + STIR4200_FIR_BOF_SIZ; i < EscSize; i++ ) { UCHAR c; switch( c = pContigPacketBuf[i] ) { case STIR4200_FIR_ESC_CHAR: pIrPacketBufFrame[TotalBytes++] = STIR4200_FIR_ESC_CHAR; pIrPacketBufFrame[TotalBytes++] = STIR4200_FIR_ESC_DATA_7D; break; case STIR4200_FIR_BOF: // BOF = EOF too pIrPacketBufFrame[TotalBytes++] = STIR4200_FIR_ESC_CHAR; pIrPacketBufFrame[TotalBytes++] = STIR4200_FIR_ESC_DATA_7E; break; case STIR4200_FIR_PREAMBLE: pIrPacketBufFrame[TotalBytes++] = STIR4200_FIR_ESC_CHAR; pIrPacketBufFrame[TotalBytes++] = STIR4200_FIR_ESC_DATA_7F; break; default: pIrPacketBufFrame[TotalBytes++] = c; } } /***********************************************/ /* Add EOF's... */ /***********************************************/ memset( &pIrPacketBufFrame[TotalBytes], STIR4200_FIR_EOF, STIR4200_FIR_EOF_SIZ ); /***********************************************/ /* Add in STIr4200 header... */ /***********************************************/ TotalBytes += STIR4200_FIR_EOF_SIZ; pFrameHeader->id1 = STIR4200_HEADERID_BYTE1; pFrameHeader->id2 = STIR4200_HEADERID_BYTE2; pFrameHeader->sizlsb = LOBYTE(TotalBytes); pFrameHeader->sizmsb = HIBYTE(TotalBytes); /***********************************************/ /* Calc size packet w/escaped data... */ /***********************************************/ *pIrPacketLen = TotalBytes + sizeof(STIR4200_FRAME_HEADER); return TRUE; } /***************************************************************************** * * Function: Diags_BufferToSirPacket * * Synopsis: convert a buffer to a Sir IR packet * * Write the IR packet into the provided buffer and report * its actual size. * * Arguments: pIrDev - pointer to device instance * pPacket - NDIS packet to convert * pIrPacketBuf - output buffer * IrPacketBufLen - output buffer size * pContigPacketBuf - temporary staging buffer (input buffer) * ContigPacketLen - input buffer size * pIrPacketLen - lenght of the converted data * * Returns: TRUE - on success * FALSE - on failure * * *****************************************************************************/ BOOLEAN Diags_BufferToSirPacket( IN PIR_DEVICE pIrDev, OUT PUCHAR pIrPacketBuf, ULONG IrPacketBufLen, IN PUCHAR pContigPacketBuf, ULONG ContigPacketLen, USHORT ExtraBOFs, OUT PULONG pIrPacketLen ) { ULONG i; ULONG I_fieldBytes, totalBytes = 0; ULONG numExtraBOFs; SLOW_IR_FCS_TYPE fcs, tmpfcs; UCHAR fcsBuf[SLOW_IR_FCS_SIZE * 2]; ULONG fcsLen = 0; UCHAR nextChar; PSTIR4200_FRAME_HEADER pFrameHeader = (PSTIR4200_FRAME_HEADER)pIrPacketBuf; PUCHAR pIrPacketBufFrame = pIrPacketBuf + sizeof(STIR4200_FRAME_HEADER); /***********************************************/ /* Make sure that the packet is big enough */ /* to be legal. It consists of an A, C, and */ /* variable-length I field. */ /***********************************************/ if( ContigPacketLen < IRDA_A_C_TOTAL_SIZE ) { DEBUGMSG(DBG_ERR, (" NdisToSirPacket(): Packet is too small\n")); return FALSE; } else { I_fieldBytes = ContigPacketLen - IRDA_A_C_TOTAL_SIZE; } /***********************************************/ /* Make sure that we won't overwrite our */ /* contiguous buffer. Make sure that the */ /* passed-in buffer can accomodate this */ /* packet's data no matter how much it */ /* grows through adding ESC-sequences, etc. */ /***********************************************/ if( (ContigPacketLen > MAX_TOTAL_SIZE_WITH_ALL_HEADERS) || (MAX_POSSIBLE_IR_PACKET_SIZE_FOR_DATA(I_fieldBytes) > IrPacketBufLen) ) { // // Packet is too big // DEBUGMSG(DBG_ERR, (" NdisToSirPacket(): Packet is too big\n")); return FALSE; } /***********************************************/ /* Compute the FCS on the packet BEFORE */ /* applying transparency fixups. The FCS */ /* also must be sent using ESC-char */ /* transparency, so figure out how large */ /* the fcs will really be. */ /***********************************************/ fcs = ComputeFCS16( pContigPacketBuf, ContigPacketLen ); for( i = 0, tmpfcs = fcs, fcsLen = 0; i < SLOW_IR_FCS_SIZE; tmpfcs >>= 8, i++ ) { UCHAR fcsbyte = tmpfcs & 0x00ff; switch( fcsbyte ) { case SLOW_IR_BOF: case SLOW_IR_EOF: case SLOW_IR_ESC: fcsBuf[fcsLen++] = SLOW_IR_ESC; fcsBuf[fcsLen++] = fcsbyte ^ SLOW_IR_ESC_COMP; break; default: fcsBuf[fcsLen++] = fcsbyte; break; } } /***********************************************/ /* Now begin building the IR frame. */ /* */ /* This is the final format: */ /* */ /* BOF (1) */ /* extra BOFs ... */ /* NdisMediumIrda packet (from NDIS): */ /* Address (1) */ /* Control (1) */ /* FCS (2) */ /* EOF (1) */ /* */ /* Prepend BOFs (extra BOFs + 1 actual BOF) */ /***********************************************/ numExtraBOFs = ExtraBOFs; if( numExtraBOFs > MAX_NUM_EXTRA_BOFS ) { numExtraBOFs = MAX_NUM_EXTRA_BOFS; } for( i = totalBytes = 0; i < numExtraBOFs; i++ ) { *(SLOW_IR_BOF_TYPE*)(pIrPacketBufFrame + totalBytes) = SLOW_IR_EXTRA_BOF; totalBytes += SLOW_IR_EXTRA_BOF_SIZE; } *(SLOW_IR_BOF_TYPE*)(pIrPacketBufFrame + totalBytes) = SLOW_IR_BOF; totalBytes += SLOW_IR_BOF_SIZE; /***********************************************/ /* Copy the NDIS packet from our contiguous */ /* buffer, applying escape-char */ /* transparency. */ /***********************************************/ for( i = 0; i < ContigPacketLen; i++ ) { nextChar = pContigPacketBuf[i]; switch( nextChar ) { case SLOW_IR_BOF: case SLOW_IR_EOF: case SLOW_IR_ESC: pIrPacketBufFrame[totalBytes++] = SLOW_IR_ESC; pIrPacketBufFrame[totalBytes++] = nextChar ^ SLOW_IR_ESC_COMP; break; default: pIrPacketBufFrame[totalBytes++] = nextChar; break; } } /***********************************************/ /* Add FCS, EOF. */ /***********************************************/ NdisMoveMemory( (PVOID)(pIrPacketBufFrame + totalBytes), (PVOID)fcsBuf, fcsLen ); totalBytes += fcsLen; *(SLOW_IR_EOF_TYPE*)(pIrPacketBufFrame + totalBytes) = (UCHAR)SLOW_IR_EOF; totalBytes += SLOW_IR_EOF_SIZE; /***********************************************/ /* Add in STIr4200 header... */ /***********************************************/ pFrameHeader->id1 = STIR4200_HEADERID_BYTE1; pFrameHeader->id2 = STIR4200_HEADERID_BYTE2; pFrameHeader->sizlsb = LOBYTE(totalBytes); pFrameHeader->sizmsb = HIBYTE(totalBytes); *pIrPacketLen = totalBytes + sizeof(STIR4200_FRAME_HEADER); return TRUE; } /***************************************************************************** * * Function: Diags_Enable * * Synopsis: Switches the STIr4200 to diagnostic mode * * Arguments: pThisDev - pointer to IR device * * Returns: NT status code * * Notes: * *****************************************************************************/ NTSTATUS Diags_Enable( IN OUT PIR_DEVICE pThisDev ) { PIRUSB_CONTEXT pThisContext; PLIST_ENTRY pListEntry; // // Make sure diags aren't already active // if( pThisDev->DiagsActive ) { DEBUGMSG(DBG_ERR, (" Diags_Enable diags already active\n")); return STATUS_UNSUCCESSFUL; } // // Get a context to switch to the new mode // pListEntry = ExInterlockedRemoveHeadList( &pThisDev->SendAvailableQueue, &pThisDev->SendLock ); if( NULL == pListEntry ) { // // This must not happen // DEBUGMSG(DBG_ERR, (" Diags_Enable failed to find a free context struct\n")); IRUSB_ASSERT( 0 ); return STATUS_UNSUCCESSFUL; } InterlockedDecrement( &pThisDev->SendAvailableCount ); pThisContext = CONTAINING_RECORD( pListEntry, IRUSB_CONTEXT, ListEntry ); pThisContext->ContextType = CONTEXT_DIAGS_ENABLE; // // Disable further interaction with the stack // InterlockedExchange( &pThisDev->DiagsPendingActivation, TRUE ); // // Queue the context and then wait // KeClearEvent( &pThisDev->EventDiags ); ExInterlockedInsertTailList( &pThisDev->SendBuiltQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendBuiltCount ); MyKeWaitForSingleObject( pThisDev, &pThisDev->EventDiags, NULL, 0 ); return pThisDev->IOCTLStatus; } /***************************************************************************** * * Function: Diags_Disable * * Synopsis: Switches the STIr4200 back to normal mode * * Arguments: pThisDev - pointer to IR device * * Returns: NT status code * * Notes: * *****************************************************************************/ NTSTATUS Diags_Disable( IN OUT PIR_DEVICE pThisDev ) { PRCV_BUFFER pRecBuf; PLIST_ENTRY pEntry; // // Make sure diags are active // if( !pThisDev->DiagsActive ) { DEBUGMSG(DBG_ERR, (" Diags_Disable diags not active\n")); return STATUS_UNSUCCESSFUL; } // // Enable interaction with the stack and no queuing of contexts is required // InterlockedExchange( &pThisDev->DiagsActive, FALSE ); InterlockedExchange( &pThisDev->DiagsPendingActivation, FALSE ); // // Get rid of all the diagnostic buffers // while( pEntry=ExInterlockedRemoveHeadList( &pThisDev->DiagsReceiveQueue, &pThisDev->DiagsReceiveLock ) ) { pRecBuf = CONTAINING_RECORD( pEntry, RCV_BUFFER, ListEntry ); InterlockedExchange( &pRecBuf->DataLen, 0 ); InterlockedExchange( (PULONG)&pRecBuf->BufferState, RCV_STATE_FREE ); } return STATUS_SUCCESS; } /***************************************************************************** * * Function: Diags_ReadRegisters * * Synopsis: Prepares a context to read the registers * * Arguments: pThisDev - pointer to IR device * pIOCTL - pointer to IOCTL descriptor * IOCTLSize - size of the IOCTL buffer * * Returns: NT status code * * Notes: * *****************************************************************************/ NTSTATUS Diags_ReadRegisters( IN OUT PIR_DEVICE pThisDev, OUT PDIAGS_READ_REGISTERS_IOCTL pIOCTL, ULONG IOCTLSize ) { PIRUSB_CONTEXT pThisContext; PLIST_ENTRY pListEntry; // // First basic validation // if( IOCTLSize < sizeof(DIAGS_READ_REGISTERS_IOCTL) ) { DEBUGMSG(DBG_ERR, (" Diags_ReadRegisters invalid output buffer\n")); return STATUS_UNSUCCESSFUL; } // // Now we get a little more sofisticated // if( ((pIOCTL->FirstRegister+pIOCTL->NumberRegisters)>(STIR4200_MAX_REG+1)) || ((IOCTLSize+1)<(sizeof(DIAGS_READ_REGISTERS_IOCTL)+pIOCTL->NumberRegisters)) ) { DEBUGMSG(DBG_ERR, (" Diags_ReadRegisters invalid output buffer\n")); return STATUS_UNSUCCESSFUL; } pThisDev->pIOCTL = pIOCTL; pThisDev->IOCTLStatus = STATUS_UNSUCCESSFUL; // // Get a context to queue // pListEntry = ExInterlockedRemoveHeadList( &pThisDev->SendAvailableQueue, &pThisDev->SendLock ); if( NULL == pListEntry ) { // // This must not happen // DEBUGMSG(DBG_ERR, (" Diags_ReadRegisters failed to find a free context struct\n")); IRUSB_ASSERT( 0 ); return STATUS_UNSUCCESSFUL; } InterlockedDecrement( &pThisDev->SendAvailableCount ); pThisContext = CONTAINING_RECORD( pListEntry, IRUSB_CONTEXT, ListEntry ); pThisContext->ContextType = CONTEXT_DIAGS_READ_REGISTERS; // // Queue the context and then wait // KeClearEvent( &pThisDev->EventDiags ); ExInterlockedInsertTailList( &pThisDev->SendBuiltQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendBuiltCount ); MyKeWaitForSingleObject( pThisDev, &pThisDev->EventDiags, NULL, 0 ); return pThisDev->IOCTLStatus; } /***************************************************************************** * * Function: Diags_WriteRegister * * Synopsis: Prepares a context to write the registers * * Arguments: pThisDev - pointer to IR device * pIOCTL - pointer to IOCTL descriptor * IOCTLSize - size of the IOCTL buffer * * Returns: NT status code * * Notes: * *****************************************************************************/ NTSTATUS Diags_WriteRegister( IN OUT PIR_DEVICE pThisDev, OUT PDIAGS_READ_REGISTERS_IOCTL pIOCTL, ULONG IOCTLSize ) { PIRUSB_CONTEXT pThisContext; PLIST_ENTRY pListEntry; // // Validation // if( (IOCTLSize < sizeof(DIAGS_READ_REGISTERS_IOCTL)) || (pIOCTL->FirstRegister>STIR4200_MAX_REG) ) { DEBUGMSG(DBG_ERR, (" Diags_WriteRegister invalid output buffer\n")); return STATUS_UNSUCCESSFUL; } pThisDev->pIOCTL = pIOCTL; pThisDev->IOCTLStatus = STATUS_UNSUCCESSFUL; // // Get a context to queue // pListEntry = ExInterlockedRemoveHeadList( &pThisDev->SendAvailableQueue, &pThisDev->SendLock ); if( NULL == pListEntry ) { // // This must not happen // DEBUGMSG(DBG_ERR, (" Diags_ReadRegisters failed to find a free context struct\n")); IRUSB_ASSERT( 0 ); return STATUS_UNSUCCESSFUL; } InterlockedDecrement( &pThisDev->SendAvailableCount ); pThisContext = CONTAINING_RECORD( pListEntry, IRUSB_CONTEXT, ListEntry ); pThisContext->ContextType = CONTEXT_DIAGS_WRITE_REGISTER; // // Queue the context and the wait // KeClearEvent( &pThisDev->EventDiags ); ExInterlockedInsertTailList( &pThisDev->SendBuiltQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendBuiltCount ); MyKeWaitForSingleObject( pThisDev, &pThisDev->EventDiags, NULL, 0 ); return pThisDev->IOCTLStatus; } /***************************************************************************** * * Function: Diags_PrepareBulk * * Synopsis: Prepares a context to do a bulk transfer * * Arguments: pThisDev - pointer to IR device * pIOCTL - pointer to IOCTL descriptor * IOCTLSize - size of the IOCTL buffer * DirectionOut - TRUE if bulk-out, FALSE if bulk-in * * Returns: NT status code * * Notes: * *****************************************************************************/ NTSTATUS Diags_PrepareBulk( IN OUT PIR_DEVICE pThisDev, OUT PDIAGS_BULK_IOCTL pIOCTL, ULONG IOCTLSize, BOOLEAN DirectionOut ) { PIRUSB_CONTEXT pThisContext; PLIST_ENTRY pListEntry; // // First basic validation // if( IOCTLSize < sizeof(DIAGS_BULK_IOCTL) ) { DEBUGMSG(DBG_ERR, (" Diags_PrepareBulk invalid input buffer\n")); return STATUS_UNSUCCESSFUL; } // // Now we get a little more sofisticated // if( IOCTLSize < (sizeof(DIAGS_BULK_IOCTL)+pIOCTL->DataSize-1) ) { DEBUGMSG(DBG_ERR, (" Diags_PrepareBulk invalid output buffer\n")); return STATUS_UNSUCCESSFUL; } pThisDev->pIOCTL = pIOCTL; pThisDev->IOCTLStatus = STATUS_UNSUCCESSFUL; // // Get a context to queue // pListEntry = ExInterlockedRemoveHeadList( &pThisDev->SendAvailableQueue, &pThisDev->SendLock ); if( NULL == pListEntry ) { // // This must not happen // DEBUGMSG(DBG_ERR, (" Diags_PrepareBulk failed to find a free context struct\n")); IRUSB_ASSERT( 0 ); return STATUS_UNSUCCESSFUL; } InterlockedDecrement( &pThisDev->SendAvailableCount ); pThisContext = CONTAINING_RECORD( pListEntry, IRUSB_CONTEXT, ListEntry ); if( DirectionOut ) pThisContext->ContextType = CONTEXT_DIAGS_BULK_OUT; else pThisContext->ContextType = CONTEXT_DIAGS_BULK_IN; // // Queue the context and then wait // KeClearEvent( &pThisDev->EventDiags ); ExInterlockedInsertTailList( &pThisDev->SendBuiltQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendBuiltCount ); MyKeWaitForSingleObject( pThisDev, &pThisDev->EventDiags, NULL, 0 ); return pThisDev->IOCTLStatus; } /***************************************************************************** * * Function: Diags_PrepareSend * * Synopsis: Prepares a diagnostic send * * Arguments: pThisDev - pointer to IR device * pIOCTL - pointer to IOCTL descriptor * IOCTLSize - size of the IOCTL buffer * * Returns: None * * Notes: * *****************************************************************************/ NTSTATUS Diags_PrepareSend( IN OUT PIR_DEVICE pThisDev, OUT PDIAGS_SEND_IOCTL pIOCTL, ULONG IOCTLSize ) { PIRUSB_CONTEXT pThisContext; PLIST_ENTRY pListEntry; ULONG Size = sizeof(DIAGS_SEND_IOCTL)+pIOCTL->DataSize-1; // // First basic validation // if( IOCTLSize < sizeof(DIAGS_SEND_IOCTL) ) { DEBUGMSG(DBG_ERR, (" Diags_PrepareBulk invalid input buffer\n")); return STATUS_UNSUCCESSFUL; } // // Now we get a little more sofisticated // if( IOCTLSize < (sizeof(DIAGS_SEND_IOCTL)+pIOCTL->DataSize-1) ) { DEBUGMSG(DBG_ERR, (" Diags_PrepareSend invalid output buffer\n")); return STATUS_UNSUCCESSFUL; } pThisDev->pIOCTL = pIOCTL; pThisDev->IOCTLStatus = STATUS_UNSUCCESSFUL; // // Get a context to queue // pListEntry = ExInterlockedRemoveHeadList( &pThisDev->SendAvailableQueue, &pThisDev->SendLock ); if( NULL == pListEntry ) { // // This must not happen // DEBUGMSG(DBG_ERR, (" Diags_PrepareSend failed to find a free context struct\n")); IRUSB_ASSERT( 0 ); return STATUS_UNSUCCESSFUL; } InterlockedDecrement( &pThisDev->SendAvailableCount ); pThisContext = CONTAINING_RECORD( pListEntry, IRUSB_CONTEXT, ListEntry ); pThisContext->ContextType = CONTEXT_DIAGS_SEND; // // Queue the context and then wait // KeClearEvent( &pThisDev->EventDiags ); ExInterlockedInsertTailList( &pThisDev->SendBuiltQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendBuiltCount ); MyKeWaitForSingleObject( pThisDev, &pThisDev->EventDiags, NULL, 0 ); return pThisDev->IOCTLStatus; } /***************************************************************************** * * Function: Diags_Receive * * Synopsis: Diagnostic receive * * Arguments: pThisDev - pointer to IR device * pIOCTL - pointer to IOCTL descriptor * IOCTLSize - size of the IOCTL buffer * * Returns: None * * Notes: * *****************************************************************************/ NTSTATUS Diags_Receive( IN OUT PIR_DEVICE pThisDev, OUT PDIAGS_RECEIVE_IOCTL pIOCTL, ULONG IOCTLSize ) { PLIST_ENTRY pListEntry; PRCV_BUFFER pRecBuf; // // First basic validation // if( IOCTLSize < sizeof(DIAGS_RECEIVE_IOCTL) ) { DEBUGMSG(DBG_ERR, (" Diags_Receive invalid input buffer\n")); return STATUS_UNSUCCESSFUL; } // // Get a received packet // pListEntry = ExInterlockedRemoveHeadList( &pThisDev->DiagsReceiveQueue, &pThisDev->DiagsReceiveLock ); if( NULL == pListEntry ) { // // No packet available // return STATUS_UNSUCCESSFUL; } pRecBuf = CONTAINING_RECORD( pListEntry, RCV_BUFFER, ListEntry ); // // Now we get a little more sofisticated // if( IOCTLSize < (sizeof(DIAGS_RECEIVE_IOCTL)+pIOCTL->DataSize-1) ) { DEBUGMSG(DBG_ERR, (" Diags_Receive invalid output buffer\n")); return STATUS_UNSUCCESSFUL; } // // Copy the data // NdisMoveMemory( pIOCTL->pData, pRecBuf->pDataBuf, pRecBuf->DataLen ); pIOCTL->DataSize = (USHORT)pRecBuf->DataLen; pThisDev->pIOCTL = pIOCTL; InterlockedExchange( &pRecBuf->DataLen, 0 ); InterlockedExchange( (PULONG)&pRecBuf->BufferState, RCV_STATE_FREE ); return STATUS_SUCCESS; } /***************************************************************************** * * Function: Diags_GetSpeed * * Synopsis: Retrieves the current speed * * Arguments: pThisDev - pointer to IR device * pIOCTL - pointer to IOCTL descriptor * IOCTLSize - size of the IOCTL buffer * * Returns: None * * Notes: * *****************************************************************************/ NTSTATUS Diags_GetSpeed( IN OUT PIR_DEVICE pThisDev, OUT PDIAGS_SPEED_IOCTL pIOCTL, ULONG IOCTLSize ) { // // First basic validation // if( IOCTLSize < sizeof(DIAGS_SPEED_IOCTL) ) { DEBUGMSG(DBG_ERR, (" Diags_GetSpeed invalid input buffer\n")); return STATUS_UNSUCCESSFUL; } pIOCTL->Speed = pThisDev->currentSpeed; return STATUS_SUCCESS; } /***************************************************************************** * * Function: Diags_SetSpeed * * Synopsis: Sets a new speed in diagnostic mode * * Arguments: pThisDev - pointer to IR device * pIOCTL - pointer to IOCTL descriptor * IOCTLSize - size of the IOCTL buffer * * Returns: None * * Notes: * *****************************************************************************/ NTSTATUS Diags_SetSpeed( IN OUT PIR_DEVICE pThisDev, OUT PDIAGS_SPEED_IOCTL pIOCTL, ULONG IOCTLSize ) { NDIS_STATUS status; USHORT i; // // First basic validation // if( IOCTLSize < sizeof(DIAGS_SPEED_IOCTL) ) { DEBUGMSG(DBG_ERR, (" Diags_SetSpeed invalid input buffer\n")); return STATUS_UNSUCCESSFUL; } if( pThisDev->currentSpeed == pIOCTL->Speed ) { // // We are already set to the requested speed. // return STATUS_SUCCESS; } DEBUGMSG(DBG_ERR, (" Diags_SetSpeed(OID_IRDA_LINK_SPEED, 0x%x, decimal %d)\n",pIOCTL->Speed, pIOCTL->Speed)); for( i = 0; i < NUM_BAUDRATES; i++ ) { if( supportedBaudRateTable[i].BitsPerSec == pIOCTL->Speed ) { // // Keep a pointer to the link speed which has // been requested. // pThisDev->linkSpeedInfo = &supportedBaudRateTable[i]; status = NDIS_STATUS_PENDING; break; //for } } // // Don't set if there is an error // if( NDIS_STATUS_PENDING != status ) { DEBUGMSG(DBG_ERR, (" Invalid link speed\n")); return STATUS_UNSUCCESSFUL; } // // Set the new speed // IrUsb_PrepareSetSpeed( pThisDev ); while( pThisDev->linkSpeedInfo->BitsPerSec != pThisDev->currentSpeed ) { NdisMSleep( 50000 ); } return STATUS_SUCCESS; } /***************************************************************************** * * Function: Diags_CompleteEnable * * Synopsis: Completes the enabling of the diagnostic state * * Arguments: pThisDev - pointer to IR device * pContext - pinter to the operation context * * Returns: None * * Notes: * *****************************************************************************/ VOID Diags_CompleteEnable( IN OUT PIR_DEVICE pThisDev, IN PVOID pContext ) { PIRUSB_CONTEXT pThisContext = pContext; // // Really enable diags // InterlockedExchange( &pThisDev->DiagsActive, TRUE ); // // Return the context // ExInterlockedInsertTailList( &pThisDev->SendAvailableQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendAvailableCount ); // // Signal // KeSetEvent( &pThisDev->EventDiags, 0, FALSE ); //signal we're done } /***************************************************************************** * * Function: Diags_CompleteReadRegisters * * Synopsis: Reads the registers and returns the value * * Arguments: pThisDev - pointer to IR device * pContext - pinter to the operation context * * Returns: None * * Notes: * *****************************************************************************/ VOID Diags_CompleteReadRegisters( IN OUT PIR_DEVICE pThisDev, IN PVOID pContext ) { PDIAGS_READ_REGISTERS_IOCTL pIOCTL = pThisDev->pIOCTL; PIRUSB_CONTEXT pThisContext = pContext; // // Read the data // pThisDev->IOCTLStatus = St4200ReadRegisters( pThisDev, pIOCTL->FirstRegister, pIOCTL->NumberRegisters ); if( pThisDev->IOCTLStatus == STATUS_SUCCESS ) { NdisMoveMemory( &pIOCTL->pRegisterBuffer, &pThisDev->StIrTranceiver.FifoDataReg+pIOCTL->FirstRegister, pIOCTL->NumberRegisters ); } // // Return the context // ExInterlockedInsertTailList( &pThisDev->SendAvailableQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendAvailableCount ); // // Signal // KeSetEvent( &pThisDev->EventDiags, 0, FALSE ); //signal we're done } /***************************************************************************** * * Function: Diags_CompleteWriteRegister * * Synopsis: Reads the registers and returns the value * * Arguments: pThisDev - pointer to IR device * pContext - pinter to the operation context * * Returns: None * * Notes: * *****************************************************************************/ VOID Diags_CompleteWriteRegister( IN OUT PIR_DEVICE pThisDev, IN PVOID pContext ) { PDIAGS_READ_REGISTERS_IOCTL pIOCTL = pThisDev->pIOCTL; PIRUSB_CONTEXT pThisContext = pContext; // // Copy the new register value // NdisMoveMemory( &pThisDev->StIrTranceiver.FifoDataReg+pIOCTL->FirstRegister, &pIOCTL->pRegisterBuffer, 1 ); // // Write to the device // pThisDev->IOCTLStatus = St4200WriteRegister( pThisDev, pIOCTL->FirstRegister ); // // Return the context // ExInterlockedInsertTailList( &pThisDev->SendAvailableQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendAvailableCount ); // // Signal // KeSetEvent( &pThisDev->EventDiags, 0, FALSE ); //signal we're done } /***************************************************************************** * * Function: Diags_Bulk * * Synopsis: Executes a diagnostic bulk transfer * * Arguments: pThisDev - pointer to IR device * pContext - pinter to the operation context * DirectionOut - TRUE if bulk-out, FALSE if bulk-in * * Returns: None * * Notes: * *****************************************************************************/ VOID Diags_Bulk( IN OUT PIR_DEVICE pThisDev, IN PVOID pContext, BOOLEAN DirectionOut ) { PDIAGS_BULK_IOCTL pIOCTL = pThisDev->pIOCTL; PIRUSB_CONTEXT pThisContext = pContext; NTSTATUS status; PIRP pIrp; PURB pUrb = NULL; PDEVICE_OBJECT pUrbTargetDev; PIO_STACK_LOCATION pNextStack; KIRQL OldIrql; IRUSB_ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL ); IRUSB_ASSERT( NULL != pThisContext ); // // Stop if a halt/reset is going on // if( pThisDev->fPendingWriteClearStall || pThisDev->fPendingHalt || pThisDev->fPendingReset || pThisDev->fPendingClearTotalStall ) { DEBUGMSG(DBG_ERR, (" Diags_Bulk abort due to pending reset or halt\n")); goto done; } pUrb = pThisDev->pUrb; NdisZeroMemory( pUrb, pThisDev->UrbLen ); // // Save the effective length // pThisDev->BufLen = pIOCTL->DataSize; // // Now that we have created the urb, we will send a // request to the USB device object. // pUrbTargetDev = pThisDev->pUsbDevObj; // // make an irp sending to usbhub // pIrp = IoAllocateIrp( (CCHAR)(pThisDev->pUsbDevObj->StackSize + 1), FALSE ); if( NULL == pIrp ) { DEBUGMSG(DBG_ERR, (" Diags_Bulk failed to alloc IRP\n")); goto done; } pIrp->IoStatus.Status = STATUS_PENDING; pIrp->IoStatus.Information = 0; pThisContext->pIrp = pIrp; // // Build our URB for USBD // pUrb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT)sizeof( struct _URB_BULK_OR_INTERRUPT_TRANSFER ); pUrb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; if( DirectionOut ) { pUrb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT ; pUrb->UrbBulkOrInterruptTransfer.PipeHandle = pThisDev->BulkOutPipeHandle; } else { pUrb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN ; pUrb->UrbBulkOrInterruptTransfer.PipeHandle = pThisDev->BulkInPipeHandle; } // short packet is not treated as an error. pUrb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK; pUrb->UrbBulkOrInterruptTransfer.UrbLink = NULL; pUrb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; pUrb->UrbBulkOrInterruptTransfer.TransferBuffer = pIOCTL->pData; pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength = (int)pIOCTL->DataSize; // // Call the class driver to perform the operation. // pNextStack = IoGetNextIrpStackLocation( pIrp ); IRUSB_ASSERT( pNextStack != NULL ); // // pass the URB to the USB driver stack // pNextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; pNextStack->Parameters.Others.Argument1 = pUrb; pNextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; IoSetCompletionRoutine( pIrp, // irp to use Diags_CompleteIrp, // routine to call when irp is done DEV_TO_CONTEXT(pThisContext), // context to pass routine TRUE, // call on success TRUE, // call on error TRUE // call on cancel ); #ifdef SERIALIZE KeClearEvent( &pThisDev->EventSyncUrb ); #endif // // Call IoCallDriver to send the irp to the usb port. // ExInterlockedInsertTailList( &pThisDev->SendPendingQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendPendingCount ); status = MyIoCallDriver( pThisDev, pUrbTargetDev, pIrp ); // // The USB driver should always return STATUS_PENDING when // it receives a write irp // IRUSB_ASSERT( status == STATUS_PENDING ); status = MyKeWaitForSingleObject( pThisDev, &pThisDev->EventSyncUrb, NULL, 0 ); if( status == STATUS_TIMEOUT ) { DEBUGMSG( DBG_ERR,(" Diags_Bulk() TIMED OUT! return from IoCallDriver USBD %x\n", status)); IrUsb_CancelIo( pThisDev, pIrp, &pThisDev->EventSyncUrb ); } done: // // Return the context // KeAcquireSpinLock( &pThisDev->SendLock, &OldIrql ); RemoveEntryList( &pThisContext->ListEntry ); KeReleaseSpinLock( &pThisDev->SendLock, OldIrql ); InterlockedDecrement( &pThisDev->SendPendingCount ); ExInterlockedInsertTailList( &pThisDev->SendAvailableQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendAvailableCount ); // // Signal // KeSetEvent( &pThisDev->EventDiags, 0, FALSE ); //signal we're done } /***************************************************************************** * * Function: Diags_Send * * Synopsis: Sends a packet through the diagnostic path * * Arguments: pThisDev - pointer to IR device * pContext - pinter to the operation context * * Returns: None * * Notes: * *****************************************************************************/ VOID Diags_Send( IN OUT PIR_DEVICE pThisDev, IN PVOID pContext ) { PDIAGS_SEND_IOCTL pIOCTL = pThisDev->pIOCTL; PIRUSB_CONTEXT pThisContext = pContext; NTSTATUS status; PIRP pIrp; PURB pUrb = NULL; PDEVICE_OBJECT pUrbTargetDev; PIO_STACK_LOCATION pNextStack; BOOLEAN fConvertedPacket; KIRQL OldIrql; ULONG BytesToWrite; IRUSB_ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL ); IRUSB_ASSERT( NULL != pThisContext ); // // Stop if a halt/reset is going on // if( pThisDev->fPendingWriteClearStall || pThisDev->fPendingHalt || pThisDev->fPendingReset || pThisDev->fPendingClearTotalStall ) { DEBUGMSG(DBG_ERR, (" Diags_Send abort due to pending reset or halt\n")); goto done; } pUrb = pThisDev->pUrb; NdisZeroMemory( pUrb, pThisDev->UrbLen ); DEBUGMSG(DBG_ERR, (" Diags_Send() packet size: %d\n", pIOCTL->DataSize)); // // Convert the packet to an ir frame and copy into our buffer // and send the irp. // if( pThisDev->currentSpeed<=MAX_SIR_SPEED ) { fConvertedPacket = Diags_BufferToSirPacket( pThisDev, (PUCHAR)pThisDev->pBuffer, MAX_IRDA_DATA_SIZE, pIOCTL->pData, pIOCTL->DataSize, pIOCTL->ExtraBOFs, &BytesToWrite ); } else if( pThisDev->currentSpeed<=MAX_MIR_SPEED ) { fConvertedPacket = Diags_BufferToFirPacket( pThisDev, (PUCHAR)pThisDev->pBuffer, MAX_IRDA_DATA_SIZE, pIOCTL->pData, pIOCTL->DataSize, &BytesToWrite ); } else { fConvertedPacket = Diags_BufferToFirPacket( pThisDev, (PUCHAR)pThisDev->pBuffer, MAX_IRDA_DATA_SIZE, pIOCTL->pData, pIOCTL->DataSize, &BytesToWrite ); } if( fConvertedPacket == FALSE ) { DEBUGMSG(DBG_ERR, (" Diags_Send() NdisToIrPacket failed. Couldn't convert packet!\n")); goto done; } // // Always force turnaround // NdisMSleep( pThisDev->dongleCaps.turnAroundTime_usec ); // // Now that we have created the urb, we will send a // request to the USB device object. // pUrbTargetDev = pThisDev->pUsbDevObj; // // make an irp sending to usbhub // pIrp = IoAllocateIrp( (CCHAR)(pThisDev->pUsbDevObj->StackSize + 1), FALSE ); if( NULL == pIrp ) { DEBUGMSG(DBG_ERR, (" Diags_Send failed to alloc IRP\n")); goto done; } pIrp->IoStatus.Status = STATUS_PENDING; pIrp->IoStatus.Information = 0; pThisContext->pIrp = pIrp; // // Build our URB for USBD // pUrb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT)sizeof( struct _URB_BULK_OR_INTERRUPT_TRANSFER ); pUrb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; pUrb->UrbBulkOrInterruptTransfer.PipeHandle = pThisDev->BulkOutPipeHandle; pUrb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT ; // short packet is not treated as an error. pUrb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK; pUrb->UrbBulkOrInterruptTransfer.UrbLink = NULL; pUrb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; pUrb->UrbBulkOrInterruptTransfer.TransferBuffer = pThisDev->pBuffer; pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength = (int)BytesToWrite; // // Call the class driver to perform the operation. // pNextStack = IoGetNextIrpStackLocation( pIrp ); IRUSB_ASSERT( pNextStack != NULL ); // // pass the URB to the USB driver stack // pNextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; pNextStack->Parameters.Others.Argument1 = pUrb; pNextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; IoSetCompletionRoutine( pIrp, // irp to use Diags_CompleteIrp, // routine to call when irp is done DEV_TO_CONTEXT(pThisContext), // context to pass routine TRUE, // call on success TRUE, // call on error TRUE // call on cancel ); #ifdef SERIALIZE KeClearEvent( &pThisDev->EventSyncUrb ); #endif // // Call IoCallDriver to send the irp to the usb port. // ExInterlockedInsertTailList( &pThisDev->SendPendingQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendPendingCount ); status = MyIoCallDriver( pThisDev, pUrbTargetDev, pIrp ); // // The USB driver should always return STATUS_PENDING when // it receives a write irp // IRUSB_ASSERT( status == STATUS_PENDING ); status = MyKeWaitForSingleObject( pThisDev, &pThisDev->EventSyncUrb, NULL, 0 ); if( status == STATUS_TIMEOUT ) { DEBUGMSG( DBG_ERR,(" Diags_Send() TIMED OUT! return from IoCallDriver USBD %x\n", status)); IrUsb_CancelIo( pThisDev, pIrp, &pThisDev->EventSyncUrb ); } done: KeAcquireSpinLock( &pThisDev->SendLock, &OldIrql ); RemoveEntryList( &pThisContext->ListEntry ); KeReleaseSpinLock( &pThisDev->SendLock, OldIrql ); InterlockedDecrement( &pThisDev->SendPendingCount ); ExInterlockedInsertTailList( &pThisDev->SendAvailableQueue, &pThisContext->ListEntry, &pThisDev->SendLock ); InterlockedIncrement( &pThisDev->SendAvailableCount ); // // Signal // KeSetEvent( &pThisDev->EventDiags, 0, FALSE ); //signal we're done } /***************************************************************************** * * Function: Diags_CompleteIrp * * Synopsis: Completes a USB operation * * Arguments: pUsbDevObj - pointer to the USB device object which * completed the irp * pIrp - the irp which was completed by the * device object * Context - the context given to IoSetCompletionRoutine * before calling IoCallDriver on the irp * The Context is a pointer to the ir device object. * * Returns: STATUS_MORE_PROCESSING_REQUIRED - allows the completion routine * (IofCompleteRequest) to stop working on the irp. * *****************************************************************************/ NTSTATUS Diags_CompleteIrp( IN PDEVICE_OBJECT pUsbDevObj, IN PIRP pIrp, IN PVOID Context ) { PIR_DEVICE pThisDev; NTSTATUS status; PIRUSB_CONTEXT pThisContext = (PIRUSB_CONTEXT)Context; PIRP pContextIrp; PURB pContextUrb; PDIAGS_BULK_IOCTL pIOCTL; // // The context given to IoSetCompletionRoutine is an IRUSB_CONTEXT struct // IRUSB_ASSERT( NULL != pThisContext ); // we better have a non NULL buffer pThisDev = pThisContext->pThisDev; IRUSB_ASSERT( NULL != pThisDev ); pContextIrp = pThisContext->pIrp; pContextUrb = pThisDev->pUrb; pIOCTL = pThisDev->pIOCTL; // // Perform various IRP, URB, and buffer 'sanity checks' // IRUSB_ASSERT( pContextIrp == pIrp ); // check we're not a bogus IRP status = pIrp->IoStatus.Status; pThisDev->IOCTLStatus = status; // // we should have failed, succeeded, or cancelled, but NOT be pending // IRUSB_ASSERT( STATUS_PENDING != status ); // // IoCallDriver has been called on this Irp; // Set the length based on the TransferBufferLength // value in the URB // pIrp->IoStatus.Information = pContextUrb->UrbBulkOrInterruptTransfer.TransferBufferLength; pIOCTL->DataSize = (USHORT)pIrp->IoStatus.Information; // // Free the IRP because we alloced it ourselves, // IoFreeIrp( pIrp ); InterlockedIncrement( (PLONG)&pThisDev->NumWrites ); IrUsb_DecIoCount( pThisDev ); // we will track count of pending irps #ifdef SERIALIZE KeSetEvent( &pThisDev->EventSyncUrb, 0, FALSE ); //signal we're done #endif return STATUS_MORE_PROCESSING_REQUIRED; } #endif