/* * TV app. interaction with nxtvepg via shared memory * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You find a copy of this * license in the file COPYRIGHT in the root directory of this release. * * THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, * BUT WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * Description: * * This module is the counterpart to winshmsrv.c in nxtvepg, * i.e. it's used on side of the TV application to comunicate * with nxtvepg. * * In the init function the module creates event handles that * are shared between the TV and EPG applications. It also * creates a thread that starts waiting for triggers on the * dedicated TV event handle. When the presence of an EPG * application is detected - either by receiving a trigger or * by existance of the event handles before they were created * in the init function - the module attaches to shared memory * (which exclusively is created by the EPG app) and initializes * the TV params, e.g. the application name. * * While the TV and EPG apps are connected they both wait for * triggers on their respective event handles. A trigger is * sent whenever any value in shared memory was changed. When * a trigger is received, the receiving app has to compare all * values in shared memory to find out which have changed. See * winshm.h for a list of parameters in shared memory. To * simplify the comparison there are index values for complex * elements, which are incremented each time the elements are * changed. Whenever non-atomic values are changed of referenced, * the shared memory mutex must be locked (i.e. "owned"). * * When debugging is enabled, i.e. compiler switches DEBUG_SWITCH_TVSIM * set to ON in mytypes.h and DPRINTF_OFF commented out below, you can * watch the message exchange with DebugView from sysinternals.com * * Author: Tom Zoerner * * $Id: winshmclnt.c,v 1.10 2004/03/28 13:18:27 tom Exp tom $ */ #define DEBUG_SWITCH DEBUG_SWITCH_TVSIM //#define DPRINTF_OFF #include #include #include #include #include "epgctl/mytypes.h" #include "epgctl/debug.h" #include "epgvbi/vbidecode.h" #include "epgvbi/btdrv.h" #include "epgvbi/winshm.h" #include "epgvbi/winshmsrv.h" #include "tvsim/winshmclnt.h" // forward function declarations static bool WinSharedMemClient_AttachShm( void ); static void WinSharedMemClient_DetachShm( void ); // --------------------------------------------------------------------------- // Declaration and initialization of module global variables // // pointer to shared memory static volatile TVAPP_COMM * pTvShm = NULL; // handle to shared memory static HANDLE map_fd = NULL; // handle to EPG app event: used to trigger the EPG app. after updates in shared memory static HANDLE epgEventHandle = NULL; // handle to TV app event: used to wait for triggers from the EPG app. static HANDLE tvEventHandle = NULL; // handle to TV mutex: used to make sure only one EPG client is running at the same time static HANDLE tvMutexHandle = NULL; // handle to shared memory mutex: used to synchronize access to shared memory static HANDLE shmMutexHandle = NULL; // handle to message thread: the thread waits for event on out TV event handle static HANDLE msgThreadHandle = NULL; // flag to tell the message receptor thread to terminate static volatile bool StopMsgThread; // set to TRUE by the message receptor when a trigger is received static volatile bool epgTriggerReceived; // error message of last request or background operation static uchar * pLastErrorText = NULL; // application parameters, passed down during initialization static const WINSHMCLNT_TVAPP_INFO * pTvAppInfo; // --------------------------------------------------------------------------- // Struct that holds state of SHM when last processed // - used to detect state changes, i.e. when the event is signaled, the // current state inside shm is compared with this cache; state changes // are equivalent to messages (e.g. incoming EPG info when epgProgInfoIdx // is incremented) // - also used to cache parameters that were handed down from GUI, // to save them while detached; they are copied into SHM upon attach // - the variables have the same meaning as the equally named ones in // shared memory, so see there for comments // typedef struct { uint32_t epgProgInfoIdx; uint32_t epgCommandIdx; bool tvGrantTuner; uint8_t tvCardIdx; uint32_t tvCurFreq; uint32_t tvCurInput; uint32_t epgReqFreq; uint32_t epgReqInput; } EPG_SHM_STATE; static EPG_SHM_STATE epgShmCache; // ---------------------------------------------------------------------------- // Save text describing error cause // - argument list has to be terminated with NULL pointer // - to be displayed by the GUI to help the user fixing the problem // static void WinSharedMemClient_SetErrorText( DWORD errCode, const char * pText, ... ) { va_list argl; const char *argv[20]; uint argc, sumlen, off, idx; // free the previous error text if (pLastErrorText != NULL) { debug0("WinSharedMemClient-SetErrorText: Warning: previous error text unprocessed - discarding"); xfree(pLastErrorText); pLastErrorText = NULL; } // collect all given strings if (pText != NULL) { argc = 1; argv[0] = pText; sumlen = strlen(pText); va_start(argl, pText); while (argc < 20 - 1) { argv[argc] = va_arg(argl, char *); if (argv[argc] == NULL) break; sumlen += strlen(argv[argc]); argc += 1; } va_end(argl); // reserve additional space for system error code if (errCode != 0) sumlen += 100; // allocate memory for sum of all strings length pLastErrorText = xmalloc(sumlen + 1); // concatenate the strings off = 0; for (idx=0; idx < argc; idx++) { strcpy(pLastErrorText + off, argv[idx]); off += strlen(argv[idx]); } if (errCode != 0) { // append system error message strcpy(pLastErrorText + off, ": "); off += 2; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, LANG_SYSTEM_DEFAULT, pLastErrorText + off, 100 - 1, NULL); off += strlen(pLastErrorText + off); if ((pLastErrorText[off - 2] == '\r') && (pLastErrorText[off - 1] == '\n')) { // remove CR/NL characters from the end of the string pLastErrorText[off - 2] = 0; } } debug1("%s", pLastErrorText); } } // --------------------------------------------------------------------------- // Retrieve and clear the last error message // - this function should always be called when an error is indicated // - the caller must free the allocated memory! // const uchar * WinSharedMemClient_GetErrorMsg( void ) { const uchar * pErrMsg = pLastErrorText; // clear the error message if (pLastErrorText != NULL) { pLastErrorText = NULL; } else debug0("WinSharedMemClient-GetErrorMsg: warning: no error message available"); // memory must be freed by the caller! return pErrMsg; } // --------------------------------------------------------------------------- // Generate EPG version or shared memory size mismatch error message // static void WinSharedMemClient_VersionErrorMsg( uint32_t epgShmSize, uint32_t epgShmVersion ) { char strBuf[50]; if (epgShmVersion != EPG_SHM_VERSION) { sprintf(strBuf, "%d.%d.%d (expected %d.%d.%d)", EPG_SHM_VERSION_MAJOR(epgShmVersion), EPG_SHM_VERSION_MINOR(epgShmVersion), EPG_SHM_VERSION_PATLEV(epgShmVersion), EPG_SHM_VERSION_MAJOR(EPG_SHM_VERSION), EPG_SHM_VERSION_MINOR(EPG_SHM_VERSION), EPG_SHM_VERSION_PATLEV(EPG_SHM_VERSION)); WinSharedMemClient_SetErrorText(0, "EPG attach failed: incompatible version ", strBuf, NULL); } else if (epgShmSize != sizeof(TVAPP_COMM)) { sprintf(strBuf, "0x%x (expected 0x%x)", epgShmSize, sizeof(TVAPP_COMM)); WinSharedMemClient_SetErrorText(0, "EPG attach failed: incompatible shared memory size ", strBuf, NULL); } else fatal0("WinSharedMemClient-VersionErrorMsg: invalid params"); } // --------------------------------------------------------------------------- // GUI request: Grant the tuner to the EPG app // - if doGrant is TRUE, the EPG application is allowed to change input source // and tuner frequency to that of an EPG content provider // - should be called whenever the user suspends TV display, i.e. no video // input is required by the TV application itself; the card can then be // used by the EPG App to update all it's databases // bool WinSharedMemClient_GrantTuner( bool doGrant ) { if ( (pTvShm != NULL) && (epgShmCache.tvGrantTuner != doGrant) ) { // wait for the semaphore and request "ownership" if (WaitForSingleObject(shmMutexHandle, INFINITE) != WAIT_FAILED) { pTvShm->tvGrantTuner = doGrant; epgShmCache.tvGrantTuner = doGrant; // release the semaphore if (ReleaseMutex(shmMutexHandle) == 0) debug1("WinSharedMemClient-GrantTuner: ReleaseMutex: %ld", GetLastError()); // wake up the receiver if (SetEvent(epgEventHandle) == 0) debug1("WinSharedMemClient-GrantTuner: SetEvent: %ld", GetLastError()); } else debug1("WinSharedMemClient-GrantTuner: WaitForSingleObject: %ld", GetLastError()); } else { // currently not connected -> save the state in the cache // the value will be copied into shared memory upon an attach epgShmCache.tvGrantTuner = doGrant; } return TRUE; } // --------------------------------------------------------------------------- // GUI request: Notify the EPG peer about a TV channel change // - the EPG app will reply with EPG info for the named station a.s.a.p // bool WinSharedMemClient_SetStation( const char * pChanName, uint cni, uint inputSrc, uint freq ) { if (pTvShm != NULL) { // wait for the semaphore and request "ownership" if (WaitForSingleObject(shmMutexHandle, INFINITE) != WAIT_FAILED) { // copy the values in the cache epgShmCache.tvCurFreq = freq; epgShmCache.tvCurInput = inputSrc; pTvShm->tvCurFreq = freq; pTvShm->tvCurInput = inputSrc; strncpy((char *) pTvShm->tvChanName, pChanName, CHAN_NAME_MAX_LEN); pTvShm->tvChanName[CHAN_NAME_MAX_LEN - 1] = 0; // Note: if a non-zero CNI value is given, can be used by the EPG app // to identify the new channel instead of the channel name. This has // the advantage that the user does not have to manually match all // channel names. // The CNI can be obtained from VPS, PDC or teletext packet 8/30/1 // during the initial channel scan; do NOT copy the currently received // CNI here; this is already passed to the EPG app and managed separately. // (note: if you obtain a CNI from P8/30/1 you must convert it to // equivalent VPS/PDC codes if available; see CniConvertP8301ToVps() // in epgvbi/cni_tables.c) pTvShm->tvChanCni = 0; // enable this only if you have CNIs in your TV app (the TV simulator does not) //pTvShm->tvChanCni = cni; pTvShm->tvChanNameIdx += 1; // release the semaphore if (ReleaseMutex(shmMutexHandle) == 0) debug1("WinSharedMemClient-SetChannel: ReleaseMutex: %ld", GetLastError()); // wake up the receiver if (SetEvent(epgEventHandle) == 0) debug1("WinSharedMemClient-SetChannel: SetEvent: %ld", GetLastError()); } else debug1("WinSharedMemClient-SetChannel: WaitForSingleObject: %ld", GetLastError()); } else { // not attached -> save the values in the cache; required upon attach epgShmCache.tvCurFreq = freq; epgShmCache.tvCurInput = inputSrc; } return TRUE; } // --------------------------------------------------------------------------- // GUI request: Notify the EPG peer about a TV channel change // - update current tuner freq and input source in shared memory; this function // does NOT request EPG info for that channel - if you need that, use the // function above // - this function must be called whenever the tuner frequency is changed, // because the EPG application must known where it's receiving VBI data // from // bool WinSharedMemClient_SetInputFreq( uint inputSrc, uint freq ) { if (pTvShm != NULL) { // wait for the semaphore and request "ownership" if (WaitForSingleObject(shmMutexHandle, INFINITE) != WAIT_FAILED) { pTvShm->tvCurInput = inputSrc; pTvShm->tvCurFreq = freq; // release the semaphore if (ReleaseMutex(shmMutexHandle) == 0) debug1("WinSharedMemClient-SetInputFreq: ReleaseMutex: %ld", GetLastError()); // wake up the receiver if (SetEvent(epgEventHandle) == 0) debug1("WinSharedMemClient-SetInputFreq: SetEvent: %ld", GetLastError()); } else debug1("WinSharedMemClient-SetInputFreq: WaitForSingleObject: %ld", GetLastError()); } return TRUE; } // --------------------------------------------------------------------------- // Fetch program information from shared memory // - note: GUI must check validity of received data // (i.e. title empty or stop time smaller start time) // bool WinSharedMemClient_GetProgInfo( time_t * pStart, time_t * pStop, uchar * pThemes, uint * pThemeCount, uchar * pTitle, uint maxTitleLen ) { bool result = FALSE; if (WaitForSingleObject(shmMutexHandle, INFINITE) != WAIT_FAILED) { if ((pTitle != NULL) && (maxTitleLen > 0)) { if (maxTitleLen > EPG_TITLE_MAX_LEN) maxTitleLen = EPG_TITLE_MAX_LEN; strncpy(pTitle, (char *) pTvShm->epgProgTitle, maxTitleLen); pTitle[maxTitleLen - 1] = 0; } if (pStart != NULL) *pStart = pTvShm->epgStartTime; if (pStop != NULL) *pStop = pTvShm->epgStopTime; if (pThemes != 0) memcpy(pThemes, (char *) pTvShm->epgPdcThemes, 7); if (pThemeCount != NULL) *pThemeCount = pTvShm->epgPdcThemeCount;; epgShmCache.epgProgInfoIdx = pTvShm->epgProgInfoIdx; if (ReleaseMutex(shmMutexHandle) == 0) debug1("WinSharedMemClient-GetProgInfo: ReleaseMutex: %ld", GetLastError()); if ((pThemes != NULL) && (maxTitleLen> 0)) dprintf1("EPG title received: \"%s\"\n", pTitle); result = TRUE; } else debug1("WinSharedMemClient-GetProgInfo: WaitForSingleObject: %ld", GetLastError()); return result; } // --------------------------------------------------------------------------- // Fetch EPG command vector from shared memory // bool WinSharedMemClient_GetCmdArgv( uint * pArgc, uint * pArgLen, char * pCmdBuf, uint cmdMaxLen ) { bool result = FALSE; if (WaitForSingleObject(shmMutexHandle, INFINITE) != WAIT_FAILED) { if ((pCmdBuf != NULL) && (cmdMaxLen > 0)) { if (cmdMaxLen > EPG_CMD_MAX_LEN) cmdMaxLen = EPG_CMD_MAX_LEN; memcpy(pCmdBuf, (char *) pTvShm->epgCommand, cmdMaxLen); } if (pArgc != NULL) *pArgc = pTvShm->epgCmdArgc; if (pArgLen != NULL) *pArgLen = pTvShm->epgCmdArgLen; // signal EPG app that command is processed and we're ready for the next command pTvShm->tvCommandIdx = pTvShm->epgCommandIdx; epgShmCache.epgCommandIdx = pTvShm->epgCommandIdx; if (ReleaseMutex(shmMutexHandle) == 0) debug1("WinSharedMemClient-GetCmdArgv: ReleaseMutex: %ld", GetLastError()); if ((pArgc != NULL) && (pCmdBuf != NULL) && (cmdMaxLen > 0)) dprintf3("EPG command received (len=%d argc=%d): \"%s\"\n", *pArgLen, *pArgc, pCmdBuf); // notify EPG app that shared memory content has changed if (SetEvent(epgEventHandle) == 0) debug1("WinSharedMemClient-GetCmdArgv: SetEvent: %ld", GetLastError()); result = TRUE; } else debug1("WinSharedMemClient-GetCmdArgv: WaitForSingleObject: %ld", GetLastError()); return result; } // --------------------------------------------------------------------------- // Fetch requested input source and tuner frequency // bool WinSharedMemClient_GetInpFreq( uint * pInputSrc, uint * pFreq ) { bool result = FALSE; if (WaitForSingleObject(shmMutexHandle, INFINITE) != WAIT_FAILED) { epgShmCache.epgReqInput = pTvShm->epgReqInput; epgShmCache.epgReqFreq = pTvShm->epgReqFreq; if (ReleaseMutex(shmMutexHandle) == 0) debug1("WinSharedMemClient-GetInpFreq: ReleaseMutex: %ld", GetLastError()); if (pInputSrc != NULL) *pInputSrc = epgShmCache.epgReqInput; if (pFreq != NULL) *pFreq = epgShmCache.epgReqFreq; dprintf2("EPG requests input %d, freq %d\n", epgShmCache.epgReqInput, epgShmCache.epgReqFreq); result = TRUE; } else debug1("WinSharedMemClient-GetInpFreq: WaitForSingleObject: %ld", GetLastError()); return result; } // --------------------------------------------------------------------------- // Process changes in shared memory after the event was signaled // - compares all shared memory elements with the previously cached value // - if a change is detected it's reported by the return code; the caller // should invoke this function repeatedly until event NONE is returned // WINSHMCLNT_EVENT WinSharedMemClient_GetEpgEvent( void ) { bool triggered; // check if we were triggered by the EPG app or if this is just a polling triggered = epgTriggerReceived; if (triggered) { // note: only clear the var if it's set to avoid race condition with msg thread epgTriggerReceived = FALSE; } if (pTvShm == NULL) { // attempt an attach only if we received a trigger on our event handle if (triggered) { // received an event while shared memory not connected yet -> attempt to attach WinSharedMemClient_AttachShm(); if (pTvShm != NULL) { // attach successful, i.e. EPG application is up and running dprintf0("EPG app started - attached SHM\n"); return SHM_EVENT_ATTACH; } else { // failed to attach: report significant errors (like version conflicts) return SHM_EVENT_ATTACH_ERROR; } } } if (pTvShm != NULL) { if (pTvShm->epgAppAlive == FALSE) { dprintf0("EPG app terminated - detaching SHM\n"); // detach from shared memory, close mutex handle; event handles remain open WinSharedMemClient_DetachShm(); return SHM_EVENT_DETACH; } else { // EPG app is running: check for changes in EPG controlled params if (epgShmCache.epgProgInfoIdx != pTvShm->epgProgInfoIdx) { // EPG information was updated return SHM_EVENT_PROG_INFO; } if (epgShmCache.epgCommandIdx != pTvShm->epgCommandIdx) { return SHM_EVENT_CMD_ARGV; } if ( (epgShmCache.epgReqInput != pTvShm->epgReqInput) || (epgShmCache.epgReqFreq != pTvShm->epgReqFreq) ) { if (epgShmCache.tvGrantTuner) { return SHM_EVENT_INP_FREQ; } } } } return SHM_EVENT_NONE; } // --------------------------------------------------------------------------- // Thread which waits for incoming messages sent by an EPG app // - to avoid dealing with concurrency problems the thread only wakes up the // main thread and triggers it to call the above envent handler function // static DWORD WINAPI WinSharedMemClient_EventThread( LPVOID dummy ) { for (;;) { if (WaitForSingleObject(tvEventHandle, INFINITE) == WAIT_FAILED) break; if (StopMsgThread == FALSE) { epgTriggerReceived = TRUE; // trigger the main thread to query shared memory status if (pTvAppInfo->pCbEpgEvent != NULL) pTvAppInfo->pCbEpgEvent(); } else break; } return 0; } // --------------------------------------------------------------------------- // Attach to shared memory // - requires that the EPG application created the shared memory before; // if not, or any other error occurs, this function returns FALSE // static bool WinSharedMemClient_AttachShm( void ) { DWORD errCode; bool result = FALSE; shmMutexHandle = OpenMutex(MUTEX_ALL_ACCESS, FALSE, SHM_MUTEX_NAME); if (shmMutexHandle != NULL) { if (WaitForSingleObject(shmMutexHandle, INFINITE) != WAIT_FAILED) { map_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, EPG_SHM_NAME); if (map_fd != NULL) { pTvShm = MapViewOfFileEx(map_fd, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TVAPP_COMM), NULL); if (pTvShm != NULL) { if (pTvShm->epgAppAlive == TRUE) { if ( (pTvShm->epgShmVersion == EPG_SHM_VERSION) && (pTvShm->epgShmSize == sizeof(TVAPP_COMM)) ) { // initialize TV app.'s params in shared memory pTvShm->tvReqTvCard = TRUE; pTvShm->tvCardIdx = epgShmCache.tvCardIdx; pTvShm->tvFeatures = pTvAppInfo->tvFeatures; pTvShm->tvAppType = pTvAppInfo->tvAppType; strncpy((char *) pTvShm->tvAppName, pTvAppInfo->pAppName, TVAPP_NAME_MAX_LEN); strncpy((char *) pTvShm->tvAppPath, pTvAppInfo->tvAppPath, TVAPP_PATH_MAX_LEN); pTvShm->tvGrantTuner = epgShmCache.tvGrantTuner; pTvShm->tvCurFreq = epgShmCache.tvCurFreq; pTvShm->tvCurInput = epgShmCache.tvCurInput; pTvShm->tvAppAlive = TRUE; // initialize state cache with EPG app.'s params epgShmCache.epgProgInfoIdx = pTvShm->epgProgInfoIdx; epgShmCache.epgCommandIdx = pTvShm->epgCommandIdx; // do not copy the actual values here, to trigger an event in the handler epgShmCache.epgReqFreq = EPG_REQ_FREQ_NONE; epgShmCache.epgReqInput = EPG_REQ_INPUT_NONE; if (SetEvent(epgEventHandle) != 0) { // set VBI buffer address (used by TTX/EPG decoder) pVbiBuf = &pTvShm->vbiBuf; result = TRUE; } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG attach failed: can't trigger EPG event", NULL); } else WinSharedMemClient_VersionErrorMsg(pTvShm->epgShmSize, pTvShm->epgShmVersion); } else debug0("WinSharedMemClient-AttachShm: EPG app no longer alive"); if (result == FALSE) { UnmapViewOfFile((void *) pTvShm); pTvShm = NULL; } } else { // failed to map shared memory -> map only first two words to check the version number errCode = GetLastError(); pTvShm = MapViewOfFileEx(map_fd, FILE_MAP_ALL_ACCESS, 0, 0, 2 * sizeof(uint32_t), NULL); if (pTvShm != NULL) { if ( (pTvShm->epgShmSize != sizeof(TVAPP_COMM)) || (pTvShm->epgShmVersion != EPG_SHM_VERSION) ) { WinSharedMemClient_VersionErrorMsg(pTvShm->epgShmSize, pTvShm->epgShmVersion); } else WinSharedMemClient_SetErrorText(errCode, "EPG attach failed: can't map shared memory", NULL); if (UnmapViewOfFile((void *) pTvShm) == 0) debug1("WinSharedMemClient-AttachShm: UnmapViewOfFile: %ld", GetLastError()); pTvShm = NULL; } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG attach failed: can't map shared memory", NULL); } if (result == FALSE) { CloseHandle(map_fd); map_fd = NULL; } } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG attach failed: can't open shared memory handle", NULL); if (ReleaseMutex(shmMutexHandle) == 0) debug1("WinSharedMemClient-AttachShm: ReleaseMutex: %ld", GetLastError()); } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG attach failed: can't lock shared memory mutex", NULL); if (result == FALSE) { CloseHandle(shmMutexHandle); shmMutexHandle = NULL; } } else { // note: this error is not reported to the user because it's not necessarily an error; // we don't know what the EPG app was trying to tell us; it might just have shut down debug1("WinSharedMemClient-AttachShm: OpenMutex: %ld", GetLastError()); } return result; } // --------------------------------------------------------------------------- // Detach from shared memory // static void WinSharedMemClient_DetachShm( void ) { // the VBI buffer is accessed by an async thread, so it must be invalidated before shm is freed pVbiBuf = NULL; if ((shmMutexHandle != NULL) && (CloseHandle(shmMutexHandle) == 0)) debug1("WinSharedMemClient-DetachShm: CloseHandle shmMutexHandle: %ld", GetLastError()); shmMutexHandle = NULL; if ((map_fd != NULL) && (CloseHandle(map_fd) == FALSE)) debug1("WinSharedMemClient-DetachShm: CloseHandle map_fd: %ld", GetLastError()); map_fd = NULL; if ((pTvShm != NULL) && (UnmapViewOfFile((void *) pTvShm) == 0)) debug1("WinSharedMemClient-DetachShm: UnmapViewOfFile: %ld", GetLastError()); pTvShm = NULL; } // --------------------------------------------------------------------------- // Initialize the module // - has to be called exactly once during startup // - create the resources which are required for inter-process communication // - an already running EPG app can be detected by checking if the resources // already are created // - if the EPG app is running, an attach is attempted right away; note: this // must be done immediately, because the BT8x8 driver must be freed; // a successful or failed attach is reported via the event pointer argument // - returns TRUE on success; // returns FALSE upon fatal errors, i.e. the module will remain "dead" // bool WinSharedMemClient_Init( const WINSHMCLNT_TVAPP_INFO * pInitInfo, uint cardIdx, WINSHMCLNT_EVENT * pEvent ) { WINSHMCLNT_EVENT attachEvent = SHM_EVENT_NONE; uint idx; bool epgAppAlive; DWORD msgThreadId; bool result = FALSE; if (pInitInfo != NULL) { // save TV application parameters pTvAppInfo = pInitInfo; // initialize the state cache for the TV app side memset(&epgShmCache, 0, sizeof(epgShmCache)); epgShmCache.tvCardIdx = cardIdx; epgShmCache.tvCurFreq = EPG_REQ_FREQ_NONE; epgShmCache.tvCurInput = EPG_REQ_INPUT_NONE; tvMutexHandle = CreateMutex(NULL, TRUE, TV_MUTEX_NAME); if (tvMutexHandle != NULL) { if (GetLastError() != ERROR_ALREADY_EXISTS) { tvEventHandle = CreateEvent(NULL, FALSE, FALSE, TV_SHM_EVENT_NAME); if (tvEventHandle != NULL) { epgEventHandle = CreateEvent(NULL, FALSE, FALSE, EPG_SHM_EVENT_NAME); if (epgEventHandle != NULL) { epgAppAlive = (GetLastError() == ERROR_ALREADY_EXISTS); StopMsgThread = FALSE; epgTriggerReceived = FALSE; msgThreadHandle = CreateThread(NULL, 0, WinSharedMemClient_EventThread, NULL, 0, &msgThreadId); if (msgThreadHandle != NULL) { if (epgAppAlive) { // EPG app already running -> open event and signal the app dprintf0("WinSharedMemClient-Init: EPG app already running, attaching SHM\n"); if ( WinSharedMemClient_AttachShm() ) { assert(pTvShm != NULL); // pointer initialized by attach // wait for the EPG app to free the driver for (idx=0; idx < 2; idx++) { if ( (pTvShm->epgHasDriver == FALSE) || ((pTvShm->epgTvCardIdx != cardIdx) && (pTvShm->epgTvCardIdx != TVAPP_CARD_REQ_ALL)) ) break; if (SetEvent(epgEventHandle) == 0) debug1("WinSharedMemClient-Init: SetEvent: %ld", GetLastError()); if (WaitForSingleObject(tvEventHandle, 1000) == WAIT_FAILED) debug1("WinSharedMemClient-Init: WaitForSingleObject tvEventHandle: %ld", GetLastError()); } dprintf2("WinSharedMemClient-Init: epgHasDriver=%d epgTvCardIdx=%d\n", pTvShm->epgHasDriver, pTvShm->epgTvCardIdx); attachEvent = SHM_EVENT_ATTACH; } else if (pLastErrorText != NULL) { // report attach failure (note that some types of errors are ignored) attachEvent = SHM_EVENT_ATTACH_ERROR; } } if (pEvent != NULL) *pEvent = attachEvent; // initialization completed successfully // (the attach to an EPG app may have failed, but this is reported separately) result = TRUE; } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG client init failed: can't create message receptor thread", NULL); if (result == FALSE) { if (CloseHandle(epgEventHandle) == 0) debug1("WinSharedMemClient-Init: CloseHandle epgEventHandle: %ld", GetLastError()); epgEventHandle = NULL; } } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG client init failed: can't create EPG event handle", NULL); if (result == FALSE) { if (CloseHandle(tvEventHandle) == 0) debug1("WinSharedMemClient-Init: CloseHandle tvEventHandle: %ld", GetLastError()); tvEventHandle = NULL; } } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG client init failed: can't create TV event handle", NULL); // the TV mutex can be always be released - its existance is used to check if the TV app is alive if (ReleaseMutex(tvMutexHandle) == 0) debug1("WintvSharedMem-Init: ReleaseMutex TV: %ld", GetLastError()); } else WinSharedMemClient_SetErrorText(0, "EPG client init failed: another TV client is already running", NULL); if (result == FALSE) { CloseHandle(tvMutexHandle); tvMutexHandle = NULL; } } else WinSharedMemClient_SetErrorText(GetLastError(), "EPG client init failed: can't create TV mutex", NULL); } else debug0("WinSharedMemClient-Init: illegal NULL ptr param"); return result; } // --------------------------------------------------------------------------- // Free allocated resources // - must be called once during shutdown // void WinSharedMemClient_Exit( void ) { // free the error message text if (pLastErrorText != NULL) { xfree(pLastErrorText); pLastErrorText = NULL; } // stop the message receptor thread & wait until it's terminated if (msgThreadHandle != NULL) { StopMsgThread = TRUE; if (tvEventHandle != NULL) { if (SetEvent(tvEventHandle) != 0) { // successfully notified the thread - wait until it exits if (WaitForSingleObject(msgThreadHandle, INFINITE) == WAIT_FAILED) debug1("WinSharedMemClient-Exit: WaitForSingleObject msgThreadHandle: %ld", GetLastError()); } else debug1("WinSharedMemClient-Exit: SetEvent tvEventHandle: %ld", GetLastError()); } else debug0("WinSharedMemClient-Exit: tvEventHandle already NULL"); if (CloseHandle(msgThreadHandle) == 0) debug1("WinSharedMemClient-Exit: CloseHandle msgThreadHandle: %ld", GetLastError()); msgThreadHandle = NULL; } // if attached, notify the EPG process that we're shutting down if (pTvShm != NULL) { pTvShm->tvAppAlive = FALSE; if ((epgEventHandle != NULL) && (SetEvent(epgEventHandle) == 0)) debug1("WinSharedMemClient-Exit: SetEvent " EPG_SHM_EVENT_NAME ": %ld", GetLastError()); } // detach from shared memory WinSharedMemClient_DetachShm(); // destroy the TV and EPG event handles if ((epgEventHandle != NULL) && (CloseHandle(epgEventHandle) == 0)) debug1("WinSharedMemClient-Exit: CloseHandle epgEventHandle: %ld", GetLastError()); if ((tvEventHandle != NULL) && (CloseHandle(tvEventHandle) == 0)) debug1("WinSharedMemClient-Exit: CloseHandle tvEventHandle: %ld", GetLastError()); epgEventHandle = NULL; tvEventHandle = NULL; // destroy the TV app mutex (allow other clients to access the EPG app) if (tvMutexHandle != NULL) { if (CloseHandle(tvMutexHandle) == 0) debug1("WinSharedMemClient-Exit: CloseHandle tvMutexHandle: %ld", GetLastError()); tvMutexHandle = NULL; } }