Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / i4b / isdntest / main.c
1 /*
2  * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  *---------------------------------------------------------------------------
26  *
27  *      main.c - i4b selftest utility
28  *      -----------------------------
29  *
30  *      $Id: main.c,v 1.16 2000/03/13 16:18:38 hm Exp $ 
31  *
32  * $FreeBSD: src/usr.sbin/i4b/isdntest/main.c,v 1.7.2.1 2001/08/01 17:45:07 obrien Exp $
33  *
34  *      last edit-date: [Mon Mar 13 17:19:26 2000]
35  *
36  *---------------------------------------------------------------------------*/
37
38 #include <stdio.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <ctype.h>
46 #include <sys/stat.h>
47 #include <sys/wait.h>
48 #include <sys/ioctl.h>
49 #include <sys/types.h>
50 #include <sys/time.h>
51
52 #include <machine/i4b_ioctl.h>
53 #include <machine/i4b_cause.h>
54
55 static void kbdrdhdl ( void );
56 static void isdnrdhdl (int isdnfd );
57
58 void handle_connect_ind(unsigned char *ptr);
59 void handle_disconnect(unsigned char *ptr);
60 void handle_connect_active_ind(unsigned char *ptr);
61
62 int connect_response(int isdnfd, unsigned int cdid, int response);
63 int disconnect_request(int isdnfd, unsigned int cdid);
64 unsigned int get_cdid(int isdnfd);
65 int connect_request(int isdnfd, unsigned int cdid);
66 int do_test(void);
67 static void cleanup(void);
68 static void usage(void);
69 void setup_wrfix(int len, unsigned char *buf);
70 int check_rd(int len, unsigned char *wbuf, unsigned char *rdbuf);
71
72 static int isdnfd;
73 char outgoingnumber[32];
74 char incomingnumber[32];
75 int debug_level = 0;
76
77 #define I4BDEVICE       "/dev/i4b"
78 #define DATADEV0        "/dev/i4brbch0"
79 #define DATAUNIT0       0
80 #define DATADEV1        "/dev/i4brbch1"
81 #define DATAUNIT1       1
82
83 unsigned int out_cdid = CDID_UNUSED;
84 unsigned int in_cdid = CDID_UNUSED;
85
86 int waitchar = 0;
87 int usehdlc = 0;
88 int controller = 0;
89 int dotest = 0;
90
91 /*---------------------------------------------------------------------------*
92  *      usage display and exit
93  *---------------------------------------------------------------------------*/
94 static void
95 usage(void)
96 {
97         fprintf(stderr, "\n");
98         fprintf(stderr, "isdntest - i4b selftest, version %d.%d.%d, compiled %s %s\n",VERSION, REL, STEP, __DATE__, __TIME__);
99         fprintf(stderr, "usage: isdntest [-c ctrl] [-d level] [-h] [-i telno] [-o telno] [-t num] [-w]\n");
100         fprintf(stderr, "       -c <ctrl>     specify controller to use\n");
101         fprintf(stderr, "       -d <level>    set debug level\n");      
102         fprintf(stderr, "       -h            use HDLC as Bchannel protocol\n");
103         fprintf(stderr, "       -i <telno>    incoming telephone number\n");
104         fprintf(stderr, "       -o <telno>    outgoing telephone number\n");
105         fprintf(stderr, "       -t <num>      send test pattern num times\n");
106         fprintf(stderr, "       -w            wait for keyboard entry to disconnect\n");
107         fprintf(stderr, "\n");
108         exit(1);
109 }
110
111 /*---------------------------------------------------------------------------*
112  *      program entry
113  *---------------------------------------------------------------------------*/
114 int
115 main(int argc, char **argv)
116 {
117         int i;
118         int c;
119         fd_set set;
120         int ret;
121         char *ptr;
122         
123         incomingnumber[0] = '\0';
124         outgoingnumber[0] = '\0';       
125         
126         while ((c = getopt(argc, argv, "c:d:hi:o:t:w")) != -1)
127         {
128                 switch(c)
129                 {
130                         case 'c':
131                                 if(isdigit(*optarg))
132                                 {
133                                         controller = strtoul(optarg, NULL, 10);
134                                 }
135                                 else
136                                 {
137                                         fprintf(stderr, "Error: option -c requires a numeric argument!\n");
138                                         usage();
139                                 }
140                                 break;
141
142                         case 'd':
143                                 if(isdigit(*optarg))
144                                 {
145                                         debug_level = strtoul(optarg, NULL, 10);
146                                 }
147                                 else
148                                 {
149                                         fprintf(stderr, "Error: option -d requires a numeric argument!\n");
150                                         usage();
151                                 }
152                                 break;
153
154                         case 'o':
155                                 i = 0;
156                                 ptr = optarg;
157
158                                 while(*ptr)
159                                 {
160                                         if(isdigit(*ptr))
161                                         {
162                                                 outgoingnumber[i++] = *ptr++;
163                                         }
164                                         else
165                                         {
166                                                 fprintf(stderr, "Error: option -o requires a numeric argument!\n");
167                                                 usage();
168                                         }
169                                 }                                       
170                                 outgoingnumber[i] = '\0';
171                                 break;
172
173                         case 'i':
174                                 i = 0;
175                                 ptr = optarg;
176
177                                 while(*ptr)
178                                 {
179                                         if(isdigit(*ptr))
180                                         {
181                                                 incomingnumber[i++] = *ptr++;
182                                         }
183                                         else
184                                         {
185                                                 fprintf(stderr, "Error: option -i requires a numeric argument!\n");
186                                                 usage();
187                                         }
188                                 }                                       
189                                 incomingnumber[i] = '\0';
190                                 break;
191
192                         case 'w':
193                                 waitchar = 1;
194                                 break;
195                                 
196                         case 'h':
197                                 usehdlc = 1;
198                                 break;
199                                 
200                         case 't':
201                                 if(isdigit(*optarg))
202                                 {
203                                         dotest = strtoul(optarg, NULL, 10);
204                                 }
205                                 else
206                                 {
207                                         fprintf(stderr, "Error: option -t requires a numeric argument!\n");
208                                         usage();
209                                 }
210                                 break;
211
212                         case '?':
213                         default:
214                                 usage();
215                                 break;
216                 }
217         }
218
219         if((strlen(incomingnumber) == 0) || (strlen(outgoingnumber) == 0))
220                 usage();
221
222         fprintf(stderr, "isdntest: accepting calls from telephone number [%s] \n", incomingnumber);
223         fprintf(stderr, "isdntest:          calling out telephone number [%s] \n", outgoingnumber);
224
225         if((atexit(cleanup)) != 0)
226         {
227                 fprintf(stderr, "isdntest: atexit error: %s\n", strerror(errno));
228                 exit(1);
229         }
230         
231         /* open isdn device */
232         
233         if((isdnfd = open(I4BDEVICE, O_RDWR)) < 0)
234         {
235                 fprintf(stderr, "\nisdntest: cannot open %s: %s\n", I4BDEVICE, strerror(errno));
236                 fprintf(stderr, "          isdnd is probably running, to use isdntest,\n");
237                 fprintf(stderr, "          terminate isdnd and then run isdntest again!\n");
238                 exit(1);
239         }
240
241         if((out_cdid = get_cdid(isdnfd)) == 0)
242         {
243                 fprintf(stderr, "isdntest: error getting cdid: %s\n", strerror(errno));
244                 exit(1);
245         }
246
247         if((connect_request(isdnfd, out_cdid)) == -1)
248         {
249                 fprintf(stderr, "isdntest: error, outgoing call failed!\n");
250                 exit(1);
251         }
252         
253         for(;;)
254         {
255                 FD_ZERO(&set);
256
257                 FD_SET(0, &set);
258
259                 FD_SET(isdnfd, &set);
260
261                 ret = select(isdnfd + 1, &set, NULL, NULL, NULL);
262
263                 if(ret > 0)
264                 {
265                         if(FD_ISSET(isdnfd, &set))
266                                 isdnrdhdl(isdnfd);
267
268                         if(FD_ISSET(0, &set))
269                                 kbdrdhdl();
270                 }
271                 else
272                 {
273                         fprintf(stderr, "isdntest: select error: %s\n", strerror(errno));
274                 }                       
275         }
276 }
277
278 /*---------------------------------------------------------------------------*
279  *      data from keyboard available
280  *---------------------------------------------------------------------------*/
281 static void
282 kbdrdhdl(void)
283 {
284         cleanup();
285         exit(2);
286 }
287
288 /*---------------------------------------------------------------------------*
289  *      data from /dev/isdn available, read and process them
290  *---------------------------------------------------------------------------*/
291 static void
292 isdnrdhdl(int isdnfd)
293 {
294         static unsigned char buf[1024];
295         int len;
296
297         if((len = read(isdnfd, buf, 1024 - 1)) > 0)
298         {
299                 switch (buf[0])
300                 {
301                         case MSG_CONNECT_IND:
302                                 handle_connect_ind(&buf[0]); 
303                                 break;
304                                 
305                         case MSG_CONNECT_ACTIVE_IND:
306                                 handle_connect_active_ind(&buf[0]); 
307                                 break;
308                                 
309                         case MSG_DISCONNECT_IND:
310                                 handle_disconnect(&buf[0]); 
311                                 break;
312                                 
313                         default:
314                                 if(debug_level)
315                                         fprintf(stderr, "isdntest: unknown message 0x%x = %c\n", buf[0], buf[0]);
316                                 break;
317                 }
318         }
319         else
320         {
321                 fprintf(stderr, "isdntest: read error, errno = %d, length = %d", errno, len);
322         }
323 }
324
325 /*---------------------------------------------------------------------------*
326  *      initiate an outgoing connection
327  *---------------------------------------------------------------------------*/
328 int
329 connect_request(int isdnfd, unsigned int cdid)
330 {
331         msg_connect_req_t mcr;
332         int ret;
333
334         bzero(&mcr, sizeof(msg_connect_req_t));
335         
336         mcr.controller = controller;
337         mcr.channel = CHAN_ANY; /* any channel */
338         mcr.cdid = cdid;        /* cdid from get_cdid() */      
339
340         if(usehdlc)
341                 mcr.bprot = BPROT_RHDLC;/* b channel protocol */
342         else
343                 mcr.bprot = BPROT_NONE; /* b channel protocol */        
344
345         mcr.driver = BDRV_RBCH;         /* raw b channel driver */
346         mcr.driver_unit = DATAUNIT0;    /* raw b channel driver unit */ 
347
348         strcpy(mcr.dst_telno, outgoingnumber);
349         strcpy(mcr.src_telno, incomingnumber);
350         
351         if((ret = ioctl(isdnfd, I4B_CONNECT_REQ, &mcr)) < 0)
352         {
353                 fprintf(stderr, "ioctl I4B_CONNECT_REQ failed: %s", strerror(errno));
354                 return(-1);
355         }
356         fprintf(stderr, "isdntest: calling out to telephone number [%s] \n", outgoingnumber);
357         return(0);
358 }
359
360 /*---------------------------------------------------------------------------*
361  *      handle setup indicator
362  *---------------------------------------------------------------------------*/
363 void
364 handle_connect_ind(unsigned char *ptr)
365 {
366         msg_connect_ind_t *msi = (msg_connect_ind_t *)ptr;
367
368         fprintf(stderr, "isdntest: incoming SETUP: from %s to %s\n",
369                                 msi->src_telno,
370                                 msi->dst_telno);
371
372         fprintf(stderr, "          channel %d, controller %d, bprot %d, cdid %d\n",
373                                 msi->channel,
374                                 msi->controller,
375                                 msi->bprot,
376                                 msi->header.cdid);
377
378         in_cdid = msi->header.cdid;
379         
380         if(strcmp(msi->dst_telno, outgoingnumber))
381         {
382                 msg_connect_resp_t msr;
383                 int ret;
384
385                 fprintf(stderr, "isdntest: ignoring incoming SETUP: my number [%s] != outgoing [%s]\n",
386                         msi->dst_telno, outgoingnumber);
387
388                 msr.cdid = in_cdid;
389                 msr.response = SETUP_RESP_DNTCRE;
390
391                 if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &msr)) < 0)
392                 {
393                         fprintf(stderr, "ioctl I4B_CONNECT_RESP ignore failed: %s", strerror(errno));
394                         exit(1);
395                 }
396
397         }
398         else
399         {
400                 msg_connect_resp_t msr;
401                 int ret;
402
403                 fprintf(stderr, "isdntest: accepting call, sending CONNECT_RESPONSE .....\n");
404
405                 msr.cdid = in_cdid;
406                 msr.response = SETUP_RESP_ACCEPT;
407
408                 if(usehdlc)
409                         msr.bprot = BPROT_RHDLC;
410                 else
411                         msr.bprot = BPROT_NONE;
412         
413                 msr.driver = BDRV_RBCH;
414                 msr.driver_unit = DATAUNIT1;
415                 
416                 if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &msr)) < 0)
417                 {
418                         fprintf(stderr, "ioctl I4B_CONNECT_RESP accept failed: %s", strerror(errno));
419                         exit(1);
420                 }
421         }
422 }
423
424 #define SLEEPTIME 5
425
426 /*---------------------------------------------------------------------------*
427  *      handle connection active
428  *---------------------------------------------------------------------------*/
429 void
430 handle_connect_active_ind(unsigned char *ptr)
431 {
432         msg_connect_active_ind_t *msi = (msg_connect_active_ind_t *)ptr;
433         int i;
434         
435         fprintf(stderr, "isdntest: connection active, cdid %d\n", msi->header.cdid);
436
437         if(out_cdid == msi->header.cdid)
438         {
439                 if(waitchar)
440                 {
441                         fprintf(stderr, "isdntest: press any key to disconnect ...%c%c%c\n", 0x07, 0x07, 0x07);
442                         getchar();
443                 }
444                 else
445                 {
446                         if(dotest)
447                         {
448                                 do_test();
449                         }
450                         else
451                         {
452                                 fprintf(stderr, "isdntest: %d secs delay until disconnect:", SLEEPTIME);
453         
454                                 for(i=0; i < SLEEPTIME;i++)
455                                 {
456                                         fprintf(stderr, " .");
457                                         sleep(1);
458                                 }
459                                 fprintf(stderr, "\n");
460                         }
461                         cleanup();
462                         exit(0);
463                 }
464         }
465 }
466
467 /*---------------------------------------------------------------------------*
468  *      handle disconnect indication
469  *---------------------------------------------------------------------------*/
470 void
471 handle_disconnect(unsigned char *ptr)
472 {
473         msg_disconnect_ind_t *mdi = (msg_disconnect_ind_t *)ptr;
474         
475         if(mdi->header.cdid == out_cdid)
476         {
477                 fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (out_cdid), cause %d\n",
478                         mdi->header.cdid, mdi->cause);
479
480                 out_cdid = CDID_UNUSED;
481         }
482         else if(mdi->header.cdid == in_cdid)
483         {
484                 fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (in_cdid), cause %d\n",
485                         mdi->header.cdid, mdi->cause);
486                 in_cdid = CDID_UNUSED;
487         }
488         else
489         {
490                 fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (???), cause %d\n",
491                         mdi->header.cdid, mdi->cause);
492         }               
493 }
494         
495 /*---------------------------------------------------------------------------*
496  *      hang up
497  *---------------------------------------------------------------------------*/
498 int
499 disconnect_request(int isdnfd, unsigned int cdid)
500 {
501         msg_discon_req_t mdr;
502         int ret;
503
504         mdr.cdid = cdid;
505         mdr.cause = (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL;
506         
507         if((ret = ioctl(isdnfd, I4B_DISCONNECT_REQ, &mdr)) < 0)
508         {
509                 fprintf(stderr, "ioctl I4B_DISCONNECT_REQ failed: %s", strerror(errno));
510                 return(-1);
511         }
512         fprintf(stderr, "isdntest: sending disconnect request\n");
513         return(0);
514 }
515
516 /*---------------------------------------------------------------------------*
517  *      get cdid from kernel
518  *---------------------------------------------------------------------------*/
519 unsigned int
520 get_cdid(int isdnfd)
521 {
522         msg_cdid_req_t mcr;
523         int ret;
524         
525         mcr.cdid = 0;
526         
527         if((ret = ioctl(isdnfd, I4B_CDID_REQ, &mcr)) < 0)
528         {
529                 fprintf(stderr, "ioctl I4B_CDID_REQ failed: %s", strerror(errno));
530                 return(0);
531         }
532         fprintf(stderr, "isdntest: got cdid %d from kernel\n", mcr.cdid);
533         return(mcr.cdid);
534 }
535
536 /*---------------------------------------------------------------------------*
537  *      make shure all cdid's are inactive before leaving
538  *---------------------------------------------------------------------------*/
539 void cleanup(void)
540 {
541         int len;
542         char buf[1024];
543         
544         if(out_cdid != CDID_UNUSED)
545         {
546                 fprintf(stderr, "isdntest: cleanup, send disconnect req for out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
547                 disconnect_request(isdnfd, out_cdid);
548         }
549
550         while((out_cdid != CDID_UNUSED) || (in_cdid != CDID_UNUSED))
551         {
552                 if(debug_level)
553                         fprintf(stderr, "isdntest: cleanup, out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
554
555                 if((len = read(isdnfd, buf, 1024 - 1)) > 0)
556                 {
557                         switch (buf[0])
558                         {
559                                 case MSG_CONNECT_IND:
560                                         handle_connect_ind(&buf[0]); 
561                                         break;
562                                         
563                                 case MSG_CONNECT_ACTIVE_IND:
564                                         handle_connect_active_ind(&buf[0]); 
565                                         break;
566                                         
567                                 case MSG_DISCONNECT_IND:
568                                         handle_disconnect(&buf[0]); 
569                                         break;
570                                         
571                                 default:
572                                         if(debug_level)                         
573                                                 fprintf(stderr, "isdntest: unknown message 0x%x = %c\n", buf[0], buf[0]);
574                                         break;
575                         }
576                 }
577                 else
578                 {
579                         fprintf(stderr, "isdntest: read error, errno = %d, length = %d", errno, len);
580                 }
581         }
582         if(debug_level) 
583                 fprintf(stderr, "isdntest: exit cleanup, out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
584 }
585
586 /*---------------------------------------------------------------------------*
587  *      test the b-channels
588  *---------------------------------------------------------------------------*/
589 int do_test(void)
590 {
591
592 #define FPH 0x3c
593 #define FPL 0x66
594
595         int fd0, fd1;
596         unsigned char wrbuf[2048];
597         unsigned char rdbuf[2048];
598         int sz;
599         fd_set set;
600         struct timeval timeout;
601         int ret;
602         int frame;
603         int errcnt;
604         int frm_len;
605         int bytecnt = 0;
606         time_t start_time;
607         time_t cur_time;
608         time_t run_time;
609         
610         if((fd0 = open(DATADEV0, O_RDWR)) == -1)
611         {
612                 fprintf(stderr, "open of %s failed: %s", DATADEV0, strerror(errno));
613                 return(-1);
614         }               
615
616         if((fd1 = open(DATADEV1, O_RDWR)) == -1)
617         {
618                 fprintf(stderr, "open of %s failed: %s", DATADEV1, strerror(errno));
619                 return(-1);
620         }               
621
622         printf("\n");
623         frame = 0;
624         errcnt = 0;     
625
626         frm_len = 2;
627
628         start_time = time(NULL);
629
630         printf(" frame size errors totalbytes  bps elapsedtime\n");
631         
632         for(;dotest > 0; dotest--)
633         {       
634                 setup_wrfix(frm_len, &wrbuf[0]);
635
636                 frame++;
637
638                 bytecnt += frm_len;
639                 
640                 printf("%6d %4d", frame, frm_len);
641                 fflush(stdout);
642                 
643                 if((sz = write(fd0, wrbuf, frm_len)) != frm_len)
644                 {
645                         fprintf(stderr, "write (%d of %d bytes) to %s failed: %s\n", sz, frm_len, DATADEV0, strerror(errno));
646                 }
647
648                 timeout.tv_sec =  2;
649                 timeout.tv_usec = 0;
650                                 
651                 FD_ZERO(&set);
652
653                 FD_SET(0, &set);
654                 FD_SET(fd1, &set);              
655
656                 ret = select(fd1+1, &set, NULL, NULL, &timeout);
657
658                 if(ret > 0)
659                 {
660                         if(FD_ISSET(fd1, &set))
661                         {
662                                 if((sz = read(fd1, rdbuf, 2048)) != frm_len)
663                                 {
664                                         fprintf(stderr, "read (%d bytes) from %s failed: %s\n", sz, DATADEV1, strerror(errno));
665                                 }
666
667                                 cur_time = time(NULL);
668                                 run_time = difftime(cur_time, start_time);
669
670                                 if(run_time == 0)
671                                         run_time = 1;
672                                 
673                                 printf(" %6d %10d %4d %2.2d:%2.2d:%2.2d     \r",
674                                         errcnt, bytecnt,
675                                         (int)((int)bytecnt/(int)run_time),
676                                         (int)run_time/3600, (int)run_time/60, (int)run_time%60);
677                                 fflush(stdout);
678
679                                 errcnt += check_rd(frm_len, &wrbuf[0], &rdbuf[0]);
680                                 
681 #ifdef NOTDEF
682                                 for(i=0; i<sz; i++)
683                                 {
684                                         printf("0x%02x ", (unsigned char)rdbuf[i]);
685                                 }
686                                 printf("\n");
687 #endif                          
688                         }
689
690                         if(FD_ISSET(0, &set))
691                         {
692                                 return(0);
693                                 printf("\n\n");
694                         }
695                 }
696                 else
697                 {
698                         fprintf(stderr, "isdntest, do_test: select error: %s\n", strerror(errno));
699                 }
700
701                 frm_len = frm_len*2;
702                 if(frm_len > 2048)
703                         frm_len = 2;
704
705         }
706         printf("\n\n");
707         return(0);
708 }
709
710 void
711 setup_wrfix(int len, unsigned char *buf)
712 {
713         register int i;
714         
715         for(i=0; i<len;)
716         {
717                 *buf = FPH;
718                 buf++;
719                 *buf = FPL;
720                 buf++;
721                 i+=2;
722         }
723 }
724
725 int             
726 check_rd(int len, unsigned char *wbuf, unsigned char *rbuf)
727 {
728         register int i;
729         int ret = 0;
730         
731         for(i=0; i<len; i++)
732         {
733                 if(*wbuf != *rbuf)
734                 {
735                         fprintf(stderr, "\nERROR, byte %d, written 0x%02x, read 0x%02x\n", i, *wbuf, *rbuf);
736                         ret++;
737                 }
738                 wbuf++;
739                 rbuf++;
740         }
741         return(ret);
742 }
743
744
745 /* EOF */