/*
 * tool for updating the firmware on a scsi terminal server
 *
 * Copyright (C) 2001 Kees Cook
 * cook@cpoint.net, http://outflux.net/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * http://www.gnu.org/copyleft/gpl.html
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include "scsi_iface.h"
#include "scsi_tools.h"
#include "firmware.h"


/* correctly find an STS device */
scsi_info * choose_device(void) {
  int i;
  char sgfile[64];
  char input[64];
  scsi_info * dev;
  int found;
  char selection;
  char choice=' ';
  char max='A'-1;

  printf("Scanning for STSs ...\n");
  for (;;) {
    found=0; /* rescan: nothing found yet */
    max='A'-1; /* reset our max below A */
    selection='A'-1; /* reset our selection too */

    /* print our header if we're not selecting a device */
    if (choice==' ') {
        printf("#) Host Chan  ID  Vendor     Product            Ver\n");
        printf("-- ---- ---- ---  ---------- ------------------ ------\n");
    }
    /* search for a LOT of devices... */
    for (i=0;i<128;i++) {
        sprintf(sgfile,"/dev/sg%d",i);

        if ((dev = scsi_open_name(sgfile))!=NULL) {
	    if (scsi_unitready(dev) && 
		dev->sginfo.lun == 0 &&
		dev->sginfo.scsi_type==TYPE_COMM &&
		(!strcmp(dev->vendor,"CenData ") ||
		 !strcmp(dev->vendor,"DigIintl") ||
		 !strcmp(dev->vendor,"MOTOROLA"))

		) {

		found=1;  /* we found something */
		selection++; /* push selection letter forward */
	        max=selection; /* update max selection */
		/* if we didn't select anything, or THIS one, print info */
    		if (choice==' ' || choice==selection) {
                   printf("%c) %4d,%4d, %2d  '%s' '%s' '%s'\n",
			selection,
                        dev->sginfo.host_no,
                        dev->sginfo.channel,
                        dev->sginfo.scsi_id,
                        dev->vendor, dev->product, dev->version
                   );
		   /* stop the for loop of devices, we got what we wanted */
		   if (choice==selection) break;
		}
	    }
	    /* done looking at device, close it up */
	    scsi_close(dev);
	    dev=NULL;
	}
    }
    /* quit if there aren't any devices */
    if (!found) {
	printf("No STS devices found!\n");
	return NULL;
    }
    /* if we selected a device, "dev" isn't NULL */
    if (dev) return dev;

    /* loop until they pick a valid selection */
    while (choice<'A' || choice>max) {
	printf("\nWhich STS do you want to update? (CR aborts) ");
	fflush(NULL);
	fgets(input,64,stdin);
	choice=toupper(input[0]); /* kick up the case */
	if (choice=='\n') {
		return NULL;
	}
	printf("\nSTS selected:\n");
    }
  }

  /* how the hell did we get here? */
  return NULL;
}

/* choose a firmware image and verify it's sanity */
firmware_img * choose_firmware(scsi_info * dev) {
	char * possible;
	char input[255];
	char devproduct[26];
	firmware_img * firmware=NULL;
	int i;
        /* for readdir */
	DIR * dir;
	struct dirent * ent;
	int found=0;
	/* for fw ver check */
	int imgmajor, imgminor, devmajor, devminor;

	if (!dev) {
		fprintf(stderr,"choose_firmware got a NULL dev?!\n");
		return NULL;
	}

	/* get rid of training spaces.  Needed for firmware filename
	   generation, and firmware product id comparison */
	strncpy(devproduct,dev->product,25);
	devproduct[24]='\0';
	for (i=strlen(devproduct)-1; i>=0 && devproduct[i]==' '; i--) {
		devproduct[i]='\0';
	}

	if (!(possible=build_firmware_filename(devproduct))) {
		fprintf(stderr,"Failed to guess at filename?!\n");
		return NULL;
	}

	while (!firmware) {
	  printf("\nIn current directory, the firmware (*.prm) files are:\n");

	  if ((dir=opendir("."))) {

		while ((ent=readdir(dir))!=NULL) {
			char * ptr;
			ptr=strstr(ent->d_name,".prm");
			if (ptr && ptr[4]=='\0') {
				found=1;
				printf("   %10s\n",ent->d_name);
			}
		}
		
		closedir(dir);
          }
	  else {
		perror("opendir");
	  }
	  if (!found) {
		printf("    none found!\n");
	  }
	  printf("\n");

	  printf("Load which firmware image? (CR = '%s', Q quits) ",possible);
	  fflush(NULL);
	  fgets(input,254,stdin);

	  if (input[0]=='q' || input[0]=='Q' || input[0]=='\0') return NULL;

	  /* tear off the \n */
	  input[strlen(input)-1]='\0';

	  /* did they want the default? */
	  if (input[0]=='\0') strncpy(input,possible,255);

	  printf("\n");

	  /* bring the file into memory */
	  if (!(firmware=load_firmware(input))) {
		fprintf(stderr,"Failed to load firmware.\n");
		continue;
	  }
	
          /* verify that we have a real STS firmware ... */
	  if (!verify_image(firmware)) {
		fprintf(stderr,"This doesn't look like an STS firmware!\n");
		unload_firmware(firmware);
		firmware=NULL;
		continue;
	  }
	  printf("Image has firmware for the '%s' (version '%s', %d bytes)\n",
		firmware->unit.name,firmware->unit.fw_ver,firmware->length);

	  /* make SURE the product matches the SCSI device */
	  if (strcmp(firmware->unit.name,devproduct)) {
		fprintf(stderr,"*** This image is NOT for the '%s'! ***\n",
			devproduct);
		unload_firmware(firmware);
		firmware=NULL;
		continue;
	  }

	  /* issue a warning if down-grading the firmware */
	  devmajor=atoi(dev->version+1);
	  devminor=atoi(dev->version+3);
	  imgmajor=atoi(firmware->unit.fw_ver+1);
	  imgminor=atoi(firmware->unit.fw_ver+3);
	  if ((devmajor==imgmajor && devminor>=imgminor) ||
	      (devmajor>=imgmajor)) {
		char reply[80];
		fprintf(stderr,"*** This image (%s) is NOT NEWER than your "
			"current firmware (%s)! ***\n",
			firmware->unit.fw_ver,dev->version);
		fprintf(stderr,"\t\tAre you SURE you want to continue? [y/N] ");
		fflush(NULL);
		fgets(reply,79,stdin);
		if (!(reply[0]=='y' || reply[0]=='Y')) {
			unload_firmware(firmware);
			firmware=NULL;
			continue;
		}
	  }

	}
	return firmware;
}


int main() {
   firmware_img * firmware;
   scsi_info * dev;
   int rc=FAILED;
   char buf[80];

   printf("stsupdate v1 Feb 13, 2001\n");

   for (;;) {
	if (!(dev=choose_device())) {
		printf("No device selected.  Exiting...\n");
		exit(0);
	}
	if (!(firmware=choose_firmware(dev))) {
		printf("No firmware selected.  Exiting...\n");
		scsi_close(dev);
		exit(0);
	}

	printf("\n*** Do not power off your STS during programming. ***\n");
	printf("\nRemember this program might destroy your STS firmware!\n");
	printf("Hit enter to start programming... ");
	fflush(NULL);
	fgets(buf,79,stdin);

	if ((rc=upload_now(dev,firmware))==BADSTATE) {
	   fprintf(stderr,"***### YIKES ###***\n");
	   fprintf(stderr,"*** Your STS failed during firmware upload ***\n");
	   fprintf(stderr,"*** DO NOT POWER OFF your STS ***\n");
	   fprintf(stderr,"*** Try again immediately ***\n");
	   fprintf(stderr,"*** If this continues, you have a new paper-weight ***\n");
	}
	else if (rc==FAILED) {
		fprintf(stderr, "Sorry, your firmware upload failed.  Please "
			"examine any error message above.\n");
	}
	else {
		printf("Firmware successfully uploaded!\n");
		printf("*** Please power-cycle your STS now. ***\n");
		printf("Hit enter when ready... ");
		fflush(NULL);
		fgets(buf,79,stdin);
	}
	unload_firmware(firmware);
	scsi_close(dev);
   }
   return 0;
}
