1 /*
2  * Copyright (C) 2012-2014 NXP Semiconductors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <phNxpNciHal_NfcDepSWPrio.h>
18 #include <phNxpLog.h>
19 #include <phNxpNciHal.h>
20 
21 #define CUSTOM_POLL_TIMEOUT 160    /* Timeout value to wait for NFC-DEP detection.*/
22 #define CLEAN_UP_TIMEOUT 250
23 #define MAX_WRITE_RETRY 5
24 
25 /******************* Global variables *****************************************/
26 extern phNxpNciHal_Control_t nxpncihal_ctrl;
27 extern NFCSTATUS phNxpNciHal_send_ext_cmd(uint16_t cmd_len, uint8_t *p_cmd);
28 static uint8_t cmd_stop_rf_discovery[] = { 0x21, 0x06, 0x01, 0x00 }; /* IDLE */
29 static uint8_t cmd_resume_rf_discovery[] = { 0x21, 0x06, 0x01, 0x03 }; /* RF_DISCOVER */
30 
31 /*RF_DISCOVER_SELECT_CMD*/
32 static uint8_t cmd_select_rf_discovery[] = {0x21,0x04,0x03,0x01,0x04,0x02 };
33 
34 static uint8_t cmd_poll[64];
35 static uint8_t cmd_poll_len = 0;
36 int discover_type = 0xFF;
37 uint32_t cleanup_timer;
38 
39 /*PRIO LOGIC related dead functions undefined*/
40 #ifdef P2P_PRIO_LOGIC_HAL_IMP
41 
42 static int iso_dep_detected = 0x00;
43 static int poll_timer_fired = 0x00;
44 static uint8_t bIgnorep2plogic = 0;
45 static uint8_t *p_iso_ntf_buff = NULL; /* buffer to store second notification */
46 static uint8_t bIgnoreIsoDep = 0;
47 static uint32_t custom_poll_timer;
48 
49 /************** NFC-DEP SW PRIO functions ***************************************/
50 
51 static NFCSTATUS phNxpNciHal_start_polling_loop(void);
52 static NFCSTATUS phNxpNciHal_stop_polling_loop(void);
53 static NFCSTATUS phNxpNciHal_resume_polling_loop(void);
54 static void phNxpNciHal_NfcDep_store_ntf(uint8_t *p_cmd_data, uint16_t cmd_len);
55 
56 
57 /*******************************************************************************
58 **
59 ** Function         cleanup_timer_handler
60 **
61 ** Description      Callback function for cleanup timer.
62 **
63 ** Returns          None
64 **
65 *******************************************************************************/
cleanup_timer_handler(uint32_t timerId,void * pContext)66 static void cleanup_timer_handler(uint32_t timerId, void *pContext)
67 {
68     NXPLOG_NCIHAL_D(">> cleanup_timer_handler.");
69 
70     NXPLOG_NCIHAL_D(">> cleanup_timer_handler. ISO_DEP not detected second time.");
71 
72     phOsalNfc_Timer_Delete(cleanup_timer);
73     cleanup_timer=0;
74     iso_dep_detected = 0x00;
75     EnableP2P_PrioLogic = FALSE;
76     return;
77 }
78 
79 /*******************************************************************************
80 **
81 ** Function         custom_poll_timer_handler
82 **
83 ** Description      Callback function for custom poll timer.
84 **
85 ** Returns          None
86 **
87 *******************************************************************************/
custom_poll_timer_handler(uint32_t timerId,void * pContext)88 static void custom_poll_timer_handler(uint32_t timerId, void *pContext)
89 {
90     NXPLOG_NCIHAL_D(">> custom_poll_timer_handler.");
91 
92     NXPLOG_NCIHAL_D(">> custom_poll_timer_handler. NFC_DEP not detected. so giving early chance to ISO_DEP.");
93 
94     phOsalNfc_Timer_Delete(custom_poll_timer);
95 
96     if (iso_dep_detected == 0x01)
97     {
98         poll_timer_fired = 0x01;
99 
100         /*
101          * Restart polling loop.
102          * When the polling loop is stopped, polling will be restarted.
103          */
104         NXPLOG_NCIHAL_D(">> custom_poll_timer_handler - restart polling loop.");
105 
106         phNxpNciHal_stop_polling_loop();
107     }
108     else
109     {
110         NXPLOG_NCIHAL_E(">> custom_poll_timer_handler - invalid flag state (iso_dep_detected)");
111     }
112 
113     return;
114 }
115 /*******************************************************************************
116 **
117 ** Function         phNxpNciHal_stop_polling_loop
118 **
119 ** Description      Sends stop polling cmd to NFCC
120 **
121 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
122 **
123 *******************************************************************************/
phNxpNciHal_stop_polling_loop()124 static NFCSTATUS phNxpNciHal_stop_polling_loop()
125 {
126     NFCSTATUS status = NFCSTATUS_SUCCESS;
127     phNxpNciHal_Sem_t cb_data;
128     pthread_t pthread;
129     discover_type = STOP_POLLING;
130 
131     pthread_attr_t attr;
132     pthread_attr_init(&attr);
133     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
134     if(pthread_create(&pthread, &attr, tmp_thread, (void*) &discover_type) != 0)
135     {
136         NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop");
137     }
138     return status;
139 }
140 
141 /*******************************************************************************
142 **
143 ** Function         phNxpNciHal_resume_polling_loop
144 **
145 ** Description      Sends resume polling cmd to NFCC
146 **
147 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
148 **
149 *******************************************************************************/
phNxpNciHal_resume_polling_loop()150 static NFCSTATUS phNxpNciHal_resume_polling_loop()
151 {
152     NFCSTATUS status = NFCSTATUS_SUCCESS;
153     phNxpNciHal_Sem_t cb_data;
154     pthread_t pthread;
155     discover_type = RESUME_POLLING;
156 
157     pthread_attr_t attr;
158     pthread_attr_init(&attr);
159     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
160     if(pthread_create(&pthread, &attr, tmp_thread, (void*) &discover_type) != 0)
161     {
162         NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop");
163     }
164     return status;
165 }
166 
167 /*******************************************************************************
168 **
169 ** Function         phNxpNciHal_start_polling_loop
170 **
171 ** Description      Sends start polling cmd to NFCC
172 **
173 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
174 **
175 *******************************************************************************/
phNxpNciHal_start_polling_loop()176 NFCSTATUS phNxpNciHal_start_polling_loop()
177 {
178     NFCSTATUS status = NFCSTATUS_FAILED;
179     phNxpNciHal_Sem_t cb_data;
180     pthread_t pthread;
181     discover_type = START_POLLING;
182 
183     pthread_attr_t attr;
184     pthread_attr_init(&attr);
185     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
186     if(pthread_create(&pthread, &attr, tmp_thread, (void*) &discover_type) != 0)
187     {
188         NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop");
189     }
190     return status;
191 }
192 
193 /*******************************************************************************
194 **
195 ** Function         phNxpNciHal_NfcDep_rsp_ext
196 **
197 ** Description      Implements algorithm for NFC-DEP protocol priority over
198 **                  ISO-DEP protocol.
199 **                  Following the algorithm:
200 **                  IF ISO-DEP detected first time,set the ISO-DEP detected flag
201 **                  and resume polling loop with 60ms timeout value.
202 **                      a) if than NFC-DEP detected than send the response to
203 **                       libnfc-nci stack and stop the timer.
204 **                      b) if NFC-DEP not detected with in 60ms, than restart the
205 **                          polling loop to give early chance to ISO-DEP with a
206 **                          cleanup timer.
207 **                      c) if ISO-DEP detected second time send the response to
208 **                          libnfc-nci stack and stop the cleanup timer.
209 **                      d) if ISO-DEP not detected with in cleanup timeout, than
210 **                          clear the ISO-DEP detection flag.
211 **
212 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
213 **
214 *******************************************************************************/
phNxpNciHal_NfcDep_rsp_ext(uint8_t * p_ntf,uint16_t * p_len)215 NFCSTATUS phNxpNciHal_NfcDep_rsp_ext(uint8_t *p_ntf, uint16_t *p_len)
216 {
217     NFCSTATUS status = NFCSTATUS_INVALID_PARAMETER;
218 
219     NXPLOG_NCIHAL_D(">> p_ntf[0]=%02x , p_ntf[1]=%02x",p_ntf[0],p_ntf[1]);
220 
221     if(p_ntf[0] == 0x41 && p_ntf[1] == 0x04)
222     {
223         //Tag selected, Disable P2P Prio logic.
224         bIgnoreIsoDep = 1;
225         NXPLOG_NCIHAL_D(">> Tag selected, Disable P2P Prio logic.");
226 
227     }
228     else if( ((p_ntf[0] == 0x61 && p_ntf[1] == 0x06) ||
229             (p_ntf[0] == 0x41 && p_ntf[1] == 0x06) ) && bIgnoreIsoDep == 1
230             )
231     {
232         //Tag deselected, enable P2P Prio logic.
233         bIgnoreIsoDep = 0x00;
234         NXPLOG_NCIHAL_D(">> Tag deselected, enable P2P Prio logic.");
235 
236     }
237     if (bIgnoreIsoDep == 0x00 &&
238             p_ntf[0] == 0x61 &&
239             p_ntf[1] == 0x05 && *p_len > 5)
240     {
241         if (p_ntf[5] == 0x04 && p_ntf[6] < 0x80)
242         {
243             NXPLOG_NCIHAL_D(">> ISO DEP detected.");
244 
245             if (iso_dep_detected == 0x00)
246             {
247                 NXPLOG_NCIHAL_D(
248                         ">> ISO DEP detected first time. Resume polling loop");
249 
250                 iso_dep_detected = 0x01;
251                 status = phNxpNciHal_resume_polling_loop();
252 
253                 custom_poll_timer = phOsalNfc_Timer_Create();
254                 NXPLOG_NCIHAL_D("custom poll timer started - %d", custom_poll_timer);
255 
256                 status = phOsalNfc_Timer_Start(custom_poll_timer,
257                         CUSTOM_POLL_TIMEOUT,
258                         &custom_poll_timer_handler,
259                         NULL);
260 
261                 if (NFCSTATUS_SUCCESS == status)
262                 {
263                     NXPLOG_NCIHAL_D("custom poll timer started");
264                 }
265                 else
266                 {
267                     NXPLOG_NCIHAL_E("custom poll timer not started!!!");
268                     status  = NFCSTATUS_FAILED;
269                 }
270 
271                 status = NFCSTATUS_FAILED;
272             }
273             else
274             {
275                 NXPLOG_NCIHAL_D(">> ISO DEP detected second time.");
276                 /* Store notification */
277                 phNxpNciHal_NfcDep_store_ntf(p_ntf, *p_len);
278 
279                 /* Stop Cleanup_timer */
280                 phOsalNfc_Timer_Stop(cleanup_timer);
281                 phOsalNfc_Timer_Delete(cleanup_timer);
282                 cleanup_timer=0;
283                 EnableP2P_PrioLogic = FALSE;
284                 iso_dep_detected = 0;
285                 status = NFCSTATUS_SUCCESS;
286             }
287         }
288         else if (p_ntf[5] == 0x05)
289         {
290             NXPLOG_NCIHAL_D(">> NFC-DEP Detected - stopping the custom poll timer");
291 
292             phOsalNfc_Timer_Stop(custom_poll_timer);
293             phOsalNfc_Timer_Delete(custom_poll_timer);
294             EnableP2P_PrioLogic = FALSE;
295             iso_dep_detected = 0;
296             status = NFCSTATUS_SUCCESS;
297         }
298         else
299         {
300             NXPLOG_NCIHAL_D(">>  detected other technology- stopping the custom poll timer");
301             phOsalNfc_Timer_Stop(custom_poll_timer);
302             phOsalNfc_Timer_Delete(custom_poll_timer);
303             EnableP2P_PrioLogic = FALSE;
304             iso_dep_detected = 0;
305             status = NFCSTATUS_INVALID_PARAMETER;
306         }
307     }
308     else if( bIgnoreIsoDep == 0x00 &&
309             ((p_ntf[0] == 0x41 && p_ntf[1] == 0x06) || (p_ntf[0] == 0x61
310             && p_ntf[1] == 0x06))
311             )
312     {
313         NXPLOG_NCIHAL_D(">> RF disabled");
314         if (poll_timer_fired == 0x01)
315         {
316             poll_timer_fired = 0x00;
317 
318             NXPLOG_NCIHAL_D(">>restarting polling loop.");
319 
320             /* start polling loop */
321             phNxpNciHal_start_polling_loop();
322             EnableP2P_PrioLogic = FALSE;
323             NXPLOG_NCIHAL_D (">> NFC DEP NOT  detected - custom poll timer expired - RF disabled");
324 
325             cleanup_timer = phOsalNfc_Timer_Create();
326 
327             /* Start cleanup_timer */
328             NFCSTATUS status = phOsalNfc_Timer_Start(cleanup_timer,
329                     CLEAN_UP_TIMEOUT,
330                     &cleanup_timer_handler,
331                     NULL);
332 
333             if (NFCSTATUS_SUCCESS == status)
334             {
335                 NXPLOG_NCIHAL_D("cleanup timer started");
336             }
337             else
338             {
339                 NXPLOG_NCIHAL_E("cleanup timer not started!!!");
340                 status  = NFCSTATUS_FAILED;
341             }
342 
343             status = NFCSTATUS_FAILED;
344         }
345         else
346         {
347             status = NFCSTATUS_SUCCESS;
348         }
349     }
350     if (bIgnoreIsoDep == 0x00 &&
351             iso_dep_detected == 1)
352     {
353         if ((p_ntf[0] == 0x41 && p_ntf[1] == 0x06) || (p_ntf[0] == 0x61
354                 && p_ntf[1] == 0x06))
355         {
356             NXPLOG_NCIHAL_D(">>iso_dep_detected Disconnect related notification");
357             status = NFCSTATUS_FAILED;
358         }
359         else
360         {
361             NXPLOG_NCIHAL_W("Never come here");
362         }
363     }
364 
365     return status;
366 }
367 /*******************************************************************************
368 **
369 ** Function         phNxpNciHal_NfcDep_store_ntf
370 **
371 ** Description      Stores the iso dep notification locally.
372 **
373 ** Returns          None
374 **
375 *******************************************************************************/
phNxpNciHal_NfcDep_store_ntf(uint8_t * p_cmd_data,uint16_t cmd_len)376 static void phNxpNciHal_NfcDep_store_ntf(uint8_t *p_cmd_data, uint16_t cmd_len)
377 {
378     p_iso_ntf_buff = NULL;
379 
380     p_iso_ntf_buff = malloc(sizeof (uint8_t) * cmd_len);
381     if (p_iso_ntf_buff == NULL)
382     {
383         NXPLOG_NCIHAL_E("Error allocating memory (p_iso_ntf_buff)");
384         return;
385     }
386     memcpy(p_iso_ntf_buff, p_cmd_data, cmd_len);
387     bIgnorep2plogic = 1;
388 }
389 
390 /*******************************************************************************
391 **
392 ** Function         phNxpNciHal_NfcDep_comapre_ntf
393 **
394 ** Description      Compare the notification with previous iso dep notification.
395 **
396 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
397 **
398 *******************************************************************************/
phNxpNciHal_NfcDep_comapre_ntf(uint8_t * p_cmd_data,uint16_t cmd_len)399 NFCSTATUS phNxpNciHal_NfcDep_comapre_ntf(uint8_t *p_cmd_data, uint16_t cmd_len)
400 {
401    NFCSTATUS status = NFCSTATUS_FAILED;
402    int32_t ret_val = -1;
403 
404    if (bIgnorep2plogic == 1)
405    {
406         ret_val = memcmp(p_cmd_data,p_iso_ntf_buff, cmd_len);
407         if(ret_val != 0)
408         {
409             NXPLOG_NCIHAL_E("Third notification is not equal to last");
410         }
411         else
412         {
413             NXPLOG_NCIHAL_E("Third notification is equal to last (disable p2p logic)");
414             status = NFCSTATUS_SUCCESS;
415         }
416         bIgnorep2plogic = 0;
417     }
418     if (p_iso_ntf_buff != NULL)
419     {
420         free(p_iso_ntf_buff);
421         p_iso_ntf_buff = NULL;
422     }
423 
424     return status;
425 }
426 
427 
phNxpNciHal_clean_P2P_Prio()428 extern NFCSTATUS phNxpNciHal_clean_P2P_Prio()
429 {
430     NFCSTATUS status = NFCSTATUS_SUCCESS;
431 
432     iso_dep_detected = 0x00;
433     EnableP2P_PrioLogic = FALSE;
434     poll_timer_fired = 0x00;
435     bIgnorep2plogic = 0x00;
436     bIgnoreIsoDep = 0x00;
437 
438     status = phOsalNfc_Timer_Stop(cleanup_timer);
439     status |= phOsalNfc_Timer_Delete(cleanup_timer);
440 
441     status |= phOsalNfc_Timer_Stop(custom_poll_timer);
442     status |= phOsalNfc_Timer_Delete(custom_poll_timer);
443     cleanup_timer=0;
444     return status;
445 }
446 
447 #endif
448 /*******************************************************************************
449 **
450 ** Function         hal_write_cb
451 **
452 ** Description      Callback function for hal write.
453 **
454 ** Returns          None
455 **
456 *******************************************************************************/
hal_write_cb(void * pContext,phTmlNfc_TransactInfo_t * pInfo)457 static void hal_write_cb(void *pContext, phTmlNfc_TransactInfo_t *pInfo)
458 {
459     phNxpNciHal_Sem_t *p_cb_data = (phNxpNciHal_Sem_t*) pContext;
460 
461     if (pInfo->wStatus == NFCSTATUS_SUCCESS)
462     {
463         NXPLOG_NCIHAL_D("hal_write_cb: write successful status = 0x%x", pInfo->wStatus);
464     }
465     else
466     {
467         NXPLOG_NCIHAL_E("hal_write_cb: write error status = 0x%x", pInfo->wStatus);
468     }
469 
470     p_cb_data->status = pInfo->wStatus;
471 
472     SEM_POST(p_cb_data);
473     return;
474 }
475 
476 /*******************************************************************************
477  **
478  ** Function         tmp_thread
479  **
480  ** Description      Thread to execute custom poll commands .
481  **
482  ** Returns          None
483  **
484  *******************************************************************************/
tmp_thread(void * tmp)485 void *tmp_thread(void *tmp)
486 {
487     NFCSTATUS status = NFCSTATUS_SUCCESS;
488     uint16_t data_len;
489     NXPLOG_NCIHAL_E("tmp_thread: enter type=0x0%x",  *((int*)tmp));
490     usleep(10*1000);
491 
492     switch( *((int*)tmp) )
493     {
494     case START_POLLING:
495     {
496         CONCURRENCY_LOCK();
497         data_len = phNxpNciHal_write_unlocked(cmd_poll_len, cmd_poll);
498         CONCURRENCY_UNLOCK();
499 
500         if(data_len != cmd_poll_len)
501         {
502             NXPLOG_NCIHAL_E("phNxpNciHal_start_polling_loop: data len mismatch");
503             status = NFCSTATUS_FAILED;
504         }
505     }
506     break;
507 
508     case RESUME_POLLING:
509     {
510         CONCURRENCY_LOCK();
511         data_len = phNxpNciHal_write_unlocked(sizeof(cmd_resume_rf_discovery),
512                 cmd_resume_rf_discovery);
513         CONCURRENCY_UNLOCK();
514 
515         if(data_len != sizeof(cmd_resume_rf_discovery))
516         {
517             NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop: data len mismatch");
518             status = NFCSTATUS_FAILED;
519         }
520     }
521     break;
522 
523     case STOP_POLLING:
524     {
525         CONCURRENCY_LOCK();
526         data_len = phNxpNciHal_write_unlocked(sizeof(cmd_stop_rf_discovery),
527                 cmd_stop_rf_discovery);
528         CONCURRENCY_UNLOCK();
529 
530         if(data_len != sizeof(cmd_stop_rf_discovery))
531         {
532             NXPLOG_NCIHAL_E("phNxpNciHal_stop_polling_loop: data len mismatch");
533             status = NFCSTATUS_FAILED;
534         }
535     }
536     break;
537 
538     case DISCOVER_SELECT:
539     {
540         CONCURRENCY_LOCK();
541         data_len = phNxpNciHal_write_unlocked(sizeof(cmd_select_rf_discovery),
542                 cmd_select_rf_discovery);
543         CONCURRENCY_UNLOCK();
544 
545         if(data_len != sizeof(cmd_resume_rf_discovery))
546         {
547             NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop: data len mismatch");
548             status = NFCSTATUS_FAILED;
549         }
550     }
551     break;
552 
553     default:
554         NXPLOG_NCIHAL_E("No Matching case");
555         status = NFCSTATUS_FAILED;
556         break;
557     }
558 
559     NXPLOG_NCIHAL_E("tmp_thread: exit");
560     return NULL;
561 }
562 /*******************************************************************************
563  **
564  ** Function         phNxpNciHal_select_RF_Discovery
565  **
566  ** Description     Sends RF_DISCOVER_SELECT_CMD
567  ** Parameters    RfID ,  RfProtocolType
568  ** Returns          NFCSTATUS_PENDING if success
569  **
570  *******************************************************************************/
phNxpNciHal_select_RF_Discovery(unsigned int RfID,unsigned int RfProtocolType)571 NFCSTATUS phNxpNciHal_select_RF_Discovery(unsigned int RfID,unsigned int RfProtocolType)
572 {
573     NFCSTATUS status = NFCSTATUS_SUCCESS;
574     phNxpNciHal_Sem_t cb_data;
575     pthread_t pthread;
576     discover_type = DISCOVER_SELECT;
577     cmd_select_rf_discovery[3]=RfID;
578     cmd_select_rf_discovery[4]=RfProtocolType;
579 
580     pthread_attr_t attr;
581     pthread_attr_init(&attr);
582     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
583     if(pthread_create(&pthread, &attr, tmp_thread, (void*) &discover_type) != 0)
584     {
585         NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop");
586     }
587 
588     return status;
589 }
590 /*******************************************************************************
591 **
592 ** Function         phNxpNciHal_NfcDep_cmd_ext
593 **
594 ** Description      Stores the polling loop configuration locally.
595 **
596 ** Returns          None
597 **
598 *******************************************************************************/
phNxpNciHal_NfcDep_cmd_ext(uint8_t * p_cmd_data,uint16_t * cmd_len)599 void phNxpNciHal_NfcDep_cmd_ext(uint8_t *p_cmd_data, uint16_t *cmd_len)
600 {
601     if (p_cmd_data[0] == 0x21 && p_cmd_data[1] == 0x03)
602     {
603         if (*cmd_len == 6 && p_cmd_data[3] == 0x01 && p_cmd_data[4] == 0x02
604                 && p_cmd_data[5] == 0x01)
605         {
606             /* DO NOTHING */
607         }
608         else
609         {
610             /* Store the polling loop configuration */
611             cmd_poll_len = *cmd_len;
612             memset(&cmd_poll, 0, cmd_poll_len);
613             memcpy(&cmd_poll, p_cmd_data, cmd_poll_len);
614         }
615     }
616 
617     return;
618 }
619