/* assorted platform-independent routines */

#include <dirent.h>
#include <malloc.h>
#include <stdarg.h>
#include <stdio.h>

#include <support/SupportDefs.h>
#include <drivers/Drivers.h>
#include <drivers/KernelExport.h>

#include <driver_settings.h>
#include <driver_settings_p.h>

#include <sio.h>

#include "bt_misc.h"
#include "platform.h"
#include "bios.h"
#include "video.h"

bool dprintf_enabled = FALSE;
bool boot_menu_enabled = FALSE;
bool fast_boot_enabled = FALSE;

/* stub for sio.c */
bool debug_output_enabled(void)
{
	return dprintf_enabled;
}

#if _SUPPORTS_RELEASE_MODE
bool is_release_mode(void)
{
	/*
	 * most of this is ripped from bootcomplete
	 *
	 * BeIA MBR is composed of 3 sectors, the first one is
	 * the acutal code, the second one is a flag sector used
	 * for boot failure detection. The third one are the
	 * string messages for the MBR.
	 *
	 * This third sector is also used for disabling bootkeys
	 * when the system is converted to release mode.
	 *
	 * If this sector contain at offset 0xF0 the string "Tori"
	 * and the checksum for the whole sector is 0 the bootkeys
	 * are disabled.
	 */

#	define RELEASEMODE_DISABLED  -1
#	define RELEASEMODE_DONT_KNOW  0
#	define RELEASEMODE_ENABLED    1

	static int release_mode_state= RELEASEMODE_DONT_KNOW;
	static char buffer[2048];

	if(release_mode_state== RELEASEMODE_DISABLED) {
		return false;
	}

	if(release_mode_state== RELEASEMODE_ENABLED) {
		return true;
	}

	release_mode_state= RELEASEMODE_DISABLED;	// sensible default

	read_disk(0x80, 2, buffer, 1);

	if(
		(buffer[0xF0]== 'T') &&
		(buffer[0xF1]== 'o') &&
		(buffer[0xF2]== 'r') &&
		(buffer[0xF3]== 'i')
	) {
		int i;
		unsigned char acum= 0;
		for(i= 0; i< 512; i++) {
			acum+= buffer[i];
		}
		
		if(acum== 0) {
			release_mode_state= RELEASEMODE_ENABLED;
		}
	}

	return release_mode_state== RELEASEMODE_ENABLED;
}
#endif

void check_boot_keys(void)
{
#if _SUPPORTS_RELEASE_MODE
    if(is_release_mode()) {
        return;
    }
#endif

	/* check alt, ctrl, or shift held down */
	if (platform_shift_state() & 0x0f)
		boot_menu_enabled = TRUE;

	while (platform_key_hit()) {
		int i;
		i = platform_get_key();
		if ((i == KEY_F1) || (i == KEY_DEL) || (i == KEY_BS)) {
			bool ode = dprintf_enabled;
			dprintf_enabled = TRUE;
			if (!ode) dprintf("debug output enabled\n");
		} if ((i == ' ') || (i == KEY_F1) || (i == KEY_F8))
			boot_menu_enabled = TRUE;
		if (i == KEY_ESC)
			fast_boot_enabled = TRUE;
	}
}

struct command_line_options *command_line_options = NULL;

void parse_command_line_options(void)
{
	struct command_line_options *p = command_line_options, *q;

	while (p) {
		char *s = p->options;
		while (*s && (*s != ':') && (*s != ' ' ) && (*s != '{') && (*s != ','))
			s++;
		if (*s == ':') {
			*s = 0;
			add_safemode_setting("driveroption ");
			add_safemode_setting(p->options);
			add_safemode_setting(" { ");
			add_safemode_setting(s+1);
			add_safemode_setting(" }\n");
		} else {
			add_safemode_setting(p->options);
			add_safemode_setting("\n");
		}
		q = p;
		p = p->next;
		free(q);
	}

	command_line_options = NULL;
}

void spin (bigtime_t usecs)
{
	bigtime_t now;

	now = system_time();

	while ((system_time() - now) < usecs)
		;
}

int printf(const char *format, ...)
{
	va_list args;
	char buf[255];
	int ret;
	
	va_start(args, format);
	ret = vsprintf(buf, format, args);
	console_print(buf);
	va_end(args);

	return ret;
}

void dprintf(const char *format, ...)
{
	va_list args;
	char buf[255];
	
	va_start(args, format);
	vsprintf(buf, format, args);
	sccwrite(0, buf, strlen(buf));
	if (dprintf_enabled) printf(buf);
	va_end(args);
}

void panic(const char *format, ...)
{
	va_list args;
	char buf[255];

	sccwrite(0, "BOOT PANIC: ", 12);

	va_start(args, format);
	vsprintf(buf, format, args);
	sccwrite(0, buf, strlen(buf));
	platform_enter_console_mode();
	printf(buf);
	va_end(args);

	sccwrite(0, "\r\n", 2);
#if _SUPPORTS_RELEASE_MODE
#if INTEL
	{
		extern bool is_release_mode(void);
		extern void write_io_8(int, uchar);
	
		if(is_release_mode()) {
		    char b[16];
			volatile uint16 * ptr;

//			writertc(SSB, 0);		/* Normal cold or warm reboot flag */
			ptr=(volatile uint16 *)(0x472);
			*ptr = 0x1234;			/* warm reboot magic value */

//			wbinvd();
//			spin(1000);

			/* trigger a reset using keyboard port */
			write_io_8(0x64, 0xfe);
			
			/* fault the cpu */
			memset(b, 0, sizeof(b));
			asm(" movl %0, %%eax; lidt (%%eax)"::"p"(b));
		}
	}
#else
#error Please create a generic version of _SUPPORTS_RELEASE_MODE in panic()
#endif
#endif
	while (1) ;
}

status_t
recurse(const char *path, status_t (*f)(void *, const char *), void *cookie)
{
	status_t err = B_OK;
	DIR *dirp;

	dirp = opendir(path);
	if (!dirp) return ENOENT;
	while (readdir(dirp)) {
		char *fname;
		struct stat st;
		fname = malloc(strlen(path) + strlen(dirp->ent.d_name) + 2);
		sprintf(fname, "%s/%s", path, dirp->ent.d_name);
		stat(fname, &st);
		if (S_ISDIR(st.st_mode))
			err = recurse(fname, f, cookie);
		else
			err = (*f)(cookie, fname);
		free(fname);
		if (err != B_OK) break;
	}
	closedir(dirp);

	return err;
}

#include <partition.h>
#include <intel_map.h>

static status_t scan_partition(void *cookie, const char *path)
{
	uchar MBR[512];
	int fd, index;

	if (strcmp(path + strlen(path) - 4, "/raw"))
		return 0;

	fd = open(path, 0);
	if (fd < 0)
		panic("scan_partition: unable to open %s\n", path);

	if (read_pos(fd, 0LL, MBR, 512) < 512) {
		dprintf("scan_partition: unable to get MBR for %s\n", path);
		close(fd);
		return 0;
	}

	/* scan partitions */
	if (id_intel_partition(MBR, 512)) {
		for (index=0;;index++) {
			struct _partition_info_ partition;
			if (get_nth_intel_partition(fd, MBR, 0, index,
					&partition, NULL, NULL, NULL) == B_OK) {
				partition_info p;

				if (partition.nblocks == 0) continue;
				
				p.offset = partition.sblock * 0x200;
				p.size = partition.nblocks * 0x200;
				p.logical_block_size = 0x200;
				p.session = 0;
				p.partition = index;
				strcpy(p.device, path + sizeof("/dev/") - 1);

				ioctl(fd, B_SET_PARTITION, &p, sizeof(p));
			} else if (index >= 4)
				break;
		}
	}

	close(fd);

	return 0;
}

status_t scan_partitions()
{
	return recurse("/dev", scan_partition, NULL);
}

uchar colormap[] = {
	0x00,0x00,0x00, 0x08,0x08,0x08, 0x10,0x10,0x10, 0x18,0x18,0x18, 
	0x20,0x20,0x20, 0x28,0x28,0x28, 0x30,0x30,0x30, 0x38,0x38,0x38, 
	0x40,0x40,0x40, 0x48,0x48,0x48, 0x50,0x50,0x50, 0x58,0x58,0x58, 
	0x60,0x60,0x60, 0x68,0x68,0x68, 0x70,0x70,0x70, 0x78,0x78,0x78, 
	0x80,0x80,0x80, 0x88,0x88,0x88, 0x90,0x90,0x90, 0x98,0x98,0x98, 
	0xa0,0xa0,0xa0, 0xa8,0xa8,0xa8, 0xb0,0xb0,0xb0, 0xb8,0xb8,0xb8, 
	0xc0,0xc0,0xc0, 0xc8,0xc8,0xc8, 0xd0,0xd0,0xd0, 0xd8,0xd8,0xd8, 
	0xe0,0xe0,0xe0, 0xe8,0xe8,0xe8, 0xf0,0xf0,0xf0, 0xf8,0xf8,0xf8, 
	0x00,0x00,0xff, 0x00,0x00,0xe5, 0x00,0x00,0xcc, 0x00,0x00,0xb3, 
	0x00,0x00,0x9a, 0x00,0x00,0x81, 0x00,0x00,0x69, 0x00,0x00,0x50, 
	0x00,0x00,0x37, 0x00,0x00,0x1e, 0xff,0x00,0x00, 0xe4,0x00,0x00, 
	0xcb,0x00,0x00, 0xb2,0x00,0x00, 0x99,0x00,0x00, 0x80,0x00,0x00, 
	0x69,0x00,0x00, 0x50,0x00,0x00, 0x37,0x00,0x00, 0x1e,0x00,0x00, 
	0x00,0xff,0x00, 0x00,0xe4,0x00, 0x00,0xcb,0x00, 0x00,0xb2,0x00, 
	0x00,0x99,0x00, 0x00,0x80,0x00, 0x00,0x69,0x00, 0x00,0x50,0x00, 
	0x00,0x37,0x00, 0x00,0x1e,0x00, 0x00,0x98,0x33, 0xff,0xff,0xff, 
	0xcb,0xff,0xff, 0xcb,0xff,0xcb, 0xcb,0xff,0x98, 0xcb,0xff,0x66, 
	0xcb,0xff,0x33, 0xcb,0xff,0x00, 0x98,0xff,0xff, 0x98,0xff,0xcb, 
	0x98,0xff,0x98, 0x98,0xff,0x66, 0x98,0xff,0x33, 0x98,0xff,0x00, 
	0x66,0xff,0xff, 0x66,0xff,0xcb, 0x66,0xff,0x98, 0x66,0xff,0x66, 
	0x66,0xff,0x33, 0x66,0xff,0x00, 0x33,0xff,0xff, 0x33,0xff,0xcb, 
	0x33,0xff,0x98, 0x33,0xff,0x66, 0x33,0xff,0x33, 0x33,0xff,0x00, 
	0xff,0x98,0xff, 0xff,0x98,0xcb, 0xff,0x98,0x98, 0xff,0x98,0x66, 
	0xff,0x98,0x33, 0xff,0x98,0x00, 0x00,0x66,0xff, 0x00,0x66,0xcb, 
	0xcb,0xcb,0xff, 0xcb,0xcb,0xcb, 0xcb,0xcb,0x98, 0xcb,0xcb,0x66, 
	0xcb,0xcb,0x33, 0xcb,0xcb,0x00, 0x98,0xcb,0xff, 0x98,0xcb,0xcb, 
	0x98,0xcb,0x98, 0x98,0xcb,0x66, 0x98,0xcb,0x33, 0x98,0xcb,0x00, 
	0x66,0xcb,0xff, 0x66,0xcb,0xcb, 0x66,0xcb,0x98, 0x66,0xcb,0x66, 
	0x66,0xcb,0x33, 0x66,0xcb,0x00, 0x33,0xcb,0xff, 0x33,0xcb,0xcb, 
	0x33,0xcb,0x98, 0x33,0xcb,0x66, 0x33,0xcb,0x33, 0x33,0xcb,0x00, 
	0xff,0x66,0xff, 0xff,0x66,0xcb, 0xff,0x66,0x98, 0xff,0x66,0x66, 
	0xff,0x66,0x33, 0xff,0x66,0x00, 0x00,0x66,0x98, 0x00,0x66,0x66, 
	0xcb,0x98,0xff, 0xcb,0x98,0xcb, 0xcb,0x98,0x98, 0xcb,0x98,0x66, 
	0xcb,0x98,0x33, 0xcb,0x98,0x00, 0x98,0x98,0xff, 0x98,0x98,0xcb, 
	0x98,0x98,0x98, 0x98,0x98,0x66, 0x98,0x98,0x33, 0x98,0x98,0x00, 
	0x66,0x98,0xff, 0x66,0x98,0xcb, 0x66,0x98,0x98, 0x66,0x98,0x66, 
	0x66,0x98,0x33, 0x66,0x98,0x00, 0x33,0x98,0xff, 0x33,0x98,0xcb, 
	0x33,0x98,0x98, 0x33,0x98,0x66, 0x33,0x98,0x33, 0x33,0x98,0x00, 
	0xe6,0x86,0x00, 0xff,0x33,0xcb, 0xff,0x33,0x98, 0xff,0x33,0x66, 
	0xff,0x33,0x33, 0xff,0x33,0x00, 0x00,0x66,0x33, 0x00,0x66,0x00, 
	0xcb,0x66,0xff, 0xcb,0x66,0xcb, 0xcb,0x66,0x98, 0xcb,0x66,0x66, 
	0xcb,0x66,0x33, 0xcb,0x66,0x00, 0x98,0x66,0xff, 0x98,0x66,0xcb, 
	0x98,0x66,0x98, 0x98,0x66,0x66, 0x98,0x66,0x33, 0x98,0x66,0x00, 
	0x66,0x66,0xff, 0x66,0x66,0xcb, 0x66,0x66,0x98, 0x66,0x66,0x66, 
	0x66,0x66,0x33, 0x66,0x66,0x00, 0x33,0x66,0xff, 0x33,0x66,0xcb, 
	0x33,0x66,0x98, 0x33,0x66,0x66, 0x33,0x66,0x33, 0x33,0x66,0x00, 
	0xff,0x00,0xff, 0xff,0x00,0xcb, 0xff,0x00,0x98, 0xff,0x00,0x66, 
	0xff,0x00,0x33, 0xff,0xaf,0x13, 0x00,0x33,0xff, 0x00,0x33,0xcb, 
	0xcb,0x33,0xff, 0xcb,0x33,0xcb, 0xcb,0x33,0x98, 0xcb,0x33,0x66, 
	0xcb,0x33,0x33, 0xcb,0x33,0x00, 0x98,0x33,0xff, 0x98,0x33,0xcb, 
	0x98,0x33,0x98, 0x98,0x33,0x66, 0x98,0x33,0x33, 0x98,0x33,0x00, 
	0x66,0x33,0xff, 0x66,0x33,0xcb, 0x66,0x33,0x98, 0x66,0x33,0x66, 
	0x66,0x33,0x33, 0x66,0x33,0x00, 0x33,0x33,0xff, 0x33,0x33,0xcb, 
	0x33,0x33,0x98, 0x33,0x33,0x66, 0x33,0x33,0x33, 0x33,0x33,0x00, 
	0xff,0xcb,0x66, 0xff,0xcb,0x98, 0xff,0xcb,0xcb, 0xff,0xcb,0xff, 
	0x00,0x33,0x98, 0x00,0x33,0x66, 0x00,0x33,0x33, 0x00,0x33,0x00, 
	0xcb,0x00,0xff, 0xcb,0x00,0xcb, 0xcb,0x00,0x98, 0xcb,0x00,0x66, 
	0xcb,0x00,0x33, 0xff,0xe3,0x46, 0x98,0x00,0xff, 0x98,0x00,0xcb, 
	0x98,0x00,0x98, 0x98,0x00,0x66, 0x98,0x00,0x33, 0x98,0x00,0x00, 
	0x66,0x00,0xff, 0x66,0x00,0xcb, 0x66,0x00,0x98, 0x66,0x00,0x66, 
	0x66,0x00,0x33, 0x66,0x00,0x00, 0x33,0x00,0xff, 0x33,0x00,0xcb, 
	0x33,0x00,0x98, 0x33,0x00,0x66, 0x33,0x00,0x33, 0x33,0x00,0x00, 
	0xff,0xcb,0x33, 0xff,0xcb,0x00, 0xff,0xff,0x00, 0xff,0xff,0x33, 
	0xff,0xff,0x66, 0xff,0xff,0x98, 0xff,0xff,0xcb, 0xff,0xff,0xff
};

/* stubs to placate linker */
int errno = B_ERROR;
int *_errnop(void) { return &errno; }
const char *strerror(status_t error) { return "err"; }
