1 /***********************************************************************************************************************
2 * DISCLAIMER
3 * This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No
4 * other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all
5 * applicable laws, including copyright laws.
6 * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
7 * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
8 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM
9 * EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES
10 * SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS
11 * SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
12 * Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of
13 * this software. By using this software, you agree to the additional terms and conditions found by accessing the
14 * following link:
15 * http://www.renesas.com/disclaimer
16 *
17 * Copyright (C) 2020 Renesas Electronics Corporation. All rights reserved.
18 ***********************************************************************************************************************/
19
20 /***********************************************************************************************************************
21 * File Name : NetworkInterface.c
22 * Device(s) : RX
23 * Description : Interfaces FreeRTOS TCP/IP stack to RX Ethernet driver.
24 ***********************************************************************************************************************/
25
26 /***********************************************************************************************************************
27 * History : DD.MM.YYYY Version Description
28 * : 07.03.2018 0.1 Development
29 ***********************************************************************************************************************/
30
31 /***********************************************************************************************************************
32 * Includes <System Includes> , "Project Includes"
33 ***********************************************************************************************************************/
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 /* FreeRTOS includes. */
40 #include "FreeRTOS.h"
41 #include "task.h"
42 #include "FreeRTOS_IP.h"
43 #include "FreeRTOS_IP_Private.h"
44 /*#include "FreeRTOS_DNS.h" */
45 #include "NetworkBufferManagement.h"
46 #include "NetworkInterface.h"
47
48 #include "r_ether_rx_if.h"
49 #include "r_pinset.h"
50
51 /***********************************************************************************************************************
52 * Macro definitions
53 **********************************************************************************************************************/
54 #define ETHER_BUFSIZE_MIN 60
55
56 #if defined( BSP_MCU_RX65N ) || defined( BSP_MCU_RX64M ) || defined( BSP_MCU_RX71M ) || defined( BSP_MCU_RX72M )
57 #if ETHER_CFG_MODE_SEL == 0
58 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC0_MII()
59 #elif ETHER_CFG_MODE_SEL == 1
60 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC0_RMII()
61 #endif
62 #elif defined( BSP_MCU_RX63N )
63 #if ETHER_CFG_MODE_SEL == 0
64 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC_MII()
65 #elif ETHER_CFG_MODE_SEL == 1
66 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC_RMII()
67 #endif
68 #endif /* if defined( BSP_MCU_RX65N ) || defined( BSP_MCU_RX64M ) || defined( BSP_MCU_RX71M ) */
69
70 #ifndef PHY_LS_HIGH_CHECK_TIME_MS
71
72 /* Check if the LinkSStatus in the PHY is still high after 2 seconds of not
73 * receiving packets. */
74 #define PHY_LS_HIGH_CHECK_TIME_MS 2000
75 #endif
76
77 #ifndef PHY_LS_LOW_CHECK_TIME_MS
78 /* Check if the LinkSStatus in the PHY is still low every second. */
79 #define PHY_LS_LOW_CHECK_TIME_MS 1000
80 #endif
81
82 /***********************************************************************************************************************
83 * Private global variables and functions
84 **********************************************************************************************************************/
85 typedef enum
86 {
87 eMACInit, /* Must initialise MAC. */
88 eMACPass, /* Initialisation was successful. */
89 eMACFailed, /* Initialisation failed. */
90 } eMAC_INIT_STATUS_TYPE;
91
92 static TaskHandle_t ether_receive_check_task_handle = 0;
93 static TaskHandle_t xTaskToNotify = NULL;
94 static BaseType_t xPHYLinkStatus;
95 static BaseType_t xReportedStatus;
96 static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit;
97
98 static int16_t SendData( uint8_t * pucBuffer,
99 size_t length );
100 static int InitializeNetwork( void );
101 static void prvEMACDeferredInterruptHandlerTask( void * pvParameters );
102 static void clear_all_ether_rx_discriptors( uint32_t event );
103
104 int32_t callback_ether_regist( void );
105 void EINT_Trig_isr( void * );
106 void get_random_number( uint8_t * data,
107 uint32_t len );
108
109 void prvLinkStatusChange( BaseType_t xStatus );
110
111 /***********************************************************************************************************************
112 * Function Name: xNetworkInterfaceInitialise ()
113 * Description : Initialization of Ethernet driver.
114 * Arguments : none
115 * Return Value : pdPASS, pdFAIL
116 **********************************************************************************************************************/
xNetworkInterfaceInitialise(void)117 BaseType_t xNetworkInterfaceInitialise( void )
118 {
119 BaseType_t xReturn;
120
121 if( xMacInitStatus == eMACInit )
122 {
123 /*
124 * Perform the hardware specific network initialization here using the Ethernet driver library to initialize the
125 * Ethernet hardware, initialize DMA descriptors, and perform a PHY auto-negotiation to obtain a network link.
126 *
127 * InitialiseNetwork() uses Ethernet peripheral driver library function, and returns 0 if the initialization fails.
128 */
129 if( InitializeNetwork() == pdFALSE )
130 {
131 xMacInitStatus = eMACFailed;
132 }
133 else
134 {
135 /* Indicate that the MAC initialisation succeeded. */
136 xMacInitStatus = eMACPass;
137 }
138
139 FreeRTOS_printf( ( "InitializeNetwork returns %s\n", ( xMacInitStatus == eMACPass ) ? "OK" : " Fail" ) );
140 }
141
142 if( xMacInitStatus == eMACPass )
143 {
144 xReturn = xPHYLinkStatus;
145 }
146 else
147 {
148 xReturn = pdFAIL;
149 }
150
151 FreeRTOS_printf( ( "xNetworkInterfaceInitialise returns %d\n", xReturn ) );
152
153 return xReturn;
154 } /* End of function xNetworkInterfaceInitialise() */
155
156
157 /***********************************************************************************************************************
158 * Function Name: xNetworkInterfaceOutput ()
159 * Description : Simple network output interface.
160 * Arguments : pxDescriptor, xReleaseAfterSend
161 * Return Value : pdTRUE, pdFALSE
162 **********************************************************************************************************************/
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t xReleaseAfterSend)163 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
164 BaseType_t xReleaseAfterSend )
165 {
166 BaseType_t xReturn = pdFALSE;
167
168 /* Simple network interfaces (as opposed to more efficient zero copy network
169 * interfaces) just use Ethernet peripheral driver library functions to copy
170 * data from the FreeRTOS+TCP buffer into the peripheral driver's own buffer.
171 * This example assumes SendData() is a peripheral driver library function that
172 * takes a pointer to the start of the data to be sent and the length of the
173 * data to be sent as two separate parameters. The start of the data is located
174 * by pxDescriptor->pucEthernetBuffer. The length of the data is located
175 * by pxDescriptor->xDataLength. */
176 if( xPHYLinkStatus != 0 )
177 {
178 if( SendData( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ) >= 0 )
179 {
180 xReturn = pdTRUE;
181 /* Call the standard trace macro to log the send event. */
182 iptraceNETWORK_INTERFACE_TRANSMIT();
183 }
184 }
185 else
186 {
187 /* As the PHY Link Status is low, it makes no sense trying to deliver a packet. */
188 }
189
190 if( xReleaseAfterSend != pdFALSE )
191 {
192 /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet
193 * buffer. The Ethernet buffer is therefore no longer needed, and must be
194 * freed for re-use. */
195 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
196 }
197
198 return xReturn;
199 } /* End of function xNetworkInterfaceOutput() */
200
201
202 /***********************************************************************************************************************
203 * Function Name: prvEMACDeferredInterruptHandlerTask ()
204 * Description : The deferred interrupt handler is a standard RTOS task.
205 * Arguments : pvParameters
206 * Return Value : none
207 **********************************************************************************************************************/
prvEMACDeferredInterruptHandlerTask(void * pvParameters)208 static void prvEMACDeferredInterruptHandlerTask( void * pvParameters )
209 {
210 NetworkBufferDescriptor_t * pxBufferDescriptor;
211 int32_t xBytesReceived = 0;
212
213 /* Avoid compiler warning about unreferenced parameter. */
214 ( void ) pvParameters;
215
216 /* Used to indicate that xSendEventStructToIPTask() is being called because
217 * of an Ethernet receive event. */
218 IPStackEvent_t xRxEvent;
219
220 uint8_t * buffer_pointer;
221
222 /* Some variables related to monitoring the PHY. */
223 TimeOut_t xPhyTime;
224 TickType_t xPhyRemTime;
225 const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
226
227 vTaskSetTimeOutState( &xPhyTime );
228 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
229
230 FreeRTOS_printf( ( "Deferred Interrupt Handler Task started\n" ) );
231 xTaskToNotify = ether_receive_check_task_handle;
232
233 for( ; ; )
234 {
235 #if ( ipconfigHAS_PRINTF != 0 )
236 {
237 /* Call a function that monitors resources: the amount of free network
238 * buffers and the amount of free space on the heap. See FreeRTOS_IP.c
239 * for more detailed comments. */
240 vPrintResourceStats();
241 }
242 #endif /* ( ipconfigHAS_PRINTF != 0 ) */
243
244 /* Wait for the Ethernet MAC interrupt to indicate that another packet
245 * has been received. */
246 if( xBytesReceived <= 0 )
247 {
248 ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
249 }
250
251 /* See how much data was received. */
252 xBytesReceived = R_ETHER_Read_ZC2( ETHER_CHANNEL_0, ( void ** ) &buffer_pointer );
253
254 if( xBytesReceived < 0 )
255 {
256 /* This is an error. Logged. */
257 FreeRTOS_printf( ( "R_ETHER_Read_ZC2: rc = %d\n", xBytesReceived ) );
258 }
259 else if( xBytesReceived > 0 )
260 {
261 /* Allocate a network buffer descriptor that points to a buffer
262 * large enough to hold the received frame. As this is the simple
263 * rather than efficient example the received data will just be copied
264 * into this buffer. */
265 pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( ( size_t ) xBytesReceived, 0 );
266
267 if( pxBufferDescriptor != NULL )
268 {
269 /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet
270 * buffer large enough to hold the received data. Copy the
271 * received data into pcNetworkBuffer->pucEthernetBuffer. Here it
272 * is assumed ReceiveData() is a peripheral driver function that
273 * copies the received data into a buffer passed in as the function's
274 * parameter. Remember! While is is a simple robust technique -
275 * it is not efficient. An example that uses a zero copy technique
276 * is provided further down this page. */
277 memcpy( pxBufferDescriptor->pucEthernetBuffer, buffer_pointer, ( size_t ) xBytesReceived );
278 /*ReceiveData( pxBufferDescriptor->pucEthernetBuffer ); */
279
280 /* Set the actual packet length, in case a larger buffer was returned. */
281 pxBufferDescriptor->xDataLength = ( size_t ) xBytesReceived;
282
283 R_ETHER_Read_ZC2_BufRelease( ETHER_CHANNEL_0 );
284
285 /* See if the data contained in the received Ethernet frame needs
286 * to be processed. NOTE! It is preferable to do this in
287 * the interrupt service routine itself, which would remove the need
288 * to unblock this task for packets that don't need processing. */
289 if( eConsiderFrameForProcessing( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer )
290 {
291 /* The event about to be sent to the TCP/IP is an Rx event. */
292 xRxEvent.eEventType = eNetworkRxEvent;
293
294 /* pvData is used to point to the network buffer descriptor that
295 * now references the received data. */
296 xRxEvent.pvData = ( void * ) pxBufferDescriptor;
297
298 /* Send the data to the TCP/IP stack. */
299 if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
300 {
301 /* The buffer could not be sent to the IP task so the buffer must be released. */
302 vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
303
304 /* Make a call to the standard trace macro to log the occurrence. */
305 iptraceETHERNET_RX_EVENT_LOST();
306 clear_all_ether_rx_discriptors( 0 );
307 }
308 else
309 {
310 /* The message was successfully sent to the TCP/IP stack.
311 * Call the standard trace macro to log the occurrence. */
312 iptraceNETWORK_INTERFACE_RECEIVE();
313 R_BSP_NOP();
314 }
315 }
316 else
317 {
318 /* The Ethernet frame can be dropped, but the Ethernet buffer must be released. */
319 vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
320 }
321 }
322 else
323 {
324 /* The event was lost because a network buffer was not available.
325 * Call the standard trace macro to log the occurrence. */
326 iptraceETHERNET_RX_EVENT_LOST();
327 clear_all_ether_rx_discriptors( 1 );
328 FreeRTOS_printf( ( "R_ETHER_Read_ZC2: Cleared descriptors\n" ) );
329 }
330 }
331
332 if( xBytesReceived > 0 )
333 {
334 /* A packet was received. No need to check for the PHY status now,
335 * but set a timer to check it later on. */
336 vTaskSetTimeOutState( &xPhyTime );
337 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
338
339 /* Indicate that the Link Status is high, so that
340 * xNetworkInterfaceOutput() can send packets. */
341 if( xPHYLinkStatus == 0 )
342 {
343 xPHYLinkStatus = 1;
344 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS assume %d\n", xPHYLinkStatus ) );
345 }
346 }
347 else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) || ( FreeRTOS_IsNetworkUp() == pdFALSE ) )
348 {
349 R_ETHER_LinkProcess( ETHER_CHANNEL_0 );
350
351 if( xPHYLinkStatus != xReportedStatus )
352 {
353 xPHYLinkStatus = xReportedStatus;
354 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", xPHYLinkStatus ) );
355 }
356
357 vTaskSetTimeOutState( &xPhyTime );
358
359 if( xPHYLinkStatus != 0 )
360 {
361 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
362 }
363 else
364 {
365 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
366 }
367 }
368 }
369 } /* End of function prvEMACDeferredInterruptHandlerTask() */
370
371
372 /***********************************************************************************************************************
373 * Function Name: vNetworkInterfaceAllocateRAMToBuffers ()
374 * Description : .
375 * Arguments : pxNetworkBuffers
376 * Return Value : none
377 **********************************************************************************************************************/
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])378 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
379 {
380 uint32_t ul;
381 uint8_t * buffer_address;
382
383 R_BSP_SECTION_OPERATORS_INIT( B_ETHERNET_BUFFERS_1 )
384
385 buffer_address = R_BSP_SECTOP( B_ETHERNET_BUFFERS_1 );
386
387 for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
388 {
389 pxNetworkBuffers[ ul ].pucEthernetBuffer = ( buffer_address + ( ETHER_CFG_BUFSIZE * ul ) );
390 }
391 } /* End of function vNetworkInterfaceAllocateRAMToBuffers() */
392
393
394 /***********************************************************************************************************************
395 * Function Name: prvLinkStatusChange ()
396 * Description : Function will be called when the Link Status of the phy has changed ( see ether_callback.c )
397 * Arguments : xStatus : true when statyus has become high
398 * Return Value : void
399 **********************************************************************************************************************/
prvLinkStatusChange(BaseType_t xStatus)400 void prvLinkStatusChange( BaseType_t xStatus )
401 {
402 if( xReportedStatus != xStatus )
403 {
404 FreeRTOS_printf( ( "prvLinkStatusChange( %d )\n", xStatus ) );
405 xReportedStatus = xStatus;
406 }
407 }
408
409 /***********************************************************************************************************************
410 * Function Name: InitializeNetwork ()
411 * Description :
412 * Arguments : none
413 * Return Value : pdTRUE, pdFALSE
414 **********************************************************************************************************************/
InitializeNetwork(void)415 static int InitializeNetwork( void )
416 {
417 ether_return_t eth_ret;
418 BaseType_t return_code = pdFALSE;
419 ether_param_t param;
420 uint8_t myethaddr[ 6 ] =
421 {
422 configMAC_ADDR0,
423 configMAC_ADDR1,
424 configMAC_ADDR2,
425 configMAC_ADDR3,
426 configMAC_ADDR4,
427 configMAC_ADDR5
428 }; /*XXX Fix me */
429
430 R_ETHER_PinSet_CHANNEL_0();
431 R_ETHER_Initial();
432 callback_ether_regist();
433
434 param.channel = ETHER_CHANNEL_0;
435 eth_ret = R_ETHER_Control( CONTROL_POWER_ON, param ); /* PHY mode settings, module stop cancellation */
436
437 if( ETHER_SUCCESS != eth_ret )
438 {
439 return pdFALSE;
440 }
441
442 eth_ret = R_ETHER_Open_ZC2( ETHER_CHANNEL_0, myethaddr, ETHER_FLAG_OFF );
443
444 if( ETHER_SUCCESS != eth_ret )
445 {
446 return pdFALSE;
447 }
448
449 return_code = xTaskCreate( prvEMACDeferredInterruptHandlerTask,
450 "ETHER_RECEIVE_CHECK_TASK",
451 512u,
452 0,
453 configMAX_PRIORITIES - 1,
454 ðer_receive_check_task_handle );
455
456 if( pdFALSE == return_code )
457 {
458 return pdFALSE;
459 }
460
461 return pdTRUE;
462 } /* End of function InitializeNetwork() */
463
464
465 /***********************************************************************************************************************
466 * Function Name: SendData ()
467 * Description :
468 * Arguments : pucBuffer, length
469 * Return Value : 0 success, negative fail
470 **********************************************************************************************************************/
SendData(uint8_t * pucBuffer,size_t length)471 static int16_t SendData( uint8_t * pucBuffer,
472 size_t length ) /*TODO complete stub function */
473 {
474 ether_return_t ret;
475 uint8_t * pwrite_buffer;
476 uint16_t write_buf_size;
477
478 /* (1) Retrieve the transmit buffer location controlled by the descriptor. */
479 ret = R_ETHER_Write_ZC2_GetBuf( ETHER_CHANNEL_0, ( void ** ) &pwrite_buffer, &write_buf_size );
480
481 if( ETHER_SUCCESS == ret )
482 {
483 if( write_buf_size >= length )
484 {
485 memcpy( pwrite_buffer, pucBuffer, length );
486 }
487
488 if( length < ETHER_BUFSIZE_MIN ) /*under minimum*/
489 {
490 memset( ( pwrite_buffer + length ), 0, ( ETHER_BUFSIZE_MIN - length ) ); /*padding*/
491 length = ETHER_BUFSIZE_MIN; /*resize*/
492 }
493
494 ret = R_ETHER_Write_ZC2_SetBuf( ETHER_CHANNEL_0, ( uint16_t ) length );
495 ret = R_ETHER_CheckWrite( ETHER_CHANNEL_0 );
496 }
497
498 if( ETHER_SUCCESS != ret )
499 {
500 return -5; /* XXX return meaningful value */
501 }
502 else
503 {
504 return 0;
505 }
506 } /* End of function SendData() */
507
508
509 /***********************************************************************************************************************
510 * Function Name: EINT_Trig_isr
511 * Description : Standard frame received interrupt handler
512 * Arguments : ectrl - EDMAC and ETHERC control structure
513 * Return Value : None
514 * Note : This callback function is executed when EINT0 interrupt occurred.
515 ***********************************************************************************************************************/
EINT_Trig_isr(void * ectrl)516 void EINT_Trig_isr( void * ectrl )
517 {
518 ether_cb_arg_t * pdecode;
519 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
520
521 pdecode = ( ether_cb_arg_t * ) ectrl;
522
523 if( pdecode->status_eesr & 0x00040000 ) /* EDMAC FR (Frame Receive Event) interrupt */
524 {
525 if( xTaskToNotify != NULL )
526 {
527 vTaskNotifyGiveFromISR( ether_receive_check_task_handle, &xHigherPriorityTaskWoken );
528 }
529
530 /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch
531 * should be performed to ensure the interrupt returns directly to the highest
532 * priority task. The macro used for this purpose is dependent on the port in
533 * use and may be called portEND_SWITCHING_ISR(). */
534 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
535 /*TODO complete interrupt handler for other events. */
536 }
537 } /* End of function EINT_Trig_isr() */
538
539
clear_all_ether_rx_discriptors(uint32_t event)540 static void clear_all_ether_rx_discriptors( uint32_t event )
541 {
542 int32_t xBytesReceived;
543 uint8_t * buffer_pointer;
544
545 /* Avoid compiler warning about unreferenced parameter. */
546 ( void ) event;
547
548 while( 1 )
549 {
550 /* See how much data was received. */
551 xBytesReceived = R_ETHER_Read_ZC2( ETHER_CHANNEL_0, ( void ** ) &buffer_pointer );
552
553 if( 0 > xBytesReceived )
554 {
555 /* This is an error. Ignored. */
556 }
557 else if( 0 < xBytesReceived )
558 {
559 R_ETHER_Read_ZC2_BufRelease( ETHER_CHANNEL_0 );
560 iptraceETHERNET_RX_EVENT_LOST();
561 }
562 else
563 {
564 break;
565 }
566 }
567 }
568
569 /***********************************************************************************************************************
570 * End of file "NetworkInterface.c"
571 **********************************************************************************************************************/
572