Initial prototype for optimized Telegesis read/write layer.
authorJoseph Morrow <joseph.l.morrow@intel.com>
Wed, 30 Sep 2015 21:38:41 +0000 (14:38 -0700)
committerJon A. Cruz <jonc@osg.samsung.com>
Fri, 2 Oct 2015 20:29:34 +0000 (20:29 +0000)
Change-Id: Ie6de7fb686f25db5870e5f6d359d3e8351e915b8
Signed-off-by: Joseph Morrow <joseph.l.morrow@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/2945
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Jon A. Cruz <jonc@osg.samsung.com>
plugins/samples/linux/SConscript
plugins/src/pluginlist.c
plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_socket.h [new file with mode: 0644]
plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_wrapper.h
plugins/zigbee_wrapper/telegesis_wrapper/include/twsocketlist.h [new file with mode: 0644]
plugins/zigbee_wrapper/telegesis_wrapper/include/twtypes.h [new file with mode: 0644]
plugins/zigbee_wrapper/telegesis_wrapper/src/SConscript
plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_socket.c [new file with mode: 0644]
plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_wrapper.c
plugins/zigbee_wrapper/telegesis_wrapper/src/twsocketlist.c [new file with mode: 0644]

index dea9b7f..bad07a3 100644 (file)
@@ -22,6 +22,7 @@ Import('env')
 import os
 import os.path
 target_os = env.get('TARGET_OS')
+target_arch = env.get('TARGET_ARCH')
 samples_env = env.Clone()
 src_dir = env.get('SRC_DIR')
 pi_dir = os.path.join(src_dir, 'plugins')
@@ -54,6 +55,11 @@ elif target_os not in ['arduino']:
                                           'coap',
                                           'plugin_interface'])
        samples_env.AppendUnique(LIBS = ['rt'])
+       if target_arch in ['x86_64', 'arm64']:
+               samples_env.AppendUnique(CPPFLAGS = ['-Llib64'])
+       else:
+               samples_env.AppendUnique(CPPFLAGS = ['-Llib'])
+       samples_env.AppendUnique(LIBS = ['pthread'])
 
 if env.get('SECURED') == '1':
     samples_env.AppendUnique(LIBS = ['tinydtls'])
index 3acd75e..ef4c8e1 100644 (file)
 #include "zigbee_wrapper.h"
 #include "utlist.h"
 #include "oic_malloc.h"
+#include "ocstack.h"
+#include "logger.h"
+
+#define TAG "pluginlist"
 
 static PIPluginBase * pluginList = NULL;
 
@@ -121,6 +125,13 @@ OCStackResult DeleteResource(PIPluginBase * plugin, PIResourceBase * resource)
 
     LL_DELETE(resourceList, resource);
 
+    OCStackResult result = OCDeleteResource(resource->piResource.resourceHandle);
+    if(result != OC_STACK_OK)
+    {
+        OC_LOG_V(ERROR, TAG, "Failed to delete resource with error: %d", result);
+        return result;
+    }
+
     OICFree (resource->piResource.uri);
     if (plugin->type == PLUGIN_ZIGBEE)
     {
diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_socket.h b/plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_socket.h
new file mode 100644 (file)
index 0000000..23cde1d
--- /dev/null
@@ -0,0 +1,165 @@
+//******************************************************************
+//
+// Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+/**
+ * @file
+ *
+ * This API only works with:
+ *      Telegesis ETRX357
+ *      CICIE R310 B110615
+ *
+ */
+
+#ifndef TELEGESISSOCKET_H_
+#define TELEGESISSOCKET_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#include "twtypes.h"
+#include "plugininterface.h"
+
+typedef enum
+{
+    TW_NETWORK_INFO = 0,
+    TW_JPAN,
+    TW_RFD,
+    TW_FFD,
+    TW_SED,
+    TW_ZED,
+    TW_MATCHDESC,
+    TW_SIMPLEDESC,
+    TW_INCLUSTER,
+    TW_WRITEATTR,
+    TW_RESPATTR,
+    TW_TEMPERATURE,
+    TW_DFTREP,
+    TW_DRLOCRSP,
+    TW_DRUNLOCKRSP,
+    TW_ACK,
+    TW_NACK,
+    TW_SEQ,
+    TW_ZENROLLREQ,
+    TW_ENROLLED,
+    TW_NONE,
+    TW_MAX_ENTRY
+} TWEntryType;
+
+/**
+ * Represents a single line within TWEntry struct.
+ */
+typedef struct
+{
+    const char * line;
+    int length;
+} TWLine;
+
+/**
+ * Represents a queue item. This is structure built after incoming data from the
+ * Telegesis adapter has put something in the buffer. A single TWEntry can contain 0+ TWLines.
+ */
+typedef struct TWEntry
+{
+    /** A pointer to the list of lines */
+    TWLine * lines;
+    /** Number of lines in this entry. */
+    int count;
+    /** The type of entry. This maps with the leading tag of any given AT response/prompt. */
+    TWEntryType type;
+    /** First two characters are an AT Error Code,
+        while third character is NULL terminator so it can be printed */
+    char atErrorCode[3];
+    /** Any read, write, parsing, or system errors are captured in this generic resultCode. */
+    TWResultCode resultCode;
+    struct TWEntry * next; // Ignore; Used internally to manage the queue.
+} TWEntry;
+
+/**
+ * Starts socket communication with the Telegesis Dongle at com location.
+ *
+ * @param plugin The plugin' scope which the socket ops will operate within.
+ *
+ * @param fileLoc The file descriptor location on the file system to start.
+ */
+TWResultCode TWStartSock(PIPlugin * plugin, const char * fileLoc);
+
+/**
+ * Issues command to a specific Telegesis Dongle.
+ *
+ * @param plugin The plugin' scope which the command will be issued.
+ *
+ * @param command The command to be issued to the Telegesis Dongle.
+ */
+TWResultCode TWIssueATCommand(PIPlugin * plugin, const char * command);
+
+/**
+ * Returns a response/prompt. If NULL, no response or prompts have been issued
+ * back by the Telegesis Dongle.
+ *
+ * @param plugin The plugin' scope which the socket is managed within.
+ *
+ * @param entry The line(s) which correspond to a single entry in the
+ * response/prompt queue. Returned by-reference.
+ *
+ * @param type The type of entry this queue item is. If not specified as TW_NONE,
+ * this API will block (for up to 5 Seconds) until an entry with the specified type
+ * has been enqueued.
+ *
+ * @brief Its best to call this function in a loop. Break from loop when this
+ * function returns NULL. Otherwise, handle the data in TWEntry. Release
+ * memory allocated by this function by passing the entry into TWDeleteEntry.
+ */
+TWResultCode TWDequeueEntry(PIPlugin * plugin, TWEntry ** entry, TWEntryType type);
+
+/**
+ * Helper function to deallocate memory of a TWEntry.
+ *
+ * Use this function when you are finished using the entry returned after
+ * calling TWDequeueLine. This will ensure your utilization of this API does
+ * not lead to memory leaks in your application.
+ *
+ * @param plugin The plugin' scope which the socket is managed within.
+ *
+ * @param entry The entry that was dequeued by calling TWDequeueLine.
+ */
+TWResultCode TWDeleteEntry(PIPlugin * plugin, TWEntry * entry);
+
+/**
+ * Helper function to retrieve the current radio's EUI.
+ *
+ * @param plugin The plugin' scope which the socket is managed within.
+ *
+ * @param eui The local radio's EUI.
+ */
+TWResultCode TWGetEUI(PIPlugin * plugin, char ** eui);
+
+/**
+ * Stops socket communication with the Telegesis Dongle within scope of plugin.
+ *
+ * @param plugin The plugin' scope the socket ops cease to operate within.
+ */
+TWResultCode TWStopSock(PIPlugin * plugin);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* TELEGESISSOCKET_H_ */
index 80a1d39..28d6825 100644 (file)
@@ -35,21 +35,12 @@ extern "C" {
 #endif // __cplusplus
 
 #include "octypes.h"
+#include "twtypes.h"
+
 #include <stdint.h>
 #include <time.h>
 #include <stdbool.h>
 
-#define SIZE_EUI                    (17)
-#define SIZE_NODEID                 (5)
-#define SIZE_CLUSTERID              (5)
-#define SIZE_ENDPOINTID             (3)
-
-#define SIZE_ZONESTATUS             (5)
-#define SIZE_ZONESTATUS_EXTENDED    (3)
-#define SIZE_ZONEID                 (3)
-#define SIZE_ZONETYPE               (5)
-#define SIZE_UPDATE_DELAY_TIME      (5)
-
 /**
  *
  * Defines a cluster id.
diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/include/twsocketlist.h b/plugins/zigbee_wrapper/telegesis_wrapper/include/twsocketlist.h
new file mode 100644 (file)
index 0000000..e06d19e
--- /dev/null
@@ -0,0 +1,76 @@
+//******************************************************************
+//
+// Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+/**
+ * @file
+ *
+ * This API only works with:
+ *      Telegesis ETRX357
+ *      CICIE R310 B110615
+ *
+ */
+
+#ifndef TWSOCKETLIST_H_
+#define TWSOCKETLIST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#include "plugintypes.h"
+#include "telegesis_socket.h"
+#include "utlist.h"
+
+// TODO: Use OICThread instead of pthread directly.
+// TODO: Use OICMutex instead of mutex directly.
+#include <pthread.h>
+
+typedef struct TWSock
+{
+    PIPlugin * plugin; // Handle
+    char * eui; // The associated Zigbee radio's EUI.
+    int fd;
+    char * buffer;
+    /** 'queue' MUST BE ACCESSED THREAD SAFE **/
+    TWEntry * queue;
+    pthread_mutex_t mutex; // TODO: Use OIC_MUTEX instead.
+    pthread_cond_t  queueCV;
+    bool isActive;
+    /** 'queue' MUST BE ACCESSED THREAD SAFE **/
+    pthread_t threadHandle;
+    pthread_attr_t threadAttr;
+    struct TWSock * next;
+} TWSock;
+
+TWResultCode TWAddTWSock(TWSock * sock, PIPlugin * plugin, const char * fileLoc);
+
+TWSock * TWGetSock(PIPlugin * plugin);
+
+TWResultCode TWDeleteTWSock(TWSock * sock);
+
+TWResultCode TWDeleteAllTWSock();
+
+TWResultCode TWFreeQueue(PIPlugin * plugin);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* TWSOCKETLIST_H_ */
diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/include/twtypes.h b/plugins/zigbee_wrapper/telegesis_wrapper/include/twtypes.h
new file mode 100644 (file)
index 0000000..6b98a64
--- /dev/null
@@ -0,0 +1,170 @@
+//******************************************************************
+//
+// Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+/**
+ * @file
+ *
+ * This API only works with:
+ *      Telegesis ETRX357
+ *      CICIE R310 B110615
+ *
+ */
+
+#ifndef TWTYPES_H_
+#define TWTYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define DEVICE_BAUDRATE                 (B19200)
+#define MAX_ZIGBEE_BYTES                (512)
+#define MAX_ZIGBEE_ENROLLED_DEVICES     (255)
+
+#define TIME_OUT_00_SECOND      (0)
+#define TIME_OUT_01_SECOND      (1)
+#define TIME_OUT_05_SECONDS     (5)
+#define TIME_OUT_07_SECONDS     (7)
+#define TIME_OUT_10_SECONDS     (10)
+#define TIME_OUT_15_SECONDS     (15)
+
+#define SIMPLEDESC_RESPONSE_EXPECTED_LINES (6)
+
+#define AT_STR_ERROR_OK         "00"
+
+#define SENDMODE                "0"
+#define SEPARATOR               ","
+#define SEPARATOR_LENGTH        strlen(SEPARATOR)
+
+#define AT_CMD_RESET                "AT&F"
+#define AT_CMD_GET_NETWORK_INFO     "AT+N"
+#define AT_CMD_ESTABLISH_NETWORK    "AT+EN"
+#define AT_CMD_PERMIT_JOIN          "AT+PJOIN:"
+#define AT_CMD_MATCH_REQUEST        "AT+MATCHREQ:"
+#define AT_CMD_SIMPLE_DESC          "AT+SIMPLEDESC:"
+#define AT_CMD_WRITE_ATR            "AT+WRITEATR:"
+#define AT_CMD_READ_ATR             "AT+READATR:"
+#define AT_CMD_RUN_ON_OFF           "AT+RONOFF:"
+#define AT_CMD_MOVE_TO_LEVEL        "AT+LCMVTOLEV:"
+#define AT_CMD_DOOR_LOCK            "AT+DRLOCK:"
+#define AT_CMD_GET_LOCAL_EUI        "ATS04?"
+#define AT_CMD_REMOTE_EUI_REQUEST   "AT+EUIREQ:"
+
+#define TW_ENDCONTROL_ERROR_STRING  "ERROR:"
+
+#define SIZE_EUI                    (17)
+#define SIZE_NODEID                 (5)
+#define SIZE_CLUSTERID              (5)
+#define SIZE_ENDPOINTID             (3)
+
+#define SIZE_ZONESTATUS             (5)
+#define SIZE_ZONESTATUS_EXTENDED    (3)
+#define SIZE_ZONEID                 (3)
+#define SIZE_ZONETYPE               (5)
+#define SIZE_UPDATE_DELAY_TIME      (5)
+
+//-----------------------------------------------------------------------------
+// Typedefs
+//-----------------------------------------------------------------------------
+typedef enum
+{
+    ZB_STATE_UNKNOWN,
+    ZB_STATE_INIT
+} TWState;
+
+typedef struct
+{
+    TWState state;
+    uint64_t panId;
+    uint64_t extPanId;
+
+    char*   remoteAttributeValueRead;
+    uint8_t remoteAtrributeValueReadLength;
+} TWStatus;
+
+typedef enum
+{
+    TW_RESULT_OK = 0,
+
+    TW_RESULT_ERROR_LINE_COUNT,
+    TW_RESULT_NO_LOCAL_PAN,
+    TW_RESULT_HAS_LOCAL_PAN,
+    TW_RESULT_NEW_LOCAL_PAN_ESTABLISHED,
+    TW_RESULT_DEVICE_JOINED,
+    TW_RESULT_FOUND_NO_MATCHED_DEVICE,
+    TW_RESULT_FOUND_MATCHED_DEVICES,
+    TW_RESULT_HAS_CLUSTERS,
+    TW_RESULT_HAS_NO_CLUSTER,
+    TW_RESULT_REMOTE_ATTR_HAS_VALUE,
+
+    TW_RESULT_UNKNOWN,
+
+    TW_RESULT_ERROR_INVALID_PARAMS,
+    TW_RESULT_ERROR_INVALID_PORT,
+    TW_RESULT_ERROR_NO_MEMORY,
+    TW_RESULT_ERROR_NOTIMPL,
+
+    TW_RESULT_ERROR = 255
+
+} TWResultCode;
+
+typedef enum
+{
+    AT_ERROR_EVERYTHING_OK  = 0,
+    AT_ERROR_NODE_IS_PART_OF_PAN = 28,
+    AT_ERROR_MESSAGE_NOT_SENT_TO_TARGET_SUCCESSFULLY   = 66,
+    AT_ERROR_INVALID_OPERATION  = 70,
+
+} TWATErrorCode;
+
+typedef enum
+{
+    TW_ENDCONTROL_OK = 0,
+    TW_ENDCONTROL_ERROR,
+    TW_ENDCONTROL_ACK,
+    TW_ENDCONTROL_SEQ,
+    TW_ENDCONTROL_MAX_VALUE
+
+} TWEndControl;
+
+typedef struct
+{
+    const char * endStr;
+    TWEndControl endControl;
+
+} TWEndControlMap;
+
+typedef TWResultCode (*TWATResultHandler)(int count, char** tokens);
+
+typedef struct
+{
+    const char *resultTxt;
+    TWATResultHandler handler;
+
+} TWATResultHandlerPair;
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* TWTYPES_H_ */
index 14c6ea8..be27059 100644 (file)
@@ -53,7 +53,9 @@ if target_os in ['darwin','ios']:
 tw_src = [
          os.path.join(src_dir, 'resource', 'c_common', 'oic_string', 'src', 'oic_string.c'),
          os.path.join(src_dir, 'resource', 'csdk', 'logger', 'src', 'logger.c'),
-        'telegesis_wrapper.c',
+         'twsocketlist.c',
+         'telegesis_socket.c',
+         'telegesis_wrapper.c'
          ]
 
 env.AppendUnique(TW_SRC = tw_src)
diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_socket.c b/plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_socket.c
new file mode 100644 (file)
index 0000000..e7cc356
--- /dev/null
@@ -0,0 +1,941 @@
+//******************************************************************
+//
+// Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#include "telegesis_socket.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/select.h>
+#include <time.h>
+
+#include "twsocketlist.h"
+#include "oic_string.h"
+#include "oic_malloc.h"
+#include "logger.h"
+
+#define TAG "Telegesis_Socket"
+
+/**
+ * New thread's main() ftn.
+ */
+void * readForever(/*PIPlugin */void * plugin);
+/**
+ * Just grabs the next char in the buffer. Called by readBufferLine() multiple times.
+ */
+char readBufferChar(int fd, ssize_t * readDataBytes);
+/**
+ * Calls readBufferChar() until line is formed.
+ */
+const char * readBufferLine(int fd);
+/**
+ * Calls readBufferLine() until a full TWEntry is formed.
+ */
+TWEntry * readEntry(int fd);
+/**
+ * Posts the TWEntry to the queue.
+ */
+TWResultCode TWEnqueueEntry(PIPlugin * plugin, TWEntry * entry);
+
+/**
+ * Defines the mapping relationships between Telegesis AT response/prompt tags and
+ * how many lines we should expect with each following the tag.
+ */
+typedef struct
+{
+    const char *resultTxt;
+    uint8_t numLines;
+    TWEntryType entryType;
+} TWEntryTypePair;
+
+static TWEntryTypePair TWEntryTypePairArray[] =
+{
+    {"+N=",             1, TW_NETWORK_INFO},
+    {"JPAN:",           1, TW_JPAN},
+    {"RFD:",            1, TW_RFD},
+    {"FFD:",            1, TW_FFD},
+    {"SED:",            1, TW_SED},
+    {"ZED:",            1, TW_ZED},
+    {"MatchDesc:",      1, TW_MATCHDESC},
+    {"SimpleDesc:",     6, TW_SIMPLEDESC},
+    {"InCluster:",      1, TW_INCLUSTER},
+    {"WRITEATTR:",      1, TW_WRITEATTR},
+    {"RESPATTR:",       1, TW_RESPATTR},
+    {"TEMPERATURE:",    1, TW_RESPATTR},
+    {"DFTREP",          1, TW_DFTREP},
+    {"DRLOCRSP:",       1, TW_DRLOCRSP},
+    {"DRUNLOCKRSP:",    1, TW_DRUNLOCKRSP},
+    {"ACK:",            1, TW_ACK},
+    {"NACK:",           1, TW_NACK},
+    {"SEQ:",            1, TW_SEQ},
+    {"ZENROLLREQ:",     1, TW_ZENROLLREQ},
+    {"ENROLLED:",       1, TW_ENROLLED},
+    {"Unknown:",        0, TW_NONE},
+    {"Unknown:",        1, TW_MAX_ENTRY}
+};
+
+TWEntry * TWEntryList = NULL;
+
+TWEntryTypePair getEntryTypePair(const char * bufferLine)
+{
+    size_t bufferLength = strlen(bufferLine);
+    for(uint8_t i = 0; i < TW_MAX_ENTRY; i++)
+    {
+        size_t resultTxtLength = strlen(TWEntryTypePairArray[i].resultTxt);
+        if((bufferLength >= resultTxtLength) &&
+           strncmp(bufferLine, TWEntryTypePairArray[i].resultTxt, resultTxtLength) == 0)
+        {
+            return TWEntryTypePairArray[i];
+        }
+    }
+    return TWEntryTypePairArray[TW_MAX_ENTRY];
+}
+
+TWResultCode TWWait(pthread_cond_t * cond, pthread_mutex_t * mutex, uint8_t timeout)
+{
+    int ret = 0;
+    // This is a blocking call which hold the calling thread until an entry
+    // has been enqueued or until the specified timeout has surpassed.
+    struct timespec abs_time;
+    clock_gettime(CLOCK_REALTIME , &abs_time);
+    abs_time.tv_sec += timeout;
+    ret = pthread_cond_timedwait(cond, mutex, &abs_time);
+
+    switch (ret)
+    {
+        case 0:
+            return TW_RESULT_OK;
+        case ETIMEDOUT:
+            OC_LOG(INFO, TAG, "Timed out waiting for CV. Non-error. Please try again.");
+            return TW_RESULT_OK;
+        case EINVAL:
+            OC_LOG(ERROR, TAG, "Cond or Mutex is invalid. OR timeout value for CV is invalid.");
+            break;
+        case EPERM:
+            OC_LOG(ERROR, TAG, "Cannot use CV because the current thread does not own the CV.");
+            break;
+    }
+
+    return TW_RESULT_ERROR;
+}
+
+TWResultCode TWGrabMutex(pthread_mutex_t * mutex)
+{
+    int ret = pthread_mutex_lock(mutex);
+
+    switch (ret)
+    {
+        case 0:
+            return TW_RESULT_OK;
+        case EINVAL:
+            OC_LOG(ERROR, TAG, "Mutex was not initialized.");
+            break;
+        case ETIMEDOUT:
+            OC_LOG(INFO, TAG, "Timed out waiting for mutex. Non-error. Please try again.");
+            return TW_RESULT_OK;
+        case EAGAIN:
+            OC_LOG(ERROR, TAG, "Maximum number of locks for mutex have been exceeded.");
+            break;
+        case EDEADLK:
+            OC_LOG(ERROR, TAG, "Deadlock OR this thread already owns the mutex.");
+            break;
+    }
+    return TW_RESULT_ERROR;
+}
+
+TWResultCode TWReleaseMutex(pthread_mutex_t * mutex)
+{
+    int ret = pthread_mutex_unlock(mutex);
+
+    switch (ret)
+    {
+        case 0:
+            return TW_RESULT_OK;
+        case EINVAL:
+            OC_LOG(ERROR, TAG, "Mutex was not initialized.");
+            break;
+        case EAGAIN:
+            OC_LOG(ERROR, TAG, "Maximum number of locks for mutex have been exceeded.");
+            break;
+        case EPERM:
+            OC_LOG(ERROR, TAG, "Cannot unlock because the current thread does not own the mutex.");
+            break;
+    }
+    return TW_RESULT_ERROR;
+}
+
+char readBufferChar(int fd, ssize_t * readDataBytes)
+{
+    // Performs read operation on fd for one character at a time.
+    if(!readDataBytes)
+    {
+        return '\0';
+    }
+    *readDataBytes = 0;
+    char byte = '\0';
+    errno = 0;
+    *readDataBytes = read(fd, &byte, sizeof(byte));
+    if(*readDataBytes < 0)
+    {
+        OC_LOG_V(ERROR, TAG, "\tCould not read from port. Errno is: %d\n", errno);
+        return '\0';
+    }
+    return byte;
+}
+
+bool isLineIgnored(const char * line, size_t length)
+{
+    if(length >= 4)
+    {
+        if(line[0] == 'N' && line[1] == 'A' && line[2] == 'C' && line[3] == 'K')
+        {
+            return true;
+        }
+    }
+    if(length >= 3)
+    {
+        if(line[0] == 'S' && line[1] == 'E' && line[2] == 'Q')
+        {
+            return true;
+        }
+        else if(line[0] == 'A' && line[1] == 'C' && line[2] == 'K')
+        {
+            return true;
+        }
+    }
+    if(length >= 2)
+    {
+        if(line[0] == 'A' && line[1] == 'T')
+        {
+            // If the line starts with "AT", then this is an echo. We ignore echos.
+            return true;
+        }
+        else if(line[0] == 'O' && line[1] == 'K')
+        {
+            //If this line is "OK", we ignore success codes. But we do end TWEntry's on "OK".
+            // (FYI, error codes are handled in readEntry() which invokes this function.)
+            return true;
+        }
+    }
+    return false;
+}
+
+const char * readBufferLine(int fd)
+{
+    char bufferChar = '\0';
+    size_t bufferLoc = 0;
+    ssize_t readDataBytes = 0;
+    char * bufferLineHold = NULL;
+    char * bufferLine = NULL;
+    bool endOfLine1 = false;
+    bool endOfLine2 = false;
+    bool ignore = false;
+    while(true)
+    {
+        if(!endOfLine1 || !endOfLine2)
+        {
+            bufferChar = readBufferChar(fd, &readDataBytes);
+            if(bufferChar == '\r')
+            {
+                endOfLine1 = true;
+                continue;
+            }
+            if(bufferChar == '\n')
+            {
+                endOfLine2 = true;
+                continue;
+            }
+        }
+        if(readDataBytes != 0 && (!endOfLine1 && !endOfLine2))
+        {
+            size_t bufferLineSize = sizeof(bufferChar)*(bufferLoc+2);
+            bufferLine = (char *) OICRealloc(bufferLineHold, bufferLineSize);
+            if(!bufferLine)
+            {
+                OC_LOG(ERROR, TAG, "Ran out of memory.");
+                return NULL;
+            }
+            bufferLine[bufferLoc] = '\0';
+            OICStrcat(bufferLine, bufferLineSize+2, &bufferChar);
+            bufferLoc++;
+            bufferLineHold = bufferLine;
+
+            OC_LOG_V(DEBUG, TAG, "Incoming: %s", bufferLine);
+        }
+        else
+        {
+            if(!bufferLine)
+            {
+                return NULL;
+            }
+            size_t bufferLineSize = sizeof(bufferChar)*(bufferLoc+2);
+            bufferLine = (char *) OICRealloc(bufferLineHold, bufferLineSize);
+            if(!bufferLine)
+            {
+                OC_LOG(ERROR, TAG, "Ran out of memory.");
+                return NULL;
+            }
+            bufferLine[bufferLoc] = '\0';
+            bufferLoc++;
+            bufferLineHold = bufferLine;
+            ignore = isLineIgnored(bufferLine, strlen(bufferLine));
+            if(ignore)
+            {
+                OICFree(bufferLine);
+                return readBufferLine(fd);
+            }
+            if(endOfLine1 && endOfLine2)
+            {
+                return bufferLine;
+            }
+            else
+            {
+                return NULL;
+            }
+        }
+    }
+}
+
+TWResultCode TWAddLineToEntry(const char * line, TWEntry * entry)
+{
+    if(!line || !entry)
+    {
+        OC_LOG(ERROR, TAG, "Invalid/NULL parameter(s) received.");
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    TWLine * twLine = (TWLine *) OICCalloc(1, sizeof(TWLine));
+    if(!twLine)
+    {
+        OC_LOG(ERROR, TAG, "Ran out of memory.");
+        return TW_RESULT_ERROR_NO_MEMORY;
+    }
+    size_t lineLength = strlen(line);
+    twLine->line = line;
+    twLine->length = lineLength;
+    size_t errorLength = strlen(TW_ENDCONTROL_ERROR_STRING);
+    size_t maxLength = (lineLength > errorLength) ? errorLength : lineLength;
+
+    if((errorLength == lineLength) &&
+       strncmp(line, TW_ENDCONTROL_ERROR_STRING, maxLength) == 0)
+    {
+        entry->atErrorCode[0] = line[errorLength];
+        entry->atErrorCode[1] = line[errorLength + 1];
+    }
+    else
+    {
+        entry->atErrorCode[0] = '0';
+        entry->atErrorCode[1] = '0';
+    }
+
+    // Null terminate the string.
+    entry->atErrorCode[2] = '\0';
+
+    if(!entry->lines)
+    {
+        entry->lines = twLine;
+    }
+    else
+    {
+        entry->lines[entry->count] = *twLine;
+    }
+    entry->count++;
+
+    return TW_RESULT_OK;
+}
+
+TWEntry * readEntry(int fd)
+{
+    // Calls readBufferLine().
+    // Forms TWEntry from 1-n lines based on the response type.
+
+    TWEntry * entry = (TWEntry *) OICCalloc(1, sizeof(TWEntry));
+    if(!entry)
+    {
+        OC_LOG(ERROR, TAG, "Ran out of memory.");
+        return NULL;
+    }
+    entry->count = 0;
+
+    const char * bufferLine = NULL;
+    TWEntryTypePair entryTypePair = { .resultTxt = NULL,
+                                      .numLines  = 0,
+                                      .entryType = TW_NONE };
+    size_t numLines = 0;
+    while(numLines == 0 || bufferLine)
+    {
+        if(numLines == 0)
+        {
+            bufferLine = readBufferLine(fd);
+            if(!bufferLine)
+            {
+                goto exit;
+            }
+            entryTypePair = getEntryTypePair(bufferLine);
+        }
+        else
+        {
+            bufferLine = readBufferLine(fd);
+        }
+        if(bufferLine != NULL)
+        {
+            entry->type = entryTypePair.entryType;
+            TWAddLineToEntry(bufferLine, entry);
+            numLines++;
+            entry->count = numLines;
+        }
+
+        if(entryTypePair.numLines != numLines)
+        {
+            entry->resultCode = TW_RESULT_ERROR_LINE_COUNT;
+        }
+        else
+        {
+            entry->resultCode = TW_RESULT_OK;
+        }
+    }
+    return entry;
+exit:
+    OICFree(entry);
+    return NULL;
+}
+
+TWResultCode TWRetrieveEUI(PIPlugin * plugin, TWSock * twSock)
+{
+    if(twSock->isActive == false)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    TWEntry * entry = NULL;
+    TWResultCode deleteResult = TW_RESULT_OK;
+    TWResultCode result = TWIssueATCommand(plugin, AT_CMD_GET_LOCAL_EUI);
+    if(result != TW_RESULT_OK)
+    {
+        return result;
+    }
+    result = TWGrabMutex(&twSock->mutex);
+    if(result != TW_RESULT_OK)
+    {
+        return result;
+    }
+    entry = readEntry(twSock->fd);
+    if(!entry)
+    {
+        result = TWReleaseMutex(&twSock->mutex);
+        if(result != TW_RESULT_OK)
+        {
+            goto exit;
+        }
+    }
+    twSock->eui = (char *) OICMalloc(strlen(entry->lines[0].line)+1);
+    if(!twSock->eui)
+    {
+        result = TW_RESULT_ERROR_NO_MEMORY;
+        goto exit;
+    }
+
+    if(SIZE_EUI != (strlen(entry->lines[0].line)+1))
+    {
+        OICFree(twSock->eui);
+        result = TW_RESULT_ERROR;
+        goto exit;
+    }
+
+    OICStrcpy(twSock->eui, SIZE_EUI, entry->lines[0].line);
+
+    result = TWReleaseMutex(&twSock->mutex);
+    if(result != TW_RESULT_OK)
+    {
+        OICFree(twSock->eui);
+        goto exit;
+    }
+exit:
+    deleteResult = TWDeleteEntry(plugin, entry);
+    if(deleteResult != TW_RESULT_OK)
+    {
+        return deleteResult;
+    }
+    return result;
+}
+
+TWResultCode TWStartSock(PIPlugin * plugin, const char * fileLoc)
+{
+    TWSock * sock = TWGetSock((PIPlugin *)plugin);
+    if(sock && sock->isActive == true)
+    {
+        // Ignore requests to start up the same socket.
+        return TW_RESULT_OK;
+    }
+    if(!sock)
+    {
+        sock = (TWSock *) OICCalloc(1, sizeof(TWSock));
+        if(!sock)
+        {
+            return TW_RESULT_ERROR_NO_MEMORY;
+        }
+    }
+    TWResultCode ret = TWAddTWSock(sock, plugin, fileLoc);
+    if(ret != 0)
+    {
+        goto exit;
+    }
+
+    ret = TWRetrieveEUI((PIPlugin *)plugin, sock);
+    if(ret != TW_RESULT_OK)
+    {
+        OC_LOG(ERROR, TAG, "Unable to retrieve Zigbee Radio's EUI.");
+        return ret;
+    }
+
+    int result = pthread_create(&(sock->threadHandle),
+                                NULL,
+                                readForever,
+                                (void *) plugin);
+    if(result != 0)
+    {
+        OC_LOG_V(ERROR, TAG, "Thread start failed with error %d", result);
+        ret = TW_RESULT_ERROR;
+        goto exit;
+    }
+
+    return TW_RESULT_OK;
+
+exit:
+    TWDeleteTWSock(sock);
+    return ret;
+}
+
+static void sigHandler(int signal)
+{
+    pthread_t tid = pthread_self();
+    (void)tid;
+    (void)signal;
+    OC_LOG_V(INFO, TAG, "Received signal on thread: %lu\n", tid);
+    OC_LOG_V(INFO, TAG, "Signal is: %d", signal);
+}
+
+void * readForever(/*PIPlugin*/ void * plugin)
+{
+    TWResultCode result = TW_RESULT_OK;
+    TWEntry * entry = NULL;
+    TWSock * twSock = TWGetSock((PIPlugin *)plugin);
+    if(!twSock)
+    {
+        OC_LOG(ERROR, TAG, "Unable to retrieve associated socket.");
+        return NULL;
+    }
+
+    pthread_t tid = pthread_self();
+    (void)tid;
+    OC_LOG_V(INFO, TAG, "ReadForever Telegesis ThreadId: %lu", tid);
+
+    struct sigaction action = { .sa_handler = 0 };
+
+    sigset_t sigmask;
+
+    action.sa_handler = sigHandler;
+    action.sa_flags = 0;
+    sigemptyset(&action.sa_mask);
+    sigaction(EINTR, &action, NULL);
+
+    fd_set readFDS;
+    FD_ZERO(&readFDS);
+    FD_SET(twSock->fd, &readFDS);
+    bool haveMutex = false;
+    while(true)
+    {
+        errno = 0;
+        // 'sigmask' is needed to catch intterupts.
+        // This interrupt happens after call to pthread_exit(..., EINTR).
+        // Once a signal handler is registered, pselect will handle interrupts by returning
+        // with '-1' and setting errno appropriately.
+        int ret = pselect(twSock->fd+1, &readFDS, NULL, NULL, NULL, &sigmask);
+        if(ret < 0)
+        {
+            if(errno == EINTR)
+            {
+                if(twSock->isActive)
+                {
+                    continue;
+                    // This EINTR signal is not for us. Do not handle it.
+                }
+                OC_LOG(DEBUG, TAG, "Thread has been joined. Exiting thread.");
+                pthread_exit(PTHREAD_CANCELED);
+                return NULL;
+            }
+            else
+            {
+                OC_LOG_V(ERROR, TAG, "Unaccounted error occurred. Exiting thread."
+                                     "Errno is: %d", errno);
+                return NULL;
+            }
+        }
+        else
+        {
+            ret = FD_ISSET(twSock->fd, &readFDS);
+            if(ret != 0)
+            {
+                // Valid data on valid socket.
+                // Grab & parse, then pass up to upper layers.
+                if(haveMutex != true)
+                {
+                    result = TWGrabMutex(&twSock->mutex);
+                    if(result != TW_RESULT_OK)
+                    {
+                        OC_LOG(ERROR, TAG, "Unable to grab mutex.");
+                        return NULL;
+                    }
+                    haveMutex = true;
+                }
+                entry = readEntry(twSock->fd);
+                if(!entry)
+                {
+                    result = TWReleaseMutex(&twSock->mutex);
+                    if(result != TW_RESULT_OK)
+                    {
+                        OC_LOG(ERROR, TAG, "Unable to release mutex.");
+                        return NULL;
+                    }
+                    haveMutex = false;
+                    // This is most likely a parsing error of the received
+                    // response. Not necessarily fatal.
+                    continue;
+                }
+                result = TWEnqueueEntry((PIPlugin *)plugin, entry);
+                if(result != TW_RESULT_OK)
+                {
+                    OC_LOG_V(ERROR, TAG, "Could not add TWEntry to queue for"
+                                          "consumption by the application"
+                                          "layer. Error is: %d", result);
+                    TWDeleteEntry((PIPlugin *)plugin, entry);
+                    // This is most likely a FATAL error, such as out of memory.
+                    break;
+                }
+
+                // Notify other threads waiting for a response that an entry has been enqueued.
+                pthread_cond_signal(&twSock->queueCV);
+
+                result = TWReleaseMutex(&twSock->mutex);
+                haveMutex = false;
+            }
+            else
+            {
+                // Unrelated data waiting elsewhere. Continue the loop.
+                continue;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+TWResultCode TWIssueATCommand(PIPlugin * plugin, const char * command)
+{
+    if(!plugin || !command)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    OC_LOG_V(INFO, TAG, "Attempt to write %s.", command);
+    TWSock * twSock = TWGetSock(plugin);
+    if(!twSock)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    if(twSock->isActive == false)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    TWResultCode result = TW_RESULT_OK;
+    TWResultCode mutexResult = TW_RESULT_OK;
+    mutexResult = TWGrabMutex(&twSock->mutex);
+    if(mutexResult != TW_RESULT_OK)
+    {
+        return mutexResult;
+    }
+    size_t sendCommandSize = (strlen(command) + strlen("\r") + 1) * sizeof(char);
+    char * sendCommand = (char *) OICCalloc(1, sendCommandSize);
+    if(!sendCommand)
+    {
+        return TW_RESULT_ERROR_NO_MEMORY;
+    }
+    char * temp = OICStrcpy(sendCommand, sendCommandSize, command);
+    if(temp != sendCommand)
+    {
+        result = TW_RESULT_ERROR;
+        goto exit;
+    }
+    temp = OICStrcat(sendCommand, sendCommandSize, "\r");
+    if(temp != sendCommand)
+    {
+        result = TW_RESULT_ERROR;
+        goto exit;
+    }
+    size_t expectedWrittenBytes = strlen(sendCommand);
+    errno = 0;
+    size_t actualWrittenBytes = write(twSock->fd, sendCommand, expectedWrittenBytes);
+    if(actualWrittenBytes <= 0)
+    {
+        OC_LOG_V(ERROR, TAG, "Could not write to port. Errno is: %d\n", errno);
+        result =  TW_RESULT_ERROR;
+        goto exit;
+    }
+    if(actualWrittenBytes != expectedWrittenBytes)
+    {
+        OC_LOG(ERROR, TAG, "Telegesis Adapter did not receive expected command. Unknown state.");
+        result = TW_RESULT_ERROR;
+    }
+
+exit:
+    mutexResult = TWReleaseMutex(&twSock->mutex);
+    if(mutexResult != TW_RESULT_OK)
+    {
+        return mutexResult;
+    }
+    OICFree(sendCommand);
+    return result;
+}
+
+TWResultCode TWEnqueueEntry(PIPlugin * plugin, TWEntry * entry)
+{
+    if(!plugin || !entry)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    TWSock * twSock = TWGetSock(plugin);
+    if(!twSock)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    if(twSock->isActive == false)
+    {
+        return TW_RESULT_ERROR;
+    }
+    LL_APPEND(twSock->queue, entry);
+    return TW_RESULT_OK;
+}
+
+TWResultCode TWDequeueEntry(PIPlugin * plugin, TWEntry ** entry, TWEntryType type)
+{
+    if(!plugin || !entry)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    TWSock * twSock = TWGetSock(plugin);
+    if(!twSock)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    if(twSock->isActive == false)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    TWResultCode ret = TW_RESULT_OK;
+
+    // If no entry is found, then this code path returns immediately.
+    ret = TWGrabMutex(&twSock->mutex);
+    if(ret != TW_RESULT_OK)
+    {
+        return ret;
+    }
+
+    if(type != TW_NONE)
+    {
+        // Wait for up to 10 seconds for the entry to put into the queue.
+        ret = TWWait(&twSock->queueCV, &twSock->mutex, TIME_OUT_10_SECONDS);
+        if(ret != TW_RESULT_OK)
+        {
+            return ret;
+        }
+    }
+
+    *entry = twSock->queue;
+    if(*entry)
+    {
+        LL_DELETE(twSock->queue, *entry);
+    }
+    ret = TWReleaseMutex(&twSock->mutex);
+    if(ret != TW_RESULT_OK)
+    {
+        return ret;
+    }
+    return ret;
+}
+
+TWResultCode TWFreeQueue(PIPlugin * plugin)
+{
+    if(!plugin)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    TWResultCode ret = TW_RESULT_OK;
+    TWEntry * entry = NULL;
+    while(true)
+    {
+        ret = TWDequeueEntry(plugin, &entry, TW_NONE);
+        if(ret != TW_RESULT_OK)
+        {
+            return ret;
+        }
+        if(entry == NULL)
+        {
+            break;
+        }
+        ret = TWDeleteEntry(plugin, entry);
+        if(ret != TW_RESULT_OK)
+        {
+            return ret;
+        }
+    }
+    return ret;
+}
+
+TWResultCode TWDeleteEntry(PIPlugin * plugin, TWEntry * entry)
+{
+    if(!plugin || !entry)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+
+    TWSock * twSock = TWGetSock(plugin);
+    if(!twSock)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    if(twSock->isActive == false)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    TWResultCode ret = TWGrabMutex(&twSock->mutex);
+    if(ret != TW_RESULT_OK)
+    {
+        return ret;
+    }
+    TWEntry * out = NULL;
+    TWEntry * tmp = NULL;
+    LL_FOREACH_SAFE(twSock->queue, out, tmp)
+    {
+        if(out == entry)
+        {
+            OC_LOG(ERROR, TAG, "Tried to delete an entry that is still in the queue. \
+                                Please dequeue the entry first.");
+            return TW_RESULT_ERROR;
+        }
+    }
+    ret = TWReleaseMutex(&twSock->mutex);
+
+    OICFree(entry);
+
+    return TW_RESULT_OK;
+}
+
+TWResultCode TWGetEUI(PIPlugin * plugin, char ** eui)
+{
+    if(!plugin || !eui)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    TWSock * twSock = TWGetSock(plugin);
+    if(!twSock)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    if(twSock->isActive == false)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    *eui = OICStrdup(twSock->eui);
+
+    return TW_RESULT_OK;
+
+}
+
+TWResultCode TWStopSock(PIPlugin * plugin)
+{
+    if(!plugin)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    TWSock * twSock = TWGetSock(plugin);
+    if(!twSock)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    if(twSock->isActive == false)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    TWResultCode ret = TWFreeQueue(plugin);
+    if(ret != TW_RESULT_OK)
+    {
+        return ret;
+    }
+
+    twSock->isActive = false;
+
+    void * retVal = NULL;
+    int pthreadRet = pthread_kill(twSock->threadHandle, EINTR);
+    if(pthreadRet != 0)
+    {
+        return TW_RESULT_ERROR;
+    }
+
+    pthreadRet = pthread_join(twSock->threadHandle, &retVal);
+    if(pthreadRet != 0)
+    {
+        switch(pthreadRet)
+        {
+            case EDEADLK:
+                OC_LOG(ERROR, TAG, "A deadlock has occurred.");
+                break;
+            case EINVAL:
+                OC_LOG(ERROR, TAG, "Thread is not joinable or another thread has already joined.");
+                break;
+            case ESRCH:
+                OC_LOG(ERROR, TAG, "No thread with the ID could be found.");
+                break;
+            default:
+                OC_LOG_V(ERROR, TAG, "Unknown error occurred when joining thread: %d", pthreadRet);
+        }
+        return TW_RESULT_ERROR;
+    }
+    if(retVal != PTHREAD_CANCELED)
+    {
+        return TW_RESULT_ERROR;
+    }
+    ret = TWDeleteTWSock(twSock);
+    if(ret != TW_RESULT_OK)
+    {
+        return ret;
+    }
+
+    return ret;
+}
index 3cbcf43..2a45992 100644 (file)
@@ -1,4 +1,4 @@
-/******************************************************************
+//******************************************************************
 //
 // Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
 //
@@ -16,7 +16,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 
 
 #include <stdio.h>
 #include "logger.h"
 
 #include "telegesis_wrapper.h"
+#include "twtypes.h"
 
 #define TAG PCF("telegesiswrapper")     // Module Name
 #define ARRAY_LENGTH            (100)
 
-#define DEVICE_BAUDRATE                 (B19200)
-#define MAX_ZIGBEE_BYTES                (512)
-#define MAX_ZIGBEE_ENROLLED_DEVICES     (255)
-
-#define TIME_OUT_00_SECOND      (0)
-#define TIME_OUT_01_SECOND      (1)
-#define TIME_OUT_05_SECONDS     (5)
-#define TIME_OUT_07_SECONDS     (7)
-#define TIME_OUT_10_SECONDS     (10)
-#define TIME_OUT_15_SECONDS     (15)
-
-#define SIMPLEDESC_RESPONSE_EXPECTED_LINES (6)
-
-#define AT_STR_ERROR_OK         "00"
-
-#define SENDMODE                "0"
-#define SEPARATOR               ","
-#define SEPARATOR_LENGTH        strlen(SEPARATOR)
-
-#define AT_CMD_RESET                "AT&F"
-#define AT_CMD_GET_NETWORK_INFO     "AT+N"
-#define AT_CMD_ESTABLISH_NETWORK    "AT+EN"
-#define AT_CMD_PERMIT_JOIN          "AT+PJOIN:"
-#define AT_CMD_MATCH_REQUEST        "AT+MATCHREQ:"
-#define AT_CMD_SIMPLE_DESC          "AT+SIMPLEDESC:"
-#define AT_CMD_WRITE_ATR            "AT+WRITEATR:"
-#define AT_CMD_READ_ATR             "AT+READATR:"
-#define AT_CMD_RUN_ON_OFF           "AT+RONOFF:"
-#define AT_CMD_MOVE_TO_LEVEL        "AT+LCMVTOLEV:"
-#define AT_CMD_DOOR_LOCK            "AT+DRLOCK:"
-#define AT_CMD_GET_LOCAL_EUI        "ATS04?"
-#define AT_CMD_REMOTE_EUI_REQUEST   "AT+EUIREQ:"
-
-
-
-
-
-//-----------------------------------------------------------------------------
-// Typedefs
-//-----------------------------------------------------------------------------
-typedef enum
-{
-    ZB_STATE_UNKNOWN,
-    ZB_STATE_INIT
-} TWState;
-
-typedef struct
-{
-    TWState state;
-    uint64_t panId;
-    uint64_t extPanId;
-
-    char*   remoteAttributeValueRead;
-    uint8_t remoteAtrributeValueReadLength;
-} TWStatus;
-
-typedef enum
-{
-    TW_RESULT_OK = 0,
-
-    TW_RESULT_NO_LOCAL_PAN,
-    TW_RESULT_HAS_LOCAL_PAN,
-    TW_RESULT_NEW_LOCAL_PAN_ESTABLISHED,
-    TW_RESULT_DEVICE_JOINED,
-    TW_RESULT_FOUND_NO_MATCHED_DEVICE,
-    TW_RESULT_FOUND_MATCHED_DEVICES,
-    TW_RESULT_HAS_CLUSTERS,
-    TW_RESULT_HAS_NO_CLUSTER,
-    TW_RESULT_REMOTE_ATTR_HAS_VALUE,
-
-    TW_RESULT_UNKNOWN,
-
-    TW_RESULT_ERROR_INVALID_PARAMS,
-    TW_RESULT_ERROR_INVALID_PORT,
-    TW_RESULT_ERROR_NO_MEMORY,
-    TW_RESULT_ERROR_NOTIMPL,
-
-    TW_RESULT_ERROR = 255
-
-} TWResultCode;
-
-typedef enum
-{
-    AT_ERROR_EVERYTHING_OK  = 0,
-    AT_ERROR_NODE_IS_PART_OF_PAN = 28,
-    AT_ERROR_MESSAGE_NOT_SENT_TO_TARGET_SUCCESSFULLY   = 66,
-    AT_ERROR_INVALID_OPERATION  = 70,
-
-} TWATErrorCode;
-
-typedef enum
-{
-    TW_ENDCONTROL_OK = 0,
-    TW_ENDCONTROL_ERROR,
-    TW_ENDCONTROL_ACK,
-    TW_ENDCONTROL_SEQ,
-    TW_ENDCONTROL_MAX_VALUE
-
-} TWEndControl;
-
-typedef struct
-{
-    const char * endStr;
-    TWEndControl endControl;
-
-} TWEndControlMap;
-
-typedef TWResultCode (*TWATResultHandler)(int count, char** tokens);
-
-typedef struct
-{
-    const char *resultTxt;
-    TWATResultHandler handler;
-
-} TWATResultHandlerPair;
-
 //-----------------------------------------------------------------------------
 // Private internal function prototypes
 //-----------------------------------------------------------------------------
@@ -1488,7 +1373,7 @@ TWResultCode HandleATResponse(char* responseArray[], int count)
                         ) == 0)
             {
                 char* tokens[ARRAY_LENGTH] = {};
-                const char* delimiters = ",\r";
+                const char* delimiters = ",\r\n";
                 int paramCount = Tokenize(responseArray[i] +
                                            strlen(g_TWATResultHandlerPairArray[k].resultTxt),
                                            delimiters, tokens);
@@ -1567,7 +1452,7 @@ TWResultCode TelNetworkInfoHandler(int count, char* tokens[])
     {
         if (tokens[i] != NULL)
         {
-            printf ("Token[%d] = %s", i, tokens[i]);
+            OC_LOG_V(INFO, TAG, "Token[%d] = %s", i, tokens[i]);
         }
     }
 
diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/src/twsocketlist.c b/plugins/zigbee_wrapper/telegesis_wrapper/src/twsocketlist.c
new file mode 100644 (file)
index 0000000..aeee38d
--- /dev/null
@@ -0,0 +1,263 @@
+//******************************************************************
+//
+// Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+/**
+ * @file
+ *
+ * This API only works with:
+ *      Telegesis ETRX357
+ *      CICIE R310 B110615
+ *
+ */
+
+#include "twsocketlist.h"
+#include "logger.h"
+#include "oic_malloc.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TAG "TWSocketList"
+
+
+/**
+ *
+ * Apply terminal settings.
+ *
+ */
+static int SetTerminalInfo(int fd, int speed, int parity, int shouldBlock);
+
+/**
+ *
+ * Internal function to close socket. For outside callers to close a socket, they must properly
+ * call TWDeleteTWSock() for the same affect to ensure all things are cleaned up on shutdown.
+ *
+ */
+TWResultCode TWCloseTWSock(TWSock * sock);
+
+static TWSock * g_twSockList = NULL;
+
+TWResultCode TWAddTWSock(TWSock * sock, PIPlugin * plugin, const char * fileLoc)
+{
+    if(!sock || !plugin || !fileLoc)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+
+    TWSock * out = NULL;
+    TWSock * temp = NULL;
+    LL_FOREACH_SAFE(g_twSockList, out, temp)
+    {
+        if(out == sock)
+        {
+            // Ignore requests to add a socket that's already in the queue.
+            return TW_RESULT_OK;
+        }
+    }
+
+    sock->plugin = plugin;
+    sock->fd = open(fileLoc, O_RDWR | O_NOCTTY | O_SYNC);
+    if(sock->fd <= 0)
+    {
+        OC_LOG_V(INFO, TAG, "Could not open port. Errno is: %d\n", errno);
+        return TW_RESULT_ERROR;
+    }
+
+    // set speed to 19,200 bps, 8n1 (no parity), no blocking.
+    int ret = SetTerminalInfo(sock->fd, DEVICE_BAUDRATE, 0, 0);
+    if(ret != 0)
+    {
+        TWResultCode result = TWCloseTWSock(sock);
+        if(result != TW_RESULT_OK)
+        {
+            return result;
+        }
+        return TW_RESULT_ERROR;
+    }
+
+    sock->buffer = NULL;
+    sock->queue = NULL;
+    pthread_mutexattr_t mutexAttr;
+    pthread_mutexattr_init(&mutexAttr);
+    pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_ERRORCHECK);
+    pthread_mutex_init(&(sock->mutex), &mutexAttr); // TODO: Use OIC_MUTEX instead.
+    pthread_cond_init(&(sock->queueCV), NULL);
+    sock->next = NULL;
+    sock->isActive = true;
+
+    LL_APPEND(g_twSockList, sock);
+    return TW_RESULT_OK;
+}
+
+TWSock * TWGetSock(PIPlugin * plugin)
+{
+    if(!plugin)
+    {
+        return NULL;
+    }
+    TWSock * out = NULL;
+    TWSock * tmp = NULL;
+    LL_FOREACH_SAFE(g_twSockList, out, tmp)
+    {
+        if(out->plugin == plugin)
+        {
+            return out;
+        }
+    }
+    return NULL;
+}
+
+TWResultCode TWCloseTWSock(TWSock * sock)
+{
+    if(!sock)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    int ret = close(sock->fd);
+    if(ret != 0)
+    {
+        OC_LOG_V(ERROR, TAG, "Could not close port. Errno is: %d", errno);
+        return TW_RESULT_ERROR;
+    }
+    return TW_RESULT_OK;
+}
+
+TWResultCode TWDeleteTWSock(TWSock * sock)
+{
+    if(!sock)
+    {
+        return TW_RESULT_ERROR_INVALID_PARAMS;
+    }
+    TWSock * out = NULL;
+    TWSock * tmp = NULL;
+    LL_FOREACH_SAFE(g_twSockList, out, tmp)
+    {
+        if(out == sock)
+        {
+            LL_DELETE(g_twSockList, out);
+        }
+    }
+
+    OICFree(sock->buffer);
+    OICFree(sock->eui);
+    TWFreeQueue(sock->plugin);
+
+    int mutexRet = pthread_mutex_destroy(&(sock->mutex));
+    if(mutexRet != 0)
+    {
+        OC_LOG_V(ERROR, TAG, "Failed to destroy mutex. Error: %d", mutexRet);
+        return TW_RESULT_ERROR;
+    }
+    TWResultCode result = TWCloseTWSock(sock);
+
+    return result;
+}
+
+TWResultCode TWDeleteAllTWSock()
+{
+    TWSock * out = NULL;
+    TWSock * tmp = NULL;
+    LL_FOREACH_SAFE(g_twSockList, out, tmp)
+    {
+        TWDeleteTWSock(out);
+    }
+    return TW_RESULT_OK;
+}
+
+/**
+ *
+ * Apply interface attribute values to terminal settings.
+ *
+ */
+int SetTerminalInfo(int fd, int speed, int parity, int shouldBlock)
+{
+    OC_LOG(INFO, TAG, "Enter SetTerminalInfo()");
+
+    int ret = 0;
+    struct termios terminalInfo = {
+                                   .c_iflag = 0
+                                  };
+
+    errno = 0;
+    ret = tcgetattr(fd, &terminalInfo);
+    if (ret != 0)
+    {
+        OC_LOG_V(ERROR, TAG, "tcgetattr() - ret=%d errno=%d", ret, errno);
+        ret = -1;
+        goto exit;
+    }
+
+    errno = 0;
+    ret = cfsetispeed (&terminalInfo, speed);
+    if (ret != 0)
+    {
+        OC_LOG_V(ERROR, TAG, "cfsetispeed() - ret=%d errno=%d", ret, errno);
+        ret = -1;
+        goto exit;
+    }
+
+    errno = 0;
+    ret = cfsetospeed (&terminalInfo, speed);
+    if (ret != 0)
+    {
+        OC_LOG_V(ERROR, TAG, "cfsetospeed() - ret=%d errno=%d", ret, errno);
+        ret = -1;
+        goto exit;
+    }
+
+    terminalInfo.c_cflag = (terminalInfo.c_cflag & ~CSIZE);     //byte size
+    terminalInfo.c_cflag |= CS8;        //byte size is 8
+
+    terminalInfo.c_cflag &= ~PARENB;    //no parity
+    terminalInfo.c_cflag |= parity;     //no parity
+
+    terminalInfo.c_cflag &= ~CSTOPB;    //1 stop bit
+
+    terminalInfo.c_cflag |= CREAD;      //enable the receiver
+
+    //Input Control Settings
+    terminalInfo.c_iflag &= ~IGNBRK;    //break condition
+
+    //Local Mode Settings
+    terminalInfo.c_lflag = 0;
+
+    // whether to block on read and read time-out
+    terminalInfo.c_cc[VMIN]  = (shouldBlock >= 1) ? 1 : 0;
+    terminalInfo.c_cc[VTIME] = 5;
+
+    //Input Control Settings
+    terminalInfo.c_oflag = 0;
+
+    errno = 0;
+    ret = tcsetattr (fd, TCSANOW, &terminalInfo);
+    if (ret != 0)
+    {
+        OC_LOG_V(ERROR, TAG, "tcsetattr - ret=%d errno=%d", ret, errno);
+        ret = -1;
+    }
+
+exit:
+    OC_LOG_V(INFO, TAG, "Leave SetTerminalInfo() with ret=%d", ret);
+    return ret;
+}