From ab9c837657b615817e38617fca3ee948ef9aad40 Mon Sep 17 00:00:00 2001 From: Joseph Morrow Date: Wed, 30 Sep 2015 14:38:41 -0700 Subject: [PATCH] Initial prototype for optimized Telegesis read/write layer. Change-Id: Ie6de7fb686f25db5870e5f6d359d3e8351e915b8 Signed-off-by: Joseph Morrow Reviewed-on: https://gerrit.iotivity.org/gerrit/2945 Tested-by: jenkins-iotivity Reviewed-by: Jon A. Cruz --- plugins/samples/linux/SConscript | 6 + plugins/src/pluginlist.c | 11 + .../telegesis_wrapper/include/telegesis_socket.h | 165 ++++ .../telegesis_wrapper/include/telegesis_wrapper.h | 13 +- .../telegesis_wrapper/include/twsocketlist.h | 76 ++ .../telegesis_wrapper/include/twtypes.h | 170 ++++ .../telegesis_wrapper/src/SConscript | 4 +- .../telegesis_wrapper/src/telegesis_socket.c | 941 +++++++++++++++++++++ .../telegesis_wrapper/src/telegesis_wrapper.c | 125 +-- .../telegesis_wrapper/src/twsocketlist.c | 263 ++++++ 10 files changed, 1642 insertions(+), 132 deletions(-) create mode 100644 plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_socket.h create mode 100644 plugins/zigbee_wrapper/telegesis_wrapper/include/twsocketlist.h create mode 100644 plugins/zigbee_wrapper/telegesis_wrapper/include/twtypes.h create mode 100644 plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_socket.c create mode 100644 plugins/zigbee_wrapper/telegesis_wrapper/src/twsocketlist.c diff --git a/plugins/samples/linux/SConscript b/plugins/samples/linux/SConscript index dea9b7f..bad07a3 100644 --- a/plugins/samples/linux/SConscript +++ b/plugins/samples/linux/SConscript @@ -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']) diff --git a/plugins/src/pluginlist.c b/plugins/src/pluginlist.c index 3acd75e..ef4c8e1 100644 --- a/plugins/src/pluginlist.c +++ b/plugins/src/pluginlist.c @@ -26,6 +26,10 @@ #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 index 0000000..23cde1d --- /dev/null +++ b/plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_socket.h @@ -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_ */ diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_wrapper.h b/plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_wrapper.h index 80a1d39..28d6825 100644 --- a/plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_wrapper.h +++ b/plugins/zigbee_wrapper/telegesis_wrapper/include/telegesis_wrapper.h @@ -35,21 +35,12 @@ extern "C" { #endif // __cplusplus #include "octypes.h" +#include "twtypes.h" + #include #include #include -#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 index 0000000..e06d19e --- /dev/null +++ b/plugins/zigbee_wrapper/telegesis_wrapper/include/twsocketlist.h @@ -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 + +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 index 0000000..6b98a64 --- /dev/null +++ b/plugins/zigbee_wrapper/telegesis_wrapper/include/twtypes.h @@ -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 +#include + +#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_ */ diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/src/SConscript b/plugins/zigbee_wrapper/telegesis_wrapper/src/SConscript index 14c6ea8..be27059 100644 --- a/plugins/zigbee_wrapper/telegesis_wrapper/src/SConscript +++ b/plugins/zigbee_wrapper/telegesis_wrapper/src/SConscript @@ -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 index 0000000..e7cc356 --- /dev/null +++ b/plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_socket.c @@ -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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_wrapper.c b/plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_wrapper.c index 3cbcf43..2a45992 100644 --- a/plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_wrapper.c +++ b/plugins/zigbee_wrapper/telegesis_wrapper/src/telegesis_wrapper.c @@ -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 @@ -35,126 +35,11 @@ #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 index 0000000..aeee38d --- /dev/null +++ b/plugins/zigbee_wrapper/telegesis_wrapper/src/twsocketlist.c @@ -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 +#include +#include +#include +#include +#include + +#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; +} -- 2.7.4