po_hi_driver_rasta_1553_brmlib.c 12 KB
Newer Older
1
2
3
4
5
/*
 * This is a part of PolyORB-HI-C distribution, a minimal
 * middleware written for generated code from AADL models.
 * You should use it with the Ocarina toolsuite.
 *
jhugues's avatar
jhugues committed
6
7
8
 * For more informations, please visit http://assert-project.net/taste
 *
 * Copyright (C) 2010-2012 ESA & ISAE.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 */

/*
 * This code has been greatly inspired by GAISLER examples.
 */

#include <deployment.h>
/* Generated code header */

#ifdef __PO_HI_NEED_DRIVER_1553_RASTA

#include <rtems.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <b1553brm.h>
julien.delange's avatar
julien.delange committed
28
#include <po_hi_debug.h>
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <drivers/po_hi_driver_rasta_1553_brmlib.h>

/* The stupid rtems name to errno table, in fact I hate it.... :)
 *
rtems_assoc_t errno_assoc[] = {
    { "OK",                 RTEMS_SUCCESSFUL,                0 },
    { "BUSY",               RTEMS_RESOURCE_IN_USE,           EBUSY },
    { "INVALID NAME",       RTEMS_INVALID_NAME,              EINVAL },
    { "NOT IMPLEMENTED",    RTEMS_NOT_IMPLEMENTED,           ENOSYS },
    { "TIMEOUT",            RTEMS_TIMEOUT,                   ETIMEDOUT },
    { "NO MEMORY",          RTEMS_NO_MEMORY,                 ENOMEM },
    { "NO DEVICE",          RTEMS_UNSATISFIED,               ENODEV },
    { "INVALID NUMBER",     RTEMS_INVALID_NUMBER,            EBADF},
    { "NOT RESOURCE OWNER", RTEMS_NOT_OWNER_OF_RESOURCE,     EPERM},
    { "IO ERROR",           RTEMS_IO_ERROR,                  EIO},
    { 0, 0, 0 },
};
*/

__po_hi_c_driver_rasta_1553_brm_t __po_hi_c_driver_1553_rasta_brmlib_open(char *driver_name)
{
	int fd;
	__po_hi_c_driver_rasta_1553_brm_t ret = NULL;
	
julien.delange's avatar
julien.delange committed
53
	__DEBUGMSG("[RASTA 1553 BRMLIB] Opening driver %s ...",driver_name);
54
55
	
	fd = open(driver_name,O_RDWR);
julien.delange's avatar
julien.delange committed
56
57
	if ( fd >= 0 )
   {
58
59
60
61
		ret = calloc(sizeof(*ret),1);
		ret->fd = fd;
		/* Initial state of driver */
		ret->mode = BRM_MODE_RT;
julien.delange's avatar
julien.delange committed
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
	   __DEBUGMSG("OK !\n");
	
	}
   else
   {
		if ( errno == ENODEV )
      {
			__DEBUGMSG(" driver %s doesn't exist\n",driver_name);
		}
      else
      {
         if ( errno == EBUSY )
         {
			   __DEBUGMSG(" channel already taken\n");
		   }
         else
         {
			   __DEBUGMSG(" unknown error, errno: %d, ret: %d\n",errno,fd);
         }
81
82
83
84
85
86
87
88
		}
	}
	
	return ret;
}

void __po_hi_c_driver_1553_rasta_brmlib_close(__po_hi_c_driver_rasta_1553_brm_t chan){
	if ( !chan || (chan->fd<0) )
julien.delange's avatar
julien.delange committed
89
   {
90
		return;
julien.delange's avatar
julien.delange committed
91
   }
92
93
94
95
	close(chan->fd);
	free(chan);
}

julien.delange's avatar
julien.delange committed
96
97
98
int __po_hi_c_driver_1553_rasta_brmlib_rt_send_multiple(__po_hi_c_driver_rasta_1553_brm_t chan, struct rt_msg *msgs, int msgcnt)
{

99
	int ret;
julien.delange's avatar
julien.delange committed
100
101
102

	if ( !chan || !msgs || (msgcnt < 0) )
   {
103
		return -1;
julien.delange's avatar
julien.delange committed
104
   }
105
106
	
	if ( msgcnt == 0 )
julien.delange's avatar
julien.delange committed
107
   {
108
		return 0;
julien.delange's avatar
julien.delange committed
109
   }
110
111
	
	ret = write(chan->fd,msgs,msgcnt);
julien.delange's avatar
julien.delange committed
112
113
114

	if ( ret < 0 )
   {
115
116
117
118
		/* something went wrong 
		 * OR in non-blocking mode
		 * that would block
		 */
julien.delange's avatar
julien.delange committed
119
120
		if ( !chan->txblk && (errno == EBUSY) )
      {
121
122
123
124
			/* would block ==> 0 sent is ok */
			return 0;
		}
			
julien.delange's avatar
julien.delange committed
125
126
		if ( errno == EINVAL )
      {
127
			/* CAN must be started before receiving */
julien.delange's avatar
julien.delange committed
128
			__DEBUGMSG("[RASTA 1553 BRMLIB] input descriptor numbering error\n");
129
130
131
			return -1;
		}

julien.delange's avatar
julien.delange committed
132
		__DEBUGMSG("[RASTA 1553 BRMLIB] error in write, errno: %d, returned: %d\n",errno,ret);
133
134
135
136
137
138
139
		return -1;
	}
	
	/* sent all of them */
	return ret;
}

julien.delange's avatar
julien.delange committed
140
141
142
int __po_hi_c_driver_1553_rasta_brmlib_rt_send(__po_hi_c_driver_rasta_1553_brm_t chan, struct rt_msg *msg)
{
   return __po_hi_c_driver_1553_rasta_brmlib_rt_send_multiple(chan,msg,1);
143
144
}

julien.delange's avatar
julien.delange committed
145
146
int __po_hi_c_driver_1553_rasta_brmlib_recv_multiple(__po_hi_c_driver_rasta_1553_brm_t chan, void *msgs, int msglen)
{
147
148
149
	int ret;
	
	if ( !chan || !msgs || (msglen<0) )
julien.delange's avatar
julien.delange committed
150
   {
151
		return -1;
julien.delange's avatar
julien.delange committed
152
   }
153
154
	
	if ( msglen == 0 )
julien.delange's avatar
julien.delange committed
155
   {
156
		__DEBUGMSG("[RASTA 1553 BRMLIB] zero length, errno: %d, returned: %d\n",errno,ret);
157
		return 0;
julien.delange's avatar
julien.delange committed
158
   }
159
160
	
	errno = 0;
julien.delange's avatar
julien.delange committed
161

162
	ret = read(chan->fd,msgs,msglen);
julien.delange's avatar
julien.delange committed
163
164
165

	if ( ret < 0 )
   {
166
167
168
169
		/* something went wrong 
		 * OR in non-blocking mode
		 * that would block
		 */
julien.delange's avatar
julien.delange committed
170
171
		if ( !chan->rxblk && (errno == EBUSY) )
      {
172
173
174
			return 0;
		}
		
julien.delange's avatar
julien.delange committed
175
		__DEBUGMSG("[RASTA 1553 BRMLIB] error in read, errno: %d, returned: %d\n",errno,ret);
176
177
178
179
180
181
182
183
184
185
		return -1;
	}
	
	/* message count is returned, not byte count */
	return ret;
}

int __po_hi_c_driver_1553_rasta_brmlib_rt_recv_multiple(__po_hi_c_driver_rasta_1553_brm_t chan, struct rt_msg *msgs, int msgcnt)
{
	if ( !chan || (chan->mode!=BRM_MODE_RT) )
julien.delange's avatar
julien.delange committed
186
   {
187
		return -1;
julien.delange's avatar
julien.delange committed
188
   }
189
190
191
192
193
194
195
196
	
	/* Read the messages */
	return __po_hi_c_driver_1553_rasta_brmlib_recv_multiple(chan,(void *)msgs,msgcnt);
}

int __po_hi_c_driver_1553_rasta_brmlib_bm_recv_multiple(__po_hi_c_driver_rasta_1553_brm_t chan, struct bm_msg *msgs, int msgcnt)
{
	if ( !chan || (chan->mode!=BRM_MODE_BM) )
julien.delange's avatar
julien.delange committed
197
   {
198
		return -1;
julien.delange's avatar
julien.delange committed
199
   }
200
201
202
203
204
	
	/* Read the messages */
	return __po_hi_c_driver_1553_rasta_brmlib_recv_multiple(chan,(void *)msgs,msgcnt);
}

julien.delange's avatar
julien.delange committed
205
206
int __po_hi_c_driver_1553_rasta_brmlib_rt_recv(__po_hi_c_driver_rasta_1553_brm_t chan, struct rt_msg *msg)
{
207
208
209
	return __po_hi_c_driver_1553_rasta_brmlib_rt_recv_multiple(chan,msg,1);
}

julien.delange's avatar
julien.delange committed
210
211
212

int __po_hi_c_driver_1553_rasta_brmlib_bm_recv(__po_hi_c_driver_rasta_1553_brm_t chan, struct bm_msg *msg)
{
213
214
215
216
	return __po_hi_c_driver_1553_rasta_brmlib_bm_recv_multiple(chan,msg,1);
}


julien.delange's avatar
julien.delange committed
217
218
int __po_hi_c_driver_1553_rasta_brmlib_set_mode(__po_hi_c_driver_rasta_1553_brm_t chan, unsigned int mode)
{
219
220
221
222
	int ret;
	unsigned int arg = mode;
	
	if ( !chan )
julien.delange's avatar
julien.delange committed
223
   {
224
		return -1;
julien.delange's avatar
julien.delange committed
225
   }
226
227
	
	ret = ioctl(chan->fd,BRM_SET_MODE,&arg);
julien.delange's avatar
julien.delange committed
228
229
230
231
232
233

	if ( ret < 0 )
   {
		if ( errno == EINVAL )
      {
			__DEBUGMSG("[RASTA 1553 BRMLIB] set_mode invalid mode: %d\n",arg);
234
235
236
			return -2;
		}
	
julien.delange's avatar
julien.delange committed
237
238
		if ( errno == ENOMEM )
      {
239
			/* started */
julien.delange's avatar
julien.delange committed
240
			__DEBUGMSG("[RASTA 1553 BRMLIB] set_mode: not enough memory\n");
241
242
243
244
			return -3;
		}
		
		/* unhandeled errors */
julien.delange's avatar
julien.delange committed
245
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_mode: failed, errno: %d, ret: %d\n",errno,ret);
246
247
248
249
250
251
252
253
254
		return -1;
	}
	
	/* Save mode */
	chan->mode = mode;
	
	return 0;
}

julien.delange's avatar
julien.delange committed
255
256
int __po_hi_c_driver_1553_rasta_brmlib_set_bus(__po_hi_c_driver_rasta_1553_brm_t chan, unsigned int bus)
{
257
258
259
	int ret;
	unsigned int arg = bus;
	if ( !chan )
julien.delange's avatar
julien.delange committed
260
   {
261
		return -1;
julien.delange's avatar
julien.delange committed
262
   }
263
264
		
	/* only for RT mode */
julien.delange's avatar
julien.delange committed
265
266
267
	if ( chan->mode != BRM_MODE_RT )
   {
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_bus: Only possible to set bus in RT mode\n");
268
269
270
271
		return -2; /* fast EINVAL... */
	}
	
	ret = ioctl(chan->fd,BRM_SET_BUS,&arg);
julien.delange's avatar
julien.delange committed
272
273
274
275
276
277

	if ( ret < 0 )
   {
		if ( errno == EINVAL )
      {
			__DEBUGMSG("[RASTA 1553 BRMLIB] set_bus: invalid bus: %d\n",arg);
278
279
280
281
			return -2;
		}
		
		/* unhandeled errors */
julien.delange's avatar
julien.delange committed
282
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_bus: failed, errno: %d, ret: %d\n",errno,ret);
283
284
285
286
287
		return -1;
	}
	return 0;
}

julien.delange's avatar
julien.delange committed
288
289
int __po_hi_c_driver_1553_rasta_brmlib_set_msg_timeout(__po_hi_c_driver_rasta_1553_brm_t chan, unsigned int timeout)
{
290
291
292
293
	int ret;
	unsigned int arg = timeout;
	
	if ( !chan )
julien.delange's avatar
julien.delange committed
294
   {
295
		return -1;
julien.delange's avatar
julien.delange committed
296
   }
297
	
julien.delange's avatar
julien.delange committed
298
299
300
	if ( !((chan->mode==BRM_MODE_BM) || (chan->mode==BRM_MODE_BC)) )
   {
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_msg_timeout: Only possible to set bus in BC & BM mode\n");
301
302
303
304
		return -2;
	}
	
	ret = ioctl(chan->fd,BRM_SET_MSGTO,&arg);
julien.delange's avatar
julien.delange committed
305
306
307
308
309

	if ( ret < 0 )
   {
		if ( errno == EBUSY )
      {
310
			/* started */
julien.delange's avatar
julien.delange committed
311
			__DEBUGMSG("[RASTA 1553 BRMLIB] set_msg_timeout: started\n");
312
313
314
			return -2;
		}
		
julien.delange's avatar
julien.delange committed
315
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_msg_timeout: failed, errno: %d, ret: %d\n",errno,ret);
316
317
318
319
320
321
		return -1;
	}
	
	return 0;
}

julien.delange's avatar
julien.delange committed
322
323
int __po_hi_c_driver_1553_rasta_brmlib_set_rt_addr(__po_hi_c_driver_rasta_1553_brm_t chan, unsigned int address)
{
324
325
326
327
	int ret;
	unsigned int arg = address;
	
	if ( !chan )
julien.delange's avatar
julien.delange committed
328
   {
329
		return -1;
julien.delange's avatar
julien.delange committed
330
   }
331
	
julien.delange's avatar
julien.delange committed
332
333
334
	if ( chan->mode != BRM_MODE_RT )
   {
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_rt_addr: not in RT mode\n");
335
336
337
338
		return -2;
	}
	
	ret = ioctl(chan->fd,BRM_SET_RT_ADDR,&arg);
julien.delange's avatar
julien.delange committed
339
340
341
342

	if ( ret < 0 )
   {
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_rt_addr: failed, errno: %d, ret: %d\n",errno,ret);
343
344
345
346
347
348
349
350
351
352
353
		return -1;
	}
	
	return 0;
}

int __po_hi_c_driver_1553_rasta_brmlib_set_std(__po_hi_c_driver_rasta_1553_brm_t chan, int std){
	int ret;
	unsigned int arg = std;
	
	if ( !chan )
julien.delange's avatar
julien.delange committed
354
   {
355
		return -1;
julien.delange's avatar
julien.delange committed
356
   }
357
358
	
	ret = ioctl(chan->fd,BRM_SET_STD,&arg);
julien.delange's avatar
julien.delange committed
359
360
361
362
363

	if ( ret < 0 )
   {
		if ( errno == EINVAL )
      {
364
			/* started */
julien.delange's avatar
julien.delange committed
365
			__DEBUGMSG("[RASTA 1553 BRMLIB] set_std: new standard not valid: %d\n",arg);
366
367
368
			return -2;
		}
		
julien.delange's avatar
julien.delange committed
369
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_filter: failed, errno: %d, ret: %d\n",errno,ret);
370
371
372
373
374
375
376
		return -1;
	}
	
	return 0;
}


julien.delange's avatar
julien.delange committed
377
378
int __po_hi_c_driver_1553_rasta_brmlib_set_txblock(__po_hi_c_driver_rasta_1553_brm_t chan, int txblocking)
{
379
380
381
382
	unsigned int arg = (txblocking) ? 1 : 0;
	int ret;
	
	if ( !chan )
julien.delange's avatar
julien.delange committed
383
   {
384
		return -1;
julien.delange's avatar
julien.delange committed
385
   }
386
387
	
	ret = ioctl(chan->fd,BRM_TX_BLOCK,&arg);
julien.delange's avatar
julien.delange committed
388
389
390
391

	if ( ret < 0 )
   {
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_txblock: failed, errno: %d, ret: %d\n",errno,ret);
392
393
		return -1;
	}
julien.delange's avatar
julien.delange committed
394

395
396
397
398
399
400
401
402
403
404
405
	/* remember blocking state */
	chan->txblk = arg;
	return 0;
}


int __po_hi_c_driver_1553_rasta_brmlib_set_rxblock(__po_hi_c_driver_rasta_1553_brm_t chan, int rxblocking){
	unsigned int arg = (rxblocking) ? 1 : 0;
	int ret;
	
	if ( !chan )
julien.delange's avatar
julien.delange committed
406
   {
407
		return -1;
julien.delange's avatar
julien.delange committed
408
   }
409
410
	
	ret = ioctl(chan->fd,BRM_RX_BLOCK,&arg);
julien.delange's avatar
julien.delange committed
411
412
413
414

	if ( ret < 0 )
   {
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_rxblock: failed, errno: %d, ret: %d\n",errno,ret);
415
416
		return -1;
	}
julien.delange's avatar
julien.delange committed
417

418
419
	/* remember blocking state */
	chan->rxblk = arg;
julien.delange's avatar
julien.delange committed
420

421
422
423
424
425
	return 0;
}

int __po_hi_c_driver_1553_rasta_brmlib_set_block(__po_hi_c_driver_rasta_1553_brm_t chan, int txblocking, int rxblocking){
	int ret;
julien.delange's avatar
julien.delange committed
426

427
	ret = __po_hi_c_driver_1553_rasta_brmlib_set_txblock(chan,txblocking);
julien.delange's avatar
julien.delange committed
428
429
430

	if ( !ret )
   {
431
432
		return __po_hi_c_driver_1553_rasta_brmlib_set_rxblock(chan,rxblocking);
	}
julien.delange's avatar
julien.delange committed
433

434
435
436
437
438
439
440
441
	return ret;
}

int __po_hi_c_driver_1553_rasta_brmlib_set_broadcast(__po_hi_c_driver_rasta_1553_brm_t chan, int broadcast){
	unsigned int arg = (broadcast) ? 1 : 0;
	int ret;
	
	if ( !chan )
julien.delange's avatar
julien.delange committed
442
   {
443
		return -1;
julien.delange's avatar
julien.delange committed
444
   }
445
446
	
	ret = ioctl(chan->fd,BRM_SET_BCE,&arg);
julien.delange's avatar
julien.delange committed
447
448
449
450

	if ( ret < 0 )
   {
		__DEBUGMSG("[RASTA 1553 BRMLIB] set_broadcast: failed, errno: %d, ret: %d\n",errno,ret);
451
452
453
454
455
456
457
458
		return -1;
	}
	
	/* remember broadcast state */
	chan->broadcast = arg;
	return 0;
}

julien.delange's avatar
julien.delange committed
459
460
int __po_hi_c_driver_1553_rasta_brmlib_bc_dolist(__po_hi_c_driver_rasta_1553_brm_t chan, struct bc_msg *msgs)
{
461
462
463
	int ret;
	
	ret = ioctl(chan->fd, BRM_DO_LIST, msgs);
julien.delange's avatar
julien.delange committed
464
465
466
467
468
469

	if ( ret < 0 )
   {
		if ( errno == EINVAL )
      {
			__DEBUGMSG("[RASTA 1553 BRMLIB] bc_dolist: not in BC mode\n");
470
471
472
			return -2;
		}

julien.delange's avatar
julien.delange committed
473
474
475
		if ( errno == EBUSY )
      {
			__DEBUGMSG("[RASTA 1553 BRMLIB] bc_dolist: busy\n");
476
477
478
			return -3;
		}
		
julien.delange's avatar
julien.delange committed
479
		__DEBUGMSG("[RASTA 1553 BRMLIB] dolist: errno %d, ret: %d\n",errno,ret);
480
481
482
483
484
		return -1;
	}
	return 0;
}

julien.delange's avatar
julien.delange committed
485
486
int __po_hi_c_driver_1553_rasta_brmlib_bc_dolist_wait(__po_hi_c_driver_rasta_1553_brm_t chan)
{
487
488
489
490
	int ret;
	unsigned int result;
	
	ret = ioctl(chan->fd, BRM_LIST_DONE, &result);
julien.delange's avatar
julien.delange committed
491
492
493
494
495
496

	if ( ret < 0 )
   {
		if ( errno == EINVAL )
      {
			__DEBUGMSG("[RASTA 1553 BRMLIB] bc_dolist: not in BC mode\n");
497
498
499
			return -2;
		}

julien.delange's avatar
julien.delange committed
500
501
502
		if ( errno == EBUSY )
      {
			__DEBUGMSG("[RASTA 1553 BRMLIB] dolist: busy\n");
503
504
505
			return -3;
		}
		
julien.delange's avatar
julien.delange committed
506
		__DEBUGMSG("[RASTA 1553 BRMLIB] bc_dolist: errno %d, ret: %d\n",errno,ret);
507
508
509
510
511
512
		return -1;
	}
	
	return result;
}

julien.delange's avatar
julien.delange committed
513
514
void __po_hi_c_driver_rasta_1553_print_rt_msg(int i, struct rt_msg *msg)
{
515
	int k, wc;
julien.delange's avatar
julien.delange committed
516

517
	wc = msg->miw >> 11;
julien.delange's avatar
julien.delange committed
518
519
520

	__DEBUGMSG("[RASTA 1553 BRMLIB] MSG[%d]: miw: 0x%x, time: 0x%x, desc: 0x%x, len: %d\n  ",i,msg->miw,msg->time,msg->desc,wc);

521
	/* print data */			
julien.delange's avatar
julien.delange committed
522
523
524
525
526
527
528
529
530
	for (k = 0; k < wc; k++)
   {
		if ( isalnum(msg->data[k]) )
      {
         __DEBUGMSG("0x%x (%c)",msg->data[k],msg->data[k]);
		}
      else
      {
			__DEBUGMSG("0x%x (.)",msg->data[k]);
531
532
533
		}
	}
	if ( k > 0 )
julien.delange's avatar
julien.delange committed
534
535
536
   {
		__DEBUGMSG("\n");
   }
537
538
}

julien.delange's avatar
julien.delange committed
539
540
void __po_hi_c_driver_rasta_1553_print_bm_msg(int i, struct bm_msg *msg)
{
541
	int k,wc,tr,desc;
julien.delange's avatar
julien.delange committed
542

543
	wc = msg->cw1 & 0x1f;	
julien.delange's avatar
julien.delange committed
544

545
	desc = (msg->cw1 >> 5) & 0x1f;
julien.delange's avatar
julien.delange committed
546

547
548
	tr = msg->cw1 & 0x0400;

julien.delange's avatar
julien.delange committed
549
550
551
	__DEBUGMSG("[RASTA 1553 BRMLIB] MSG[%d]: miw: 0x%x, cw1: 0x%x, cw2: 0x%x, desc: %d\n",i,msg->miw,msg->cw1,msg->cw2,desc);

	__DEBUGMSG("         sw1: 0x%x, sw2: 0x%x, tim: 0x%x, len: %d\n",msg->sw1,msg->sw2,msg->time,wc);
552
553
554
	
	/* no message data in BC transmit commands */
	if ( tr )
julien.delange's avatar
julien.delange committed
555
   {
556
		return;
julien.delange's avatar
julien.delange committed
557
   }
558
	
julien.delange's avatar
julien.delange committed
559
	__DEBUGMSG("         ");
560
561
	
	/* print data */			
julien.delange's avatar
julien.delange committed
562
563
564
565
566
567
568
569
570
	for (k = 0; k<wc; k++)
   {
		if ( isalnum(msg->data[k]) )
      {
         __DEBUGMSG("0x%x (%c)",msg->data[k],msg->data[k]);
		}
      else
      {
			__DEBUGMSG("0x%x (.)",msg->data[k]);
571
572
		}
	}
julien.delange's avatar
julien.delange committed
573

574
	if ( k > 0 )
julien.delange's avatar
julien.delange committed
575
576
577
   {
		__DEBUGMSG("\n");
   }
578
579
580
}

#endif