Exemplo n.º 1
0
void *
deviceThread(void *pData)
{
    const char *dlsym_error;

    CDeviceItem *pDevItem = (CDeviceItem *)pData;
    if (NULL == pDevItem) {
        syslog(LOG_CRIT, "No device item defined. Aborting device thread!");
        return NULL;
    }

    // Must have a valid pointer to the control object
    CControlObject *pCtrlObj = pDevItem->m_pCtrlObject;
    if (NULL == pCtrlObj) {
        syslog(LOG_CRIT, "No control object defined. Aborting device thread!");
        return NULL;
    }

    // We need to create a clientobject and add this object to the list
    CClientItem *pClientItem = pDevItem->m_pClientItem = new CClientItem;
    if (NULL == pClientItem) {
        return NULL;
    }

    // This is now an active Client
    pClientItem->m_bOpen = true;
    if (VSCP_DRIVER_LEVEL1 == pDevItem->m_driverLevel) {
        pClientItem->m_type = CLIENT_ITEM_INTERFACE_TYPE_DRIVER_LEVEL1;
    } else if (VSCP_DRIVER_LEVEL2 == pDevItem->m_driverLevel) {
        pClientItem->m_type = CLIENT_ITEM_INTERFACE_TYPE_DRIVER_LEVEL2;
    } else if (VSCP_DRIVER_LEVEL3 == pDevItem->m_driverLevel) {
        pClientItem->m_type = CLIENT_ITEM_INTERFACE_TYPE_DRIVER_LEVEL3;
    }

    char datebuf[80];
    time_t now = time(NULL);
    vscp_getTimeString(datebuf, sizeof(datebuf), &now);
    pClientItem->m_strDeviceName = pDevItem->m_strName;
    pClientItem->m_strDeviceName += "|Started at ";
    pClientItem->m_strDeviceName += datebuf;

    syslog(LOG_DEBUG,
           "Devicethread: Starting %s",
           pClientItem->m_strDeviceName.c_str());

    // Add the client to the Client List
    pthread_mutex_lock(&pCtrlObj->m_clientList.m_mutexItemList);
    if (!pCtrlObj->addClient(pClientItem,
                             pDevItem->m_interface_guid.getClientID())) {
        // Failed to add client
        delete pDevItem->m_pClientItem;
        pDevItem->m_pClientItem = NULL;

        pthread_mutex_unlock(&pCtrlObj->m_clientList.m_mutexItemList);
        syslog(LOG_ERR,
               "Devicethread: Failed to add client. Terminating thread.");
        return NULL;
    }
    pthread_mutex_unlock(&pCtrlObj->m_clientList.m_mutexItemList);

    // Client now have GUID set to server GUID + channel id
    // If device has a non NULL GUID replace the client GUID preserving
    // the channel id with that GUID
    if (!pClientItem->m_guid.isNULL()) {
        memcpy(
          pClientItem->m_guid.m_id, pDevItem->m_interface_guid.getGUID(), 12);
    }

    void *hdll;
    if (VSCP_DRIVER_LEVEL3 != pDevItem->m_driverLevel) {
        // Load dynamic library
        hdll = dlopen(pDevItem->m_strPath.c_str(), RTLD_LAZY);
        if (!hdll) {
            syslog(LOG_ERR,
                   "Devicethread: Unable to load dynamic library. path = %s",
                   pDevItem->m_strPath.c_str());
            return NULL;
        }
    } else { // Level III driver

        //  Startup Level III driver
        std::string executable = pDevItem->m_strPath;

        pid_t pid = fork();
        if (pid == -1) {
            syslog(LOG_ERR,
                   "Failed to start level III driver %s (fork).",
                   pDevItem->m_strName.c_str());
        } else if (pid == 0) {

            // we're in child

            // Set process group to child process' pid.  Then killing -pid
            // of the parent will kill the process and all of its children.
            setsid();

            // Arguments:    TODO
            //      user
            //      password
            //      config parameters...
            // execvp( *argv, const_cast<char**>(argv) );

            // Wait on child
            int status;
            waitpid(pid, &status, 0);
        }
    }

    if (VSCP_DRIVER_LEVEL1 == pDevItem->m_driverLevel) {

        // Now find methods in library
        syslog(
          LOG_INFO, "Loading level I driver: %s", pDevItem->m_strName.c_str());

        // * * * * CANAL OPEN * * * *
        pDevItem->m_proc_CanalOpen =
          (LPFNDLL_CANALOPEN)dlsym(hdll, "CanalOpen");
        const char *dlsym_error = dlerror();
        dlsym_error             = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_DEBUG,
                   "%s : Unable to get dl entry for CanalOpen.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * CANAL CLOSE * * * *
        pDevItem->m_proc_CanalClose =
          (LPFNDLL_CANALCLOSE)dlsym(hdll, "CanalClose");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalClose.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL GETLEVEL * * * *
        pDevItem->m_proc_CanalGetLevel =
          (LPFNDLL_CANALGETLEVEL)dlsym(hdll, "CanalGetLevel");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalGetLevel.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL SEND * * * *
        pDevItem->m_proc_CanalSend =
          (LPFNDLL_CANALSEND)dlsym(hdll, "CanalSend");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalSend.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL DATA AVAILABLE * * * *
        pDevItem->m_proc_CanalDataAvailable =
          (LPFNDLL_CANALDATAAVAILABLE)dlsym(hdll, "CanalDataAvailable");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalDataAvailable.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL RECEIVE * * * *
        pDevItem->m_proc_CanalReceive =
          (LPFNDLL_CANALRECEIVE)dlsym(hdll, "CanalReceive");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalReceive.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL GET STATUS * * * *
        pDevItem->m_proc_CanalGetStatus =
          (LPFNDLL_CANALGETSTATUS)dlsym(hdll, "CanalGetStatus");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalGetStatus.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL GET STATISTICS * * * *
        pDevItem->m_proc_CanalGetStatistics =
          (LPFNDLL_CANALGETSTATISTICS)dlsym(hdll, "CanalGetStatistics");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalGetStatistics.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL SET FILTER * * * *
        pDevItem->m_proc_CanalSetFilter =
          (LPFNDLL_CANALSETFILTER)dlsym(hdll, "CanalSetFilter");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalSetFilter.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL SET MASK * * * *
        pDevItem->m_proc_CanalSetMask =
          (LPFNDLL_CANALSETMASK)dlsym(hdll, "CanalSetMask");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalSetMask.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL GET VERSION * * * *
        pDevItem->m_proc_CanalGetVersion =
          (LPFNDLL_CANALGETVERSION)dlsym(hdll, "CanalGetVersion");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalGetVersion.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL GET DLL VERSION * * * *
        pDevItem->m_proc_CanalGetDllVersion =
          (LPFNDLL_CANALGETDLLVERSION)dlsym(hdll, "CanalGetDllVersion");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalGetDllVersion.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // * * * * CANAL GET VENDOR STRING * * * *
        pDevItem->m_proc_CanalGetVendorString =
          (LPFNDLL_CANALGETVENDORSTRING)dlsym(hdll, "CanalGetVendorString");
        dlsym_error = dlerror();
        if (dlsym_error) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalGetVendorString.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        // ******************************
        //     Generation 2 Methods
        // ******************************

        // * * * * CANAL BLOCKING SEND * * * *
        pDevItem->m_proc_CanalBlockingSend =
          (LPFNDLL_CANALBLOCKINGSEND)dlsym(hdll, "CanalBlockingSend");
        dlsym_error = dlerror();
        if (dlsym_error) {
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalBlockingSend. Probably "
                   "Generation 1 driver.",
                   pDevItem->m_strName.c_str());
            pDevItem->m_proc_CanalBlockingSend = NULL;
        }

        // * * * * CANAL BLOCKING RECEIVE * * * *
        pDevItem->m_proc_CanalBlockingReceive =
          (LPFNDLL_CANALBLOCKINGRECEIVE)dlsym(hdll, "CanalBlockingReceive");
        dlsym_error = dlerror();
        if (dlsym_error) {
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalBlockingReceive. "
                   "Probably Generation 1 driver.",
                   pDevItem->m_strName.c_str());
            pDevItem->m_proc_CanalBlockingReceive = NULL;
        }

        // * * * * CANAL GET DRIVER INFO * * * *
        pDevItem->m_proc_CanalGetdriverInfo =
          (LPFNDLL_CANALGETDRIVERINFO)dlsym(hdll, "CanalGetDriverInfo");
        dlsym_error = dlerror();
        if (dlsym_error) {
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for CanalGetDriverInfo. "
                   "Probably Generation 1 driver.",
                   pDevItem->m_strName.c_str());
            pDevItem->m_proc_CanalGetdriverInfo = NULL;
        }

        // Open the device
        pDevItem->m_openHandle = pDevItem->m_proc_CanalOpen(
          (const char *)pDevItem->m_strParameter.c_str(),
          pDevItem->m_DeviceFlags);

        // Check if the driver opened properly
        if (pDevItem->m_openHandle <= 0) {
            syslog(LOG_ERR,
                   "Failed to open driver. Will not use it! [%s] ",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL;
        }

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level I Driver open.",
                   pDevItem->m_strName.c_str());
        }

        // Get Driver Level
        pDevItem->m_driverLevel =
          pDevItem->m_proc_CanalGetLevel(pDevItem->m_openHandle);

        //  * * * Level I Driver * * *

        // Check if blocking driver is available
        if (NULL != pDevItem->m_proc_CanalBlockingReceive) {

            // * * * * Blocking version * * * *

            if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
                syslog(LOG_DEBUG,
                       "%s: [Device tread] Level I blocking version.",
                       pDevItem->m_strName.c_str());
            }

            /////////////////////////////////////////////////////////////////////////////
            //                      Device write worker thread
            /////////////////////////////////////////////////////////////////////////////

            if (pthread_create(&pDevItem->m_level1WriteThread,
                               NULL,
                               deviceLevel1WriteThread,
                               pDevItem)) {
                syslog(LOG_CRIT,
                       "%s: Unable to run the device write worker thread.",
                       pDevItem->m_strName.c_str());
                // pDevItem->m_openHandle = pDevItem->m_proc_CanalOpen();
                dlclose(hdll);
                return NULL;
            }

            /////////////////////////////////////////////////////////////////////////////
            // Device read worker thread
            /////////////////////////////////////////////////////////////////////////////
            if (pthread_create(&pDevItem->m_level1ReceiveThread,
                               NULL,
                               deviceLevel1ReceiveThread,
                               pDevItem)) {
                syslog(LOG_CRIT,
                       "%s: Unable to run the device read worker thread.",
                       pDevItem->m_strName.c_str());
                pDevItem->m_bQuit = true;
                pthread_join(pDevItem->m_level1WriteThread, NULL);
                // pDevItem->m_openHandle = pDevItem->m_proc_CanalOpen();
                dlclose(hdll);
                return NULL;
            }

            // Just sit and wait until the end of the world as we know it...
            while (!pDevItem->m_bQuit) {
                sleep(1);
            }

            // Signal worker threads to quit
            pDevItem->m_bQuit = true;

            if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
                syslog(LOG_DEBUG,
                       "%s: [Device tread] Level I work loop ended.",
                       pDevItem->m_strName.c_str());
            }

            // Wait for workerthreads to abort
            pthread_join(pDevItem->m_level1WriteThread, NULL);
            pthread_join(pDevItem->m_level1ReceiveThread, NULL);
        } else {

            // * * * * Non blocking version * * * *

            if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
                syslog(LOG_DEBUG,
                       "%s: [Device tread] Level I NON Blocking version.",
                       pDevItem->m_strName.c_str());
            }

            bool bActivity;
            while (pDevItem->m_bQuit) {

                bActivity = false;
                /////////////////////////////////////////////////////////////////////////////
                //                           Receive from device
                /////////////////////////////////////////////////////////////////////////////
                canalMsg msg;
                if (pDevItem->m_proc_CanalDataAvailable(
                      pDevItem->m_openHandle)) {

                    if (CANAL_ERROR_SUCCESS ==
                        pDevItem->m_proc_CanalReceive(pDevItem->m_openHandle,
                                                      &msg)) {

                        bActivity = true;

                        // There must be room in the receive queue
                        if (pCtrlObj->m_maxItemsInClientReceiveQueue >
                            pCtrlObj->m_clientOutputQueue.size()) {

                            vscpEvent *pvscpEvent = new vscpEvent;
                            if (NULL != pvscpEvent) {

                                // Set driver GUID if set
                                /*if ( pDevItem->m_interface_guid.isNULL() ) {
                                    pDevItem->m_interface_guid.writeGUID(
                                pvscpEvent->GUID );
                                }
                                else {
                                    // If no driver GUID set use interface GUID
                                    pItem->m_guid.writeGUID( pvscpEvent->GUID );
                                }*/

                                // Convert CANAL message to VSCP event
                                vscp_convertCanalToEvent(
                                  pvscpEvent, &msg, pClientItem->m_guid.m_id);

                                pvscpEvent->obid = pClientItem->m_clientID;

                                pthread_mutex_lock(
                                  &pCtrlObj->m_mutexClientOutputQueue);
                                pCtrlObj->m_clientOutputQueue.push_back(
                                  pvscpEvent);
                                sem_post(&pCtrlObj->m_semClientOutputQueue);
                                pthread_mutex_unlock(
                                  &pCtrlObj->m_mutexClientOutputQueue);
                            }
                        }
                    }
                } // data available

                // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
                //          Send messages (if any) in the output queue
                // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

                // Check if there is something to send
                if (pClientItem->m_clientInputQueue.size()) {

                    bActivity = true;

                    std::deque<vscpEvent *>::iterator it;

                    pthread_mutex_lock(&pClientItem->m_mutexClientInputQueue);
                    vscpEvent *pqueueEvent =
                      pClientItem->m_clientInputQueue.front();
                    pthread_mutex_lock(&pClientItem->m_mutexClientInputQueue);

                    // Trow away Level II event on Level I interface
                    if ((CLIENT_ITEM_INTERFACE_TYPE_DRIVER_LEVEL1 ==
                         pClientItem->m_type) &&
                        (pqueueEvent->vscp_class > 512)) {
                        // Remove the event and the node
                        pClientItem->m_clientInputQueue.pop_front();
                        syslog(LOG_ERR,
                               "Level II event on Level I queue thrown away. "
                               "class=%d, type=%d",
                               pqueueEvent->vscp_class,
                               pqueueEvent->vscp_type);
                        vscp_deleteVSCPevent(pqueueEvent);
                        continue;
                    }

                    canalMsg canalMsg;
                    vscp_convertEventToCanal(&canalMsg, pqueueEvent);
                    if (CANAL_ERROR_SUCCESS ==
                        pDevItem->m_proc_CanalSend(pDevItem->m_openHandle,
                                                   &canalMsg)) {
                        // Remove the event and the node
                        pClientItem->m_clientInputQueue.pop_front();
                        delete pqueueEvent;
                    } else {
                        // Another try
                        // pCtrlObj->m_semClientOutputQueue.Post();
                        // vscp_deleteVSCPevent(pqueueEvent);  TODO ????
                    }

                } // events

                if (!bActivity) {
                    usleep(100000); // 100 ms
                }

                bActivity = false;

            } // while working - non blocking

        } // if blocking/non blocking

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level I Work loop ended.",
                   pDevItem->m_strName.c_str());
        }

        // Close CANAL channel
        pDevItem->m_proc_CanalClose(pDevItem->m_openHandle);

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level I Closed.",
                   pDevItem->m_strName.c_str());
        }

    level1_driver_exit:

        pDevItem->m_bQuit = true;
        pthread_join(pDevItem->m_level1WriteThread, NULL);
        pthread_join(pDevItem->m_level1ReceiveThread, NULL);

        dlclose(hdll);

    } else if (VSCP_DRIVER_LEVEL2 == pDevItem->m_driverLevel) {

        // Now find methods in library
        syslog(LOG_INFO,
               "Loading level II driver: <%s>",
               pDevItem->m_strName.c_str());

        // * * * * VSCP OPEN * * * *
        if (NULL == (pDevItem->m_proc_VSCPOpen =
                       (LPFNDLL_VSCPOPEN)dlsym(hdll, "VSCPOpen"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPOpen.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP CLOSE * * * *
        if (NULL == (pDevItem->m_proc_VSCPClose =
                       (LPFNDLL_VSCPCLOSE)dlsym(hdll, "VSCPClose"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPClose.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP BLOCKINGSEND * * * *
        if (NULL ==
            (pDevItem->m_proc_VSCPBlockingSend =
               (LPFNDLL_VSCPBLOCKINGSEND)dlsym(hdll, "VSCPBlockingSend"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPBlockingSend.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP BLOCKINGRECEIVE * * * *
        if (NULL == (pDevItem->m_proc_VSCPBlockingReceive =
                       (LPFNDLL_VSCPBLOCKINGRECEIVE)dlsym(
                         hdll, "VSCPBlockingReceive"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPBlockingReceive.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP GETLEVEL * * * *
        if (NULL == (pDevItem->m_proc_VSCPGetLevel =
                       (LPFNDLL_VSCPGETLEVEL)dlsym(hdll, "VSCPGetLevel"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPGetLevel.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP GET DLL VERSION * * * *
        if (NULL ==
            (pDevItem->m_proc_VSCPGetDllVersion =
               (LPFNDLL_VSCPGETDLLVERSION)dlsym(hdll, "VSCPGetDllVersion"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPGetDllVersion.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP GET VENDOR STRING * * * *
        if (NULL == (pDevItem->m_proc_VSCPGetVendorString =
                       (LPFNDLL_VSCPGETVENDORSTRING)dlsym(
                         hdll, "VSCPGetVendorString"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPGetVendorString.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP GET DRIVER INFO * * * *
        if (NULL ==
            (pDevItem->m_proc_CanalGetdriverInfo =
               (LPFNDLL_VSCPGETVENDORSTRING)dlsym(hdll, "VSCPGetDriverInfo"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPGetDriverInfo.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP GET WEB PAGE TEMPLATE * * * *
        if (NULL == (pDevItem->m_proc_VSCPGetWebPageTemplate =
                       (LPFNDLL_VSCPGETWEBPAGETEMPLATE)dlsym(
                         hdll, "VSCPGetWebPageTemplate"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPGetWebPageTemplate.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP GET WEB PAGE INFO * * * *
        if (NULL ==
            (pDevItem->m_proc_VSCPGetWebPageInfo =
               (LPFNDLL_VSCPGETWEBPAGEINFO)dlsym(hdll, "VSCPGetWebPageInfo"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPGetWebPageInfo.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        // * * * * VSCP WEB PAGE UPDATE * * * *
        if (NULL ==
            (pDevItem->m_proc_VSCPWebPageupdate =
               (LPFNDLL_VSCPWEBPAGEUPDATE)dlsym(hdll, "VSCPWebPageupdate"))) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: Unable to get dl entry for VSCPWebPageupdate.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: Discovered all methods\n",
                   pDevItem->m_strName.c_str());
        }

        // Username, password, host and port can be set in configuration file.
        // Read in them here if they are.
        std::string strHost("127.0.0.1");
        short port = 9598;

        std::deque<std::string> tokens;
        vscp_split(tokens, pDevItem->m_strParameter, ";");
        if (false == tokens.empty()) {

            CVariable variable;

            // Get prefix
            std::string prefix = tokens.front();
            tokens.pop_front();

            // Check if username is specified in the configuration file
            CUserItem *pAdminUser =
              pDevItem->m_pCtrlObject->m_userList.getUser(USER_ID_ADMIN);
            if (pCtrlObj->m_variables.find(
                  pDevItem->m_strName + "_username", pAdminUser, variable)) {
                std::string str;
                if (VSCP_DAEMON_VARIABLE_CODE_STRING == variable.getType()) {
                    str                        = variable.getValue();
                    pCtrlObj->m_driverUsername = str;
                }
            }

            // Check if password is specified in the configuration file
            if (pCtrlObj->m_variables.find(
                  pDevItem->m_strName + "_password", pAdminUser, variable)) {
                std::string str;
                if (VSCP_DAEMON_VARIABLE_CODE_STRING == variable.getType()) {
                    str                        = variable.getValue();
                    pCtrlObj->m_driverPassword = str;
                }
            }

            // Check if host is specified in the configuration file
            if (pCtrlObj->m_variables.find(
                  pDevItem->m_strName + "_host", pAdminUser, variable)) {
                std::string str;
                if (VSCP_DAEMON_VARIABLE_CODE_STRING == variable.getType()) {
                    str     = variable.getValue();
                    strHost = str;
                }
            }

            // Check if host is specified in the configuration file
            if (pCtrlObj->m_variables.find(
                  pDevItem->m_strName + "_port", pAdminUser, variable)) {
                std::string str;
                if (VSCP_DAEMON_VARIABLE_CODE_INTEGER == variable.getType()) {
                    str  = variable.getValue();
                    port = vscp_readStringValue(str);
                }
            }
        }

        // Open up the driver
        pDevItem->m_openHandle = pDevItem->m_proc_VSCPOpen(
          pCtrlObj->m_driverUsername.c_str(),
          (const char *)pCtrlObj->m_driverPassword.c_str(),
          (const char *)strHost.c_str(),
          port,
          (const char *)pDevItem->m_strName.c_str(),
          (const char *)pDevItem->m_strParameter.c_str());

        if (0 == pDevItem->m_openHandle) {
            // Free the library
            syslog(LOG_ERR,
                   "%s: [Device tread] Unable to open VSCP "
                   "driver (check username/password/path/"
                   "rights). Possible additional info from driver "
                   "in syslog.",
                   pDevItem->m_strName.c_str());
            return NULL;
        }

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level II Open.",
                   pDevItem->m_strName.c_str());
        }

        /////////////////////////////////////////////////////////////////////////////
        // Device write worker thread
        /////////////////////////////////////////////////////////////////////////////

        if (pthread_create(&pDevItem->m_level2WriteThread,
                           NULL,
                           deviceLevel2WriteThread,
                           pDevItem)) {
            syslog(LOG_CRIT,
                   "%s: Unable to run the device Level II write worker thread.",
                   pDevItem->m_strName.c_str());
            dlclose(hdll);
            return NULL; // TODO close dll
        }

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level II Write thread created.",
                   pDevItem->m_strName.c_str());
        }

        /////////////////////////////////////////////////////////////////////////////
        // Device read worker thread
        /////////////////////////////////////////////////////////////////////////////

        if (pthread_create(&pDevItem->m_level2ReceiveThread,
                           NULL,
                           deviceLevel2ReceiveThread,
                           pDevItem)) {
            syslog(LOG_CRIT,
                   "%s: Unable to run the device Level II read worker thread.",
                   pDevItem->m_strName.c_str());
            pDevItem->m_bQuit = true;
            pthread_join(pDevItem->m_level2WriteThread, NULL);
            dlclose(hdll);
            return NULL; // TODO close dll, kill other thread
        }

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level II Write thread created.",
                   pDevItem->m_strName.c_str());
        }

        // Just sit and wait until the end of the world as we know it...
        while (!pDevItem->m_bQuit) {
            sleep(1);
        }

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level II Closing.",
                   pDevItem->m_strName.c_str());
        }

        // Close channel
        pDevItem->m_proc_VSCPClose(pDevItem->m_openHandle);

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level II Closed.",
                   pDevItem->m_strName.c_str());
        }

    level2_driver_exit:

        pDevItem->m_bQuit = true;
        pthread_join(pDevItem->m_level2WriteThread, NULL);
        pthread_join(pDevItem->m_level2ReceiveThread, NULL);

        // Unload dll
        dlclose(hdll);

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level II Done waiting for threads.",
                   pDevItem->m_strName.c_str());
        }

    } else if (VSCP_DRIVER_LEVEL3 == pDevItem->m_driverLevel) {

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level III Start server loop.",
                   pDevItem->m_strName.c_str());
        }

        // Just sit and wait until the end of the world as we know it...
        while (!pDevItem->m_bQuit) {
            sleep(1);
        }

        if (pCtrlObj->m_debugFlags[0] & VSCP_DEBUG1_DRIVER) {
            syslog(LOG_DEBUG,
                   "%s: [Device tread] Level II End server loop.",
                   pDevItem->m_strName.c_str());
        }
    }

    // Remove messages in the client queues
    pthread_mutex_lock(&pCtrlObj->m_clientList.m_mutexItemList);
    pCtrlObj->removeClient(pClientItem);
    pthread_mutex_unlock(&pCtrlObj->m_clientList.m_mutexItemList);

    return NULL;
}
void clientThreadLevel2( void *pThreadObject )
{
	char *p;
	char buf[ MAX_PATH ];
	vscpEvent *pEvent;
	dllnode *pNode;
	CControlObject *pctrlObject = NULL;
	HANDLE hMap;

	// http://msdn.microsoft.com/msdnmag/issues/0300/security/default.aspx
	// here we use a SECURITY_DESCRIPTOR to say
	// that we don't want *any DACL at all*
	SECURITY_DESCRIPTOR sd;
	InitializeSecurityDescriptor( &sd,
									              SECURITY_DESCRIPTOR_REVISION);
	SetSecurityDescriptorDacl(&sd, TRUE, 0, FALSE);

	SECURITY_ATTRIBUTES sa = { sizeof sa, &sd, TRUE };

 	  
	_clientstruct clientInfo;
	memcpy( &clientInfo, (_clientstruct *)pThreadObject, sizeof( _clientstruct ) );
	pctrlObject = clientInfo.m_pctrlObject;
  
	// We need to create a clientobject and add this object to the list
	CClientItem *pClientItem = new CClientItem;
	if ( NULL == pClientItem ) {
	
		delete (_clientstruct *)pThreadObject;	// Delete the client structure
		ExitThread( -1 );
 
	}
	
	// This is a Level II Client
	pClientItem->m_ClientLevel = CLIENT_ITEM_LEVEL2;
	
	// We need to get the shared memory
	sprintf( buf, CANAL_LISTEN_CLIENT_SHM_TEMPLATE, clientInfo.m_shmid );
	if ( NULL == ( hMap = 
		::OpenFileMapping( FILE_MAP_WRITE, 
                        TRUE, 
							          buf ) ) )  {
		delete (_clientstruct *)pThreadObject;	// Delete the client structure
		ExitThread( -1 );
	}
	
	struct __shmCanalLevelII *pLevel2 = 
		(struct __shmCanalLevelII *)MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 );

	if ( NULL == pLevel2 ) {
		delete (_clientstruct *)pThreadObject;	// Delete the client structure
		CloseHandle( hMap );
		ExitThread( -1 );	
	}

	void *pDataArea= pLevel2->m_data;
	vscpEvent *pEventArea = &pLevel2->m_VSCP_Event;
	pLevel2->m_command = VSCP_COMMAND_NOOP;
	
	
	// Create the client command semaphore
	HANDLE hSemCmd;
	sprintf( buf, CANAL_CLIENT_COMMAND_SEM_TEMPLATE, clientInfo.m_shmid );
	if ( NULL == ( hSemCmd = CreateSemaphore( &sa, 0, 1, buf ) ) ) {
		delete (_clientstruct *)pThreadObject;	// Delete the client structure
		CloseHandle( hMap );
		ExitThread( -1 );	
	}

	// Create the client Done semaphore
	HANDLE hSemDone;
	sprintf( buf, CANAL_CLIENT_DONE_SEM_TEMPLATE, clientInfo.m_shmid );
	if ( NULL == ( hSemDone = CreateSemaphore( &sa, 0, 1, buf ) ) ) {
		delete (_clientstruct *)pThreadObject;	// Delete the client structure
		CloseHandle( hSemCmd );
		CloseHandle( hMap );
		ExitThread( -1 );	
	}
	
	// Share the command semaphore
	pLevel2->m_semCmd = clientInfo.m_shmid;
				
	// Add the client to the Client List
	pctrlObject->m_wxClientMutex.Lock();
	pctrlObject->addClient( pClientItem );
	pctrlObject->m_wxClientMutex.Unlock();
	
	pLevel2->m_test = 0xaa55;	// Confirm to client that we are here 
				
	pClientItem->m_bOpen = true;
	while ( pClientItem->m_bOpen && !pctrlObject->m_bQuit ) {
			
		// Wait for command from client
		if ( WAIT_OBJECT_0 != WaitForSingleObject( hSemCmd,  500 ) ) {
			continue;
		}
			
		switch( pLevel2->m_command ) {
		
			// * * * *  O P E N  * * * *
			case VSCP_COMMAND_OPEN:

				wxLogTrace("wxTRACE_Canald_LevelII", "ClientThread2: Open");
				pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;
				break;
					
			// * * * *  C L O S E  * * * *
			case VSCP_COMMAND_CLOSE:

				wxLogTrace("wxTRACE_Canald_LevelII", "ClientThread2: Close");
				pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;	
				pClientItem->m_bOpen = false;	 
				break;
				
			// * * * *  S E N D  * * * *
			case VSCP_COMMAND_SEND:
					
				wxLogTrace("wxTRACE_Canald_LevelII", "ClientThread2: Send %X", pClientItem->m_clientID );
				pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;
						
				pNode = new dllnode;
				if ( NULL != pNode ) {

					pEvent = new vscpEvent;		// Create new VSCP Message
						
					if ( NULL != pEvent ) {
					
						// Save the originating clients id so
						// this client dont get the message back
						pNode->obid = pClientItem->m_clientID;
								
						// Copy message								
						memcpy( pEvent, pEventArea, sizeof( vscpEvent ) );
					
						// And data...
						if ( pEvent->sizeData > 0 ) {	
						
							// Copy in data
							pEvent->pdata = new uint8_t[ pEvent->sizeData ];
							if ( NULL != pEvent->pdata ) {
								memcpy( pEvent->pdata, pDataArea ,pEvent->sizeData );
							}
							
						}
						else {
							// No data
							pEvent->pdata = NULL;
						}
						
						// Check if GUID of interface should be used
						// as packet GUID
						if ( pLevel2->m_cmdArg1 ) {
							memcpy( pEvent->GUID, pClientItem->m_GUID, 16 );
						}
						
						// Statistics
						pClientItem->m_statistics.cntTransmitData += pEvent->sizeData;
						pClientItem->m_statistics.cntTransmitFrames++;

						// We use the message id as the sort key for the message
						// in the queue. The high part of this id is the priority
						// for the message.
						// The non-id part should possibly be nulled but this depends
						// on if we regard the message as "sent" or not. If not, a 
						// message with a lower id should be transfered first over the 
						// bus.
								
						pNode->Key = pEvent->head;
						pNode->pKey = &pNode->Key;
						pNode->pObject = pEvent;
								
						// There must be room in the send queue
						if ( pctrlObject->m_maxItemsInSendQueue > 
							pctrlObject->m_sendLevel2Queue.nCount ) {						
							
							pctrlObject->m_wxQ2OutMutex.Lock();
							
							if ( !dll_addNode( &pctrlObject->m_sendLevel2Queue, pNode ) ) {
								pLevel2->m_Response = CANAL_RESPONSE_ERROR;	
								pLevel2->m_ResponseCode = CANAL_IFERROR_SEND_STORAGE;
							}
							else {
								pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;
								SetEvent( pctrlObject->m_hEventSendQ2 );
							}
		
							pctrlObject->m_wxQ2OutMutex.Unlock();
						}
						else {
						
							pLevel2->m_Response = CANAL_RESPONSE_ERROR;	
							pLevel2->m_ResponseCode = CANAL_IFERROR_BUFFER_FULL;
									
							if ( NULL != pNode ) {
								if ( NULL != pNode->pObject ) {
									if ( NULL != pEvent->pdata )  delete [] pEvent->pdata;
									delete pEvent;
								}
								delete pNode;
							}		
						}
					}
					else {
								
						delete pNode;
								
						pLevel2->m_Response = CANAL_RESPONSE_ERROR;
						pLevel2->m_ResponseCode = CANAL_IFERROR_SEND_MSG_ALLOCATON;
						
					}
				} // dllNode  
				break;	
				
				
			// * * * *  R E C E I V E  * * * *
			case VSCP_COMMAND_RECEIVE:
					
				wxLogTrace("wxTRACE_Canald_LevelII", "ClientThread2: Receive - before client lock %X", pClientItem->m_clientID );
				pClientItem->m_wxMsgMutex.Lock();
				
				if ( !pClientItem->m_bOpen ) {
					pLevel2->m_Response = CANAL_RESPONSE_ERROR;
					pLevel2->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;	
				}
				else {
								
					dllnode *pNode; 
					vscpEvent *pEvent = (vscpEvent *)pEventArea;
										 
					if ( NULL != ( pNode = pClientItem->m_inputQueue.pHead ) ) {
						
						// There is a message available
						pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;	
						
						// Copy message
						memcpy( pEvent, pNode->pObject, sizeof( vscpEvent ) );
						
						// If there is data it also must be copied
						if ( ( pEvent->sizeData > 0 ) && ( NULL != pEvent->pdata ) ) {
							memcpy( pDataArea, pEvent->pdata, pEvent->sizeData );							
						}
						
						// Remove the old data
						if ( NULL != pEvent->pdata ) delete pEvent->pdata;
						pEvent->pdata = NULL;	 // Data stored in message

						// Remove the node
						dll_removeNode( &pClientItem->m_inputQueue, pNode );

						// If queue empty reset receive flag
						if ( NULL == ( pNode = pClientItem->m_inputQueue.pHead ) ) {
							ResetEvent( pClientItem->m_hEventReceive );
						}

					}	
				}	
				
				pClientItem->m_wxMsgMutex.Unlock();
				break;	
				
				
			// * * * *  D A T A  A V A I L A B L E  * * * *
			case VSCP_COMMAND_CHECKDATA:
	 
				//wxLogTrace("wxTRACE_Canald_LevelII", "ClientThread%n: Data Available %X", pClientItem->m_clientID. pClientItem->m_inputQueue.nCount);	
				pClientItem->m_wxMsgMutex.Lock();
			
				if ( !pClientItem->m_bOpen ) {
					pLevel2->m_Response = CANAL_RESPONSE_ERROR;
					pLevel2->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;	
				}
				else {
					pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;

					unsigned int *pnMsg = (unsigned int *)pDataArea;
					//*pnMsg = dll_getNodeCount( &pClientItem->m_inputQueue ); 		 
					*pnMsg = pClientItem->m_inputQueue.nCount;
				}
				pClientItem->m_wxMsgMutex.Unlock();
				break;	
			
			
			// * * * *  S T A T U S  * * * *
			case VSCP_COMMAND_STATUS:

				if ( !pClientItem->m_bOpen ) {
					pLevel2->m_Response = CANAL_RESPONSE_ERROR;
					pLevel2->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;	
				}
				else {
					pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;

					// Status
					memcpy( pDataArea, &pClientItem->m_status, sizeof( canalStatus ) );
			
				} 
				break;	
				
				
			// * * * *  S T A T I S T I C S  * * * *
			case VSCP_COMMAND_STATISTICS:

				if ( !pClientItem->m_bOpen ) {
					pLevel2->m_Response = CANAL_RESPONSE_ERROR;
					pLevel2->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;	
				}
				else {
					memcpy( pDataArea, 
							&pClientItem->m_statistics, 
							sizeof( &pClientItem->m_statistics ) );
					pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;
				} 
				break;
				
				
			// * * * *  F I L T E R  * * * *
			case VSCP_COMMAND_FILTER:

				if ( !pClientItem->m_bOpen ) {
					pLevel2->m_Response = CANAL_RESPONSE_ERROR;
					pLevel2->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;	
				}
				else {
						
					// Set filter
					memcpy( &pClientItem->m_filter, pDataArea, sizeof( vscpEventFilter ) );
					pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;

				}  
				break;	
	
			
			// * * * *  V E R S I O N  * * * *
			case VSCP_COMMAND_VERSION:
			
				// Get version
				p = (char *)pDataArea;
				*( p + POS_VSCPD_MAJOR_VERSION ) = CANAL_MAIN_VERSION;
				*( p + POS_VSCPD_MINOR_VERSION ) = CANAL_MINOR_VERSION;
				*( p + POS_VSCPD_SUB_VERSION ) = CANAL_SUB_VERSION;
				
				pLevel2->m_Response = CANAL_RESPONSE_SUCCESS; 	
				break;
				
				
			// * * * *  G E T  I / F  G U I D   * * * *
			case VSCP_COMMAND_GET_GUID:				
				memcpy( pDataArea, pClientItem->m_GUID, 16 ); 
				pLevel2->m_Response = CANAL_RESPONSE_SUCCESS; 
				break;
				
				
			// * * * *  S E T  I / F  G U I D   * * * *
			case VSCP_COMMAND_SET_GUID:			
				memcpy( pClientItem->m_GUID, pDataArea, 16 );
				pLevel2->m_Response = CANAL_RESPONSE_SUCCESS; 
				break;	
				
				
			// * * * *  G E T  C H A N N E L  I D   * * * *
			case VSCP_COMMAND_GET_CHID:		
				break;	
			
				
			// * * * *  N O O P  * * * *
			case VSCP_COMMAND_NOOP:
				wxLogTrace("wxTRACE_Canald_LevelII", "ClientThread2: NOOP %X", pClientItem->m_clientID);
				pLevel2->m_Response = CANAL_RESPONSE_SUCCESS;
				break;


			default:
				wxLogTrace("wxTRACE_Canald_LevelII", "ClientThread2: Unknown command %X", pClientItem->m_clientID);
				pLevel2->m_Response = CANAL_RESPONSE_ERROR;
				pLevel2->m_ResponseCode = CANAL_IFERROR_UNKNOWN_COMMAND;
				break;			
		
		}

		// We are Done
		ReleaseSemaphore( hSemDone, 1, NULL );
				 
	}	// while running

	wxLogTrace( "wxTRACE_Canald_LevelII", "ClientThread2: About to end. ClientId = %X", pClientItem->m_clientID );
	 
	// Remove messages in the client queues   
	pctrlObject->removeClient( pClientItem );
	
	wxLogDebug("ClientThread2: Removed client item.");
	
	delete (_clientstruct *)pThreadObject;

	CloseHandle( hMap );
	CloseHandle( hSemCmd );
	CloseHandle( hSemDone );

	wxLogDebug("ClientThread2: Exit.");
	ExitThread( 0 );
 
}
void clientThreadLevel1( void *pThreadObject )
{
    char buf[ MAX_PATH ];
    char *p;
    canalMsg *pMsg;
    dllnode *pNode;
    CControlObject *pctrlObject = NULL;
    HANDLE hMap;

    wxLogDebug("Level I Client Started");

    // http://msdn.microsoft.com/msdnmag/issues/0300/security/default.aspx
    // here we use a SECURITY_DESCRIPTOR to say
    // that we don't want *any DACL at all*
    SECURITY_DESCRIPTOR sd;
    InitializeSecurityDescriptor( &sd,
                                  SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&sd, TRUE, 0, FALSE);

    SECURITY_ATTRIBUTES sa = { sizeof sa, &sd, TRUE };


    _clientstruct clientInfo;
    memcpy( &clientInfo, (_clientstruct *)pThreadObject, sizeof( _clientstruct ) );
    pctrlObject = clientInfo.m_pctrlObject;

    // We need to create a clientobject and add this object to the list
    CClientItem *pClientItem = new CClientItem;
    if ( NULL == pClientItem ) {

        delete (_clientstruct *)pThreadObject;	// Delete the client structure
        ExitThread( -1 );

    }

    wxLogDebug("Level I Client 1");

    // This is a Level II Client
    pClientItem->m_ClientLevel = CLIENT_ITEM_LEVEL1;

    // We need to get the shared memory
    sprintf( buf, CANAL_LISTEN_CLIENT_SHM_TEMPLATE, clientInfo.m_shmid );
    if ( NULL == ( hMap =
                       ::OpenFileMapping( FILE_MAP_WRITE,
                                          TRUE,
                                          buf ) ) )  {
        delete (_clientstruct *)pThreadObject;	// Delete the client structure
        ExitThread( -1 );
    }


    wxLogDebug("Level I Client 5");
    struct __shmCanalLevelI *pLevel1 =
        (struct __shmCanalLevelI *)MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 );

    if ( NULL == pLevel1 ) {
        delete (_clientstruct *)pThreadObject;	// Delete the client structure
        CloseHandle( hMap );
        ExitThread( -1 );
    }

    wxLogDebug("Level I Client 6");
    void *pDataArea= pLevel1->m_data;
    pLevel1->m_command = CANAL_COMMAND_NOOP;



    // Create the client command semaphore
    HANDLE hSemCmd;
    sprintf( buf, CANAL_CLIENT_COMMAND_SEM_TEMPLATE, clientInfo.m_shmid );
    if ( NULL == ( hSemCmd = CreateSemaphore( &sa, 0, 1, buf ) ) ) {
        delete (_clientstruct *)pThreadObject;	// Delete the client structure
        CloseHandle( hMap );
        ExitThread( -1 );
    }

    wxLogDebug("Level I Client 7");

    // Create the client Done semaphore
    HANDLE hSemDone;
    sprintf( buf, CANAL_CLIENT_DONE_SEM_TEMPLATE, clientInfo.m_shmid );
    if ( NULL == ( hSemDone = CreateSemaphore( &sa, 0, 1, buf ) ) ) {
        delete (_clientstruct *)pThreadObject;	// Delete the client structure
        CloseHandle( hSemCmd );
        CloseHandle( hMap );
        ExitThread( -1 );
    }

    wxLogDebug("Level I Client 7");
    // Share the command semaphore
    pLevel1->m_semCmd = clientInfo.m_shmid;

    // Add the client to the Client List
    pctrlObject->m_wxClientMutex.Lock();
    pctrlObject->addClient( pClientItem );
    pctrlObject->m_wxClientMutex.Unlock();

    pLevel1->m_test = 0xaa55;	// Confirm to client that we are here

    pClientItem->m_bOpen = true;
    while ( pClientItem->m_bOpen && !pctrlObject->m_bQuit ) {

        // Wait for command from client
        if ( WAIT_OBJECT_0 != WaitForSingleObject( hSemCmd,  500 ) ) {
            continue;
        }

        switch( pLevel1->m_command ) {

        // * * * *  O P E N  * * * *
        case CANAL_COMMAND_OPEN:
            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            break;

        // * * * *  C L O S E  * * * *
        case CANAL_COMMAND_CLOSE:
            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            pClientItem->m_bOpen = false;
            break;

        // * * * *  S E N D  * * * *
        case CANAL_COMMAND_SEND:
            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;

            pNode = new dllnode;
            if ( NULL != pNode ) {

                pMsg = new canalMsg;	// Create new Canal Message

                if ( NULL != pMsg ) {

                    // Save the originating clients id so
                    // this client dont get the message back
                    pNode->obid = pClientItem->m_clientID;

                    // Copy message
                    memcpy( pMsg, pDataArea, sizeof( canalMsg ) );

                    // Statistics
                    pClientItem->m_statistics.cntTransmitData +=
                        pMsg->sizeData;
                    pClientItem->m_statistics.cntTransmitFrames++;

                    // We use the message id as the sort key for the message
                    // in the queue. The high part of this id is the priority
                    // for the message.
                    // The non-id part should possibly be nulled but this depends
                    // on if we regard the message as "sent" or not. If not, a
                    // message with a lower id should be transfered first over the
                    // bus.

                    pNode->Key = pMsg->id;
                    pNode->pKey = &pNode->Key;
                    pNode->pObject = pMsg;

                    // There must be room in the send queue
                    if ( pctrlObject->m_maxItemsInSendQueue >
                            pctrlObject->m_sendLevel1Queue.nCount ) {

                        pctrlObject->m_wxQ1OutMutex.Lock();
                        if ( !dll_addNode( &pctrlObject->m_sendLevel1Queue, pNode ) ) {
                            pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                            pLevel1->m_ResponseCode = CANAL_IFERROR_SEND_STORAGE;
                        }
                        else {
                            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
                            SetEvent( pctrlObject->m_hEventSendQ1 );
                        }
                        pctrlObject->m_wxQ1OutMutex.Unlock();
                    }
                    else {

                        pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                        pLevel1->m_ResponseCode = CANAL_IFERROR_BUFFER_FULL;

                        if ( NULL != pNode ) {
                            if ( NULL != pNode->pObject ) {
                                delete pMsg;
                            }
                            delete pNode;
                        }
                    }
                }
                else {

                    delete pNode;

                    pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                    pLevel1->m_ResponseCode = CANAL_IFERROR_SEND_MSG_ALLOCATON;

                }
            } // dllNode
            break;


        // * * * *  R E C E I V E  * * * *
        case CANAL_COMMAND_RECEIVE:

            if ( !pClientItem->m_bOpen ) {
                pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                pLevel1->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;
            }
            else {

                pctrlObject->m_wxClientMutex.Lock();

                dllnode *pNode;
                canalMsg *pMsg = (canalMsg *)pDataArea;

                if ( NULL != ( pNode = pClientItem->m_inputQueue.pHead ) ) {

                    // There is a message available
                    pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
                    memcpy( pMsg, pNode->pObject, sizeof( canalMsg ) );

                    // Remove the node
                    dll_removeNode( &pClientItem->m_inputQueue, pNode );

                    // If queue empty reset receive flag
                    if ( NULL == ( pNode = pClientItem->m_inputQueue.pHead ) ) {
                        ResetEvent( pClientItem->m_hEventReceive );
                    }

                }

                pctrlObject->m_wxClientMutex.Unlock();

            }
            break;


        // * * * *  D A T A  A V A I L A B L E  * * * *
        case CANAL_COMMAND_CHECKDATA:

            if ( !pClientItem->m_bOpen ) {
                pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                pLevel1->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;
            }
            else {
                pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;

                unsigned int *pnMsg = (unsigned int *)pDataArea;
                //*pnMsg = dll_getNodeCount( &pClient->m_inputQueue );
                *pnMsg = pClientItem->m_inputQueue.nCount;
            }
            break;


        // * * * *  S T A T U S  * * * *
        case CANAL_COMMAND_STATUS:

            if ( !pClientItem->m_bOpen ) {
                pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                pLevel1->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;
            }
            else {
                pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;

                // Status
                memcpy( pDataArea, &pClientItem->m_status, sizeof( canalStatus ) );

            }
            break;


        // * * * *  S T A T I S T I C S  * * * *
        case CANAL_COMMAND_STATISTICS:

            if ( !pClientItem->m_bOpen ) {
                pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                pLevel1->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;
            }
            else {
                memcpy( pDataArea, &pClientItem->m_statistics, sizeof( &pClientItem->m_statistics ) );
                pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            }
            break;


        // * * * *  F I L T E R  * * * *
        case CANAL_COMMAND_FILTER:

            if ( !pClientItem->m_bOpen ) {
                pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                pLevel1->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;
            }
            else {

                // Set filter
                memcpy(  &pClientItem->m_filter, pDataArea, sizeof( pClientItem->m_filter ) );
                pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            }
            break;

        // * * * *  M A S K  * * * *
        case CANAL_COMMAND_MASK:

            if ( !pClientItem->m_bOpen ) {
                pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                pLevel1->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;
            }
            else {

                // Set mask
                memcpy( &pClientItem->m_mask, pDataArea, sizeof( pClientItem->m_mask ) );
                pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;

            }
            break;


        // * * * *  B A U D R A T E  * * * *
        case CANAL_COMMAND_BAUDRATE:

            if ( !pClientItem->m_bOpen ) {
                pLevel1->m_Response = CANAL_RESPONSE_ERROR;
                pLevel1->m_ResponseCode = CANAL_IFERROR_CHANNEL_CLOSED;
            }
            else {

                // Set baudrate
                // ============
                //		We don't have yo do more here the baudrate change
                //		is just simulated in the pipe interface
                pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            }
            break;

        // * * * *  V E R S I O N  * * * *
        case CANAL_COMMAND_VERSION:
            // Get version
            p = (char *)pDataArea;
            *( p + POS_VSCPD_MAJOR_VERSION ) = CANAL_MAIN_VERSION;
            *( p + POS_VSCPD_MINOR_VERSION ) = CANAL_MINOR_VERSION;
            *( p + POS_VSCPD_SUB_VERSION ) = CANAL_SUB_VERSION;
            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            break;

        // * * * *  D L L - V E R S I O N  * * * *
        case CANAL_COMMAND_DLL_VERSION:

            // Get DLL version
            //		This application returns the vscp daemon
            //		version.
            p = (char *)pDataArea;
            *( p + POS_VSCPD_MAJOR_VERSION ) = VSCPD_MAJOR_VERSION;
            *( p + POS_VSCPD_MINOR_VERSION ) = VSCPD_MINOR_VERSION;
            *( p + POS_VSCPD_SUB_VERSION ) = VSCPD_SUB_VERSION;
            *( p + POS_VSCPD_SUB_VERSION + 1 ) = 0;
            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            break;

        // * * * *  V E N D O R  S T R I N G  * * * *
        case CANAL_COMMAND_VENDOR_STRING:
            // Get vendor string
            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            p = (char *)pDataArea;
            strcpy( p, VENDOR_STRING );
            break;

        // * * * *  L E V E L  * * * *
        case CANAL_COMMAND_LEVEL:

            // Get i/f supported levels
            p = (char *)pDataArea;
            *( p + 0 ) = 0;
            *( p + 1 ) = 0;
            *( p + 2 ) = 0;
            *( p + 3 ) = CANAL_LEVEL_STANDARD;
            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            break;

        // * * * *  N O O P  * * * *
        case CANAL_COMMAND_NOOP:

            pLevel1->m_Response = CANAL_RESPONSE_SUCCESS;
            break;

        default:

            pLevel1->m_Response = CANAL_RESPONSE_ERROR;
            pLevel1->m_ResponseCode = CANAL_IFERROR_UNKNOWN_COMMAND;
            break;

        }

        // We are Done
        ReleaseSemaphore( hSemDone, 1, NULL );

    }	// while running


    // Client not needed anymore
    pctrlObject->removeClient( pClientItem );

    delete (_clientstruct *)pThreadObject;

    CloseHandle( hMap );
    CloseHandle( hSemCmd );
    CloseHandle( hSemDone );
    ExitThread( 0 );

}