/*
 * Get's the status of the drive through the GET EVENT / STATUS
 * NOTIFICATION command. 
 *
 * Requires a patched 2.2 kernel or 2.3.14 and newer - get the patch from
 * www.kernel.dk. Just grab the newest one.
 * 
 *
 * Jens Axboe, 1999
 * Adjusted by Erik Andersen
 */

#include 
#include 
#include 
#include 
#include 
#include 

/* if we don't have one, we have neither */
#ifndef GPCMD_INQUIRY
#define GPCMD_INQUIRY				0x12
#define GPCMD_GET_CONFIGURATION			0x46
#defined GPCMD_GET_EVENT_STATUS_NOTIFICATION	0x4a
#endif

int do_get_configuration(int fd);
int do_event_status(int fd);

int main(int argc, char **argv)
{
	char *program;
	char device[80];
	int fd, remov_media, i, k;
	char buffer[4096];
	struct cdrom_generic_command cgc;
	long datalength;
	long temp;

	program = argv[0];
	++argv;
	--argc;

	if (argv[0] == 0 )
		strcpy(device, "/dev/cdrom" );
	else if (strcmp (argv[0], "-v") == 0) {
			fprintf (stderr, "usage: %s \n", program);
			exit (1);
		}
	else
		strncpy(device, argv[0], 80);

	fd = open(device, O_RDONLY|O_NONBLOCK);
	if (fd == -1) {
		perror("open");
		return 1;
	}
	
	/* First do an INQUIRY -- find out what we have */
	memset(&cgc, 0, sizeof(cgc));
	memset(buffer, 0, sizeof(buffer));
	cgc.cmd[0] = GPCMD_INQUIRY;
	cgc.cmd[4] = cgc.buflen = 36;
	memcpy(buffer, &cgc, sizeof(cgc));
	
	if (ioctl(fd, CDROM_SEND_PACKET, buffer) < 0) {
		perror("GPCMD_INQUIRY");
		return 1;
	}

	for (i=8;i<15;i++)
		if (buffer[i]==' '&&buffer[i+1]==' ') buffer[i]='\0';
	if (buffer[15]==' ') buffer[15]='\0';
	for (i=16;i<32;i++)
		if (buffer[i]==' '&&buffer[i+1]==' ') buffer[i]='\0';
	if (buffer[31]==' ') buffer[31]='\0';

	printf("Drive: %.8s %.16s\n", &buffer[8], &buffer[16]);
	printf("peripheral type=");
	switch (buffer[0] & 0x3f) {
		case 0: printf("hard drive\n");break;
		case 5: printf("CD/DVD drive\n");break;
		case 7: printf("Optical memory\n");break;
		case 0x1f: printf("Unknown or none\n");break;
		default: printf("This shouldn't happen!\n");
	}
	remov_media=(buffer[1] & 0x80)? 1 : 0;
	printf("Removable media?=%s\n", remov_media? "Yes" : "No");
	printf("Interface Type=%s\n", ((buffer[2]&0xc0)==0)? "ATAPI":"SCSI");
	printf("ATAPI version=");
	switch ((buffer[3] & 0xf0)>>4) {
		case 0: printf("ATAPI ??0??\n");break;
		case 1: printf("ATAPI ??1??\n");break;
		case 2: printf("ATAPI 2.6\n");break;
		case 3: printf("Mt. Fuji (cool!)\n");break;
		default: printf("Unknown -- %x\n", (buffer[3] & 0xf0)>>4);break;
	}

	buffer[3] = 3 << 4;

	if ((buffer[3] & 0xf0)>>4 == 3)
		printf("drive supports Mt. Fuji\n");
	else
		return 0;

	/* drive supports Mt. Fuji */
	if ( (buffer[3] & 0xf0)>>4 == 3) {
	    printf("-- testing GET_CONFIGURATION:\n");
	    i=do_get_configuration(fd);
	    if(i!=0)
		return i;
	}
	
	if ( (buffer[3] & 0xf0)>>4 == 3) {
	    printf("-- testing GET_EVENT_STATUS_NOTIFICATION:\n");
	    i=do_event_status(fd);
	    if(i!=0)
		return i;
	}
	
	return 0;
}





int do_get_configuration(int fd)
{
    char *program;
    char device[80];
    int remov_media, i, k;
    char buffer[4096];
    struct cdrom_generic_command cgc;
    long datalength;
    long temp;

    /* Now do a GET_CONFIGURATION -- poke and prod for details */
    /* First, just get the header to find size */
    memset(&cgc, 0, sizeof(cgc));
    memset(buffer, 0, sizeof(buffer));
    cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
    cgc.cmd[8] = cgc.buflen = 8;
    memcpy(buffer, &cgc, sizeof(cgc));
    if (ioctl(fd, CDROM_SEND_PACKET, buffer) < 0) {
	    if (errno == EIO) {
	    	printf("Profiles not supported\n");
		return 0;
	    }
	    perror("GPCMD_GET_CONFIGURATION  header");
	    return 1;
    }
    
    /* get the rest since we know the size now */
    memset(&cgc, 0, sizeof(cgc));
    cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
    cgc.cmd[8] = cgc.buflen = buffer[3] | (buffer[2] << 8);
    memset(buffer, 0, sizeof(buffer));
    memcpy(buffer, &cgc, sizeof(cgc));
    if (ioctl(fd, CDROM_SEND_PACKET, buffer) < 0) {
	    perror("GPCMD_GET_CONFIGURATION");
	    return 1;
    }

    datalength = buffer[3] | (buffer[2] << 8) | (buffer[1] << 16) | 
	(buffer[0] << 24);
    
    printf("------------------------\n");
    printf("Current device profile=");
    switch (buffer[7] | (buffer[6] << 8)) {
	    case 1: printf("hard drive\n");break;
	    case 2: printf("hard drive w/rem. media\n");break;
	    case 8: printf("CD-ROM drive\n");break;
	    case 9: printf("CD-R drive\n");break;
	    case 0xa: printf("CD-RW drive\n");break;
	    case 0x10: printf("DVD-ROM drive\n");break;
	    case 0x11: printf("DVD-R drive\n");break;
	    case 0x12: printf("DVD-RAM drive\n");break;
	    default: printf("Something else!\n");
    }


    for (k=8; k>1)? "Yes" : "No" );
	//printf("Version=%d\n", buffer[k+2]&0x3c>>2 );
	//printf("Additional Length=%d\n", buffer[k+3] );
	
	k+=(buffer[k+3])+4;
    }
    printf("------------------------\n");
    return 0;
}





/* Not working properly yet... */
/* buggy! */
int do_event_status(int fd)
{
    char *program;
    char device[80];
    int remov_media, i, k;
    char buffer[4096];
    struct cdrom_generic_command cgc;
    long datalength;
    long temp;

    /* Now do a GET_EVENT_STATUS_NOTIFICATION */
    memset(&cgc, 0, sizeof(cgc));
    memset(buffer, 0, sizeof(buffer));
    cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
    cgc.cmd[1] = 1;
    cgc.cmd[4] = 16;
    cgc.cmd[8] = cgc.buflen = 8;
    memcpy(buffer, &cgc, sizeof(cgc));
    
    if (ioctl(fd, CDROM_SEND_PACKET, buffer) < 0) {
	    if (errno == EIO) {
	    	printf("Event notification not supported\n");
		return 0;
	    }
	    perror("GPCMD_GET_EVENT_STATUS_NOTIFICATION");
	    return 1;
    }

    /* list supported notification classes */
    printf("event classes supported by this drive :\n");
    if (buffer[3] & 2)
	    printf("\toperational change request/notification\n");
    if (buffer[3] & 4)
	    printf("\tpower management\n");
    if (buffer[3] & 8)
	    printf("\texternal request\n");
    if (buffer[3] & 16)
	    printf("\tmedia\n");
    if (buffer[3] & 32)
	    printf("\tmulti-host\n");
    if (buffer[3] & 64)
	    printf("\tdevice busy\n");

    
    printf("we are testing for : ");
    switch (buffer[2]) {
    case 0: printf("no requested event classes are supported\n"); break;
    case 1: printf("operational change request/notification\n"); break;
    case 2: printf("power management\n"); break;
    case 3: printf("external request\n"); break;
    case 4: printf("media\n"); break;
    case 5: printf("multi-host\n"); break;
    case 6: printf("device busy\n"); break;
    case 7: printf("reserved\n"); break;
    default: printf("notification class is bogus\n"); break;
    }

    printf("media event : ");
    switch (buffer[4]) {
    case 0: printf("no media change\n"); break;
    case 1: printf("eject request\n"); break;
    case 2: printf("new media\n"); break;
    case 3: printf("media removal\n"); break;
    case 4: printf("media change\n"); break;
    default: printf("reserved code\n"); break;
	    }

    printf("tray status : ");
    if (buffer[5] & 1)
	    printf("empty\n");
    else if (buffer[5] & 2)
	    printf("loaded\n");
    else 
	    printf("\n");
}