/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: device_error.c,v 1.4.2.1 92/06/24 13:27:54 marker Exp $"
#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmips.h>
#include <sys/errno.h>
#include <sys/stat.h>

extern int errno;
int first_call = 1;

main(argc, argv)
	int argc;
	char **argv;
{
	extern int optind;
	extern char *optarg;
	int start = 0;
	int count = 1;
	int repeat = 1;
	int type = 0;
	int base = 0;
	int length = 0;
	int error = 0;
	int device = 0;
	int ioctl = 0;
	int ret;
	struct device_error mydevice;
	signed char c;
	int errflg = 0;
	struct stat buf;
	char path[MAXPATHLEN];

	if (argc == 1) {
		Usage (0);
	}

	argc--;
	argv++;

	if (strcmp (argv[0], "add") == 0) {
		if (argc == 1) {
			Usage (1);
		}
		if (argv[1][0] != '-') {
			if ((ret = stat(argv[1], &buf)) < 0) {
				if (errno == ENOENT) {
					memcpy (path, "/dev/dsk/", 9);
					strcpy (&path[9], argv[1]);
					ret = stat(path, &buf);
				}
				if (ret < 0) {
					perror("device_error: stat device");
					exit (2);
				} 
			}
			device = buf.st_rdev;
			argv++;
			argc--;
		}

		while ((c = getopt(argc, argv, "s:c:r:b:l:e:t:i:d:")) != EOF) {
			switch (c) {
			case 'd':
				if ((ret = stat(optarg, &buf)) < 0) {
					if (errno == ENOENT) {
						memcpy (path, "/dev/dsk/", 9);
						strcpy (&path[9], optarg);
						ret = stat(path, &buf);
					}
					if (ret < 0) {
						perror("device_error: stat device");
						exit (2);
					} 
				}
				device = buf.st_rdev;
				break;

			case 'i':
				ioctl = atoi (optarg);
				break;

			case 's':
				start = atoi (optarg);
				break;

			case 'c':
				count = atoi (optarg);
				break;

			case 'r':
				repeat = atoi (optarg);
				break;

			case 't':
				type |= atot (optarg);
				break;

			case 'b':
				base = atoi (optarg);
				break;

			case 'l':
				length = atoi (optarg);
				break;

			case 'e':
				error = atoe (optarg);
				break;

			case '?':
			default:
				errflg++;
				break;
			}
		}
		if (errflg) {
			Usage (1);
		}

		mydevice.version = DEVICE_ERROR_VERSION_1;
		mydevice.index = 0;
		mydevice.dev = device;
		mydevice.number = 0;
		mydevice.start = start;
		mydevice.count = count;
		mydevice.repeat = repeat;
		mydevice.base = base;
		mydevice.extent = length;
		mydevice.error = error;
		mydevice.ioctl = ioctl;
		if (type == 0) {
			mydevice.type = DEVICE_READ_ERROR;
		} else {
			mydevice.type = type;
		}

		if ((ret = sysmips (MIPS_DEVICE_ERROR, DEVICE_ERROR_ADD_ENTRY, &mydevice)) < 0) {
			perror ("device_error: add");
			exit (2);
		} else {
			printf ("device_error: index: %d.\n", ret);
		}
	} else if (strcmp (argv[0], "delete") == 0) {
		if (argc < 2) {
			Usage (2);
		} else {
			argc--;
			argv++;
			while (argc) {
				device = atoi (argv[0]);
				if ((ret = sysmips (MIPS_DEVICE_ERROR, DEVICE_ERROR_DELETE_ENTRY, NULL, device)) < 0) {
					perror ("device_error: delete");
					exit (2);
				}
				argc--;
				argv++;
			}
		}
	} else if (strcmp (argv[0], "get") == 0) {
		if (argc < 2) {
			Usage (3);
		} else {
			argc--;
			argv++;
			while (argc) {
				device = atoi (argv[0]);
				if ((ret = sysmips (MIPS_DEVICE_ERROR, DEVICE_ERROR_GET_ENTRY, &mydevice, device)) < 0) {
					perror ("device_error: get");
					exit (2);
				}
				display_device (&mydevice);
				argc--;
				argv++;
			}
		}
	} else if (strcmp (argv[0], "list") == 0) {
		if (argc != 1) {
			Usage (4);
		} else {
			count = 0;
			while ((count = sysmips (MIPS_DEVICE_ERROR, DEVICE_ERROR_GET_NEXT_ENTRY, &mydevice, count)) > 0) {
				display_device (&mydevice);
			}
			if (count < 0) {
				perror ("device_error: list");
				exit (2);
			}
		}
	} else Usage (0);

	exit (0);
}

int
atoe (str)
	char *str;
{
	extern int sys_nerr;
	extern char *sys_errnolist[];
	int idx;

	for (idx = 0; idx < sys_nerr; idx++) {
		if (strcmp (sys_errnolist[idx], str) == 0) {
			break;
		}
	}

	if (idx == sys_nerr) {
		return (0);
	} else {
		return (idx);
	}
}

char *
etoa (error)
	int error;
{
	extern int sys_nerrno;
	extern char *sys_errnolist[];
	static char errstr[64];

	if ((error < 0) || (error >= sys_nerr)) {
		sprintf (errstr, "%d", error);
		return (errstr);
	} else {
		return sys_errnolist[error];
	}
}

int
atot (str)
	char *str;
{
	u_int type;

	type = 0;
	while (*str) {
		switch (*str) {
		case 'r':
			type |= DEVICE_READ_ERROR;
			break;

		case 'w':
			type |= DEVICE_WRITE_ERROR;
			break;

		case 'o':
			type |= DEVICE_OPEN_ERROR;
			break;

		case 'c':
			type |= DEVICE_CLOSE_ERROR;
			break;

		case 'i':
			type |= DEVICE_IOCTL_ERROR;
			break;

		default:
			printf ("device_error: unknown type '%s'\n", *str);
			exit (3);
		}
		str++;
	}
	return (type);
}

char *
ttoa (type)
	u_int type;
{
	static char str[6], *p;

	memset (str, NULL, sizeof (str));
	p = str;
	if (type & DEVICE_READ_ERROR) {
		*p = 'r';
		p++;
	}
	if (type & DEVICE_WRITE_ERROR) {
		*p = 'w';
		p++;
	}
	if (type & DEVICE_OPEN_ERROR) {
		*p = 'o';
		p++;
	}
	if (type & DEVICE_CLOSE_ERROR) {
		*p = 'c';
		p++;
	}
	if (type & DEVICE_IOCTL_ERROR) {
		*p = 'i';
		p++;
	}
	return (str);
}

int
display_device (device)
	struct device_error *device;
{
	if (first_call) {
		first_call = 0;
		printf ("%5s%7s%7s%6s%6s%7s%6s%7s%7s%6s%6s\n",
			"Index", "Device", "Number", "Start", "Count",
			"Repeat", "Type", "Base", "Length", "Ioctl", "Error");
	}

	printf ("%5d%3d,%-3d%7d%6d%6d%7d%6s%7d%7d%6d%6s\n",
		device->index, device->dev >> 8, device->dev & 0xFF,
		device->number, device->start,
		device->count, device->repeat, ttoa (device->type),
		device->base, device->extent, device->ioctl, etoa (device->error));
}

int
Usage(option)
	int option;
{
	switch (option) {
	case 0:			/* */
		printf ("Usage: device_error [add | delete | get | list]\n");
		break;

	case 1:			/* add */
		printf ("Usage: device_error add [device] [-s start] [-c count] [-r repeat] [-t type] [-b base] [-l length] [-e error] [-i ioctl] [-d device]\n");
		break;

	case 2:			/* delete */
		printf ("Usage: device_error delete index\n");
		break;

	case 3:			/* get */
		printf ("Usage: device_error get index\n");
		break;

	case 4:			/* list */
		printf ("Usage: device_error list\n");
		break;

	default:
		printf ("device_error: unknown option\n");
		break;
	}

	exit (1);
}
