/* From p_gortmaker@yahoo.com Wed Jan 24 19:50:14 2001 */
/*
Perhaps you want a copy of this for your site (along with hdtypes).
It is in the same vein - i.e. for stone age machines.  Came across
an old Olivetti 386sx16 which doesn't have BIOS option to set 2nd
disk. BIOS ROM has multi language support so control of many
options typically handled by BIOS are instead dealt with by DOS
configuration program which is alas long since lost before I ever
obtained the machine...

Paul.
*/

/*
 *
 * Print or set hard disk types in the CMOS RAM. Relies on /dev/nvram
 * driver being present in the kernel (or module loaded). To set either
 * drive type you must supply number for both drive types. BIOS table
 * of C/H/S parameters for each type can be obtained from hdtypes.c
 *
 * Compile: gcc -s -Wall cmoshdtype.c -o cmoshdtype
 * Usage: cmoshdtype [drive0 drive1]
 *
 * Tech info:
 *   CMOS 0x12 has disk0 data in high nibble, disk1 data in low nibble.
 *   If data is 0 to 0x0e then that is the type (i.e. zero to 14).
 *   If data is 0x0f then type is greater than 15 and stored in 
 *   CMOS 0x19 (0x1a for drive1).  Type 15 doesn't exist for this reason.
 *
 *						Paul Gortmaker, 08/2000.
 *
 * This code is released under the GPL.
 *
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/nvram.h>


/* /dev/nvram excludes 14 registers of RTC */
#define RTC_OFFSET 0x0e

int
main (int argc, char **argv) {

	int fd, i, set=0, hd0, hd1, twelve_high, twelve_low;
	unsigned char data;

	fd = open("/dev/nvram", O_RDWR);
	if (fd == -1) {
		perror("open - /dev/nvram");
		exit(errno);
	}

	lseek(fd, 0x0e - RTC_OFFSET, SEEK_SET);
	read(fd, &data, 1);
	if (data != 0) {
		printf("CMOS status byte is non-zero - exiting!\n");
		exit(-1);
	}

	lseek(fd, 0x12 - RTC_OFFSET, SEEK_SET);
	read(fd, &data, 1);

	twelve_high = (data&0xf0)>>4;
	twelve_low = data&0x0f;

	data = twelve_high;
	if (twelve_high == 0x0f) {
		lseek(fd, 0x19 - RTC_OFFSET, SEEK_SET);
		read(fd, &data, 1);
	}
	printf("Current Disk 0: Type %d\n", data);

	data = twelve_low;
	if (twelve_low == 0x0f) {
		lseek(fd, 0x1a - RTC_OFFSET, SEEK_SET);
		read(fd, &data, 1);
	}
	printf("Current Disk 1: Type %d\n", data);

	if (argc == 3) {
		hd0 = atoi(argv[1]);
		hd1 = atoi(argv[2]);
		set=1;
	}
	if (set == 0) exit(0);

	if (hd0 == 15 || hd0 > 50) exit(-EINVAL);
	if (hd1 == 15 || hd1 > 50) exit(-EINVAL);

	printf("Setting disk0 to type %d, disk1 to type %d\n", hd0, hd1);

	twelve_high = hd0 < 0x0f ? hd0 : 0x0f;
	twelve_low = hd1 < 0x0f ? hd1 : 0x0f;

	data=(twelve_high<<4) + twelve_low;

	lseek(fd, 0x12 - RTC_OFFSET, SEEK_SET);
	write(fd, &data, 1);

	if (twelve_high == 0x0f) {
		data = hd0;
		lseek(fd, 0x19 - RTC_OFFSET, SEEK_SET);
		write(fd, &data, 1);
	}

	if (twelve_low == 0x0f) {
		data = hd1;
		lseek(fd, 0x1a - RTC_OFFSET, SEEK_SET);
		write(fd, &data, 1);
	}

	i = ioctl(fd, NVRAM_SETCKS);
	if (i == -1) {
		perror("ioctl NVRAM_SETCKS - /dev/nvram");
		exit(errno);
	}
	return 0;
}
