//=========================================================================== // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved. // //=========================================================================== // // filename: cdevcom.c // // External Device Communication Functions // //--------------------------------------------------------- // // NOTES: // 1. Code is un-optimized for clarity // 2. Minimal error checking. // 3. Protocol is tested with SVO-5800 (RS-422) and SVBK-10 (RS-232) machines // 4. Timecode reading is supported, but dealing with multiple // calling threads is nasty, so be sure you know what you're // doing before you change anything. // // //--------------------------------------------------------- #include #include #include #include #include #include #include "ctimecod.h" #include "cdevcom.h" //---------------------------------------------------------- // structures // SVO-5800/SVBK-1 Command Structure typedef struct tagSV_CMD{ BYTE bCmd1; // Command byte 1 - lo nybble has data byte // count BYTE bCmd2; // Command byte 2 BYTE bCmd3; // Data if required by command, or checksum if none BYTE bCmd4; // Checksum if 1 data byte BYTE bByteCnt; // How many bytes in the command (including checksum) int iRespCnt; // How many bytes in the response } SV_CMD; // SVO Commands - this is the command table. // ***IT MUST BE IN THE SAME ORDER AS THE COMMAND LIST IN CDEVCOM.H!!!!! static SV_CMD SvoCmdTbl[] = { {0x20,1,0x21,0,3,3}, // Play {0x20,0,0x20,0,3,3}, // Stop {0x61,0x0c,0x11,0x7e,4,11}, // Request LTC {0x41,0x36,1,0x78,4,3}, // Timer Mode Select: LTC {0x20,0x20,0x40,0,3,3}, // Rewind {0x21,0x12,0x0,0x33,4,3}, // Freeze on {0x20,1,0x21,0,3,3}, // Freeze off (really a play command) {0x20,0x04,0x24,0,3,3}, // Standby off {0x20,0x05,0x25,0,3,3}, // Standby on {0x00,0x11,0x11,0,3,5}, // Device Type Request {0x61,0x20,0x08,0x89,4,11}, // Status Request - 8 bytes {0x20,0x0f,0x2f,0,3,3}, // Eject {0x00,0x0c,0x0c,0,3,3}, // Local On {0x00,0x1d,0x1d,0,3,3}, // Local Off {0x20,0x10,0x30,0,3,3}, // Fast Forward {0x20,0x02,0x22,0,3,3}, // Record {0xFF,0,0,0,0,0} // Device Clear }; // device type request table typedef struct tagSVO_DEVTYPE{ BYTE bData1; // 1st data byte of response BYTE bData2; // 2nd data byte of response } SVO_DEVTYPE; static SVO_DEVTYPE m_SvoDevTbl[] = { {0x80, 0x0c}, {0x81, 0x0c}, {0x80, 0x08}, {0x80, 0x08}, {0x81, 0x08}, {0x10, 0x2c}, {0x11, 0x2c}, {0x10, 0x28}, {0x11, 0x28}, {0x10, 0x20}, {0x11, 0x20}, {0x10, 0x21}, {0x11, 0x21}, {0x20, 0x51} }; const int m_DevTypeCnt = sizeof(m_SvoDevTbl); const TCHAR *m_strSvoDevID[] = { "EVO-9850", "EVO-9850P", "EVO-9800", "EVO-9800A", "EVO-9800P", "SVO-9600", "SVO-9620", "SVP-9000", "SVP-9020", "SVO-5800", "SVO-5800P", "SVO-5600", "SVO-5600P", "UVW-1800", }; const TCHAR *m_strUnknownDevID = {"Unknown Device"}; // SVBK Command Table // ***IT MUST BE IN THE SAME ORDER AS THE COMMAND LIST IN CDEVCOM.H!!!!! static SV_CMD m_SvbkCmdTbl[] = { {0x3A,0,0,0,1,1}, // Play {0x3F,0,0,0,1,1}, // Stop {0xA0,0,0,0,1,9}, // Request LTC {0xFF,0,0,0,0,0}, // Timer Mode Select: LTC {0xAC,0,0,0,1,1}, // Rewind {0x4F,0,0,0,1,1}, // Freeze on {0x3A,0,0,0,1,1}, // Freeze off {0xFF,0,0,0,0,0}, // Standby off {0xFF,0,0,0,0,0}, // Standby on {0x8F,0,0,0,1,1}, // Device Type Request {0xD7,0,0,0,1,5}, // Status Request - 5 bytes {0x2A,0,0,0,1,2}, // Eject {0xFF,0,0,0,0,0}, // Local On {0xFF,0,0,0,0,0}, // Local Off {0xAB,0,0,0,1,1}, // Fast Forward {0xFA,0xFA,0xCA,0,3,1}, // Record {0x56,0,0,0,1,1} // Device Clear }; const TCHAR *m_strSvbkDevID = {"SVBK-1"}; const int m_SvbkDevType = 0x80; // which machine's command table are we using? SV_CMD *pVcrCmdTable = m_SvbkCmdTbl; int m_VcrType; // 0 for SVO, 1 for SVBK static TCHAR m_SvbkBuf[16]; // for multi-byte commands // for simulation mode static BYTE m_SvoAck[] = { 0x10,0x01,0x11}; static DWORD m_SvoAckCount = 3L; static BYTE SvbkAck[] = { 0x0A}; static DWORD m_SvbkAckCount = 1L; static TCHAR m_strDevID[] = "Pretend Machine"; static DWORD m_SimulatedFramecount = 0L; static long m_lNDFTable[] = {1080000L,108000L,18000L,1800L,300L,30L,10L,1L}; // COM port stuff #define COMM_TIMEOUT_MS = 60; static TCHAR m_DevPortCom1[] = "COM1"; static TCHAR m_DevPortCom2[] = "COM2"; static TCHAR m_DevPortCom3[] = "COM3"; static TCHAR m_DevPortCom4[] = "COM4"; // init static members HANDLE CDevCom::m_hDevPort = 0; TCHAR * CDevCom::m_pcDevPort = NULL; BYTE CDevCom::m_bLastCmd = 0; BYTE CDevCom::m_LastMotionCmd = 0; int CDevCom::m_CurCmdCookie = 1; int CDevCom::m_LastSentCmdCookie = -1; DCB CDevCom::m_dcb = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0}; int CDevCom::m_PendingResponseCnt = 0; BOOL CDevCom::m_bSimulateHardware = FALSE; COMMTIMEOUTS CDevCom::m_ctmoTimeout = { 0,0,0,0,0}; COMSTAT CDevCom::m_csCommStat = { 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,}; // Command Queue stuff to deal with multi-threading #define COMMANDLISTSIZE 10 #define RESPONSE_SLEEP_TIME 5 // in milliseconds #define RESPONSE_RETRY_MAX 20 // should never have to wait for more than this // numer of sleep times // Use Generic list template class to build command queue. Use of // such a queue will allow the communications class to be thread safe. static CGenericList CommandList("List of Pending Commands"); //intialize an empty array of command objects COMMANDOBJECT CommandObjects[] = { {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff}, {-1,-1, 0xffff} }; // initialize an empty array of response objects - we don't create a linked // list of these because we don't expect to every have more that two or three // buffered responses RESPONSEOBJECT ResponseObject[] = { { -1, -1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, { -1, -1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, { -1, -1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }; #define MAX_RESPONSE_OBJECTS 3 #define DbgFunc(a) DbgLog(( LOG_TRACE, \ 2, \ TEXT("CDevCom::%d"), \ a \ )); //--------------------------------------------------------- // // CDevCom constructor // //--------------------------------------------------------- CDevCom::CDevCom(CBaseFilter *pFilter, HRESULT *phr) : m_pCritSec(NULL) { m_pFilter = pFilter; // need some help with timecode m_pTimecode = new CTimecode(); if (m_pTimecode == NULL) { *phr = E_OUTOFMEMORY; return; } *phr = S_OK; return; } //--------------------------------------------------------- // // Destructor // //--------------------------------------------------------- CDevCom::~CDevCom() { } //--------------------------------------------------------- // // OpenDevPort // // Tries to open the requested port. If port is not available, // enters "simulation" mode // //--------------------------------------------------------- DWORD CDevCom::OpenDevPort( int Port ) { TCHAR resp[20]; DWORD LastError; DWORD dwCommError; int Cookie; timeBeginPeriod(1); // setup 1ms timer for timestamping m_bSimulateHardware = FALSE; // this may have been set TRUE previously // get the port switch (Port){ case DEV_PORT_COM1: m_pcDevPort = m_DevPortCom1; break; case DEV_PORT_COM2: m_pcDevPort = m_DevPortCom2; break; case DEV_PORT_COM3: m_pcDevPort = m_DevPortCom3; break; case DEV_PORT_COM4: m_pcDevPort = m_DevPortCom4; break; case DEV_PORT_SIM: case DEV_PORT_DIAQ: case DEV_PORT_ARTI: default: if (m_hDevPort) CloseDevPort(); m_bSimulateHardware = TRUE; return 0L; } // open the port if ( (m_hDevPort = CreateFile( m_pcDevPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) == (HANDLE) -1 ){ // we're in simulation mode DWORD dwTemp = GetLastError(); m_bSimulateHardware = TRUE; return 1L; } // get the Device Control Block if ( !GetCommState( m_hDevPort, &m_dcb ) ) { CloseDevPort(); m_bSimulateHardware = TRUE; return 1L; } // put in our values m_dcb.BaudRate = 38400; m_dcb.fDtrControl = DTR_CONTROL_ENABLE; m_dcb.fDsrSensitivity = FALSE; m_dcb.fOutX = FALSE; m_dcb.fInX = FALSE; m_dcb.fNull = FALSE; m_dcb.fRtsControl = RTS_CONTROL_ENABLE; m_dcb.fAbortOnError = TRUE; m_dcb.ByteSize = 8; m_dcb.Parity = ODDPARITY; m_dcb.StopBits = ONESTOPBIT; if ( !SetCommState(m_hDevPort, &m_dcb) ){ // failure forces simulation mode m_bSimulateHardware = TRUE; return 1L; } // set the comm timeout if ( !GetCommTimeouts(m_hDevPort, &m_ctmoTimeout) ) { CloseDevPort(); m_bSimulateHardware = TRUE; return 1L; } // timeout after COMM_TIMEOUT_MS milliseconds m_ctmoTimeout.ReadIntervalTimeout = 15L; m_ctmoTimeout.ReadTotalTimeoutMultiplier = 10L; m_ctmoTimeout.ReadTotalTimeoutConstant = 60L; m_ctmoTimeout.WriteTotalTimeoutMultiplier = 1L; m_ctmoTimeout.WriteTotalTimeoutConstant = 60L; if ( !SetCommTimeouts(m_hDevPort, &m_ctmoTimeout) ) { CloseDevPort(); m_bSimulateHardware = TRUE; return 1L; } // now let's see if there is some hardware there // try an SVO type machine first pVcrCmdTable = SvoCmdTbl; m_VcrType = 0; if (SendDevCmd(DEVICE_TYPE_REQUEST, &Cookie) != 0L) { GetDevResponse(resp, Cookie); //cleanup CloseDevPort(); m_bSimulateHardware = TRUE; m_PendingResponseCnt = 0; return 1L; } if ( GetDevResponse(resp, Cookie) != 0L){ // try the SVBK now pVcrCmdTable = m_SvbkCmdTbl; m_VcrType = 1; FlushDevResponseBuffer(); // clean out the UART ClearCommError( m_hDevPort, &dwCommError, &m_csCommStat ); // need to change the baud rate among other things m_dcb.BaudRate = 9600; m_dcb.Parity = NOPARITY; if ( !SetCommState(m_hDevPort, &m_dcb) ){ // set simulation mode LastError = GetLastError(); m_bSimulateHardware = TRUE; return 1L; } Sleep(30L); // give the machine time to recover SendDevCmd(DEVICE_CLEAR, &Cookie); GetDevResponse(resp, Cookie); // DEVICE_CLEAR puts the machine in pause, so put it in stop SendDevCmd(STOP, &Cookie); GetDevResponse(resp, Cookie); Sleep(30L); // give the machine time to recover FlushDevResponseBuffer(); // clean out the UART ClearCommError( m_hDevPort, &dwCommError, &m_csCommStat ); if (SendDevCmd(DEVICE_TYPE_REQUEST, &Cookie) != 0L) { GetDevResponse(resp, Cookie); //cleanup CloseDevPort(); m_bSimulateHardware = TRUE; m_PendingResponseCnt = 0; return 1L; } // we don't actually check the response value here because // it is not always consistant. if ( GetDevResponse(resp, Cookie) != 0L){ CloseDevPort(); m_bSimulateHardware = TRUE; return 1L; } // cleanup ClearCommError( m_hDevPort, &dwCommError, &m_csCommStat ); FlushDevResponseBuffer(); // clean out the UART return 0L; } return 0L; } //--------------------------------------------------------- // // CloseDevPort // // Shuts things down // //--------------------------------------------------------- DWORD CDevCom::CloseDevPort( void ) { DWORD LastError; timeEndPeriod(1); // restore timer period if ( m_hDevPort && !m_bSimulateHardware ) { // close the port if (DeleteFile(m_pcDevPort)) { LastError = GetLastError(); return DEV_COMM_ERR_COMMUNICATION_ERROR; } m_pcDevPort = NULL; if ( !CloseHandle( m_hDevPort ) ) { LastError = GetLastError(); return DEV_COMM_ERR_COMMUNICATION_ERROR; } m_hDevPort = 0; return 0L; } m_bSimulateHardware = FALSE; return 0L; } //--------------------------------------------------------- // // FlushDevResponseBuffer // // A cleanup method // //--------------------------------------------------------- DWORD CDevCom::FlushDevResponseBuffer( void ) { DWORD LastError; DWORD dwRetByteCnt; DWORD dwCommError; char buf[16]; if (m_bSimulateHardware) return 0L; m_PendingResponseCnt = 0; if (PurgeComm(m_hDevPort, PURGE_RXCLEAR)) { LastError = GetLastError(); return DEV_COMM_ERR_COMMUNICATION_ERROR; } ClearCommError( m_hDevPort, &dwCommError, &m_csCommStat ); if (m_csCommStat.cbInQue) { if ( !ReadFile( m_hDevPort, buf, (DWORD)m_csCommStat.cbInQue, (LPDWORD)&dwRetByteCnt, (LPOVERLAPPED)NULL ) ){ LastError = GetLastError(); // could stand a bit better error handling here return DEV_COMM_ERR_COMMUNICATION_ERROR; } } return 0L; } //--------------------------------------------------------- // // SendDevCmd // // Sends command to device (or queues it up for xmission). // Returned a Cookie so the caller can find the // correct response // //--------------------------------------------------------- DWORD CDevCom::SendDevCmd( int Cmd, int *pCookie) { CAutoLock lock(this); PCOMMANDOBJECT pCmdObj; DWORD dwret; // return immediately if command is not supported if (pVcrCmdTable[Cmd].bCmd1 == 0xFF) return DEV_COMM_ERR_COMMAND_NOT_SUPPORTED; // get a new cookie if (m_CurCmdCookie++ == 0) m_CurCmdCookie++; // queue the command if the response has not yet been // pulled for the previously sent command if (m_PendingResponseCnt){ // some extra instructions here for debugging pCmdObj = AddCommand(Cmd, m_CurCmdCookie); if (pCmdObj == NULL){ ASSERT(pCmdObj); return DEV_COMM_ERR_QUEUE_OVERFLOW; } *pCookie = m_CurCmdCookie; return 0L; } dwret = ReallySendDevCmd( Cmd, m_CurCmdCookie); *pCookie = m_CurCmdCookie; return dwret; } //--------------------------------------------------------- // // ReallySendDevCmd // // This does the actual sending of queued commands // //--------------------------------------------------------- DWORD CDevCom::ReallySendDevCmd( int Cmd, int Cookie ) { SV_CMD *svCommand; BYTE bCmdSequenceCnt; DWORD dwBytesWritten; DWORD LastError; DWORD dwret = 0L; TCHAR *pCmd; int i; m_bLastCmd = Cmd; if ( m_isMotionCmd(Cmd) ) // do we still need to know this? m_LastMotionCmd = Cmd; if (!m_bSimulateHardware) { // get the command from the table and shoot it out svCommand = &pVcrCmdTable[Cmd]; // we handle the two machine types differently because the SVBK // machine might have multiple command/response pair sequences if (m_VcrType == 0) { // SVO has one sequence and can send multiple bytes i = (int)svCommand->bByteCnt; bCmdSequenceCnt = 1; } else { // SVBK only sends out one byte per sequence i = 1; bCmdSequenceCnt = svCommand->bByteCnt; } pCmd = (TCHAR *)svCommand; while (bCmdSequenceCnt) { // do this loop for each sequence // send out the command if ( !WriteFile( m_hDevPort, (TCHAR *)pCmd, i, (LPDWORD)&dwBytesWritten, NULL ) ) { LastError = GetLastError(); dwret = DEV_COMM_ERR_COMMUNICATION_ERROR; } if (m_VcrType == 1 && bCmdSequenceCnt > 1) { // must pick up response byte if another sequence is pending m_PendingResponseCnt++; ReallyGetDevResponse(m_SvbkBuf,1); // point to the next command byte in the sequence pCmd++; // special handling for SVBK's Eject command if (svCommand->bCmd1 == 0x2a) Sleep(1000); // wait while EJECT completes } bCmdSequenceCnt--; } } m_LastSentCmdCookie = Cookie; m_PendingResponseCnt++; return dwret; } //--------------------------------------------------------- // // GetDevResponse // // Returns device response to command. If command // hasn't really been sent yet (still in queue), error // is returned in the DWORD // //--------------------------------------------------------- DWORD CDevCom::GetDevResponse( TCHAR *buf, int Cookie ) { CAutoLock lock(this); DWORD dwret = 0L; int temp; PCOMMANDOBJECT ptemp; POSITION posNextCommand; BOOL bNeedCommand = FALSE; // is this command's response buffered? if ( IsResponseBuffered(Cookie, &temp) ) { GetBufferedResponse( buf, Cookie ); // don't send another command out since // a response wasn't cleared out of the COMM port return dwret; } // was this the last command sent? if (Cookie == m_LastSentCmdCookie) { // get the response dwret = ReallyGetDevResponse( buf, pVcrCmdTable[m_bLastCmd].iRespCnt ); if (dwret) return dwret; } else { // since it wasn't, is a response pending? if (m_PendingResponseCnt) { dwret = BufferResponse(); if (dwret) return dwret; } bNeedCommand = TRUE; } // since the command hasn't been sent it must be in the queue, // so send something temp = CommandList.GetCount(); if (temp) { posNextCommand = CommandList.GetHeadPosition(); ptemp = CommandList.Get(posNextCommand); dwret = ReallySendDevCmd( ptemp->Command, ptemp->Cookie ); ptemp = CommandList.RemoveHead(); RemoveCommand(ptemp); return DEV_COMM_ERR_COMMAND_NOT_SENT_YET; } else if (bNeedCommand) return DEV_COMM_ERR_COMMAND_MISSING; return dwret; } //--------------------------------------------------------- // // ReallyGetDevResponse // // Actually handles communications to get device response // //--------------------------------------------------------- DWORD CDevCom::ReallyGetDevResponse( TCHAR *buf, int bytecnt ) { DWORD dwRetByteCnt; DWORD LastError; m_PendingResponseCnt--; if (m_bSimulateHardware) { (GetSimulatedResponse(buf)); } else { if ( !ReadFile( m_hDevPort, buf, (DWORD)bytecnt, (LPDWORD)&dwRetByteCnt, (LPOVERLAPPED)NULL ) ){ LastError = GetLastError(); // could stand a bit better error handling here return DEV_COMM_ERR_COMMUNICATION_ERROR; } if ( dwRetByteCnt != (DWORD)bytecnt ) { return DEV_COMM_ERR_RESPONSE_MISMATCH; } // SVBK doesn't use checksums and its timecode reading // is a bit unpredictable if (m_VcrType == 0) { if ( TestResponse(buf, dwRetByteCnt) ) return DEV_COMM_ERR_RESPONSE_CHECKSUM; } else { // sometimes an extra wierd character is sent // during timecode reading, so we need to watch for it if (bytecnt == pVcrCmdTable[READ_TC].iRespCnt) { // this was a timecode response, so check for multiple // 0xA1's or 0xA2's (+ and -) at the beginning of the buffer. // If we have more than one, we need to get another byte out of // the receive buffer if (buf[1] == 0xA1 || buf[1] == 0xA2) { if ( !ReadFile( m_hDevPort, &buf[pVcrCmdTable[READ_TC].iRespCnt], 1L, (LPDWORD)&dwRetByteCnt, (LPOVERLAPPED)NULL ) ){ LastError = GetLastError(); // could stand a bit better error handling here return DEV_COMM_ERR_COMMUNICATION_ERROR; } if ( dwRetByteCnt != 1L ) return DEV_COMM_ERR_RESPONSE_MISMATCH; } } } } return S_OK; } //--------------------------------------------------------- // // BufferResponse // // Pulls a response out of the COMM device for the most // recently send command // //--------------------------------------------------------- DWORD CDevCom::BufferResponse(void) { int i; // get an available slot in the array for (i=0; i=MAX_RESPONSE_OBJECTS) return DEV_COMM_ERR_RESPONSE_OVERFLOW; // put in the cookie and response byte count ResponseObject[i].Cookie = m_LastSentCmdCookie; ResponseObject[i].ResponseByteCount = pVcrCmdTable[m_bLastCmd].iRespCnt; // get the response return ReallyGetDevResponse( ResponseObject[i].buf, ResponseObject[i].ResponseByteCount ); } //--------------------------------------------------------- // // GetBufferedResponse // // Pulls a response out of the response array // //--------------------------------------------------------- DWORD CDevCom::GetBufferedResponse( TCHAR *buf, int Cookie) { int i; if ( !IsResponseBuffered( Cookie, &i ) ) return DEV_COMM_ERR_RESPONSE_MISSING; // copy the response into the return buffer memcpy( buf, ResponseObject[i].buf, ResponseObject[i].ResponseByteCount); // Clear the entry ResponseObject[i].Cookie = -1; ResponseObject[i].ResponseByteCount = -1; // get the response return S_OK; } //--------------------------------------------------------- // // IsResponseBuffered // // Searches the response array for the given cookie // //--------------------------------------------------------- BOOL CDevCom::IsResponseBuffered( int Cookie, int *index) { int i; // find the response in the array for (i=0; i RESPONSE_RETRY_MAX) return DEV_COMM_ERR_RESPONSE_TIMEOUT; dwret = GetDevResponse(buf, Cookie); if (dwret != 0L && dwret != DEV_COMM_ERR_COMMAND_NOT_SENT_YET) return dwret; if (dwret) Sleep(RESPONSE_SLEEP_TIME); }while (dwret); return dwret; } //--------------------------------------------------------- // // GetSimulatedResponse // // Used for simulation mode // //--------------------------------------------------------- DWORD CDevCom::GetSimulatedResponse( TCHAR *buf ) { switch (m_bLastCmd) { case READ_TC: if (m_LastMotionCmd == PLAY || m_LastMotionCmd == FREEZE_OFF) { m_SimulatedFramecount++; if ( m_SimulatedFramecount > 2591999L ) m_SimulatedFramecount = 0; } else if (m_LastMotionCmd == REWIND) { if (m_SimulatedFramecount >2591999L) m_SimulatedFramecount = 2591999L; else m_SimulatedFramecount-=10; } GetSimulatedTimecode(buf, m_SimulatedFramecount); return 0L; case DEVICE_TYPE_REQUEST: strcpy(buf, m_strDevID); return 0L; case PLAY: case STOP: case TIMER_MODE_SELECT_LTC: case REWIND: case FREEZE_ON: case FREEZE_OFF: case STANDBY_OFF: default: if (m_VcrType == 0) strncpy(buf, (TCHAR *)m_SvoAck, m_SvoAckCount); else strncpy(buf, (TCHAR *)SvbkAck, m_SvbkAckCount); return 0L; } return 0L; } //--------------------------------------------------------- // // m_isMotionCmd // // private helper method to differentiate Motion commands // from things like Timecode requests (sometimes timecode // requests need to be handled differently) // //--------------------------------------------------------- BOOL CDevCom::m_isMotionCmd( BYTE Cmd ) { switch (Cmd) { case READ_TC: case TIMER_MODE_SELECT_LTC: return FALSE; case PLAY: case STOP: case REWIND: case FFWD: case RECORD: case FREEZE_ON: case FREEZE_OFF: case STANDBY_OFF: case EJECT: return TRUE; default: return FALSE; } } //--------------------------------------------------------- // // ProcessDeviceTypeResponse // // Convert response to device type request to string if possible // //--------------------------------------------------------- void CDevCom::ProcessDeviceTypeResponse( TCHAR *bufin, TCHAR *bufout) { int i; if (m_bSimulateHardware) { strcpy( bufout, bufin ); return; } // handle the two machine types differently if (m_VcrType == 0) { // compare the 2 data bytes of the response to the list of known // machine types. If we find a match, return the corresponding // device type string for (i=0; ibCassetteOut = OAFALSE; pStatus->bLocal = OAFALSE; return FALSE; } // we only process two bits of information right now, and // it's hardcoded for clarity and simplicity if (m_VcrType == 0) { // SVO if (bufin[2] & 0x20) pStatus->bCassetteOut = OATRUE; else pStatus->bCassetteOut = OAFALSE; if (bufin[2] & 0x01) pStatus->bLocal = OATRUE; else pStatus->bLocal = OAFALSE; } else { // SVBK if (bufin[0] == 0x0b) // machine can send a NAK if not ready return TRUE; if (bufin[0] & 0x08) pStatus->bCassetteOut = OATRUE; else pStatus->bCassetteOut = OAFALSE; // dummy response for remote/local because that // info is not available pStatus->bLocal = OAFALSE; } return FALSE; } //--------------------------------------------------------- // // AddCommand // // Insert pending command in empty space in array // //--------------------------------------------------------- PCOMMANDOBJECT CDevCom::AddCommand( int Cmd, int Cookie ){ int i; POSITION pos; for (i=0; iCommand = -1; pCmdObj->Cookie = -1; return; } //--------------------------------------------------------- // // TestResponse // // Calculates checksum on response and compares it to embedded // checksum. Returns FALSE if OK, TRUE if not // //--------------------------------------------------------- BOOL CDevCom::TestResponse( TCHAR *buf, int Count ) { BYTE CalcCheckSum = 0; int i; for ( i=0; i<(Count-1); i++) { CalcCheckSum += (BYTE)buf[i]; } if ( CalcCheckSum != (BYTE)buf[(Count-1)] ) return TRUE; return FALSE; } //--------------------------------------------------------- // // ProcessVcrTC - converts the timecode response to a binary // framecount. Handles both SVO and SVBK timecode formats // //--------------------------------------------------------- DWORD CDevCom::ProcessVcrTC( TCHAR *inbuf, DWORD *dwFCM, long *ptimecode, long *puserbits) { BYTE btemp; WORD wtemp; static TCHAR outbuf[12]; static TIMECODE_SAMPLE tcsTimecode; int i=0; TCHAR *ptemp; // look at first byte to determine SVO or SVBK if ((inbuf[0] & 0x70) != 0x70 ) { // SVO's command value // SVBK timecode format is 8 byte ASCII, Hx10 first // hours. Can't tell whether it's drop or nondrop, // so we'll stay nondrop SVBK also has an unpredictable // number of 0xA1's at the head of the buffer, so we must correct. while ( inbuf[i] == (TCHAR)0xA1 || inbuf[i] == (TCHAR)0xA2 ) { i++; } ptemp =&inbuf[i]; wtemp = AM_TIMECODE_30NONDROP; // hours outbuf[0] = ptemp[0]; outbuf[1] = ptemp[1]; outbuf[2] = ':'; // minutes outbuf[3] = ptemp[2]; outbuf[4] = ptemp[3]; outbuf[5] = ':'; // seconds outbuf[6] = ptemp[4]; outbuf[7] = ptemp[5]; outbuf[8] = ':'; // frames outbuf[9] = ptemp[6]; outbuf[10] = ptemp[7]; // terminate outbuf[11] = 0; // convert to binary tcsTimecode.timecode.wFrameRate = wtemp; m_pTimecode->ConvertStringToTimecode(outbuf, &tcsTimecode); *ptimecode = tcsTimecode.timecode.dwFrames; *dwFCM = (DWORD)wtemp; *puserbits = (DWORD)0L; // forget userbits } else { // SVO // SVO t/c format is this: // BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0 //DATA1 x DF 10F | 1F //DATA2 x 10S | 1S //DATA3 x 10M | 1M //DATA4 x x 10H | 1H // // It lives in bytes 2 thru 5 in the response from the machine, and // userbits live in bytes 6 thru 9 // just yank em out and put em in the output buffer // get the dropframe flag if ( inbuf[2] & 0x40 ) wtemp = AM_TIMECODE_30DROP; else wtemp = AM_TIMECODE_30NONDROP; // hours btemp = inbuf[5] & 0x30; btemp = btemp>>(4*sizeof(TCHAR)); btemp += '0'; outbuf[0] = btemp; outbuf[1] = (inbuf[5] & 0x0f) + '0'; outbuf[2] = ':'; // minutes btemp = inbuf[4] & 0x70; btemp = btemp>>(4*sizeof(TCHAR)); outbuf[3] = btemp + '0'; outbuf[4] = (inbuf[4] & 0x0f) + '0'; outbuf[5] = ':'; // seconds btemp = inbuf[3] & 0x70; btemp = btemp>>(4*sizeof(TCHAR)); outbuf[6] = btemp + '0'; outbuf[7] = (inbuf[3] & 0x0f) + '0'; if ( AM_TIMECODE_30DROP == wtemp ) outbuf[8] = ';'; else outbuf[8] = ':'; // frames btemp = inbuf[2] & 0x30; btemp = btemp>>(4*sizeof(TCHAR)); outbuf[9] = btemp + '0'; outbuf[10] = (inbuf[2] & 0x0f) + '0'; // terminate outbuf[11] = 0; // convert to binary tcsTimecode.timecode.wFrameRate = wtemp; m_pTimecode->ConvertStringToTimecode(outbuf, &tcsTimecode); *ptimecode = tcsTimecode.timecode.dwFrames; *dwFCM = (DWORD)wtemp; *puserbits = (DWORD)inbuf[6]; } return 0; } //--------------------------------------------------------- // // For no-hardware mode // //--------------------------------------------------------- void CDevCom::GetSimulatedTimecode( TCHAR * timecode, long framecount ) { long temp; int i; if (m_VcrType == 0) { // SVO t/c format is this: // BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0 //DATA1 x DF 10F | 1F //DATA2 x 10S | 1S //DATA3 x 10M | 1M //DATA4 x x 10H | 1H // // It lives in bytes 2 thru 5 in the response from the machine // Bytes 6 thru 9 are user bits, and 10 is the checksum // put in the command timecode[0] = 0x74; timecode[1] = 0x04; // convert the frame count to Svo t/c format temp = framecount / m_lNDFTable[0]; // hours framecount -= temp * m_lNDFTable[0]; timecode[5] = (TCHAR)temp << (4*sizeof(TCHAR)); temp = framecount / m_lNDFTable[1]; framecount -= temp * m_lNDFTable[1]; timecode[5] |= temp; temp = framecount / m_lNDFTable[2]; // minutes framecount -= temp * m_lNDFTable[2]; timecode[4] = (TCHAR)temp << (4*sizeof(TCHAR)); temp = framecount / m_lNDFTable[3]; framecount -= temp * m_lNDFTable[3]; timecode[4] |= temp; temp = framecount / m_lNDFTable[4]; // seconds framecount -= temp * m_lNDFTable[4]; timecode[3] = (TCHAR)temp << (4*sizeof(TCHAR)); temp = framecount / m_lNDFTable[5]; framecount -= temp * m_lNDFTable[5]; timecode[3] |= temp; temp = framecount / m_lNDFTable[6]; // frames framecount -= temp * m_lNDFTable[6]; timecode[2] = (TCHAR)temp << (4*sizeof(TCHAR)); timecode[2] |= framecount; // do the checksum timecode[10] = 0; for (i=0; i<11; i++) { timecode[10] +=timecode[i]; } } else { // SVBK timecode format is 8 byte ASCII, Hx10 first // hours. Can't tell whether it's drop or nondrop, so we'll stay // nondrop temp = framecount / m_lNDFTable[0]; // hours framecount -= temp * m_lNDFTable[0]; timecode[0] = (TCHAR)temp | 0x30; temp = framecount / m_lNDFTable[1]; framecount -= temp * m_lNDFTable[1]; timecode[1] = temp | 0x30; temp = framecount / m_lNDFTable[2]; // minutes framecount -= temp * m_lNDFTable[2]; timecode[2] = (TCHAR)temp | 0x30; temp = framecount / m_lNDFTable[3]; framecount -= temp * m_lNDFTable[3]; timecode[3] = temp | 0x30; temp = framecount / m_lNDFTable[4]; // seconds framecount -= temp * m_lNDFTable[4]; timecode[4] = (TCHAR)temp | 0x30; temp = framecount / m_lNDFTable[5]; framecount -= temp * m_lNDFTable[5]; timecode[5] = temp | 0x30; temp = framecount / m_lNDFTable[6]; // frames framecount -= temp * m_lNDFTable[6]; timecode[6] = (TCHAR)temp | 0x30; timecode[7] = framecount | 0x30; } return; } // eof cdevcom.cpp