/*
 *  linux/kernel/blk_drv/ramdisk.c
 *
 *  Written by Theodore Ts'o, 12/2/91
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 */


#include <linux/config.h>
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/segment.h>

#include <machine/ktypes.h>
#include <machine/sys_info.h>

#define MAJOR_NR 1
#include "blk.h"

extern ulong mm_phys_to_virt (ulong addr);

char	*rd_start;
int	rd_length = 0;

static void do_rd_request(void)
{
	int	len;
	char	*addr;

repeat:
	INIT_REQUEST;
	addr = rd_start + (CURRENT->sector << 9);
	len = CURRENT->nr_sectors << 9;
	if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) {
		end_request(0);
		goto repeat;
	}
	if (CURRENT-> cmd == WRITE) {
		(void ) memcpy(addr,
			      CURRENT->buffer,
			      len);
	} else if (CURRENT->cmd == READ) {
		(void) memcpy(CURRENT->buffer,
			      addr,
			      len);
	} else
		panic("unknown ramdisk-command");
	end_request(1);
	goto repeat;
}

static struct file_operations rd_fops = {
	NULL,			/* lseek - default */
	block_read,		/* read - general block-dev read */
	block_write,		/* write - general block-dev write */
	NULL,			/* readdir - bad */
	NULL,			/* select */
	NULL,			/* ioctl */
	NULL,			/* mmap */
	NULL,			/* no special open code */
	NULL			/* no special release code */
};

/*
 * Returns amount of memory which needs to be reserved
 * and copies ramdisk image to the buffer if necessary.
 * It will also set the ROOT DEVICE to the ramdisk.
 */
long rd_init(long mem_start, int length)
{
	struct minix_super_block *s;
	int  nblocks;
	char *rdp;	     /* current location of ramdisk */

	if (register_blkdev(MAJOR_NR,"rd",&rd_fops)) {
		printk("Unable to get major %d for ramdisk\n",MAJOR_NR);
		return 0;
	}
	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
	rd_start = (char *) mem_start;
	rd_length = length;

	if (length > 0) {
	    printk("Ram disk: %d bytes, starting at %#lx\n", rd_length,
		   (ulong) rd_start);

	    /* get current address of ramdisk */
	    rdp = (char *)mm_phys_to_virt (sys_info.ramdisk_addr);

	    s = (struct minix_super_block *)(rdp + BLOCK_SIZE);

	    if (s->s_magic != MINIX_SUPER_MAGIC) {
		/* No ram disk image present, assume normal floppy boot */
		printk ("ramdisk has no super block magic number %x\n",
			s->s_magic);
		return 0;
	    }

	    nblocks = s->s_nzones << s->s_log_zone_size;
	    if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
		printk("Ram disk image too big!  (%d blocks, %d avail)\n",
		       nblocks, rd_length >> BLOCK_SIZE_BITS);
		return 0;
	    }
	    printk("Loading %d bytes into ram disk\n",
		   nblocks << BLOCK_SIZE_BITS);

	    /* copy the ram disk image */
	    memcpy (rd_start, rdp, nblocks << BLOCK_SIZE_BITS);

	    /* set the root device to the ram disk */
	    ROOT_DEV=0x0101;
	}

	return(length);
}
