/*
 * $Source: e:/source/driver/sleep/RCS/dsl.c,v $
 * $Revision: 1.6 $
 * $Date: 1997/06/03 23:28:24 $
 * $Author: vitus $
 *
 * Displays and/or modifies device sleeping timers.  Sample
 * application.
 * Compile with...
 * IBM CSet++ 2.1	- icc -Q -W2all -O [-Gd] dsl.c
 * GNU C 2.7.0		- gcc -Wall -O3 [-Zcrtdll] dsl.c
 * WatCom C/C++ 10.5	- wcl386 -zq -bt=os2v2 -3s -wx -oax dsl.c
 *
 * $Log: dsl.c,v $
 * Revision 1.6  1997/06/03 23:28:24  vitus
 * - expanded '-?' output
 *
 * Revision 1.5  1997/05/07 23:53:27  vitus
 * - tests both forms of version query
 * - calls READ_MSGS to get size of required buffer
 *
 * Revision 1.4  1997/04/07 03:35:03  vitus
 * Changed program options
 * Added possibility to change/display timeout counter
 *
 * Revision 1.3  1997/04/05 01:50:24  vitus
 * DisplayVersion shows registered-flag
 * Test: display datasize before/after call
 *
 * Revision 1.2  1997/03/05 21:58:26  vitus
 * Changed for new timeout in seconds
 * Added mode to display saved messages
 *
 * Revision 1.1  1997/02/06 01:25:42  vitus
 * Initial revision
 *
 */
static char const id[]="$Id: dsl.c,v 1.6 1997/06/03 23:28:24 vitus Exp $";

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define INCL_DOS
#include <os2.h>

#include "dskslpub.h"




#define DEVICENAME	"dsleeps$"		/* DSKSLEEP.FLT is actual... */

#define MODE_DISPLAY	0
#define MODE_CHANGE	1
#define MODE_MESSAGE	2
#define MODE_QUERY	3
#define MODE_IMMEDIATE	4


/*
 * Global variables
 */
char	szPrgName[_MAX_PATH];
char	fVerbose = 0;
char	fMode = MODE_DISPLAY;


void
usage(void)
{
    printf("usage: %s [-?] [-m | -q | {-c|-i} a1,d1,t1 [a2,d2,t2 ...]]\n", szPrgName);
}
void
help(void)
{
    usage();
    printf("\nDisplays and/or changes sleeping times/timers\n"
	   " -m\tdisplay saved messages\n"
	   " -c\tpermanent change of disk timeout (minutes)\n"
	   " -q\tdisplay *seconds* left until disk stops\n"
	   " -i\tchange *seconds* left until disk stops\n"
	   " -?\tthis text\n\n"
	   " a,d,t\ta value ('t' minutes or seconds) for disk 'd' on adapter 'a'\n"
	   "Examples:\n"
	   " 'dsl'\t\t\tdisplay current timeout values\n"
	   " 'dsl -c 1,0,10'\tchange timeout for first disk on second adapter\n\t\t\tto 10 minutes\n"
	   " 'dsl -c 0,0,0'\t\tchange timeout for first disk on first adapter\n\t\t\tto 0 minutes (never stop that disk)\n"
	   " 'dsl -i 1,1,0'\t\tset timer for second disk on second adapter\n\t\t\tto 0 seconds (immediately stop that disk)\n"
	   "\nRemember that any access to a disk will reset it's timer value.  So '-i'\nmight not work for you.\n");
}




/*#
 * NAME
 *	DisplayVersion
 * CALL
 *	DisplayVersion(hd)
 * PARAMETER
 *	hd		handle to open device
 * RETURNS
 *	OS error code
 * GLOBAL
 *	none
 * DESPRIPTION
 *	Displays driver version.
 * REMARKS
 */
int
DisplayVersion(HFILE hd)
{
    APIRET		rc;
    DSKSL_VER_DATA	data;
    ULONG		datasize;


    /* Issue IOCtl in two fashions
     * - 16bit data: only version
     * - DSKSL_VER_DATA: registered info, too */

    do
    {
	datasize = sizeof(data.version);
	rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_QUERY_VERSION,
			 NULL, 0, NULL, &data.version, datasize, &datasize);
	if( rc )
	{
	    fprintf(stderr, "DSKSL_QUERY_VERSION - error %lu\n", rc);
	    break;
	}
	printf("Installed: DSKSLEEP.FLT %u.%02u\n",
	       HIBYTE(data.version), LOBYTE(data.version));

	datasize = sizeof(data);
	rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_QUERY_VERSION,
			 NULL, 0, NULL, &data, datasize, &datasize);
	if( rc )
	{
	    fprintf(stderr, "DSKSL_QUERY_VERSION - error %lu\n", rc);
	    break;
	}

	printf("Installed: DSKSLEEP.FLT %u.%02u (%s)\n",
	       HIBYTE(data.version), LOBYTE(data.version),
	       (data.flags&0x01 ? "registered" : "unregistered"));
    }
    while( 0 );

    return (int)rc;
}




/*#
 * NAME
 *	DisplayMessages
 * CALL
 *	DisplayMessages(hd)
 * PARAMETER
 *	hd		handle to open device
 * RETURNS
 *	OS error code
 * GLOBAL
 *	none
 * DESPRIPTION
 *	Displays messages saved during driver startup.
 * REMARKS
 */
int
DisplayMessages(HFILE hd)
{
    APIRET	rc;
    USHORT	cb = 0;				/* keep compiler happy */
    unsigned	i;
    ULONG	datasize;
    DSKSL_MSGS_DATA * data = NULL;

    do
    {
	/* First: read size of stored messages */

	datasize = sizeof(cb);
	rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_READ_MSGS,
			 NULL, 0, NULL, &cb, datasize, &datasize);
	if( rc )
	{
	    fprintf(stderr, "DSKSL_READ_MSGS - error %lu\n", rc);
	    break;
	}

	/* Second: allocate buffer big enough to hold
	   those messages (but no single byte more) */

	datasize = FIELDOFFSET(DSKSL_MSGS_DATA,msg) + (ULONG)cb;
	data = malloc(datasize);
	if( data == NULL )
	{
	    fprintf(stderr, "out of memory\n");
	    rc = (APIRET)-3;
	    break;
	}

	/* Third: read and display messages */

	rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_READ_MSGS,
			 NULL, 0, NULL, data, datasize, &datasize);
	if( rc )
	{
	    fprintf(stderr, "DSKSL_READ_MSGS - error %lu\n", rc);
	    break;
	}

	printf("------------------------------"
	       "------------------------------\n");
	for( i = 0;
	    i < data->cb - FIELDOFFSET(DSKSL_MSGS_DATA,msg);
	    i += strlen(&data->msg[i])+1 )
	{
	    printf("%s\n", &data->msg[i]);
	}
	printf("------------------------------"
	       "------------------------------\n");
    }
    while( 0 );

    if( data != NULL )
	free(data);
    return (int)rc;
}




/*#
 * NAME
 *	DisplaySettings
 * CALL
 *	DisplaySettings(hd)
 * PARAMETER
 *	hd		handle to open device
 * RETURNS
 *	OS error code
 * GLOBAL
 *	none
 * DESPRIPTION
 *	Displays all devices and timeout values.
 * REMARKS
 */
int
DisplaySettings(HFILE hd)
{
    APIRET	    rc;
    ULONG	    datasize = 4 + 40 * sizeof(DEVICE_TIMEOUT);
    PDSKSL_QL_DATA  data = malloc( datasize );
    unsigned	    i;

    if( data == NULL )
    {
	fprintf(stderr, "out of memory\n");
	return -3;
    }

    if( fVerbose )
	printf("datasize(in) %lu\n", datasize);
    rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_QUERY_TIMEOUT,
		     NULL, 0, NULL, data, datasize, &datasize);
    if( fVerbose )
	printf("datasize(out) %lu\n", datasize);

    if( rc )
	fprintf(stderr, "DSKSL_QUERY_TIMEOUT - error %lu\n", rc);
    else
    {
	/* Calculate number of entries. */

	unsigned cnt = ((data->cb > datasize ?
			datasize : data->cb)
			- FIELDOFFSET(DSKSL_QL_DATA,list))
			 / sizeof(data->list[0]);

	/* Display nice table */

	printf(" Adapter Unit\tMinutes\n");
	for( i = 0; i < cnt; ++i )
	    printf(" %3u\t %3u\t %4lu\n",
		   data->list[i].adapter, data->list[i].unit,
		   data->list[i].seconds / 60UL );
    }

    free( data );
    return (int)rc;
}


		 

/*#
 * NAME
 *	ChangeSetting
 * CALL
 *	ChangeSetting(hd,a,d,m)
 * PARAMETER
 *	a		adapter index
 *	d		unit index
 *	m		minutes before sleep (0: never)
 * RETURNS
 *	OS error code
 * GLOBAL
 *	none
 * DESPRIPTION
 *	Changes the timeout value for a single device.
 * REMARKS
 */
int
ChangeSetting(HFILE hd,UCHAR a,UCHAR d,ULONG m)
{
    APIRET		rc;
    ULONG		parmsize = sizeof(DSKSL_SETTO_PARM);
    DSKSL_SETTO_PARM	parm;

    if( fVerbose )
	printf("--- new: %u %u %lu\n", a, d, m);

    parm.cb = (USHORT)parmsize;
    parm.reserved[0] = parm.reserved[1] = 0;
    parm.list[0].adapter = a;
    parm.list[0].unit = d;
    parm.list[0].seconds = m * 60UL;
    parm.list[0].reserved[0] = parm.list[0].reserved[1] = 0;

    if( fVerbose )
	printf("parmsize(in) %lu\n", parmsize);
    rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_SET_TIMEOUT,
		     &parm, parmsize, &parmsize, NULL, 0, NULL);
    if( fVerbose )
	printf("parmsize(out) %lu\n", parmsize);

    if( rc )
	fprintf(stderr, "DSKSL_SET_TIMEOUT - error %lu\n", rc);
    return (int)rc;
}




/*#
 * NAME
 *	DisplayCurrent
 * CALL
 *	DisplayCurrent(hd)
 * PARAMETER
 *	hd		handle to open device
 * RETURNS
 *	OS error code
 * GLOBAL
 *	none
 * DESPRIPTION
 *	Displays all device timers.
 * REMARKS
 */
int
DisplayCurrent(HFILE hd)
{
    APIRET	rc;
    ULONG	parmsize;
    ULONG	datasize = 4 + 40 * sizeof(DEVICE_TIMEOUT);
    PDSKSL_QL_DATA  data = malloc( datasize );
    unsigned	devcnt;
    unsigned	i;

    if( data == NULL )
    {
	fprintf(stderr, "out of memory\n");
	return -3;
    }

    do
    {
	if( fVerbose )
	    printf("datasize(in) %lu\n", datasize);
	rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_QUERY_TIMEOUT,
			 NULL, 0, NULL, data, datasize, &datasize);
	if( fVerbose )
	    printf("datasize(out) %lu\n", datasize);

	if( rc )
	{
	    fprintf(stderr, "DSKSL_QUERY_TIMEOUT - error %lu\n", rc);
	    break;
	}

	/* Calculate number of entries. */

	devcnt = ((data->cb > datasize ?
		   datasize : data->cb)
		  - FIELDOFFSET(DSKSL_QL_DATA,list))
	    / sizeof(data->list[0]);

	/* Display nice table */

	printf(" Adapter Unit\tSeconds\n");
	for( i = 0; i < devcnt; ++i )
	{
	    DSKSL_DEVSTATE_PARM	coord;
	    DSKSL_DEVSTATE_DATA	current;

	    coord.adapter = data->list[i].adapter;
	    coord.unit = data->list[i].unit;

	    parmsize = sizeof(coord);
	    datasize = sizeof(current);
	    if( fVerbose )
		printf("datasize(in) %lu\n", datasize);
	    rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_QUERY_DEVSTATE,
			     &coord, parmsize, &parmsize,
			     &current, datasize, &datasize);
	    if( fVerbose )
		printf("datasize(out) %lu\n", datasize);

	    if( rc )
	    {
		fprintf(stderr, "DSKSL_QUERY_DEVSTATE - error %lu\n", rc);
		break;
	    }

	    printf(" %3u\t %3u\t %4lu\n",
		   coord.adapter, coord.unit, current.seconds);
	}
    }
    while( 0 );

    free( data );
    return (int)rc;
}




/*#
 * NAME
 *	ChangeCurrent
 * CALL
 *	ChangeCurrent(hd,a,d,s)
 * PARAMETER
 *	a		adapter index
 *	d		unit index
 *	s		seconds before sleep (0: immediate)
 * RETURNS
 *	OS error code
 * GLOBAL
 *	none
 * DESPRIPTION
 *	Changes the current counter for a single device.
 * REMARKS
 */
int
ChangeCurrent(HFILE hd,UCHAR a,UCHAR d,ULONG s)
{
    APIRET		rc;
    ULONG		parmsize = sizeof(DSKSL_DEVSTATE_PARM);
    DSKSL_DEVSTATE_PARM	parm;
    ULONG		datasize = sizeof(DSKSL_DEVSTATE_DATA);
    DSKSL_DEVSTATE_DATA	data;

    if( fVerbose )
	printf("--- new: %u %u %lu\n", a, d, s);

    parm.adapter = a;
    parm.unit = d;
    data.seconds = s;

    if( fVerbose )
    {
	printf("parmsize(in) %lu\n", parmsize);
	printf("datasize(in) %lu\n", datasize);
    }
    rc = DosDevIOCtl(hd, IOCTL_DSKSLEEP_CATEGORY, DSKSL_SET_DEVSTATE,
		     &parm, parmsize, &parmsize,
		     &data, datasize, &datasize);
    if( fVerbose )
    {
	printf("parmsize(in) %lu\n", parmsize);
	printf("datasize(in) %lu\n", datasize);
    }

    if( rc )
	fprintf(stderr, "DSKSL_SET_DEVSTATE - error %lu\n", rc);
    return (int)rc;
}




/*#
 * NAME
 *	main
 * CALL
 *	main(argc,argv)
 * PARAMETER
 *	argc,argv	as usual
 * RETURNS
 *	0		OK
 *	/0		some error
 * GLOBAL
 *	szPrgName, fVerbose
 * DESPRIPTION
 *	Main routine, parses parameter and calls requested
 *	subfunctions.
 * REMARKS
 */
int
main(int argc,char *argv[])
{
    HFILE	hd = 0;
    ULONG	action_taken;
    APIRET	rc;
    int		result;

    strcpy( szPrgName, argv[0] );
    while( argc > 1  &&  argv[1][0] == '-' )
    {
	switch( argv[1][1] )
	{
	  case '?':
	    help();
	    return 0;

	  case 'v':
	    fVerbose = 1;
	    break;

	  case 'm':
	    fMode = MODE_MESSAGE;
	    break;

	  case 'c':
	    fMode = MODE_CHANGE;
	    break;

	  case 'q':
	    fMode = MODE_QUERY;
	    break;

	  case 'i':
	    fMode = MODE_IMMEDIATE;
	    break;

	  default:
	    fprintf(stderr, "%s: unknown arg \"%s\"\n", szPrgName, argv[1]);
	    return -1;
	}
	--argc;
	++argv;
    }

    /* Open device driver, no special settings.  Use DENYNONE
     * so other processes (daemon?) may open concurrently. */

    rc = DosOpen( DEVICENAME, &hd, &action_taken,
		 0, 0,
		 OPEN_ACTION_FAIL_IF_NEW|OPEN_ACTION_OPEN_IF_EXISTS,
		 OPEN_SHARE_DENYNONE|OPEN_ACCESS_READWRITE,
		 NULL );
    if( rc )
    {
	fprintf(stderr, "DosOpen(%s) - error %lu\n", DEVICENAME, rc);
	return (int)rc;
    }

    DisplayVersion(hd);				/* always display version */
    switch( fMode )
    {
      case MODE_MESSAGE:
	if( argc != 1 )
	{
	    fprintf(stderr, "argument count error\n");
	    result = -1;
	}
	else
	    result = DisplayMessages(hd);
	break;

      case MODE_DISPLAY:
	if( argc != 1 )
	{
	    fprintf(stderr, "argument count error\n");
	    result = -1;
	}
	else
	{
	    printf("Current settings:\n");
	    result = DisplaySettings(hd);
	}
	break;

      case MODE_CHANGE:
	printf("Current settings:\n");
	result = DisplaySettings(hd);
	if( result != 0 )
	    break;				/* don't change */
	if( argc <= 1 )				/* at least one triple */
	{
	    fprintf(stderr, "missing device timeout\n");
	    result = -1;
	}
	else
	{
	    for(; argc > 1; --argc,++argv )
	    {
		unsigned a = 0xFF, d = 0xFF;	/* dsksleep won't find them... */
		ULONG	 m = 0;

		/* Parse argument (spaces important!) and
		 * call ChangeSetting(). */

		sscanf(argv[1], "%u ,%u ,%lu", &a, &d, &m);
		result = ChangeSetting(hd, (UCHAR)a, (UCHAR)d, m);
	    }
	    printf("After changing:\n");
	    DisplaySettings(hd);		/* ignore errors */
	}
	break;

      case MODE_QUERY:
	if( argc != 1 )
	{
	    fprintf(stderr, "argument count error\n");
	    result = -1;
	}
	else
	    result = DisplayCurrent(hd);
	break;

      case MODE_IMMEDIATE:
	if( argc <= 1 )				/* at least one triple */
	{
	    fprintf(stderr, "missing device timeout\n");
	    result = -1;
	}
	else
	{
	    for(; argc > 1; --argc,++argv )
	    {
		unsigned a = 0xFF, d = 0xFF;	/* dsksleep won't find them... */
		ULONG	 m = 0xFFFFFF;

		/* Parse argument (spaces important!) and
		 * call ChangeCurrent(). */

		sscanf(argv[1], "%u ,%u ,%lu", &a, &d, &m);
		result = ChangeCurrent(hd, (UCHAR)a, (UCHAR)d, m);
	    }
	    printf("After changing:\n");
	    DisplayCurrent(hd);			/* ignore errors */
	}
	break;

      default:
	result = -1;
	break;
    }

    DosClose( hd );				/* be a nice guy */

    return result;
}
