/*	$NetBSD: devopen.c,v 1.5 2011/12/25 06:09:09 tsutsui Exp $	*/

/*-
 * Copyright (c) 2004 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by UCHIYAMA Yasushi.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>

#include <netinet/in.h>
#include <lib/libsa/dev_net.h>
#include <lib/libsa/ufs.h>
#include <lib/libsa/nfs.h>
#include <lib/libsa/dev_net.h>
#include <machine/sbd.h>

#include "local.h"

extern uint8_t kernel_binary[];
extern int kernel_binary_size;

extern struct fs_ops datafs_ops;
extern struct fs_ops bfs_ops;
struct fs_ops ufs_ops = FS_OPS(ufs);
struct fs_ops nfs_ops = FS_OPS(nfs);

extern struct devsw netdevsw;
extern struct devsw dkdevsw;
char fname[16];

/* Referenced by libsa/open.c */
struct fs_ops file_system[1];
int nfsys = 1;
struct devsw devsw[1];
int ndevs = 1;

int
devopen(struct open_file *f, const char *request, char **file)
{
	char *p, *filename;
	int disk, partition;
	void *addr;
	size_t size;

	strcpy(fname, request);

	filename = 0;
	for (p = fname; *p; p++) {
		if (*p == ':') {
			filename = p + 1;
			*p = '\0';
			break;
		}
	}

	if (filename == 0) {	/* not a loader's request. probably ufs_ls() */
		printf("request=%s\n", request);
		f->f_dev = &dkdevsw;
		file_system[0] = ufs_ops;
		devsw[0] = dkdevsw;
		*file = "/";
		return 0;
	}

	/* Data section */
	if (strcmp(fname, "mem") == 0) {
		data_attach(kernel_binary, kernel_binary_size);
		*file = "noname";
		f->f_flags |= F_NODEV;
		file_system[0] = datafs_ops;
		printf("data(compiled):noname\n");
		return 0;
	}

	/* NFS boot */
	if (strcmp(fname, "nfs") == 0) {
		if (!DEVICE_CAPABILITY.network_enabled) {
			printf("Network disabled.\n");
			return -1;
		}
		try_bootp = true;
		file_system[0] = nfs_ops;
		f->f_dev = &netdevsw;
		if (*filename == '\0') {
			printf("set kernel filename. ex.) nfs:netbsd\n");
			return 1;
		}
		*--filename = '/';
		*file = filename;
		printf("nfs:/%s\n", filename);
		net_open(f);
		return 0;
	}

	/* FD boot */
	if (strcmp(fname, "fd") == 0) {
		printf("floppy(boot):/%s (ustarfs)\n", filename);
		f->f_dev = &dkdevsw;
		file_system[0] = datafs_ops;
		devsw[0] = dkdevsw;
		device_attach(NVSRAM_BOOTDEV_FLOPPYDISK, -1, -1);
		if (!ustarfs_load(filename, &addr, &size))
			return -1;
		data_attach(addr, size);
		*file = filename;
		return 0;
	}

	/* Disk boot */
	if (strncmp(fname, "sd", 2) == 0) {
		enum fstype fs;
		if (!DEVICE_CAPABILITY.disk_enabled) {
			printf("Disk disabled.\n");
			return -1;
		}

		disk = fname[2] - '0';
		partition = fname[3] - 'a';
		if (disk < 0 || disk > 9 || partition < 0 || partition > 15) {
			fs = FSTYPE_USTARFS;
			printf("disk(boot):%s ", filename);
			device_attach(NVSRAM_BOOTDEV_HARDDISK, -1, -1);
		} else {
			fs = fstype(partition);
			printf("disk(%d,%d):/%s ",
			    disk, partition, filename);
			device_attach(NVSRAM_BOOTDEV_HARDDISK, disk, partition);
		}

		switch (fs) {
		case FSTYPE_UFS:
			printf(" (ufs)\n");
			f->f_dev = &dkdevsw;
			file_system[0] = ufs_ops;
			devsw[0] = dkdevsw;
			break;
		case FSTYPE_BFS:
			printf(" (bfs)\n");
			f->f_flags |= F_NODEV;
			file_system[0] = bfs_ops;
			break;
		case FSTYPE_USTARFS:
			printf(" (ustarfs)\n");
			f->f_dev = &dkdevsw;
			file_system[0] = datafs_ops;
			devsw[0] = dkdevsw;
			if (!ustarfs_load(filename, &addr, &size))
				return -1;
			data_attach(addr, size);
			break;
		}
		*file = filename;
		return 0;
	}

	printf("%s invalid.\n", fname);

	return -1;
}
