1 /*
2 * National Semiconductor DP8390 and clone
3 * Network Interface Controller.
4 */
5 #include "u.h"
6 #include "lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11
12 #include "etherif.h"
13 #include "ether8390.h"
14
15 enum { /* NIC core registers */
16 Cr = 0x00, /* command register, all pages */
17
18 /* Page 0, read */
19 Clda0 = 0x01, /* current local DMA address 0 */
20 Clda1 = 0x02, /* current local DMA address 1 */
21 Bnry = 0x03, /* boundary pointer (R/W) */
22 Tsr = 0x04, /* transmit status register */
23 Ncr = 0x05, /* number of collisions register */
24 Fifo = 0x06, /* FIFO */
25 Isr = 0x07, /* interrupt status register (R/W) */
26 Crda0 = 0x08, /* current remote DMA address 0 */
27 Crda1 = 0x09, /* current remote DMA address 1 */
28 Rsr = 0x0C, /* receive status register */
29 Cntr0 = 0x0D, /* frame alignment errors */
30 Cntr1 = 0x0E, /* CRC errors */
31 Cntr2 = 0x0F, /* missed packet errors */
32
33 /* Page 0, write */
34 Pstart = 0x01, /* page start register */
35 Pstop = 0x02, /* page stop register */
36 Tpsr = 0x04, /* transmit page start address */
37 Tbcr0 = 0x05, /* transmit byte count register 0 */
38 Tbcr1 = 0x06, /* transmit byte count register 1 */
39 Rsar0 = 0x08, /* remote start address register 0 */
40 Rsar1 = 0x09, /* remote start address register 1 */
41 Rbcr0 = 0x0A, /* remote byte count register 0 */
42 Rbcr1 = 0x0B, /* remote byte count register 1 */
43 Rcr = 0x0C, /* receive configuration register */
44 Tcr = 0x0D, /* transmit configuration register */
45 Dcr = 0x0E, /* data configuration register */
46 Imr = 0x0F, /* interrupt mask */
47
48 /* Page 1, read/write */
49 Par0 = 0x01, /* physical address register 0 */
50 Curr = 0x07, /* current page register */
51 Mar0 = 0x08, /* multicast address register 0 */
52 };
53
54 enum { /* Cr */
55 Stp = 0x01, /* stop */
56 Sta = 0x02, /* start */
57 Txp = 0x04, /* transmit packet */
58 Rd0 = 0x08, /* remote DMA command */
59 Rd1 = 0x10,
60 Rd2 = 0x20,
61 RdREAD = Rd0, /* remote read */
62 RdWRITE = Rd1, /* remote write */
63 RdSEND = Rd1|Rd0, /* send packet */
64 RdABORT = Rd2, /* abort/complete remote DMA */
65 Ps0 = 0x40, /* page select */
66 Ps1 = 0x80,
67 Page0 = 0x00,
68 Page1 = Ps0,
69 Page2 = Ps1,
70 };
71
72 enum { /* Isr/Imr */
73 Prx = 0x01, /* packet received */
74 Ptx = 0x02, /* packet transmitted */
75 Rxe = 0x04, /* receive error */
76 Txe = 0x08, /* transmit error */
77 Ovw = 0x10, /* overwrite warning */
78 Cnt = 0x20, /* counter overflow */
79 Rdc = 0x40, /* remote DMA complete */
80 Rst = 0x80, /* reset status */
81 };
82
83 enum { /* Dcr */
84 Wts = 0x01, /* word transfer select */
85 Bos = 0x02, /* byte order select */
86 Las = 0x04, /* long address select */
87 Ls = 0x08, /* loopback select */
88 Arm = 0x10, /* auto-initialise remote */
89 Ft0 = 0x20, /* FIFO threshold select */
90 Ft1 = 0x40,
91 Ft1WORD = 0x00,
92 Ft2WORD = Ft0,
93 Ft4WORD = Ft1,
94 Ft6WORD = Ft1|Ft0,
95 };
96
97 enum { /* Tcr */
98 Crc = 0x01, /* inhibit CRC */
99 Lb0 = 0x02, /* encoded loopback control */
100 Lb1 = 0x04,
101 LpbkNORMAL = 0x00, /* normal operation */
102 LpbkNIC = Lb0, /* internal NIC module loopback */
103 LpbkENDEC = Lb1, /* internal ENDEC module loopback */
104 LpbkEXTERNAL = Lb1|Lb0, /* external loopback */
105 Atd = 0x08, /* auto transmit disable */
106 Ofst = 0x10, /* collision offset enable */
107 };
108
109 enum { /* Tsr */
110 Ptxok = 0x01, /* packet transmitted */
111 Col = 0x04, /* transmit collided */
112 Abt = 0x08, /* tranmit aborted */
113 Crs = 0x10, /* carrier sense lost */
114 Fu = 0x20, /* FIFO underrun */
115 Cdh = 0x40, /* CD heartbeat */
116 Owc = 0x80, /* out of window collision */
117 };
118
119 enum { /* Rcr */
120 Sep = 0x01, /* save errored packets */
121 Ar = 0x02, /* accept runt packets */
122 Ab = 0x04, /* accept broadcast */
123 Am = 0x08, /* accept multicast */
124 Pro = 0x10, /* promiscuous physical */
125 Mon = 0x20, /* monitor mode */
126 };
127
128 enum { /* Rsr */
129 Prxok = 0x01, /* packet received intact */
130 Crce = 0x02, /* CRC error */
131 Fae = 0x04, /* frame alignment error */
132 Fo = 0x08, /* FIFO overrun */
133 Mpa = 0x10, /* missed packet */
134 Phy = 0x20, /* physical/multicast address */
135 Dis = 0x40, /* receiver disabled */
136 Dfr = 0x80, /* deferring */
137 };
138
139 typedef struct {
140 uchar status;
141 uchar next;
142 uchar len0;
143 uchar len1;
144 } Hdr;
145
146 void
dp8390getea(Ether * ether,uchar * ea)147 dp8390getea(Ether* ether, uchar* ea)
148 {
149 Dp8390 *ctlr;
150 uchar cr;
151 int i;
152
153 ctlr = ether->ctlr;
154
155 /*
156 * Get the ethernet address from the chip.
157 * Take care to restore the command register
158 * afterwards.
159 */
160 ilock(ctlr);
161 cr = regr(ctlr, Cr) & ~Txp;
162 regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
163 for(i = 0; i < Eaddrlen; i++)
164 ea[i] = regr(ctlr, Par0+i);
165 regw(ctlr, Cr, cr);
166 iunlock(ctlr);
167 }
168
169 void
dp8390setea(Ether * ether)170 dp8390setea(Ether* ether)
171 {
172 int i;
173 uchar cr;
174 Dp8390 *ctlr;
175
176 ctlr = ether->ctlr;
177
178 /*
179 * Set the ethernet address into the chip.
180 * Take care to restore the command register
181 * afterwards. Don't care about multicast
182 * addresses as multicast is never enabled
183 * (currently).
184 */
185 ilock(ctlr);
186 cr = regr(ctlr, Cr) & ~Txp;
187 regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
188 for(i = 0; i < Eaddrlen; i++)
189 regw(ctlr, Par0+i, ether->ea[i]);
190 regw(ctlr, Cr, cr);
191 iunlock(ctlr);
192 }
193
194 static void*
_dp8390read(Dp8390 * ctlr,void * to,ulong from,ulong len)195 _dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
196 {
197 uchar cr;
198 int timo;
199
200 /*
201 * Read some data at offset 'from' in the card's memory
202 * using the DP8390 remote DMA facility, and place it at
203 * 'to' in main memory, via the I/O data port.
204 */
205 cr = regr(ctlr, Cr) & ~Txp;
206 regw(ctlr, Cr, Page0|RdABORT|Sta);
207 regw(ctlr, Isr, Rdc);
208
209 /*
210 * Set up the remote DMA address and count.
211 */
212 len = ROUNDUP(len, ctlr->width);
213 regw(ctlr, Rbcr0, len & 0xFF);
214 regw(ctlr, Rbcr1, (len>>8) & 0xFF);
215 regw(ctlr, Rsar0, from & 0xFF);
216 regw(ctlr, Rsar1, (from>>8) & 0xFF);
217
218 /*
219 * Start the remote DMA read and suck the data
220 * out of the I/O port.
221 */
222 regw(ctlr, Cr, Page0|RdREAD|Sta);
223 rdread(ctlr, to, len);
224
225 /*
226 * Wait for the remote DMA to complete. The timeout
227 * is necessary because this routine may be called on
228 * a non-existent chip during initialisation and, due
229 * to the miracles of the bus, it's possible to get this
230 * far and still be talking to a slot full of nothing.
231 */
232 for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
233 ;
234
235 regw(ctlr, Isr, Rdc);
236 regw(ctlr, Cr, cr);
237
238 return to;
239 }
240
241 void*
dp8390read(Dp8390 * ctlr,void * to,ulong from,ulong len)242 dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
243 {
244 void *v;
245
246 ilock(ctlr);
247 v = _dp8390read(ctlr, to, from, len);
248 iunlock(ctlr);
249
250 return v;
251 }
252
253 static void*
dp8390write(Dp8390 * ctlr,ulong to,void * from,ulong len)254 dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len)
255 {
256 ulong crda;
257 uchar cr;
258 int timo, width;
259
260 top:
261 /*
262 * Write some data to offset 'to' in the card's memory
263 * using the DP8390 remote DMA facility, reading it at
264 * 'from' in main memory, via the I/O data port.
265 */
266 cr = regr(ctlr, Cr) & ~Txp;
267 regw(ctlr, Cr, Page0|RdABORT|Sta);
268 regw(ctlr, Isr, Rdc);
269
270 len = ROUNDUP(len, ctlr->width);
271
272 /*
273 * Set up the remote DMA address and count.
274 * This is straight from the DP8390[12D] datasheet,
275 * hence the initial set up for read.
276 * Assumption here that the A7000 EtherV card will
277 * never need a dummyrr.
278 */
279 if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){
280 if(ctlr->width == 2)
281 width = 1;
282 else
283 width = 0;
284 crda = to-1-width;
285 regw(ctlr, Rbcr0, (len+1+width) & 0xFF);
286 regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF);
287 regw(ctlr, Rsar0, crda & 0xFF);
288 regw(ctlr, Rsar1, (crda>>8) & 0xFF);
289 regw(ctlr, Cr, Page0|RdREAD|Sta);
290
291 for(timo=0;; timo++){
292 if(timo > 10000){
293 print("ether8390: dummyrr timeout; assuming nodummyrr\n");
294 ctlr->dummyrr = 0;
295 goto top;
296 }
297 crda = regr(ctlr, Crda0);
298 crda |= regr(ctlr, Crda1)<<8;
299 if(crda == to){
300 /*
301 * Start the remote DMA write and make sure
302 * the registers are correct.
303 */
304 regw(ctlr, Cr, Page0|RdWRITE|Sta);
305
306 crda = regr(ctlr, Crda0);
307 crda |= regr(ctlr, Crda1)<<8;
308 if(crda != to)
309 panic("crda write %d to %d\n", crda, to);
310
311 break;
312 }
313 }
314 }
315 else{
316 regw(ctlr, Rsar0, to & 0xFF);
317 regw(ctlr, Rsar1, (to>>8) & 0xFF);
318 regw(ctlr, Rbcr0, len & 0xFF);
319 regw(ctlr, Rbcr1, (len>>8) & 0xFF);
320 regw(ctlr, Cr, Page0|RdWRITE|Sta);
321 }
322
323 /*
324 * Pump the data into the I/O port
325 * then wait for the remote DMA to finish.
326 */
327 rdwrite(ctlr, from, len);
328 for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
329 ;
330
331 regw(ctlr, Isr, Rdc);
332 regw(ctlr, Cr, cr);
333
334 return (void*)to;
335 }
336
337 static void
ringinit(Dp8390 * ctlr)338 ringinit(Dp8390* ctlr)
339 {
340 regw(ctlr, Pstart, ctlr->pstart);
341 regw(ctlr, Pstop, ctlr->pstop);
342 regw(ctlr, Bnry, ctlr->pstop-1);
343
344 regw(ctlr, Cr, Page1|RdABORT|Stp);
345 regw(ctlr, Curr, ctlr->pstart);
346 regw(ctlr, Cr, Page0|RdABORT|Stp);
347
348 ctlr->nxtpkt = ctlr->pstart;
349 }
350
351 static uchar
getcurr(Dp8390 * ctlr)352 getcurr(Dp8390* ctlr)
353 {
354 uchar cr, curr;
355
356 cr = regr(ctlr, Cr) & ~Txp;
357 regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
358 curr = regr(ctlr, Curr);
359 regw(ctlr, Cr, cr);
360
361 return curr;
362 }
363
364 static void
receive(Ether * ether)365 receive(Ether* ether)
366 {
367 Dp8390 *ctlr;
368 uchar curr, *p;
369 Hdr hdr;
370 ulong count, data, len;
371 RingBuf *ring;
372
373 ctlr = ether->ctlr;
374 for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){
375 data = ctlr->nxtpkt*Dp8390BufSz;
376 if(ctlr->ram)
377 memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr));
378 else
379 _dp8390read(ctlr, &hdr, data, sizeof(Hdr));
380
381 /*
382 * Don't believe the upper byte count, work it
383 * out from the software next-page pointer and
384 * the current next-page pointer.
385 */
386 if(hdr.next > ctlr->nxtpkt)
387 len = hdr.next - ctlr->nxtpkt - 1;
388 else
389 len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1;
390 if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
391 len--;
392
393 len = ((len<<8)|hdr.len0)-4;
394
395 /*
396 * Chip is badly scrogged, reinitialise the ring.
397 */
398 if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop
399 || len < 60 || len > sizeof(Etherpkt)){
400 print("dp8390: H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%lud\n",
401 hdr.status, hdr.next, hdr.len0, hdr.len1, len);
402 regw(ctlr, Cr, Page0|RdABORT|Stp);
403 ringinit(ctlr);
404 regw(ctlr, Cr, Page0|RdABORT|Sta);
405
406 return;
407 }
408
409 /*
410 * If it's a good packet read it in to the software buffer.
411 * If the packet wraps round the hardware ring, read it in
412 * two pieces.
413 */
414 ring = ðer->rb[ether->ri];
415 if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && ring->owner == Interface){
416 p = ring->pkt;
417 ring->len = len;
418 data += sizeof(Hdr);
419
420 if((data+len) >= ctlr->pstop*Dp8390BufSz){
421 count = ctlr->pstop*Dp8390BufSz - data;
422 if(ctlr->ram)
423 memmove(p, (void*)(ether->mem+data), count);
424 else
425 _dp8390read(ctlr, p, data, count);
426 p += count;
427 data = ctlr->pstart*Dp8390BufSz;
428 len -= count;
429 }
430 if(len){
431 if(ctlr->ram)
432 memmove(p, (void*)(ether->mem+data), len);
433 else
434 _dp8390read(ctlr, p, data, len);
435 }
436
437 /*
438 * Copy the packet to whoever wants it.
439 */
440 ring->owner = Host;
441 ether->ri = NEXT(ether->ri, ether->nrb);
442 }
443
444 /*
445 * Finished with this packet, update the
446 * hardware and software ring pointers.
447 */
448 ctlr->nxtpkt = hdr.next;
449
450 hdr.next--;
451 if(hdr.next < ctlr->pstart)
452 hdr.next = ctlr->pstop-1;
453 regw(ctlr, Bnry, hdr.next);
454 }
455 }
456
457 static void
txstart(Ether * ether)458 txstart(Ether* ether)
459 {
460 int len;
461 Dp8390 *ctlr;
462 RingBuf *ring;
463 uchar minpkt[ETHERMINTU], *rp;
464
465 ctlr = ether->ctlr;
466
467 /*
468 * This routine is called both from the top level and from interrupt
469 * level and expects to be called with ctlr already locked.
470 */
471 if(ether->tbusy)
472 return;
473 ring = ðer->tb[ether->ti];
474 if(ring->owner != Interface)
475 return;
476
477 /*
478 * Make sure the packet is of minimum length;
479 * copy it to the card's memory by the appropriate means;
480 * start the transmission.
481 */
482 len = ring->len;
483 rp = ring->pkt;
484 if(len < ETHERMINTU){
485 rp = minpkt;
486 memmove(rp, ring->pkt, len);
487 memset(rp+len, 0, ETHERMINTU-len);
488 len = ETHERMINTU;
489 }
490
491 if(ctlr->ram)
492 memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len);
493 else
494 dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len);
495
496 regw(ctlr, Tbcr0, len & 0xFF);
497 regw(ctlr, Tbcr1, (len>>8) & 0xFF);
498 regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
499
500 ether->tbusy = 1;
501 }
502
503 static void
transmit(Ether * ether)504 transmit(Ether* ether)
505 {
506 Dp8390 *ctlr;
507
508 ctlr = ether->ctlr;
509
510 ilock(ctlr);
511 txstart(ether);
512 iunlock(ctlr);
513 }
514
515 static void
overflow(Ether * ether)516 overflow(Ether *ether)
517 {
518 Dp8390 *ctlr;
519 uchar txp;
520 int resend;
521
522 ctlr = ether->ctlr;
523
524 /*
525 * The following procedure is taken from the DP8390[12D] datasheet,
526 * it seems pretty adamant that this is what has to be done.
527 */
528 txp = regr(ctlr, Cr) & Txp;
529 regw(ctlr, Cr, Page0|RdABORT|Stp);
530 delay(2);
531 regw(ctlr, Rbcr0, 0);
532 regw(ctlr, Rbcr1, 0);
533
534 resend = 0;
535 if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0)
536 resend = 1;
537
538 regw(ctlr, Tcr, LpbkNIC);
539 regw(ctlr, Cr, Page0|RdABORT|Sta);
540 receive(ether);
541 regw(ctlr, Isr, Ovw);
542 regw(ctlr, Tcr, LpbkNORMAL);
543
544 if(resend)
545 regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
546 }
547
548 static void
interrupt(Ureg *,void * arg)549 interrupt(Ureg*, void* arg)
550 {
551 Ether *ether;
552 Dp8390 *ctlr;
553 RingBuf *ring;
554 uchar isr, r;
555
556 ether = arg;
557 ctlr = ether->ctlr;
558
559 /*
560 * While there is something of interest,
561 * clear all the interrupts and process.
562 */
563 ilock(ctlr);
564 regw(ctlr, Imr, 0x00);
565 while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){
566 if(isr & Ovw){
567 overflow(ether);
568 regw(ctlr, Isr, Ovw);
569 }
570
571 /*
572 * Packets have been received.
573 * Take a spin round the ring.
574 */
575 if(isr & (Rxe|Prx)){
576 receive(ether);
577 regw(ctlr, Isr, Rxe|Prx);
578 }
579
580 /*
581 * A packet completed transmission, successfully or
582 * not. Start transmission on the next buffered packet,
583 * and wake the output routine.
584 */
585 if(isr & (Txe|Ptx)){
586 r = regr(ctlr, Tsr);
587 if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){
588 print("dp8390: Tsr#%2.2ux|", r);
589 }
590
591 regw(ctlr, Isr, Txe|Ptx);
592
593 ring = ðer->tb[ether->ti];
594 ring->owner = Host;
595 ether->ti = NEXT(ether->ti, ether->ntb);
596 ether->tbusy = 0;
597 txstart(ether);
598 }
599
600 if(isr & Cnt){
601 regr(ctlr, Cntr0);
602 regr(ctlr, Cntr1);
603 regr(ctlr, Cntr2);
604 regw(ctlr, Isr, Cnt);
605 }
606 }
607 regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
608 iunlock(ctlr);
609 }
610
611 static void
attach(Ether * ether)612 attach(Ether* ether)
613 {
614 Dp8390 *ctlr;
615 uchar r;
616
617 ctlr = ether->ctlr;
618
619 /*
620 * Enable the chip for transmit/receive.
621 * The init routine leaves the chip in monitor
622 * mode. Clear the missed-packet counter, it
623 * increments while in monitor mode.
624 * Sometimes there's an interrupt pending at this
625 * point but there's nothing in the Isr, so
626 * any pending interrupts are cleared and the
627 * mask of acceptable interrupts is enabled here.
628 */
629 r = Ab;
630 ilock(ctlr);
631 regw(ctlr, Isr, 0xFF);
632 regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
633 regw(ctlr, Rcr, r);
634 r = regr(ctlr, Cntr2);
635 regw(ctlr, Tcr, LpbkNORMAL);
636 iunlock(ctlr);
637 USED(r);
638 }
639
640 static void
detach(Ether * ether)641 detach(Ether* ether)
642 {
643 int timo;
644 Dp8390 *ctlr;
645
646 /*
647 * Stop the chip. Set the Stp bit and wait for the chip
648 * to finish whatever was on its tiny mind before it sets
649 * the Rst bit.
650 * The timeout is needed because there may not be a real
651 * chip there if this is called when probing for a device
652 * at boot.
653 */
654 ctlr = ether->ctlr;
655 regw(ctlr, Cr, Page0|RdABORT|Stp);
656 regw(ctlr, Rbcr0, 0);
657 regw(ctlr, Rbcr1, 0);
658 for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--)
659 ;
660 }
661
662 int
dp8390reset(Ether * ether)663 dp8390reset(Ether* ether)
664 {
665 Dp8390 *ctlr;
666
667 ctlr = ether->ctlr;
668
669 /*
670 * This is the initialisation procedure described
671 * as 'mandatory' in the datasheet, with references
672 * to the 3C503 technical reference manual.
673 */
674 detach(ether);
675 if(ctlr->width != 1)
676 regw(ctlr, Dcr, Ft4WORD|Ls|Wts);
677 else
678 regw(ctlr, Dcr, Ft4WORD|Ls);
679
680 regw(ctlr, Rbcr0, 0);
681 regw(ctlr, Rbcr1, 0);
682
683 regw(ctlr, Tcr, LpbkNIC);
684 regw(ctlr, Rcr, Mon);
685
686 /*
687 * Init the ring hardware and software ring pointers.
688 * Can't initialise ethernet address as it may not be
689 * known yet.
690 */
691 ringinit(ctlr);
692 regw(ctlr, Tpsr, ctlr->tstart);
693
694 /*
695 * Clear any pending interrupts and mask then all off.
696 */
697 regw(ctlr, Isr, 0xFF);
698 regw(ctlr, Imr, 0);
699
700 /*
701 * Leave the chip initialised,
702 * but in monitor mode.
703 */
704 regw(ctlr, Cr, Page0|RdABORT|Sta);
705
706 /*
707 * Set up the software configuration.
708 */
709 ether->attach = attach;
710 ether->transmit = transmit;
711 ether->interrupt = interrupt;
712 ether->detach = detach;
713
714 return 0;
715 }
716