/*****************************************************************************
* 	  Copyright (c) 1988 Thinking Machines Corporation, Inc.,	     *
*		of Cambridge, Mass.   All rights reserved.		     *
*									     *
*  This notice is intended as a precaution against inadvertent publication   *
*  and does not constitute an admission or acknowledgement that publication  *
*  has occurred or constitute a waiver of confidentiality.		     *
*									     *
*  Connection Machine software is the proprietary and confidential property  *
*  of Thinking Machines Corporation.					     *
*****************************************************************************/

#ifndef lint
static char rcsid[] =
"$Header: /cm/src/iodev/exer/RCS/serial.c,v 1.13 1991/08/27 15:46:49 sharakan Exp $";
#endif

#include <stdio.h>
#include <sys/time.h>
#include <fcntl.h>
#include <cm/cm_file.h>
#include <cm/cm_param.h>
#include <cm/cm_mount.h>
#include <cm/cm_errno.h>	/* for CMFS_errno */
#include <cm/cm_stat.h>
#include <sys/types.h>
#include <tmc/vmeio-ioctl.h>

int intf = 0;

extern char *CMFS_sys_errlist[];
extern int CMFS_sys_nerr;
#ifndef CMFS_errormsg
#define CMFS_errormsg \
	(CMFS_errno<0 || CMFS_errno>CMFS_sys_nerr)? "(CMFS errno out of range)": CMFS_sys_errlist[CMFS_errno]
#endif /* CMFS_errormsg */

#define PARTIAL 1
#define WHOLE 0
#define HEADER_LENGTH	512
#define TRAILER_LENGTH	512
#define BOUNDARY_MARKER	0x59		/* watch out for sign extension */
#define DISKBLOCKSIZE	16384
#define VMEIO_ALIGNMENT 8

/*
#define TEST 1
#define NO_EOF_TEST 1
*/

struct test_table
{
  int length;
} test_table[] =
#ifdef TEST
{
  49,
};
#else
{
    49,				/* a bit of a block */
    (32*1024),			/* two blocks */
    (16 * 1024) - 49,		/* The rest of a block */	/* 3 blocks */
    (4 * 16 * 1024) - 17,	/* Most of four blocks */
    17,				/* the rest of that block */	/* 7 blocks */
    (16 * 1024) - 127,		/* Most of a block */
    127,			/* The rest of the block */	/* 8 blocks */
    24,				/* beginning of cmword */
    400,			/* middle of cmword */
    88,				/* end of cmword */
    10000,			/* middle of disk block */
    5872,			/* end of disk block */		/* 9 blocks */
    (36 * 1024),		/* beginning of block, 2+ blocks */
    (34 * 1024),		/* middle of block, 2+ block */
    (26 * 1024),		/* end of block, 1+ block */	/* 15 blocks */
    (2 * 1024),				/* get off block boundary*/
    (32 * 1024),		/* 2 full blocks */		/* 17+ */
    (16 * 1024),		/* 1 full blocks */		/* 18+ */
    (80 * 1024),		/* 5 full blocks */		/* 23+ */
    (64 * 1024),		/* 4 full blocks */		/* 27+ */
    (78 * 1024),		/* 4+ blocks */			/* 32 */
    49,					/* get off aligned boundary */
    (32 * 1024),		/* 2 full blocks */		/* 34+ */
    (16 * 1024),		/* 1 full blocks */		/* 35+ */
    (80 * 1024),		/* 5 full blocks */		/* 40+ */
    (64 * 1024),		/* 4 full blocks */		/* 44+ */
    (78 * 1024),		/* 4+ blocks */			/* 48+ */
    1999 + (2 * 16 * 1024),	/* 2+ blocks */			/* 51  */
};
#endif
static int fd;
static char *write_buf;
static char *read_buf;
static int check_data = 1;
static int timing_only = 0;

#if !defined(sgi)
char *sprintf();
#endif

#ifndef sun
#define memalign(x,y) malloc(y)
#endif

char *malloc(), *CMFS_vmeio_allocate();
extern int CMI_soft_error_print;
extern int CMI_vmeio_fd[];
extern struct vmeio_config CMI_vcf[];

static long
tv_usec_diff(tv0, tv1)
struct timeval *tv0, *tv1;
{
  long val;

  val = (tv1->tv_sec - tv0->tv_sec) * 1000000;
  val += tv1->tv_usec - tv0->tv_usec;
  return val;
}

static int use_vmeio_for_bufs;
static int use_random_lengths;
static int use_unaligned_bufs;
static int real_table_count;
static int total_loops;
static int total_length = 0;
static int real_total_length = 0;
static int nerrors = 0;

main(ac, av)
int ac;
char **av;
{
  extern char *optarg;
  extern int optind, opterr, optopt;

  int c;
  int i,retval;
  char file_name[100];
  int pass;
  int Spass = 1;
  int Npass = 8;
  int qflag = 0;
  int vmeio_fd;
  extern char CMI_current_dv_hostname[];
  struct cm_stat statp;
  int unit;
  struct cm_statfs cmfs_info;
  struct timeval time;
  int diskblocksize,striping_factor;
  
  CMI_soft_error_print = 1;
  
  opterr = 0;

  setlinebuf(stdout);
  setlinebuf(stderr);

  while ((c=getopt(ac, av, "i:s:p:tnq")) != -1)
    switch (c)
      {
      case 'i':
	intf = atoi(optarg);
	break;
      case 's':
	Spass = atoi(optarg);
	break;
      case 'p':
	Npass = atoi(optarg);
	break;
      case 't':
	timing_only = 1;
	break;
      case 'n':
	check_data = 0;
	break;
      case 'q':
	qflag++;
	break;
      case '?':
	fprintf(stderr,"Usage: %s [-i #] [-s <starting pass #>] [-p <# passes>] [-t] [-n]\n",av[0]);
	exit(2);
      default:
	fprintf(stderr,"Getopt error!\n");
	exit(2);
      }

  sprintf(file_name, "serial.%d", getpid());
  
  if ((fd = 
       CMFS_open(file_name, CMFS_O_TRUNC | CMFS_O_CREAT | CMFS_O_RDWR, 0666)) < 0){
    fprintf(stderr,"Open of %s failed: ", file_name);
    CMFS_perror("");
    return;
  }
  
  CMFS_unlink(file_name);

  if (CMFS_statfs(file_name, &cmfs_info) < 0)
    {
      fprintf(stderr, "CMFS_statfs() failed: ");
      CMFS_perror("");
      return;
    }
  striping_factor = cmfs_info.cmf_stripe;
  diskblocksize = DISKBLOCKSIZE * striping_factor;

  /*
   * make sure RMW scratch area gets allocated first,
   * so rest of DRAM is contiguous.  This also does the open
   * and get_configuration ioctl on the vmeio
   */
  (void) CMI_use_vmeio(fd,intf);

  /* Seed the random number generator */
  if (gettimeofday(&time, 0) < 0)
    {
      perror("gettimeofday");
    }
  srandom((int)(time.tv_usec));

  printf("Beginning test suite on file server \"%s\" (striping factor %d)...\n", 
	 CMI_current_dv_hostname,
	 striping_factor);

  for (i=0;  i < sizeof(test_table)/sizeof(test_table[0]);  i++)
    {
#ifndef TEST
      test_table[i].length *= striping_factor;
#endif
      real_total_length += test_table[i].length;
    }
  real_table_count = i;

  (void) CMFS_serial_ftruncate_file(fd, (qflag?4:12)*1024*1024);
  
  for (pass = Spass; pass < Spass+Npass; pass++) {
    switch ((pass-1) % 8)
      {
      case 0:
	use_vmeio_for_bufs = 1;
	use_random_lengths = 0;
	use_unaligned_bufs = 0;
	break;
      case 1:
	use_vmeio_for_bufs = 0;
	use_random_lengths = 0;
	use_unaligned_bufs = 0;
	break;
      case 2:
	use_vmeio_for_bufs = 1;
	use_random_lengths = 1;
	use_unaligned_bufs = 0;
	break;
      case 3:
	use_vmeio_for_bufs = 0;
	use_random_lengths = 1;
	use_unaligned_bufs = 0;
	break;
      case 4:
	use_vmeio_for_bufs = 1;
	use_random_lengths = 0;
	use_unaligned_bufs = 1;
	break;
      case 5:
	use_vmeio_for_bufs = 0;
	use_random_lengths = 0;
	use_unaligned_bufs = 1;
	break;
      case 6:
	use_vmeio_for_bufs = 1;
	use_random_lengths = 1;
	use_unaligned_bufs = 1;
	break;
      case 7:
	use_vmeio_for_bufs = 0;
	use_random_lengths = 1;
	use_unaligned_bufs = 1;
      }

    CMFS_serial_lseek(fd, 0, 0);
    printf("\nPass %d: ", pass);

  if (use_vmeio_for_bufs && CMI_vmeio_fd[intf] < 0)
    {
      fprintf(stderr,"Could not find vmeio %d\n",intf);
      use_vmeio_for_bufs = 0;
    }

    total_loops =
      (qflag ?
       1 : (use_vmeio_for_bufs ?
	    ((CMI_vcf[intf].cf_dram_size ?
	      (12*1024*1024) : (6*1024*1024))/(real_total_length*2)) : 1));

    total_length = real_total_length * total_loops;

    if (!timing_only)
      {
	if (use_vmeio_for_bufs)
	  {
	    printf("*** Using %d bytes of VMEIO %d's DRAM for buffers ***\n", 
		   (total_length + HEADER_LENGTH + TRAILER_LENGTH +
		    VMEIO_ALIGNMENT) * 2 + TRAILER_LENGTH,intf);
	    
	    if ((read_buf = 
		 CMFS_vmeio_allocate
		 (total_length + HEADER_LENGTH + TRAILER_LENGTH*2 +
		  VMEIO_ALIGNMENT,intf)) == NULL)
	      {
		printf("VMEIO dram buffer not available for read\n");
		CMFS_perror("CMFS_vmeio_allocate failed");
		continue;
	      }
	    if ((write_buf =
		 CMFS_vmeio_allocate
		 (total_length + HEADER_LENGTH + TRAILER_LENGTH +
		  VMEIO_ALIGNMENT,intf)) == NULL)
	      {
		printf("VMEIO dram buffer not available for write\n");
		CMFS_perror("CMFS_vmeio_allocate failed");
		continue;
	      }
	  }
	else
	  {
	  printf("*** Using %d bytes of system memory for buffers ***\n",
		   (total_length + HEADER_LENGTH + TRAILER_LENGTH +
		    VMEIO_ALIGNMENT) * 2 + TRAILER_LENGTH);
	  
	  if ((write_buf =
	       malloc(total_length + HEADER_LENGTH + TRAILER_LENGTH +
		      VMEIO_ALIGNMENT)) == NULL) {
	    perror("malloc of write buffer failed");
	    exit(1);
	  }
	  
	  if ((read_buf =
	       malloc(total_length + HEADER_LENGTH + TRAILER_LENGTH*2 +
		      VMEIO_ALIGNMENT)) == NULL) {
	    perror("malloc of read buffer failed");
	    exit(1);
	  }
	}

	if (use_unaligned_bufs)
	  {
	    write_buf += 4;
	    read_buf += 4;
	    printf("*** Using unaligned buffers ***\n");
	  }
	
	if ((unit = CMI_use_vmeio(fd,write_buf)) != -1)
	  printf("*** Using VMEIO %d for serial transfer ***\n",unit);
	else
	  printf("*** Using ETHERNET for serial transfer ***\n");
#ifndef NO_EOF_TEST
	test_eof_handling(read_buf,write_buf);
#endif
	printf(". Testing sequential writes/reads\n");

	if (check_data)
	  {
	    if ((CMFS_serial_lseek(fd, 0, 0)) < 0)
	      {
		CMFS_perror("CMFS_serial_lseek failed");
		exit(1);
	      }

	    fill_buffers
	      (write_buf, total_length + TRAILER_LENGTH, 0);

	    if ((retval=
		 CMFS_serial_write_file(fd, write_buf, total_length + TRAILER_LENGTH)) == -1)
	      {
		CMFS_perror("CMFS_serial_write_file failed");
		exit(1);
	      }
	    else if (retval != total_length + TRAILER_LENGTH)
	      {
		fprintf(stderr,"CMFS_serial_write_file length error: %d out of %d bytes written\n",
			retval,total_length + TRAILER_LENGTH);
		exit(1);
	      }
	  }
	
	if (use_random_lengths) {
	  int current_offset = 0;
	  int length;
	  
	  printf(". Using random lengths...\n");

	  while (current_offset < total_length) {
	    if ((CMFS_serial_lseek(fd, current_offset, 0)) < 0)
	      {
		CMFS_perror("CMFS_serial_lseek failed");
		exit(1);
	      }
	    length = random() % (total_length / 16);
	    if (current_offset + length > total_length)
	      length = total_length - current_offset;
	    printf(". offset %8u (block %3d, offset %6d), length %d\n",
		   current_offset,
		   current_offset/diskblocksize, 
		   current_offset % diskblocksize,
		   length);
	    if (check_data)
	      fill_buffers(&write_buf[current_offset], length, i+1);
	    if ((retval=write_and_then_read
		 (&write_buf[current_offset], read_buf, length)) < 0)
	      nerrors++;
	    if (check_data)
	      if(check_entire_file
		 (current_offset + length + TRAILER_LENGTH) < 0)
		nerrors++;
	    current_offset += length;
	  }
	} else {
	  int current_offset = 0;

	  int length;
	  int ii;
	  
	  printf(". Using lengths from table...\n");
	  for (ii = 0; ii < total_loops; ++ii)
	    {
	    for (i=0; i < real_table_count; i++)
	      {
		if ((CMFS_serial_lseek(fd, current_offset, 0)) < 0)
		  {
		    CMFS_perror("CMFS_serial_lseek failed");
		    exit(1);
		  }
		length = test_table[i].length;
		printf(". offset %8u (block %3d, offset %6d), length %d\n",
		       current_offset,
		       current_offset/diskblocksize, 
		       current_offset % diskblocksize,
		       length);
		if (check_data)
		  fill_buffers(&write_buf[current_offset], length, i+1);
		if (write_and_then_read
		    (&write_buf[current_offset], read_buf, length) < 0)
		  nerrors ++;
		if (check_data)
		  if (check_entire_file
		      (current_offset + length + TRAILER_LENGTH) < 0)
		    nerrors++;
		current_offset += length;
	      }
	  }
	}

	/* Free buffers */
	if (use_unaligned_bufs)
	  {
	    write_buf -= 4;
	    read_buf -= 4;
	  }
	if (use_vmeio_for_bufs)
	  {
	    CMFS_vmeio_free(write_buf,intf);
	    CMFS_vmeio_free(read_buf,intf);
	  }
	else
	  {
	    free(write_buf);
	    free(read_buf);
	  }
      }
#if 0
    else
      CMFS_stat(".",&statp);	/* XXX SYNC WITH FSSERVER */
#endif    
    if (use_vmeio_for_bufs)
      printf("\n. Testing read and write timing using VMEIO %d's DRAM...\n",
	     intf);
    else
      printf("\n. Testing read and write timing using system memory...\n");

    for (i = 1;
	 i < (qflag ? 5 : (use_vmeio_for_bufs ? CMI_vcf[intf].cf_dram_size*24+7 : 5));
	 i++)
	 {
      int ret;
      int pattern;
      struct timeval starttime, endtime;
      double timediff;
      register int *ip;
      
      switch((i-1)%4) {
      case 0:
	pattern = 0;
	break;
      case 1:
	pattern = 0xa5a5a5a5;
	break;
      case 2:
	pattern = 0x5a5a5a5a;
	break;
      case 3:
	pattern = 0xffffffff;
	break;
      }
      
      if (use_vmeio_for_bufs) {
	write_buf = (char *)CMFS_vmeio_allocate(i * 1024 * 1024,intf);
	if (write_buf == NULL) {
	  printf("Cannot allocate %d mb VMEIO dram for large xfer test\n",i);
	  CMFS_perror("CMFS_vmeio_allocate failed");
	  goto freeit;
	}
      } else {
	write_buf = (char *)memalign(8, i*1024*1024);
	if (write_buf == NULL) {
	  perror("malloc");
	  exit(1);
	}
      }
      
      if (i == 1)
	{
	  if ((unit = CMI_use_vmeio(fd,write_buf)) != -1)
	    printf("*** Using VMEIO %d for serial transfer ***\n",unit);
	  else
	    printf("*** Using ETHERNET for serial transfer ***\n");
	}

      if (check_data)
	for (ip = (int *)write_buf; ip < (int *)(&write_buf[i*1024*1024]);
	     ip++)
	  *ip = pattern;
      
      (void) CMFS_serial_lseek(fd, 0, 0);
      if (i == 1)
	{
	  printf(". %6d byte write ... ", DISKBLOCKSIZE * striping_factor);
	  fflush(stdout);

	  if (gettimeofday(&starttime, 0) < 0) {
	    perror("gettimeofday");
	  }
	  ret = CMFS_serial_write_file
	    (fd, write_buf, DISKBLOCKSIZE * striping_factor);
	  if (gettimeofday(&endtime, 0) < 0) {
	    perror("gettimeofday");
	  }
	  if (ret != DISKBLOCKSIZE * striping_factor) {
	    fprintf(stderr, "%d kbyte write failed, it returned %d\n",
		    DISKBLOCKSIZE * striping_factor, ret);
	    CMFS_perror("serial write failed");
	    break;
	  } else {
	    timediff = (double) tv_usec_diff(&starttime, &endtime)
	      / 1e6;
	    printf("%.2f seconds (%7.4f mbytes/sec)\n",
		   timediff, ((DISKBLOCKSIZE * striping_factor)/timediff)/1e6);
	  }
	  CMFS_serial_lseek(fd, 0, 0);
	  printf(". %6d byte read  ... ",DISKBLOCKSIZE * striping_factor);
	  fflush(stdout);
	  if (gettimeofday(&starttime, 0) < 0) {
	    perror("gettimeofday");
	  }
	  ret = CMFS_serial_read_file
	    (fd, write_buf, DISKBLOCKSIZE * striping_factor);
	  if (gettimeofday(&endtime, 0) < 0) {
	    perror("gettimeofday");
	  }
	  if (ret < 0) {
	    fprintf(stderr, "%d kbyte read failed, it returned %d\n",
		    DISKBLOCKSIZE * striping_factor, ret);
	    CMFS_perror("serial read failed");
	    break;
	  } else {
	    timediff = (double) tv_usec_diff(&starttime, &endtime)
	      / 1e6;
	    printf("%.2f seconds (%7.4f mbytes/sec)\n",
		   timediff, ((DISKBLOCKSIZE * striping_factor)/timediff)/1e6);
	  }
	  if (check_data)
	    for (ip = (int *)write_buf;
		 ip < (int *)(&write_buf[DISKBLOCKSIZE * striping_factor]);
		 ip++)
	      if (*ip != pattern)
		printf("Data compare error, word %d: got 0x%x expected 0x%x\n",
		       (int *)write_buf - ip,
		       *ip, pattern);
	  
	  CMFS_serial_lseek(fd, 0, 0);
	}
      printf(". %2d mbyte write ... ", i); fflush(stdout);

      if (gettimeofday(&starttime, 0) < 0) {
	perror("gettimeofday");
      }
      ret = CMFS_serial_write_file(fd, write_buf, i*1024*1024);
      if (gettimeofday(&endtime, 0) < 0) {
	perror("gettimeofday");
      }
      if (ret != i*1024*1024) {
	fprintf(stderr, "%d mbyte write failed, it returned %d\n",
		i, ret);
	CMFS_perror("serial write failed");
	break;
      } else {
	timediff = (double) tv_usec_diff(&starttime, &endtime)
	  / 1e6;
	printf("%.2f seconds (%7.4f mbytes/sec)\n",
	       timediff, ((i*1024*1024)/timediff)/1e6);
      }
      
      CMFS_serial_lseek(fd, 0, 0);
      printf(". %2d mbyte read  ... ", i); fflush(stdout);
      if (gettimeofday(&starttime, 0) < 0) {
	perror("gettimeofday");
      }
      ret = CMFS_serial_read_file(fd, write_buf, i*1024*1024);
      if (gettimeofday(&endtime, 0) < 0) {
	perror("gettimeofday");
      }
      if (ret < 0) {
	fprintf(stderr, "%d mbyte read failed, it returned %d\n",
		i, ret);
	CMFS_perror("Large serial read failed");
	break;
      } else {
	timediff = (double) tv_usec_diff(&starttime, &endtime)
	  / 1e6;
	printf("%.2f seconds (%7.4f mbytes/sec)\n",
	       timediff, ((i*1024*1024)/timediff)/1e6);
      }
      if (check_data)
	for (ip = (int *)write_buf; ip < (int *)(&write_buf[i*1024*1024]);
	     ip++)
	  if (*ip != pattern)
	    printf("Data compare error, word %d: got 0x%x expected 0x%x\n",
		   (int *)write_buf - ip,
		   *ip, pattern);
      
    freeit:      
      if (use_vmeio_for_bufs)
	CMFS_vmeio_free(write_buf,intf);
      else
	free(write_buf);
    }
  }
  printf("Done, %d failures\n", nerrors);
  CMFS_close(fd);
}




write_and_then_read(write_buffer, read_buffer, length)
char *write_buffer;
char *read_buffer;
int length;
{
  int ret;
  char tmp_data[TRAILER_LENGTH];
  
  if (check_data)
    {
      bcopy(write_buffer + length, tmp_data, TRAILER_LENGTH);
      mark(write_buffer + length, TRAILER_LENGTH);
    }
  
  if ((ret = CMFS_serial_write_file(fd, write_buffer, length)) != length)
    {
      printf("CMFS_serial_write of length %d failed - it returned %d (%s)\n",
	     length, ret, (ret < 0) ? CMFS_errormsg : "no error");
      return -1;
    }
  
  if (check_data)
    {
      bcopy(tmp_data, write_buffer + length, TRAILER_LENGTH);
      bzero(read_buffer + HEADER_LENGTH, length);
      mark(read_buffer, HEADER_LENGTH);
      mark(read_buffer + HEADER_LENGTH + length, TRAILER_LENGTH);
    }
  
  /* Seek back relative to the current position */
  if (CMFS_serial_lseek(fd, -length, CMFS_L_INCR) < 0)	{
    printf("CMFS_serial_lseek of length %d failed\n", length);
    return -2;
  }
  
  if ((ret = CMFS_serial_read_file(fd, read_buffer + HEADER_LENGTH, length)) 
      != length)	{
    printf("CMFS_serial_read of length %d failed - it returned %d (%s)\n",
	   length, ret, (ret < 0) ? CMFS_errormsg : "no error");
    return -2;
  }
  
  if (check_data) {
    if (bcmp(write_buffer, read_buffer + HEADER_LENGTH, length) != 0)
      {
	printf("Compare failed in partial read back\n");
	find_wrong_bytes
	  (write_buffer, read_buffer + HEADER_LENGTH, length, PARTIAL);
	return -2;
      } 
    
    if (verify_mark
	(read_buffer, read_buffer + HEADER_LENGTH + length) < 0)
      {
	printf("Compare failed in partial read back - on marks\n");
	return -2;
      } 
  }
  return 0;
}





check_entire_file(length)
int length;
{
  int ret;
  
  bzero(read_buf + HEADER_LENGTH, length);
  mark(read_buf, HEADER_LENGTH);
  mark(read_buf + HEADER_LENGTH + length, TRAILER_LENGTH);
  
  if (CMFS_serial_lseek(fd, 0, 0) < 0)
    {
      printf("CMFS_serial_lseek failed\n");
      return -1;
    }
  
  if ((ret = CMFS_serial_read_file
       (fd, read_buf + HEADER_LENGTH, length)) != length)
    {
      printf("CMFS_serial_read of length %d failed - it returned %d (%s)\n",
	     length, ret, (ret < 0) ? CMFS_errormsg : "no error");
      return -1;
    }
  
  if (check_data)
    {
      if (bcmp(write_buf, read_buf + HEADER_LENGTH, length) != 0)
	{
	  printf("Compare failed in whole file read back\n");
	  find_wrong_bytes
	    (write_buf, read_buf + HEADER_LENGTH, length, WHOLE);
	  return -1;
	}
      
      if (verify_mark(read_buf, read_buf + HEADER_LENGTH + length) < 0)
	{
	  printf("Compare failed in whole file read back - on marks\n");
	  return -1;
	}
    }
  return 0;
}

fill_buffers(addr, length, magic)
register char *addr;
int length;
{
    register long *long_addr;
    int odd_chars;
    register int i;

    odd_chars = (4 - ((int) addr & 3)) % 4;

    for (i=0;  i<odd_chars;  i++)	{
	*(unsigned char *)addr++ = (unsigned char) 0xaa;
    }

    long_addr = (long *) addr;
    length -= odd_chars;

    for (i=0;  i<(length - 3);  i+=4)	{
	*long_addr++ = (i/4 | (magic << 24));
    }

    addr = (char *) long_addr;

    for (i=0;  i<(length&3);  i++)	{
	*addr++ = 0x55;
    }
}



mark(buf, length)
char *buf;
int length;
{
	int i;

	for (i=0;  i<length;  i++)
		*buf++ = BOUNDARY_MARKER;
}


int
verify_mark(header, trailer)
char *header;
char *trailer;
{
    int i;

    for (i=0;  i<HEADER_LENGTH;  i++)
	if (*header++ != BOUNDARY_MARKER)
	    return(-1);

    for (i=0;  i<TRAILER_LENGTH;  i++)
	if (*trailer++ != BOUNDARY_MARKER)
	    return(-1);

    return(0);
}


int max_error_bytes_printed = 10;
                
find_wrong_bytes(p, q, length, partialp)
register char *p;
register char *q;
register int length;
int partialp;
{
    register int i;
    int count = 0;

    for (i=0;  i<length; i++)      {
	if (*p != *q)       {
	    if (count < max_error_bytes_printed)
		printf("%sbyte %d: wrote %x read %x\n",
		       (partialp == PARTIAL) ? "Relative " : "",
		       i,
		       (unsigned char)*p, 
		       (unsigned char)*q);
	    count++;
	}
	p++; q++;
    }

    printf("%d total errors\n", count);

}

test_eof_handling(read_buffer,write_buffer)
char *read_buffer, *write_buffer;
{
  int fd;
  char namebuf[100];
  int current_offset;
  int i,n;
  
  sprintf(namebuf, "serial-eof-%d", getpid());
  
  (void) CMFS_unlink(namebuf);
  if ((fd = 
       CMFS_open
       (namebuf, CMFS_O_TRUNC | CMFS_O_CREAT | CMFS_O_RDWR, 0666)) < 0)
    {
      printf("Open of %s failed: %s\n", namebuf, CMFS_errormsg);
      return;
    }
  (void) CMFS_unlink(namebuf);
  
  printf(". Testing serial file EOF handling using %s\n",
	 use_random_lengths ? "random lengths" : "lengths from table");

  current_offset = 0;
  
  check_length(fd,0);
  CMFS_serial_ftruncate_file(fd, 0);
  check_length(fd, 0);
  
  for (i = 0; i < real_table_count; i++)
    {
      int to_write;
      
      if (use_random_lengths)
	{
	  to_write = random() % (real_total_length/16);
	  if (current_offset + to_write > real_total_length)
	    to_write = real_total_length - current_offset;
	  if (current_offset == real_total_length)
	    break;
	}
      else
	to_write = test_table[i].length;
      
      printf(". Testing length %d at offset %d\n", to_write,current_offset);
      
      /*
       * write at EOF to extend file
       */
      if ((n = CMFS_serial_write_file(fd, write_buffer, to_write)) !=
	  to_write)
	{
	  printf("\nCMFS write error: got %d, expected %d: %s\n",
		 n, to_write, CMFS_errormsg);
	  nerrors++;
	}
      
      if (check_length(fd, current_offset+to_write) < 0)
	nerrors++;
      
      (void) CMFS_serial_lseek(fd, 0, 0);
      
      if ((n = CMFS_serial_read_file(fd, read_buffer, real_total_length)) !=
	  current_offset+to_write)
	{
	  printf("\nCMFS read error: Read %d, expected %d: %s\n",
		 n, current_offset+to_write, (n < 0) ? CMFS_errormsg: "no error");
	  nerrors++;
	}
      /*
       * Seek back to beginning of file for next write
       */
      (void) CMFS_serial_lseek(fd, 0, 0);

      /*
       * Write at beginning of file
       */
      if ((n = CMFS_serial_write_file(fd, write_buffer, to_write)) !=
	  to_write)
	{
	  printf("\nCMFS write error: got %d, expected %d: %s\n",
		 n, to_write, CMFS_errormsg);
	  nerrors++;
	}

      /*
       * length should not have changed
       */
      if (check_length(fd, current_offset+to_write) < 0)
	nerrors++;
      
      (void) CMFS_serial_lseek(fd, 0, 0);
      
      if ((n = CMFS_serial_read_file(fd, read_buffer, real_total_length)) !=
	  current_offset+to_write)
	{
	  printf("\nCMFS read error: Read %d, expected %d: %s\n",
		 n, current_offset+to_write, (n < 0) ? CMFS_errormsg: "no error");
	  nerrors++;
	}
      current_offset += to_write;
    }
}

check_length(fd, exp)
{
    struct cm_stat cmsbuf;

    if (CMFS_fstat(fd, &cmsbuf) < 0) {
	printf("\nCMFS_fstat failed: %s\n", CMFS_errormsg);
	return;
    }

    if (cmsbuf.cmst_size != exp) {
	printf("\ncmst_size(%d)  != %d\n",
	       cmsbuf.cmst_size, exp);
	return -1;
    }
    return 0;
}


