This source file includes following definitions.
- ax25_rt_rx_frame
- ax25_rt_device_down
- ax25_rt_ioctl
- ax25_rt_get_info
- ax25_cs_get_info
- ax25_rt_autobind
- ax25_ip_mode_set
- ax25_ip_mode_get
- ax25_dev_get_dev
- ax25_dev_get_value
- ax25_dev_device_up
- ax25_dev_device_down
- ax25_dev_ioctl
- ax25_bpq_get_info
- ax25_bpq_get_addr
- ax25_bpq_ioctl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 #include <linux/config.h>
33 #ifdef CONFIG_AX25
34 #include <linux/errno.h>
35 #include <linux/types.h>
36 #include <linux/socket.h>
37 #include <linux/in.h>
38 #include <linux/kernel.h>
39 #include <linux/sched.h>
40 #include <linux/timer.h>
41 #include <linux/string.h>
42 #include <linux/sockios.h>
43 #include <linux/net.h>
44 #include <net/ax25.h>
45 #include <linux/inet.h>
46 #include <linux/netdevice.h>
47 #include <linux/skbuff.h>
48 #include <net/sock.h>
49 #include <asm/segment.h>
50 #include <asm/system.h>
51 #include <linux/fcntl.h>
52 #include <linux/mm.h>
53 #include <linux/interrupt.h>
54
55 #define AX25_ROUTE_MAX 40
56
57 static struct ax25_route {
58 struct ax25_route *next;
59 ax25_address callsign;
60 struct device *dev;
61 ax25_digi *digipeat;
62 struct timeval stamp;
63 int n;
64 char ip_mode;
65 } *ax25_route = NULL;
66
67 static struct ax25_dev {
68 struct ax25_dev *next;
69 struct device *dev;
70 unsigned short values[AX25_MAX_VALUES];
71 } *ax25_device = NULL;
72
73 void ax25_rt_rx_frame(ax25_address *src, struct device *dev, ax25_digi *digi)
74 {
75 unsigned long flags;
76 extern struct timeval xtime;
77 struct ax25_route *ax25_rt;
78 struct ax25_route *oldest;
79 int count;
80
81 count = 0;
82 oldest = NULL;
83
84 for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
85 if (count == 0 || (ax25_rt->stamp.tv_sec != 0 && ax25_rt->stamp.tv_sec < oldest->stamp.tv_sec))
86 oldest = ax25_rt;
87
88 if (ax25cmp(&ax25_rt->callsign, src) == 0 && ax25_rt->dev == dev) {
89 if (ax25_rt->stamp.tv_sec != 0)
90 ax25_rt->stamp = xtime;
91 ax25_rt->n++;
92 return;
93 }
94
95 count++;
96 }
97
98 if (count > AX25_ROUTE_MAX) {
99 oldest->callsign = *src;
100 oldest->dev = dev;
101 if (oldest->digipeat != NULL) {
102 kfree_s(oldest->digipeat, sizeof(ax25_digi));
103 oldest->digipeat = NULL;
104 }
105 oldest->stamp = xtime;
106 oldest->n = 1;
107 oldest->ip_mode = ' ';
108 return;
109 }
110
111 if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL)
112 return;
113
114 ax25_rt->callsign = *src;
115 ax25_rt->dev = dev;
116 ax25_rt->digipeat = NULL;
117 ax25_rt->stamp = xtime;
118 ax25_rt->n = 1;
119 ax25_rt->ip_mode = ' ';
120
121 if (digi != NULL) {
122 if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
123 kfree_s(ax25_rt, sizeof(struct ax25_route));
124 return;
125 }
126 memcpy(ax25_rt->digipeat, digi, sizeof(ax25_digi));
127 }
128
129 save_flags(flags);
130 cli();
131
132 ax25_rt->next = ax25_route;
133 ax25_route = ax25_rt;
134
135 restore_flags(flags);
136 }
137
138 void ax25_rt_device_down(struct device *dev)
139 {
140 struct ax25_route *s, *t, *ax25_rt = ax25_route;
141
142 while (ax25_rt != NULL) {
143 s = ax25_rt;
144 ax25_rt = ax25_rt->next;
145
146 if (s->dev == dev) {
147 if (ax25_route == s) {
148 ax25_route = s->next;
149 if (s->digipeat != NULL)
150 kfree_s((void *)s->digipeat, sizeof(ax25_digi));
151 kfree_s((void *)s, (sizeof *s));
152 } else {
153 for (t = ax25_route; t != NULL; t = t->next) {
154 if (t->next == s) {
155 t->next = s->next;
156 if (s->digipeat != NULL)
157 kfree_s((void *)s->digipeat, sizeof(ax25_digi));
158 kfree_s((void *)s, sizeof(*s));
159 break;
160 }
161 }
162 }
163 }
164 }
165 }
166
167 int ax25_rt_ioctl(unsigned int cmd, void *arg)
168 {
169 unsigned long flags;
170 struct ax25_route *s, *t, *ax25_rt;
171 struct ax25_routes_struct route;
172 struct device *dev;
173 int i, err;
174
175 switch (cmd) {
176 case SIOCADDRT:
177 if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
178 return err;
179 memcpy_fromfs(&route, arg, sizeof(route));
180 if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
181 return -EINVAL;
182 if (route.digi_count > AX25_MAX_DIGIS)
183 return -EINVAL;
184 for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
185 if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == dev) {
186 if (ax25_rt->digipeat != NULL) {
187 kfree_s(ax25_rt->digipeat, sizeof(ax25_digi));
188 ax25_rt->digipeat = NULL;
189 }
190 if (route.digi_count != 0) {
191 if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
192 return -ENOMEM;
193 ax25_rt->digipeat->lastrepeat = 0;
194 ax25_rt->digipeat->ndigi = route.digi_count;
195 for (i = 0; i < route.digi_count; i++) {
196 ax25_rt->digipeat->repeated[i] = 0;
197 ax25_rt->digipeat->calls[i] = route.digi_addr[i];
198 }
199 }
200 ax25_rt->stamp.tv_sec = 0;
201 return 0;
202 }
203 }
204 if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL)
205 return -ENOMEM;
206 ax25_rt->callsign = route.dest_addr;
207 ax25_rt->dev = dev;
208 ax25_rt->digipeat = NULL;
209 ax25_rt->stamp.tv_sec = 0;
210 ax25_rt->n = 0;
211 ax25_rt->ip_mode = ' ';
212 if (route.digi_count != 0) {
213 if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
214 kfree_s(ax25_rt, sizeof(struct ax25_route));
215 return -ENOMEM;
216 }
217 ax25_rt->digipeat->lastrepeat = 0;
218 ax25_rt->digipeat->ndigi = route.digi_count;
219 for (i = 0; i < route.digi_count; i++) {
220 ax25_rt->digipeat->repeated[i] = 0;
221 ax25_rt->digipeat->calls[i] = route.digi_addr[i];
222 }
223 }
224 save_flags(flags);
225 cli();
226 ax25_rt->next = ax25_route;
227 ax25_route = ax25_rt;
228 restore_flags(flags);
229 break;
230
231 case SIOCDELRT:
232 if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
233 return err;
234 memcpy_fromfs(&route, arg, sizeof(route));
235 if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
236 return -EINVAL;
237 ax25_rt = ax25_route;
238 while (ax25_rt != NULL) {
239 s = ax25_rt;
240 ax25_rt = ax25_rt->next;
241 if (s->dev == dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
242 if (ax25_route == s) {
243 ax25_route = s->next;
244 if (s->digipeat != NULL)
245 kfree_s((void *)s->digipeat, sizeof(ax25_digi));
246 kfree_s((void *)s, (sizeof *s));
247 } else {
248 for (t = ax25_route; t != NULL; t = t->next) {
249 if (t->next == s) {
250 t->next = s->next;
251 if (s->digipeat != NULL)
252 kfree_s((void *)s->digipeat, sizeof(ax25_digi));
253 kfree_s((void *)s, sizeof(*s));
254 break;
255 }
256 }
257 }
258 }
259 }
260 break;
261 }
262
263 return 0;
264 }
265
266 int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
267 {
268 struct ax25_route *ax25_rt;
269 int len = 0;
270 off_t pos = 0;
271 off_t begin = 0;
272 int i;
273
274 cli();
275
276 len += sprintf(buffer, "callsign dev count time mode digipeaters\n");
277
278 for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
279 len += sprintf(buffer + len, "%-9s %-4s %5d %9d",
280 ax2asc(&ax25_rt->callsign),
281 ax25_rt->dev ? ax25_rt->dev->name : "???",
282 ax25_rt->n,
283 ax25_rt->stamp.tv_sec);
284
285 switch (ax25_rt->ip_mode) {
286 case 'V':
287 case 'v':
288 len += sprintf(buffer + len, " vc");
289 break;
290 case 'D':
291 case 'd':
292 len += sprintf(buffer + len, " dg");
293 break;
294 default:
295 len += sprintf(buffer + len, " *");
296 break;
297 }
298
299 if (ax25_rt->digipeat != NULL)
300 for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
301 len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
302
303 len += sprintf(buffer + len, "\n");
304
305 pos = begin + len;
306
307 if (pos < offset) {
308 len = 0;
309 begin = pos;
310 }
311
312 if (pos > offset + length)
313 break;
314 }
315
316 sti();
317
318 *start = buffer + (offset - begin);
319 len -= (offset - begin);
320
321 if (len > length) len = length;
322
323 return len;
324 }
325
326 int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
327 {
328 ax25_uid_assoc *pt;
329 int len = 0;
330 off_t pos = 0;
331 off_t begin = 0;
332
333 cli();
334
335 len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
336
337 for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
338 len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
339
340 pos = begin + len;
341
342 if (pos < offset) {
343 len = 0;
344 begin = pos;
345 }
346
347 if (pos > offset + length)
348 break;
349 }
350
351 sti();
352
353 *start = buffer + (offset - begin);
354 len -= offset - begin;
355
356 if (len > length) len = length;
357
358 return len;
359 }
360
361
362
363
364 int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
365 {
366 struct ax25_route *ax25_rt;
367 ax25_address *call;
368
369 for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
370 if (ax25cmp(&ax25_rt->callsign, addr) == 0) {
371
372
373
374 if ((ax25->device = ax25_rt->dev) == NULL)
375 continue;
376 if ((call = ax25_findbyuid(current->euid)) == NULL) {
377 if (ax25_uid_policy && !suser())
378 return -EPERM;
379 call = (ax25_address *)ax25->device->dev_addr;
380 }
381 memcpy(&ax25->source_addr, call, sizeof(ax25_address));
382 if (ax25_rt->digipeat != NULL) {
383 if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
384 return -ENOMEM;
385 memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
386 }
387 if (ax25->sk != NULL)
388 ax25->sk->zapped = 0;
389
390 return 0;
391 }
392 }
393
394 return -EINVAL;
395 }
396
397
398
399
400
401 void ax25_ip_mode_set(ax25_address *callsign, struct device *dev, char ip_mode)
402 {
403 struct ax25_route *ax25_rt;
404
405 for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
406 if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev) {
407 ax25_rt->ip_mode = ip_mode;
408 return;
409 }
410 }
411 }
412
413
414
415
416 char ax25_ip_mode_get(ax25_address *callsign, struct device *dev)
417 {
418 struct ax25_route *ax25_rt;
419
420 for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next)
421 if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev)
422 return ax25_rt->ip_mode;
423
424 return ' ';
425 }
426
427 static struct ax25_dev *ax25_dev_get_dev(struct device *dev)
428 {
429 struct ax25_dev *s;
430
431 for (s = ax25_device; s != NULL; s = s->next)
432 if (s->dev == dev)
433 return s;
434
435 return NULL;
436 }
437
438
439
440
441 unsigned short ax25_dev_get_value(struct device *dev, int valueno)
442 {
443 struct ax25_dev *ax25_dev;
444
445 if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) {
446 printk("ax25_dev_get_flag called with invalid device\n");
447 return 1;
448 }
449
450 return ax25_dev->values[valueno];
451 }
452
453
454
455
456
457 void ax25_dev_device_up(struct device *dev)
458 {
459 unsigned long flags;
460 struct ax25_dev *ax25_dev;
461
462 if ((ax25_dev = (struct ax25_dev *)kmalloc(sizeof(struct ax25_dev), GFP_ATOMIC)) == NULL)
463 return;
464
465 ax25_dev->dev = dev;
466
467 ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
468 ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
469 ax25_dev->values[AX25_VALUES_NETROM] = AX25_DEF_NETROM;
470 ax25_dev->values[AX25_VALUES_TEXT] = AX25_DEF_TEXT;
471 ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF;
472 ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE;
473 ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW;
474 ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW;
475 ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1 * PR_SLOWHZ;
476 ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2 * PR_SLOWHZ;
477 ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3 * PR_SLOWHZ;
478 ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2;
479 ax25_dev->values[AX25_VALUES_DIGI] = AX25_DEF_DIGI;
480
481 save_flags(flags);
482 cli();
483
484 ax25_dev->next = ax25_device;
485 ax25_device = ax25_dev;
486
487 restore_flags(flags);
488 }
489
490 void ax25_dev_device_down(struct device *dev)
491 {
492 struct ax25_dev *s, *t, *ax25_dev = ax25_device;
493
494 while (ax25_dev != NULL) {
495 s = ax25_dev;
496 ax25_dev = ax25_dev->next;
497
498 if (s->dev == dev) {
499 if (ax25_device == s) {
500 ax25_device = s->next;
501 kfree_s((void *)s, (sizeof *s));
502 } else {
503 for (t = ax25_device; t != NULL; t = t->next) {
504 if (t->next == s) {
505 t->next = s->next;
506 kfree_s((void *)s, sizeof(*s));
507 break;
508 }
509 }
510 }
511 }
512 }
513 }
514
515 int ax25_dev_ioctl(unsigned int cmd, void *arg)
516 {
517 struct ax25_parms_struct ax25_parms;
518 struct device *dev;
519 struct ax25_dev *ax25_dev;
520 int err;
521
522 switch (cmd) {
523 case SIOCAX25SETPARMS:
524 if (!suser())
525 return -EPERM;
526 if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_parms))) != 0)
527 return err;
528 memcpy_fromfs(&ax25_parms, arg, sizeof(ax25_parms));
529 if ((dev = ax25rtr_get_dev(&ax25_parms.port_addr)) == NULL)
530 return -EINVAL;
531 if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
532 return -EINVAL;
533 if (ax25_parms.values[AX25_VALUES_IPDEFMODE] != 'D' &&
534 ax25_parms.values[AX25_VALUES_IPDEFMODE] != 'V')
535 return -EINVAL;
536 if (ax25_parms.values[AX25_VALUES_AXDEFMODE] != MODULUS &&
537 ax25_parms.values[AX25_VALUES_AXDEFMODE] != EMODULUS)
538 return -EINVAL;
539 if (ax25_parms.values[AX25_VALUES_NETROM] != 0 &&
540 ax25_parms.values[AX25_VALUES_NETROM] != 1)
541 return -EINVAL;
542 if (ax25_parms.values[AX25_VALUES_TEXT] != 0 &&
543 ax25_parms.values[AX25_VALUES_TEXT] != 1)
544 return -EINVAL;
545 if (ax25_parms.values[AX25_VALUES_BACKOFF] != 'E' &&
546 ax25_parms.values[AX25_VALUES_BACKOFF] != 'L')
547 return -EINVAL;
548 if (ax25_parms.values[AX25_VALUES_CONMODE] != 0 &&
549 ax25_parms.values[AX25_VALUES_CONMODE] != 1)
550 return -EINVAL;
551 if (ax25_parms.values[AX25_VALUES_WINDOW] < 1 ||
552 ax25_parms.values[AX25_VALUES_WINDOW] > 7)
553 return -EINVAL;
554 if (ax25_parms.values[AX25_VALUES_EWINDOW] < 1 ||
555 ax25_parms.values[AX25_VALUES_EWINDOW] > 63)
556 return -EINVAL;
557 if (ax25_parms.values[AX25_VALUES_T1] < 1)
558 return -EINVAL;
559 if (ax25_parms.values[AX25_VALUES_T2] < 1)
560 return -EINVAL;
561 if (ax25_parms.values[AX25_VALUES_T3] < 1)
562 return -EINVAL;
563 if (ax25_parms.values[AX25_VALUES_N2] < 1 ||
564 ax25_parms.values[AX25_VALUES_N2] > 31)
565 return -EINVAL;
566 if ((ax25_parms.values[AX25_VALUES_DIGI] &
567 ~(AX25_DIGI_INBAND | AX25_DIGI_XBAND)) != 0)
568 return -EINVAL;
569 memcpy(ax25_dev->values, ax25_parms.values, AX25_MAX_VALUES * sizeof(short));
570 ax25_dev->values[AX25_VALUES_T1] *= PR_SLOWHZ;
571 ax25_dev->values[AX25_VALUES_T1] /= 2;
572 ax25_dev->values[AX25_VALUES_T2] *= PR_SLOWHZ;
573 ax25_dev->values[AX25_VALUES_T3] *= PR_SLOWHZ;
574 break;
575
576 case SIOCAX25GETPARMS:
577 if ((err = verify_area(VERIFY_WRITE, arg, sizeof(struct ax25_parms_struct))) != 0)
578 return err;
579 memcpy_fromfs(&ax25_parms, arg, sizeof(ax25_parms));
580 if ((dev = ax25rtr_get_dev(&ax25_parms.port_addr)) == NULL)
581 return -EINVAL;
582 if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
583 return -EINVAL;
584 memcpy(ax25_parms.values, ax25_dev->values, AX25_MAX_VALUES * sizeof(short));
585 ax25_parms.values[AX25_VALUES_T1] *= 2;
586 ax25_parms.values[AX25_VALUES_T1] /= PR_SLOWHZ;
587 ax25_parms.values[AX25_VALUES_T2] /= PR_SLOWHZ;
588 ax25_parms.values[AX25_VALUES_T3] /= PR_SLOWHZ;
589 memcpy_tofs(arg, &ax25_parms, sizeof(ax25_parms));
590 break;
591 }
592
593 return 0;
594 }
595
596 #ifdef CONFIG_BPQETHER
597 static struct ax25_bpqdev {
598 struct ax25_bpqdev *next;
599 struct device *dev;
600 ax25_address callsign;
601 } *ax25_bpqdev = NULL;
602
603 int ax25_bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
604 {
605 struct ax25_bpqdev *bpqdev;
606 int len = 0;
607 off_t pos = 0;
608 off_t begin = 0;
609
610 cli();
611
612 len += sprintf(buffer, "dev callsign\n");
613
614 for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
615 len += sprintf(buffer + len, "%-4s %-9s\n",
616 bpqdev->dev ? bpqdev->dev->name : "???",
617 ax2asc(&bpqdev->callsign));
618
619 pos = begin + len;
620
621 if (pos < offset) {
622 len = 0;
623 begin = pos;
624 }
625
626 if (pos > offset + length)
627 break;
628 }
629
630 sti();
631
632 *start = buffer + (offset - begin);
633 len -= (offset - begin);
634
635 if (len > length) len = length;
636
637 return len;
638 }
639
640 ax25_address *ax25_bpq_get_addr(struct device *dev)
641 {
642 struct ax25_bpqdev *bpqdev;
643
644 for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next)
645 if (bpqdev->dev == dev)
646 return &bpqdev->callsign;
647
648 return NULL;
649 }
650
651 int ax25_bpq_ioctl(unsigned int cmd, void *arg)
652 {
653 unsigned long flags;
654 struct ax25_bpqdev *bpqdev;
655 struct ax25_bpqaddr_struct bpqaddr;
656 struct device *dev;
657 int err;
658
659 switch (cmd) {
660 case SIOCAX25BPQADDR:
661 if ((err = verify_area(VERIFY_READ, arg, sizeof(bpqaddr))) != 0)
662 return err;
663 memcpy_fromfs(&bpqaddr, arg, sizeof(bpqaddr));
664 if ((dev = dev_get(bpqaddr.dev)) == NULL)
665 return -EINVAL;
666 for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
667 if (bpqdev->dev == dev) {
668 bpqdev->callsign = bpqaddr.addr;
669 return 0;
670 }
671 }
672 if ((bpqdev = (struct ax25_bpqdev *)kmalloc(sizeof(struct ax25_bpqdev), GFP_ATOMIC)) == NULL)
673 return -ENOMEM;
674 bpqdev->dev = dev;
675 bpqdev->callsign = bpqaddr.addr;
676 save_flags(flags);
677 cli();
678 bpqdev->next = ax25_bpqdev;
679 ax25_bpqdev = bpqdev;
680 restore_flags(flags);
681 break;
682 }
683
684 return 0;
685 }
686
687 #endif
688
689 #endif