// Kompilieren mit:
// g++ -Wall scsilist.c -o scsilist

//
// Big parts are taken from Linux-Kernel and hpscanpbm-0.3a.tar.gz,
// because I dislike programming C/C++. It's so ugly. ;-)
// Stephan Löscher (loescher@leo.org)
//

//#include <ctype.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <iostream.h>
#include <scsi/sg.h>

int sg_xfer(int fid,unsigned char *src,int len,unsigned char *dest,
                int reply_len)
{
        int count, result;
        static struct sg_header hdr;
        static char buf[4096 + sizeof(struct sg_header)];
        hdr.pack_len = sizeof(hdr) + len;
        hdr.reply_len = sizeof(hdr) + reply_len;
        memcpy(buf, &hdr, sizeof(hdr));
        memcpy(buf + sizeof(hdr), src, len);
        if (write(fid, buf, hdr.pack_len) == -1) {
                perror("sg_xfer(write)");
                return -1;
        }
        if ((count = read(fid,buf,hdr.reply_len)) == -1) {
                perror("sg_xfer(read)");
                return -1;
        }
        result = ((struct sg_header*)buf)->result;
        if (result) {
                fprintf(stderr,"sg_xfer: result is %d\n",result);
        }
	// Der Typecast auf unsigned stimmt hoffentlich ? :-)
        if ((unsigned)count > sizeof(struct sg_header)) {
                count -= sizeof(struct sg_header);
                memcpy(dest,buf + sizeof(hdr),count);
        }
        else
                count = 0;
        return count;
}


int inquire_device(int fid,char *mfg,char *model)
{
        static unsigned char inquiry[] = { 0x12,0,0,0,255,0 };
        unsigned char reply[256];
        if (sg_xfer(fid,inquiry,sizeof(inquiry),reply,sizeof(reply)) < 16)
                return -1;
        memcpy(mfg,reply + 8, 8);
        memcpy(model,reply + 16, 16);
        return reply[0];
}


int write_data(int fid,char *data,int len)
{
        // alt: char write_6[4096];
	unsigned char write_6[4096];
        memset(write_6,0,6);
        write_6[0] = 0xa;
        write_6[2] = len >> 16;
        write_6[3] = len >> 8;
        write_6[4] = len;
        memcpy(write_6 + 6, data, len);
        if (sg_xfer(fid,write_6,6 + len,0,0) < 0)
                return -1;
        return 0;
}


int read_data(int fid,char *dest,int len)
{
        static unsigned char read_6[6] = { 8,0,0,0,0,0 };
        read_6[2] = len >> 16;
        read_6[3] = len >> 8;
        read_6[4] = len;
        if ((len=sg_xfer(fid,read_6,6,(unsigned char*)dest,len)) < 0)
                return -1;
        return len;
}


void write_string(int fid,char *format,...)
{
        va_list args;
        char buf[1024];
        va_start(args, format);
        vsprintf(buf,format,args);
        write_data(fid,buf,strlen(buf));
}


int inquire(int fid,char *inq)
{
        char reply[128];
        write_string(fid,inq);
        if (read_data(fid,reply,sizeof(reply))) 
	{
                return atoi(strchr(reply,'d') + 1);
        }
        else
                return -1;
}


int ListDevice(char *devname)
{
        int fd = open(devname, O_RDWR);
        if (fd != -1) 
	{
		fprintf(stdout,"Device: %s\n",devname);
                char mfr[8], model[16];
                int type = inquire_device(fd,mfr,model);
	        fprintf(stdout,"Herst.: %s:\n",mfr);
                fprintf(stdout,"Modell: %s\n",model);
		fprintf(stdout,"Typ:   %2d   ",type);
		switch(type)
		{
		case 0: fprintf(stdout,"Direct-Access"); 	break;
                case 1: fprintf(stdout,"Sequential-Access"); 	break;
                case 2: fprintf(stdout,"Printer"); 		break;
                case 3: fprintf(stdout,"Processor"); 		break;
                case 4: fprintf(stdout,"WORM"); 		break;
                case 5: fprintf(stdout,"CD-ROM"); 		break;
                case 6: fprintf(stdout,"Scanner"); 		break;
                case 7: fprintf(stdout,"Optical Device"); 	break;
		case 8: fprintf(stdout,"Medium Changer"); 	break;
                case 9: fprintf(stdout,"Communications");	break;
		default: fprintf(stdout,"Unbekannt!"); 		break;
		}
 		fprintf(stdout,"\n\n");
                close(fd);
        }
	else
	{
	  /*	  if (!strcmp(devname,"/dev/sga"))*/
	  if (!strcmp(devname,"/dev/sg0"))
	  {
	  fprintf(stdout,"Kann nicht lesen von Device: %s\n",devname);
	  cout << "Stellen Sie sicher, daß /dev/sg* existieren und SCSI-Generic verfügbar ist\n(im Kernel oder Modul sg.o). Außerdem müssen die Rechte richtig gesetzt sein.\n\n";
	  }
	}
	return fd;
}


void FindAllDevices(void)
{
  char buf[16];
  int i;
  /* 
     for (i='a'; i <= 'z'; i++) 
     {
     sprintf(buf,"/dev/sg%c",i);
     if (ListDevice(buf) == -1) return;
     }
  */
  for (i=0; i <= 15; i++) 
    {
      sprintf(buf,"/dev/sg%d",i);
      if (ListDevice(buf) == -1) return;
    }
}


int main(void)
{
	fprintf(stdout,"\nSCSI-List 1.1 von Stephan Loescher\n");
	fprintf(stdout,"Es werden alle generic-SCSI-devices aufgelistet.\n\n");
        FindAllDevices();
	return 0;
}
