Imported Upstream version 0.9.1
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / caretransmission_singlethread.c
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
26 #include "caremotehandler.h"
27 #include "caprotocolmessage.h"
28 #include "oic_malloc.h"
29 #include "logger.h"
30
31 #ifndef __ARDUINO__
32 #include <sys/time.h>
33 #endif
34
35 #define TAG "RT"
36
37 typedef struct
38 {
39     /** last sent time. microseconds **/
40     uint64_t timeStamp;
41
42     /** retransmission count **/
43     uint8_t triedCount;
44
45     /** coap PDU message id **/
46     uint16_t messageId;
47
48     /** remote endpoint **/
49     CARemoteEndpoint_t *endpoint;
50
51     /** coap PDU **/
52     void *pdu;
53
54     /** coap PDU size**/
55     uint32_t size;
56
57 } CARetransmissionData_t;
58
59 static CARetransmission_t *g_retransmissionPtr = NULL;
60
61 /**
62  * getCurrent monotonic time.
63  *
64  * @return current time in microseconds.
65  */
66 uint64_t getCurrentTimeInMicroSeconds();
67
68 /**
69  * @brief   check timeout routine
70  * @param   currentTime     [IN]microseconds
71  * @param   timeStamp       [IN]microseconds
72  * @param   triedCount      [IN]Number of retransmission tried.
73  * @return  true if the timeout period has elapsed, false otherwise.
74  */
75 static bool CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint8_t triedCount)
76 {
77     OIC_LOG(DEBUG, TAG, "IN");
78     // #1. calculate timeout
79     uint64_t timeOut = (2 << triedCount) * 1000000;
80
81     if (currentTime >= timeStamp + timeOut)
82     {
83         OIC_LOG_V(DEBUG, TAG, "timeout=%d, tried cnt=%d", (2 << triedCount), triedCount);
84         return true;
85     }
86
87     OIC_LOG(DEBUG, TAG, "OUT");
88     return false;
89 }
90
91 void CACheckRetransmissionList()
92 {
93     uint32_t len = u_arraylist_length(g_retransmissionPtr->dataList);
94
95     OIC_LOG_V(DEBUG, TAG, "len=%d", len);
96     for (uint32_t i = 0; i < len; i++)
97     {
98         CARetransmissionData_t *retData =
99                 (CARetransmissionData_t *) u_arraylist_get(g_retransmissionPtr->dataList, i);
100
101         if (NULL == retData)
102         {
103             continue;
104         }
105
106         uint64_t currentTime = getCurrentTimeInMicroSeconds();
107
108         OIC_LOG_V(DEBUG, TAG, "currtime=%lu", currentTime);
109         if (CACheckTimeout(currentTime, retData->timeStamp, retData->triedCount))
110         {
111
112             OIC_LOG(DEBUG, TAG, "RTdata-Success");
113             // #2. if time's up, send the data.
114             if (NULL != g_retransmissionPtr->dataSendMethod)
115             {
116                 OIC_LOG_V(DEBUG, TAG, "retry CON data-msgid=%d", retData->messageId);
117                 g_retransmissionPtr->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
118             }
119
120             // #3. increase the retransmission count and update timestamp.
121             retData->timeStamp = currentTime;
122             retData->triedCount++;
123         }
124
125         // #4. if tried count is max, remove the retransmission data from list.
126         if (retData->triedCount >= g_retransmissionPtr->config.tryingCount)
127         {
128             CARetransmissionData_t *removedData = (CARetransmissionData_t *) u_arraylist_remove(
129                     g_retransmissionPtr->dataList, i);
130             if (NULL == removedData)
131             {
132                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
133                 return;
134             }
135
136             OIC_LOG(DEBUG, TAG, "max trycount rchd");
137
138             OIC_LOG_V(DEBUG, TAG, "max trycount, remove retransmission CON data!!, messageid=%d",
139                       removedData->messageId);
140
141             // callback for retransmit timeout
142             if (NULL != g_retransmissionPtr->timeoutCallback)
143             {
144                 g_retransmissionPtr->timeoutCallback(removedData->endpoint, removedData->pdu,
145                                                      removedData->size);
146             }
147
148             CADestroyRemoteEndpointInternal(removedData->endpoint);
149             OICFree(removedData->pdu);
150
151             OICFree(removedData);
152
153             // modify loop value.
154             len = u_arraylist_length(g_retransmissionPtr->dataList);
155             --i;
156         }
157     }
158 }
159
160 void CARetransmissionBaseRoutine(void *threadValue)
161 {
162     CARetransmission_t *context = (CARetransmission_t *) threadValue;
163
164     if (NULL == context)
165     {
166         OIC_LOG(ERROR, TAG, "cnxt null");
167         return;
168     }
169
170     if (true == context->isStop)
171     {
172         OIC_LOG(DEBUG, TAG, "thread stopped");
173         return;
174     }
175     g_retransmissionPtr = context;
176     CACheckRetransmissionList();
177 }
178
179 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
180                                       CADataSendMethod_t retransmissionSendMethod,
181                                       CATimeoutCallback_t timeoutCallback,
182                                       CARetransmissionConfig_t *config)
183 {
184     OIC_LOG(DEBUG, TAG, "IN");
185     if (NULL == context)
186     {
187         OIC_LOG(ERROR, TAG, "cnxt null");
188         return CA_STATUS_INVALID_PARAM;
189     }
190
191     memset(context, 0, sizeof(CARetransmission_t));
192
193     CARetransmissionConfig_t cfg;
194     memset(&cfg, 0, sizeof(CARetransmissionConfig_t));
195
196     if (NULL == config)
197     {
198         // setDefault
199         cfg.supportType = (CATransportType_t) DEFAULT_RETRANSMISSION_TYPE;
200         cfg.tryingCount = DEFAULT_RETRANSMISSION_COUNT;
201     }
202     else
203     {
204         cfg = *config;
205     }
206
207     // set send thread data
208     context->dataSendMethod = retransmissionSendMethod;
209     context->timeoutCallback = timeoutCallback;
210     context->config = cfg;
211     context->isStop = false;
212     context->dataList = u_arraylist_create();
213
214     // Enable TimedAction for CACheckRetransmissionList API
215     g_retransmissionPtr = context;
216     OIC_LOG(DEBUG, TAG, "OUT");
217     return CA_STATUS_OK;
218 }
219
220 CAResult_t CARetransmissionSentData(CARetransmission_t *context, const CARemoteEndpoint_t *endpoint,
221                                     const void *pdu, uint32_t size)
222 {
223     OIC_LOG(DEBUG, TAG, "IN");
224     if (NULL == context || NULL == endpoint || NULL == pdu)
225     {
226         OIC_LOG(ERROR, TAG, "error");
227         return CA_STATUS_INVALID_PARAM;
228     }
229
230     // #0. check support connectivity type
231     if (!(context->config.supportType & endpoint->transportType))
232     {
233         OIC_LOG(ERROR, TAG, "error");
234         OIC_LOG_V(ERROR, TAG, "not supported conntype=%d", endpoint->transportType);
235         return CA_NOT_SUPPORTED;
236     }
237
238     // #1. check PDU method type and get message id.
239     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
240     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
241
242     OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d,msgid=%d", type, messageId);
243
244     if (CA_MSG_CONFIRM != type)
245     {
246         OIC_LOG(DEBUG, TAG, "not supported message type");
247         return CA_NOT_SUPPORTED;
248     }
249
250     // create retransmission data
251     CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
252             1, sizeof(CARetransmissionData_t));
253
254     if (NULL == retData)
255     {
256         OIC_LOG(ERROR, TAG, "error");
257         return CA_MEMORY_ALLOC_FAILED;
258     }
259
260     // copy PDU data
261     void *pduData = (void *) OICMalloc(size);
262     if (NULL == pduData)
263     {
264         OICFree(retData);
265         OIC_LOG(ERROR, TAG, "error");
266         return CA_MEMORY_ALLOC_FAILED;
267     }
268     memcpy(pduData, pdu, size);
269
270     // clone remote endpoint
271     CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
272     if (NULL == remoteEndpoint)
273     {
274         OICFree(retData);
275         OICFree(pduData);
276         OIC_LOG(ERROR, TAG, "error");
277         return CA_MEMORY_ALLOC_FAILED;
278     }
279
280     // #2. add additional information. (time stamp, retransmission count...)
281     retData->timeStamp = getCurrentTimeInMicroSeconds();
282     retData->triedCount = 0;
283     retData->messageId = messageId;
284     retData->endpoint = remoteEndpoint;
285     retData->pdu = pduData;
286     retData->size = size;
287
288     // #3. add data into list
289     u_arraylist_add(context->dataList, (void *) retData);
290
291     // #4. Initiate Re-transmission for added entry
292     g_retransmissionPtr = context;
293     CACheckRetransmissionList();
294     OIC_LOG(DEBUG, TAG, "OUT");
295     return CA_STATUS_OK;
296 }
297
298 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
299                                         const CARemoteEndpoint_t *endpoint, const void *pdu,
300                                         uint32_t size, void **retransmissionPdu)
301 {
302     OIC_LOG(DEBUG, TAG, "IN");
303     if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
304     {
305         OIC_LOG(ERROR, TAG, "error");
306         return CA_STATUS_INVALID_PARAM;
307     }
308
309     // #0. check support connectivity type
310     if (!(context->config.supportType & endpoint->transportType))
311     {
312         OIC_LOG_V(DEBUG, TAG, "not supp conntype=%d", endpoint->transportType);
313         return CA_STATUS_OK;
314     }
315
316     // #1. check PDU method type and get message id.
317     // ACK, RST --> remove the CON data
318     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
319     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
320
321     OIC_LOG_V(DEBUG, TAG, "recv pdu, msgtype=%d,msgid=%d", type, messageId);
322
323     if (CA_MSG_ACKNOWLEDGE != type && CA_MSG_RESET != type)
324     {
325         return CA_STATUS_OK;
326     }
327
328     uint32_t len = u_arraylist_length(context->dataList);
329
330     // find index
331     for (uint32_t i = 0; i < len; i++)
332     {
333         CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
334                 context->dataList, i);
335
336         if (NULL == retData)
337         {
338             continue;
339         }
340
341         // found index
342         if (NULL != retData->endpoint && retData->messageId == messageId
343             && (retData->endpoint->transportType == endpoint->transportType))
344         {
345             // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
346             // if retransmission was finish..token will be unavailable.
347             if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
348             {
349                 OIC_LOG(DEBUG, TAG, "CA_EMPTY");
350
351                 if (NULL == retData->pdu)
352                 {
353                     OIC_LOG(ERROR, TAG, "retData->pdu is null");
354                     OICFree(retData);
355                     return CA_STATUS_FAILED;
356                 }
357
358                 // copy PDU data
359                 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
360                 if (NULL == (*retransmissionPdu))
361                 {
362                     OICFree(retData);
363                     OIC_LOG(ERROR, TAG, "error");
364                     return CA_MEMORY_ALLOC_FAILED;
365                 }
366                 memcpy((*retransmissionPdu), retData->pdu, retData->size);
367             }
368
369             // #2. remove data from list
370             CARetransmissionData_t *removedData = (CARetransmissionData_t *) u_arraylist_remove(
371                     context->dataList, i);
372             if (NULL == removedData)
373             {
374                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
375                 return CA_STATUS_FAILED;
376             }
377
378             OIC_LOG_V(DEBUG, TAG, "remove RTCON data, msgid=%d", messageId);
379
380             CADestroyRemoteEndpointInternal(removedData->endpoint);
381             OICFree(removedData->pdu);
382
383             OICFree(removedData);
384
385             break;
386         }
387     }
388
389     OIC_LOG(DEBUG, TAG, "OUT");
390     return CA_STATUS_OK;
391 }
392
393 CAResult_t CARetransmissionStop(CARetransmission_t *context)
394 {
395     OIC_LOG(DEBUG, TAG, "IN");
396     if (NULL == context)
397     {
398         OIC_LOG(ERROR, TAG, "error");
399         return CA_STATUS_INVALID_PARAM;
400     }
401
402     // set stop flag
403     context->isStop = true;
404     OIC_LOG(DEBUG, TAG, "OUT");
405     return CA_STATUS_OK;
406 }
407
408 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
409 {
410     OIC_LOG(DEBUG, TAG, "IN");
411     if (NULL == context)
412     {
413         OIC_LOG(ERROR, TAG, "error");
414         return CA_STATUS_INVALID_PARAM;
415     }
416
417     u_arraylist_free(&context->dataList);
418     OIC_LOG(DEBUG, TAG, "OUT");
419     return CA_STATUS_OK;
420 }
421
422 uint64_t getCurrentTimeInMicroSeconds()
423 {
424     OIC_LOG(DEBUG, TAG, "IN");
425     uint64_t currentTime = 0;
426
427 #ifdef __ARDUINO__
428     currentTime = millis() * 1000;
429
430     OIC_LOG_V(DEBUG, TAG, "currtime=%lu", currentTime);
431 #else
432     struct timeval tv;
433     gettimeofday(&tv, NULL);
434     currentTime = tv.tv_sec * USECS_PER_SEC + tv.tv_usec;
435 #endif
436
437     OIC_LOG(DEBUG, TAG, "OUT");
438     return currentTime;
439 }
440