Implementation of connectivity abstraction feature Release v0.61
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / caretransmission_singlethread.cpp
1 /******************************************************************
2  *
3  * Copyright 2014 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************/
20 #include "caretransmission_singlethread.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <TimedAction.h>
26
27 #include "caremotehandler.h"
28 #include "caprotocolmessage.h"
29 #include "oic_malloc.h"
30 #include "logger.h"
31
32 #define TAG "RET"
33
34 typedef struct
35 {
36     /** last sent time. microseconds **/
37     uint64_t timeStamp;
38     /** retransmission count **/
39     uint8_t triedCount;
40     /** coap PDU message id **/
41     uint16_t messageId;
42     /** remote endpoint **/
43     CARemoteEndpoint_t* endpoint;
44     /** coap PDU **/
45     void* pdu;
46     /** coap PDU size**/
47     uint32_t size;
48 } CARetransmissionData_t;
49
50 static TimedAction gRcvAction = TimedAction(2000, CACheckRetransmissionList);
51 static CARetransmission_t *gRetransmissionPtr = NULL;
52
53 /**
54  * getCurrent monotonic time
55  *
56  * microseconds
57  */
58 uint64_t getCurrentTimeInMicroSeconds();
59
60 /**
61  * timeout routine
62  * 2sec -> 4sec -> 8sec -> 16sec
63  *
64  * microseconds
65  */
66 static CABool_t CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint8_t triedCount)
67 {
68     OIC_LOG(DEBUG, TAG, "IN");
69     // #1. calculate timeout
70     uint64_t timeOut = (2 << triedCount) * 1000000;
71
72     if (currentTime >= timeStamp + timeOut)
73     {
74         OIC_LOG_V(DEBUG, TAG, "%d sec time out!!, tried count(%d)", (2 << triedCount), triedCount);
75         return CA_TRUE;
76     }
77
78     OIC_LOG(DEBUG, TAG, "OUT");
79     return CA_FALSE;
80 }
81
82 void CACheckRetransmissionList()
83 {
84     OIC_LOG(DEBUG, TAG, "IN");
85     uint64_t currentTime = 0;
86
87     uint32_t i = 0;
88     uint32_t len = u_arraylist_length(gRetransmissionPtr->dataList);
89
90     OIC_LOG_V(DEBUG, TAG, "len=%d", len);
91     for (i = 0; i < len; i++)
92     {
93         CARetransmissionData_t* retData = 
94             (CARetransmissionData_t*)u_arraylist_get(gRetransmissionPtr->dataList, i);
95
96         if (retData == NULL)
97             continue;
98
99         currentTime = getCurrentTimeInMicroSeconds();
100
101         OIC_LOG_V(DEBUG, TAG, "currtime=%lu", currentTime);
102         if (CACheckTimeout(currentTime, retData->timeStamp, retData->triedCount))
103         {
104
105             OIC_LOG(DEBUG, TAG, "CCT-Success, retransmit data");
106             // #2. if time's up, send the data.
107             if (gRetransmissionPtr->dataSendMethod != NULL)
108             {
109                 OIC_LOG_V(DEBUG, TAG, "retry CON data, msgid=%d", retData->messageId);
110                 gRetransmissionPtr->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
111             }
112
113             // #3. increase the retransmission count and update timestamp.
114             retData->timeStamp = currentTime;
115             retData->triedCount++;
116         }
117
118         // #4. if tried count is max, remove the retransmission data from list.
119         if (retData->triedCount >= gRetransmissionPtr->config.tryingCount)
120         {
121             CARetransmissionData_t* removedData = 
122                 (CARetransmissionData_t*)u_arraylist_remove(gRetransmissionPtr->dataList, i);
123
124             OIC_LOG_V(DEBUG, TAG, "max trycount, remove retransmission CON data!!, messageid=%d",
125                                                                             removedData->messageId);
126
127             CADestroyRemoteEndpointInternal(removedData->endpoint);
128             OICFree(removedData->pdu);
129
130             OICFree(removedData);
131
132             // modify loop value.
133             len = u_arraylist_length(gRetransmissionPtr->dataList);
134             --i;
135         }
136     }
137     OIC_LOG(DEBUG, TAG, "OUT");
138 }
139
140 void CARetransmissionBaseRoutine(void *threadValue)
141 {
142     CARetransmission_t *context = (CARetransmission_t *) threadValue;
143
144     if (context == NULL)
145     {
146         OIC_LOG_V(DEBUG, TAG, "error");
147         return;
148     }
149
150     if (CA_TRUE == context->isStop)
151     {
152         OIC_LOG(DEBUG, TAG, "Not reqd");
153         return;
154     }
155     gRetransmissionPtr = context;
156     gRcvAction.check();
157 }
158
159 CAResult_t CARetransmissionInitialize(CARetransmission_t* context,
160                                             CADataSendMethod_t retransmissionSendMethod,
161                                             CARetransmissionConfig_t* config)
162 {
163     if (context == NULL)
164     {
165         OIC_LOG_V(DEBUG, TAG, "thread instance is empty..");
166         return CA_STATUS_FAILED;
167     }
168
169     OIC_LOG_V(DEBUG, TAG, "thread initialize..");
170
171     memset(context, 0, sizeof(CARetransmission_t));
172
173     CARetransmissionConfig_t cfg;
174     memset(&cfg, 0, sizeof(CARetransmissionConfig_t));
175
176     if (config == NULL)
177     {
178         // setDefault
179         cfg.supportType = (CAConnectivityType_t)DEFAULT_RETRANSMISSION_TYPE;
180         cfg.tryingCount = DEFAULT_RETRANSMISSION_COUNT;
181     }
182     else
183     {
184         cfg = *config;
185     }
186
187     // set send thread data
188     context->dataSendMethod = retransmissionSendMethod;
189     context->config = cfg;
190     context->isStop = CA_FALSE;
191     context->dataList = u_arraylist_create();
192
193     // Enable TimedAction for CACheckRetransmissionList API
194     gRetransmissionPtr = context;
195     gRcvAction.enable();
196
197     return CA_STATUS_OK;
198 }
199
200 CAResult_t CARetransmissionSentData(CARetransmission_t* context,
201                                                 const CARemoteEndpoint_t* endpoint,
202                                                 const void* pdu, uint32_t size)
203 {
204     if (context == NULL || endpoint == NULL || pdu == NULL)
205     {
206         OIC_LOG_V(DEBUG, TAG, "error");
207         return CA_STATUS_INVALID_PARAM;
208     }
209
210     // #0. check support connectivity type
211     if (!(context->config.supportType & endpoint->connectivityType))
212     {
213         OIC_LOG_V(DEBUG, TAG, "not supported conntype=%d", endpoint->connectivityType);
214         return CA_STATUS_OK;
215     }
216
217     // #1. check PDU method type and get message id.
218     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
219     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
220
221     OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d,msgid=%d", type, messageId);
222
223     if (type != CA_MSG_CONFIRM)
224     {
225         return CA_STATUS_OK;
226     }
227
228     // create retransmission data
229     CARetransmissionData_t* retData = (CARetransmissionData_t*) OICMalloc(
230             sizeof(CARetransmissionData_t));
231
232     if (retData == NULL)
233     {
234         OIC_LOG_V(DEBUG, TAG, "memory error!!");
235         return CA_MEMORY_ALLOC_FAILED;
236     }
237     memset(retData, 0, sizeof(CARetransmissionData_t));
238
239     // copy PDU data
240     void* pduData = (void*) OICMalloc(sizeof(int8_t) * size);
241     if (pduData == NULL)
242     {
243         OICFree(retData);
244         OIC_LOG_V(DEBUG, TAG, "memory error!!");
245         return CA_MEMORY_ALLOC_FAILED;
246     }
247     memset(pduData, 0, sizeof(int8_t) * size);
248     memcpy(pduData, pdu, sizeof(int8_t) * size);
249
250     // clone remote endpoint
251     CARemoteEndpoint_t* remoteEndpoint = CACloneRemoteEndpoint(endpoint);
252     if (remoteEndpoint == NULL)
253     {
254         OICFree(retData);
255         OICFree(pduData);
256         OIC_LOG_V(DEBUG, TAG, "memory error!!");
257         return CA_MEMORY_ALLOC_FAILED;
258     }
259
260     // #2. add additional information. (time stamp, retransmission count...)
261     retData->timeStamp = getCurrentTimeInMicroSeconds();
262     retData->triedCount = 0;
263     retData->messageId = messageId;
264     retData->endpoint = remoteEndpoint;
265     retData->pdu = pduData;
266     retData->size = size;
267
268     // #3. add data into list
269     u_arraylist_add(context->dataList, (void*) retData);
270
271     // #4. Initiate Re-transmission for added entry
272     gRetransmissionPtr = context;
273     CACheckRetransmissionList();
274
275     return CA_STATUS_OK;
276 }
277
278 CAResult_t CARetransmissionReceivedData(CARetransmission_t* context,
279         const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
280 {
281     if (context == NULL || endpoint == NULL || pdu == NULL)
282     {
283         OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
284         return CA_STATUS_INVALID_PARAM;
285     }
286
287     // #0. check support connectivity type
288     if (!(context->config.supportType & endpoint->connectivityType))
289     {
290         OIC_LOG_V(DEBUG, TAG, "not supported conntype=%d", endpoint->connectivityType);
291         return CA_STATUS_OK;
292     }
293
294     // #1. check PDU method type and get message id.
295     // ACK, RST --> remove the CON data
296     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
297     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
298
299     OIC_LOG_V(DEBUG, TAG, "recv pdu, msgtype=%d,msgid=%d", type, messageId);
300
301     if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
302     {
303         return CA_STATUS_OK;
304     }
305
306     uint32_t i = 0;
307     uint32_t len = u_arraylist_length(context->dataList);
308
309     // find index
310     for (i = 0; i < len; i++)
311     {
312         CARetransmissionData_t* retData = 
313             (CARetransmissionData_t*)u_arraylist_get(context->dataList, i);
314
315         if (retData == NULL)
316             continue;
317
318         // found index
319         if ((retData->endpoint->connectivityType == endpoint->connectivityType)
320                 && retData->messageId == messageId)
321             break;
322     }
323
324     // #2. remove data from list
325     if (i < len)
326     {
327         CARetransmissionData_t* removedData = 
328             (CARetransmissionData_t*)u_arraylist_remove(context->dataList, i);
329
330         OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)", messageId);
331
332         CADestroyRemoteEndpointInternal(removedData->endpoint);
333         OICFree(removedData->pdu);
334
335         OICFree(removedData);
336     }
337
338     return CA_STATUS_OK;
339 }
340
341 CAResult_t CARetransmissionStop(CARetransmission_t* context)
342 {
343     if (context == NULL)
344     {
345         OIC_LOG_V(DEBUG, TAG, "context is empty..");
346         return CA_STATUS_FAILED;
347     }
348
349     OIC_LOG_V(DEBUG, TAG, "retransmission stop request!!");
350
351     // Disable TimedAction for CACheckRetransmissionList API
352     gRcvAction.disable();
353
354     // set stop flag
355     context->isStop = CA_TRUE;
356
357     return CA_STATUS_OK;
358 }
359
360 CAResult_t CARetransmissionDestroy(CARetransmission_t* context)
361 {
362     if (context == NULL)
363     {
364         OIC_LOG_V(DEBUG, TAG, "context is empty..");
365         return CA_STATUS_FAILED;
366     }
367
368     OIC_LOG_V(DEBUG, TAG, "retransmission context destroy..");
369
370     u_arraylist_free(context->dataList);
371
372     return CA_STATUS_OK;
373 }
374
375 uint64_t getCurrentTimeInMicroSeconds()
376 {
377     OIC_LOG(DEBUG, TAG, "IN");
378     uint64_t currentTime = 0;
379
380 /*
381 #ifdef __ANDROID__
382     struct timespec getTs;
383
384     memset(&getTs, 0, sizeof(getTs));
385     clock_gettime(CLOCK_MONOTONIC, &getTs);
386
387     currentTime = (getTs.tv_sec * 1000000000 + getTs.tv_nsec)/1000;
388     OIC_LOG_V(DEBUG, TAG, "current time = %d",  currentTime);
389 #else if __ARDUINO__
390     currentTime = micros();
391 #else
392     currentTime = g_get_monotonic_time();
393 */
394 #ifdef __ARDUINO__
395     currentTime = micros();
396
397     OIC_LOG_V(DEBUG, TAG, "currtime=%lu", currentTime);
398 #else
399     currentTime = g_get_monotonic_time();
400 #endif
401
402     OIC_LOG(DEBUG, TAG, "OUT");
403     return currentTime;
404 }