/*
mu-hdparm, reduced for muLinux
Based on hdparm.c, by by Mark S. Lord (c) 1994-1997

NOTE:
Support only power mode settings.

*/



#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <linux/hdreg.h>
#include <linux/fs.h>
#include <linux/major.h>

char *prgname;
unsigned int standby;
int fd;
char devname[64];
long parm, multcount;
struct stat stat_buf;
int is_scsi_hd=0;

enum {UNDEF,SET_STANDBY,GET_POWERMODE, 
SET_STANDBYNOW, SET_SLEEPNOW};

int op=UNDEF;

/* functions */


void no_scsi (void)
{
        if (is_scsi_hd) {
                fputs (" operation not supported on SCSI disks\n", stderr);
                exit(EINVAL);
        }
}

int usage()
{
printf("%s - get/set hard disk parameters \n\
- Based on hdparm.c (Mark Lord <mlord@pobox.com>) \n\
- rustic version by M.Andreoli\n",prgname);
printf("Usage:   %s [options] device\n", prgname);
printf("options:\n");
printf("\t%-15s%s","-h","this cruft\n");
printf("\t%-15s%s","-S timeout","set standby (spindown) timeout to x\n");
printf("\t%-15s%s","-C","check IDE power mode status\n");
printf("\t%-15s%s","-y","put IDE drive in standby mode, now\n");

printf("--- Note\n");
printf("\
A value \n\
of  zero means [off].  Values from 1 to 240 specify \n\
multiples of 5 seconds, for timeouts from 5 seconds \n\
to 20 minutes.  Values from 241 to 251 specify from \n\
1 to 11 units of 30 minutes, for timeouts  from  30 \n\
minutes  to  5.5 hours.  A value of 252 signifies a \n\
timeout of 21 minutes, 253  sets  a  vendor-defined \n\
timeout,  and 255 is interpreted as 21 minutes plus \n\
15 seconds. \n\
");


exit(0);
}

static void interpret_standby (unsigned int standby)
{
	printf(" (");
	switch(standby) {
		case 0:		printf("off");
				break;
		case 252:	printf("21 minutes");
				break;
		case 253:	printf("vendor-specific");
				break;
		case 254:	printf("?reserved");
				break;
		case 255:	printf("21 minutes + 15 seconds");
				break;
		default:
			if (standby <= 240) {
				unsigned int secs = standby * 5;
				unsigned int mins = secs / 60;
				secs %= 60;
				if (mins)	  printf("%d minutes", mins);
				if (mins && secs) printf(" + ");
				if (secs)	  printf("%d seconds", secs);
			} else if (standby <= 251) {
				unsigned int mins = (standby - 240) * 30;
				unsigned int hrs  = mins / 60;
				mins %= 60;
				if (hrs)	  printf("%d hours", hrs);
				if (hrs && mins)  printf(" + ");
				if (mins)	  printf("%d minutes", mins);
			} else
				printf("illegal value)\n");
			break;
	}
	printf(")\n");
}


main(int argc, char *argv[])
{
char *s=argv[0];
int i;

/* prgname */

prgname=argv[0];

        while ( *s != '\0' ) {
                if ( *s++ == '/' )
                        prgname = s;
        }

/* syntax */

i=1;

while (i<=argc-1)
{

if ( argv[i][0] == '-' )
        switch( argv[i][1] )
        {
	case 'h': usage();
        case 'S': { standby=atoi(argv[++i]); strcpy(devname,argv[++i]);
		op=SET_STANDBY;
		break;}
	case 'C': op=GET_POWERMODE; strcpy(devname,argv[++i]);
		break;
	case 'y': op=SET_STANDBYNOW; strcpy(devname,argv[++i]);
		break;
        default: usage();
        }
i++;
}

if (op==UNDEF) usage();

/* chech device */

	if (stat(devname,&stat_buf)) {
		perror(devname);
		exit(errno);
	}

	if ((major(stat_buf.st_rdev) == SCSI_DISK_MAJOR) 
#ifdef MD_MAJOR
		|| (major(stat_buf.st_rdev) == MD_MAJOR)
#endif
							) 
		is_scsi_hd = 1;
	else
		if (major(stat_buf.st_rdev) != IDE0_MAJOR
		 && major(stat_buf.st_rdev) != IDE1_MAJOR
#ifdef IDE2_MAJOR
		 && major(stat_buf.st_rdev) != IDE2_MAJOR
#endif
#ifdef IDE3_MAJOR
		 && major(stat_buf.st_rdev) != IDE3_MAJOR
#endif
	                                                      ) {
		fprintf(stderr,"%s is not a hard disk.\n",devname);
		exit(EINVAL);
	}

/* open device */

        fd = open (devname, O_RDONLY);
        if (fd < 0) {
                perror(devname);
                exit(errno);
        }

/* Ok, operate */

switch(op)
{
case SET_STANDBY:
	{
	unsigned char args[4] = {WIN_SETIDLE1,standby,0,0};
	printf("%s: setting standby to %ld", devname,standby);
	interpret_standby(standby);
	if (ioctl(fd, HDIO_DRIVE_CMD, &args))
	perror(" HDIO_DRIVE_CMD failed");
	break;
	}

case GET_POWERMODE:
	{	
	#define WIN_CHECKPOWERMODE      0x98 
                unsigned char args[4] = {WIN_CHECKPOWERMODE,0,0,0};
                const char *state;
                no_scsi();
                if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
                        if (errno != EIO || args[0] != 0 || args[1] != 0) {
                                /* perror(" HDIO_DRIVE_CMD failed"); */
                                state = "unknown";
                        } else {
                                state = "sleeping";
                        }
                } else {
                        if (args[2] == 255)
                                state = "active/idle";
                        else
                                state = "standby";
                }
                printf("%s: drive state is:  %s\n",devname, state);
		break;
		}
case SET_STANDBYNOW:
	{
		#define WIN_STANDBYNOW 0x94   
                unsigned char args[4] = {WIN_STANDBYNOW,0,0,0};
                no_scsi();
                        printf("%s: issuing standby command\n",devname);
                if (ioctl(fd, HDIO_DRIVE_CMD, &args))
                        perror(" HDIO_DRIVE_CMD failed");
	break;
	}

} /* end switch */

} /* end main */
