/**************************************************************************** * C++ Header file: ANCOM.H * Description: Analyzer communication driver. * ****************************************************************************/ // Requires windows.h, process.h #define DllExport __declspec(dllexport) #define DllImport __declspec(dllimport) #ifdef _EXPORTING #define CLASS_DECLSPEC DllExport #else #define CLASS_DECLSPEC DllImport #endif struct ThrowAbort { int abortType; ThrowAbort( int abt ) { abortType = abt; } }; #ifndef ANCOM_CPP DllImport short linkSelection; DllImport char *quitMessages[]; DllImport short ancomTimeoutTypes; DllImport char ancomDriverName[]; DllImport DvrStat *dvrStat; #endif enum { QM_SELF, QM_MASTER, QM_GETFAIL, QM_GET, QM_SENDFAIL, QM_SEND, QM_TXUNINIT, QM_RXUNINIT }; // quitMessages IDs. /**************************************************************************** * Class: DvrApi * Description: This provides a shell around the unstructured CreateFile/ * DeviceIoControl API presented to applications by a ring 0 driver. * Authors: David McCracken * ............... notes ................................................ *- DvrApi's constructor doesn't call open or init, both of which are needed * before the object can be used. This allows greater application and user * involvement in configuration but also leaves the program vulnerable to * attempts to use an initialized driver. This is cheaply caught in the * RxClient constructor. However, since there is only one TxServer per app, * this is usually static and can't be constructed using an initialized driver. * Therefore, some TxServer functions need to verify that the driver is * initialized. *.......................................................................*/ class CLASS_DECLSPEC DvrApi { public: struct { ToDvr toDvr; FromDvr fromDvr; }; DvrApi( int toForceDllSegment = 0 ); void command( UINT cmd ); ULONG getTimeout( char *dest, short max, short idx ) { toDvr.gto.dest = dest; toDvr.gto.max = max; toDvr.gto.idx = idx; command( CDXDVR_GETTIMEOUT ); return fromDvr.gto; } void setTimeout( short idx, ULONG timeout ) { toDvr.sto.idx = idx; toDvr.sto.timeout = timeout; command( CDXDVR_SETTIMEOUT ); } void clearStatus( UCHAR clearFailMap, UCHAR clearStatusMap ) { toDvr.cs.clearFailMap = clearFailMap; toDvr.cs.clearStatusMap = clearStatusMap; command( CDXDVR_CLEARSTATUS ); } /* command( CDXDVR_GETSTATUS ) must preceed statusMap, failMap, debugVal functions. e.g. comDvr.command( CDXDVR_GETSTATUS ); if( comDvr.debugVal() != 0 ) breakpoint here; */ USHORT statusMap( void ) { return fromDvr.gs.statusMap; } USHORT failMap( void ) { return fromDvr.gs.failMap; } ULONG debugVal( void ) { return fromDvr.gs.debugVal; } void getStatusName( char *dest, short max, short idx ) { toDvr.gsn.dest = dest; toDvr.gsn.max = max; toDvr.gsn.idx = idx; command( CDXDVR_GETSTATUSNAME ); } void reinit( void ) { command( CDXDVR_REINIT ); } void test( UINT op = 0, UINT val = 0 ) { toDvr.test.op = op; toDvr.test.val = val; command( CDXDVR_TEST ); } }; /**************************************************************************** * Class: TxServer * Description: Interfaces app threads to the driver's transmit queue. There * is only one TxServer per app. Threads are able to share it because it uses a * MUTEX around class data manipulation. A MUTEX is used instead of * CriticalSection in order to protect the ring 0 driver from multiple app's * TxServers. Like DvrApi, a TxServer instance can be instantiated before the * link type is known. * Authors: David McCracken *.......................................................................*/ class CLASS_DECLSPEC TxServer // One of these per app. { enum { EVENT_ABORT = 0, EVENT_TX = 1 }; // events indices. HANDLE txMutex; // Process-relative handle to the multi-process MUTEX. HANDLE events[2]; // ABORT (process-specific) and TX (multi-process). public: TxServer( void ) { txMutex = 0; } // Use as uninitialized indicator. void init( DvrApi *dvr, HANDLE abortEvent ); void reinit( void ); bool isInitialized( void ) { return txMutex != 0; } ULONG sendMsg( UCHAR *msg, DWORD wait = INFINITE ); bool isReceived( ULONG msgId ); }; /**************************************************************************** * Class: RxClient * Description: Interfaces app threads to the driver's message input mechanism. * Each thread needs its own instance because it is uniquely registered with * the driver. Unlike DvrApi and TxServer, an RxClient cannot be instantiated * until the driver has been selected and initialized. * Authors: David McCracken * ............... notes ................................................ * - acceptRange and rejectRange check for a valid object (this != 0) to avoid * crashing if called through an unassigned RxClient *. Normally, RxClients are * not instantiated by new, which is the only way that this situation could * occur, as the user program has no means of calling the functions without * either an inherently valid instance or a questionable pointer. I put the * test in because the debugger program (cdxdbg) does instantiate its one * rxClient using new. Probably no other program has any legitimate reason to * do this, but the test is very cheap insurance. See cdxdbg.cpp rxThreadProc * and CaptureRxDlgProc notes. *.......................................................................*/ enum { RCGM_NORMAL = 0, // Discard previous (if arg matches) and waits for next. RCGM_ONLY_DISCARD, // Discard previous and return 0. RCGM_NO_WAIT // Discard previous and immediately return next or 0 }; // RxClient.getMsg modes class CLASS_DECLSPEC RxClient { enum { EVENT_ABORT = 0, EVENT_RX = 1 }; // events indices HANDLE events[2]; // ABORT and RX DvrApi *dvr; Qctl *qp; public: RxClient( DvrApi *dvr, HANDLE abortEvent, UCHAR begin = 0, UCHAR end = 255 ); // Default filter accepts every message (0-255). ~RxClient( void ) { unregister(); } void unregister( void ); void setRxRange( UCHAR begin, UCHAR end, UINT acceptReject ); UCHAR *getMsg( UCHAR *prev, short mode = RCGM_NORMAL ); USHORT *msgCounter( void ) { return &qp->mcount; } void testMultiple( int msgCount ); }; /**************************************************************************** * Class: ComThreadApi * Description: provides a wrapper for the unstructured beginthread interface. * Authors: David McCracken * .............. notes ........................................................ * - The constructor only assigns MB_UNINIT to the mailbox to prevent an * incompletely initialized instance from being used. Since nothing else is * done, a ComThreadApi object can be instantiated before the driver is known. * However, the driver must be known before the object can be used (this is the * purpose of MB_UNINIT) * * - The parent must have a unique ComThreadApi for each simultaneous thread in * order to have a unique mailbox and quitFor. Currently, each ComThreadApi * object contains an embedded ComThreadArg, which adds 20 bytes to its * instance. This could be declared static and one ComThreadArg could be * defined for the entire application. However, this approach requires each * thread to make local copies of ComThreadArg elements and the parent to be * careful not to start one thread before a previously started one has been * born. Meeting these requirements reduces the memory savings and increases * complexity. The resulting memory saving doesn't seem worth the trouble. *.......................................................................*/ enum { MB_UNINIT, MB_DEAD, MB_BORN, MB_UNBORN, MB_KILL }; // mailbox values. typedef struct { DvrApi *pDvr; int *pMailbox; char **pQuitFor; HANDLE abortEvent; long extra; /* Optional application information, typically a pointer to data, which must be global to be passed between threads. Unless the child thread specifically uses it, this is not needed and can default to 0. */ } ComThreadArg; class CLASS_DECLSPEC ComThreadApi { ComThreadArg arg; public: int mailbox; char *quitFor; ComThreadApi( void ) { mailbox = MB_UNINIT; } bool isInit( void ) { return mailbox != MB_UNINIT; } bool isActive( void ) { return mailbox > MB_DEAD; } bool isRunning( void ) { return mailbox == MB_BORN; } bool isInLimbo( void ) { return mailbox > MB_BORN; } void start( void (*func)( ComThreadArg * ), DvrApi *dvr, HANDLE abortEvent, long extra = 0 ) { arg.pDvr = dvr; arg.pMailbox = &mailbox; arg.pQuitFor = &quitFor; arg.abortEvent = abortEvent; mailbox = MB_UNBORN; arg.extra = extra; _beginthread( (void (__cdecl *)(void *))func, 0, (void *)&arg ); } void kill( HANDLE abort ); }; /**************************************************************************** * Class: TxMsgThread * Description: Com thread specialized for single message transmitter. By * sending a message through this instead of directly to TxServer, a main (user * interface) thread retains the ability to abort on transmit pipe hangup. * Functions: *- send = pass a message to the thread for transmitting. *- kill = abort the thread. * Authors: David McCracken * ............. notes .................................................... * - The kill function is used by the parent (presumably) to kill the thread. * This doesn't affect the TxMsgThread object in any way other than changing * the contents of msg and sigEvent. First a NULL message is sent to the thread * in case it is blocking on message input. Then ComThreadApi.kill is called. * ComThreadApi must be called, even though the thread is usually blocking on * message input, because the thread may occasionally be blocking on access to * the transmit queue, but it would report failure of the thread to wake up and * die if the thread were blocking on message input. Sending a NULL message * when the thread is blocked on queue access doesn't evoke an error message, * because it is either awakened from the block by exception, in which case it * never sees the message, or the queue clears, the thread wakes up (sends the * blocked message), sees the NULL message and dies. ...........................................................................*/ class TxMsgThread: public ComThreadApi { public: UCHAR *msg; HANDLE sigEvent; // Signals the thread to pick up msg for transmit. TxMsgThread( void ) { sigEvent = CreateEvent( NULL, // Default security for 95/98. NT and 2000 require SYNCHRONIZE. FALSE, // Automatic reset. FALSE, // Initially not signalled. NULL ); // No name. } ~TxMsgThread() { CloseHandle( sigEvent ); } void send( UCHAR *src ) { msg = src; SetEvent( sigEvent ); } void kill( HANDLE abort ) { send( 0 ); // Do before ComThreadApi::kill. ComThreadApi::kill( abort ); } }; /**************************************************************************** * Class: ComThread * Authors: David McCracken * Description: Shell for any communication thread, whether transmit, receive, * or both. This only serves to avoid cut and pasting and to ensure that * certain things are done by any thread. * Functions: *- Constructor * Set the default exit message to direct request by master. * Set mailbox to MB_BORN. *- Destructor * Set mailbox to MB_DEAD. * endthread. *- quitFor: set the exit message to the one selected by throw. This is normally * used only in catch blocks, as the default serves otherwise. * * ............. notes .................................................... * - ComThread exists to ensure that communication threads are well behaved. A * thread can operate without doing these things but other parts of the program * would then be unable to determine the status of the thread. It would be * slightly more efficient to let each thread do these jobs itself, because the * ComThread object requires an otherwise unnecessary copy of the ComThreadArg * pointer to use in class functions other than the constructor. * * - This version assumes a ComThreadArg embedded in each ComThreadApi object. * This adds 20 bytes to each instance but it enables successive threads to be * started without waiting for each to be born. The ComThreadApi class could be * defined with a static ComThreadArg to save some memory at the expense of * additional launching complexity, i.e. wait for born before starting another * thread. However, then it is necessary to make local copies of the mailbox * and quitFor pointers (and the driver pointer unless a local DvrApi instance * is used). ComThread would contain these and assign them in the constructor. * However, assigning MB_BORN to the mailbox would have to be deferred until * the driver were used within the try block, as it is not possible to begin * the try block in the constructor (even though it is inline) and close it in * the thread function. * ...........................................................................*/ class ComThread { ComThreadArg *cta; public: ComThread( ComThreadArg *ctap, char *defaultQuitFor ) { cta = ctap; // Copy for class functions. *ctap->pQuitFor = defaultQuitFor; *ctap->pMailbox = MB_BORN; } ~ComThread( void ) { *cta->pMailbox = MB_DEAD; _endthread(); } int& mailbox( void ) { return *cta->pMailbox; } void quitFor( char *reason ) { *cta->pQuitFor = reason; } }; enum { LINK_HSL, LINK_ECP, LINK_USB }; enum { SHOW_LINK = 1, SHOW_LINKMATCH = 2, SHOW_LINKFAIL = 4, SHOW_LINKALL = 0xFFFF }; // openDriver:show. short CLASS_DECLSPEC openDriver( DvrApi *pDvr, short linkSel, HWND hwnd, char *pathexe, USHORT show, FindPorts *fp ); void CLASS_DECLSPEC closeDriver( void ); int CLASS_DECLSPEC getOsVersion( void ); void CLASS_DECLSPEC simulateRx( UCHAR *msg );