/*****************************************************************************

       Copyright © 1995, 1996 Digital Equipment Corporation,
                       Maynard, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, provided  
that the copyright notice and this permission notice appear in all copies  
of software and supporting documentation, and that the name of Digital not  
be used in advertising or publicity pertaining to distribution of the software 
without specific, written prior permission. Digital grants this permission 
provided that you prominently mark, as not part of the original, any 
modifications made to this software or documentation.

Digital Equipment Corporation disclaims all warranties and/or guarantees  
with regard to this software, including all implied warranties of fitness for 
a particular purpose and merchantability, and makes no representations 
regarding the use of, or the results of the use of, the software and 
documentation in terms of correctness, accuracy, reliability, currentness or
otherwise; and you rely on the software, documentation and results solely at 
your own risk. 

******************************************************************************/
/*
 *  devices.c.   This part of the miniloader contains the ersatz (as in fake
 *  rollexs) Linux kernel.
 *
 *  david.rusling@reo.mts.dec.com.
 */
#include <linux/config.h>
#include <linux/bios32.h>
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/tqueue.h>
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/major.h>
#include <linux/genhd.h>
#include <linux/string.h>
#include <asm/hwrpb.h>
#include <asm/page.h>
#include <asm/unaligned.h>
#include <asm/bitops.h>
#include <linux/interrupt.h>

#include "milo.h"
#include "uart.h"
#include "fs.h"

#include <drivers/scsi/scsi.h>

#if 0
#define DEBUG_DEVICES 1
#endif

extern void video_init(void);

#ifdef MINI_EMBED_LINUX
/*****************************************************************************
 * Initialise any devices                                                    *
 *****************************************************************************/
void device_init(void)
{ 
    unsigned long start_mem, end_mem;
/*
 *  init
 */
#ifdef DEBUG_DEVICES
    printk("device_init: (EMBEDDED) called\n");
#endif

#ifdef MINI_SERIAL_ECHO
    printk("Initializing IRQs\n");
#endif
    init_IRQ();

    printk("Initializing timers\n");
    init_timers();

    /*
     * Get some temporary memory.
     */
    start_mem = (unsigned long) kmalloc(8 * PAGE_SIZE, 0);
    end_mem = start_mem + (8 * PAGE_SIZE);
#ifdef DEBUG_DEVICES
    printk("...start_mem @ 0x%p, end_mem @ 0x%p\n", start_mem, end_mem);
#endif

/*
 *  If we are setting up PCI, then do so.   
 */
#ifdef CONFIG_PCI

#ifdef CONFIG_ALPHA_LCA
#ifdef DEBUG_DEVICES
    printk("...LCA specific initialization\n");
#endif
    start_mem = (unsigned char *)lca_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif
#ifdef CONFIG_ALPHA_APECS
#ifdef DEBUG_DEVICES
    printk("...APECS specific initialization\n");
#endif
    start_mem = (unsigned char *)apecs_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif

#ifdef CONFIG_ALPHA_ALCOR
#ifdef DEBUG_DEVICES
    printk("...ALCOR specific initialization\n");
#endif
    start_mem = (unsigned char *)alcor_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif

    printk("Configuring PCI\n");
    start_mem = (unsigned char *)pci_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif

    /* init the video */
    video_init();

    /* initialise the keyboard */
#ifdef MINI_SERIAL_ECHO
    printk("Initializing keyboard\n");
#endif
    kbd_init();
}

void show_devices(void)
{
    printk("Devices:\n    embedded\n");
}

#else LINUX_AT
/*
 *  Globals
 */
#ifdef CONFIG_BLK_DEV_FD
#define MAX_BUFFER_SECTORS 18
#endif

unsigned long intr_count = 0 ;

struct dummy {
    char dummy;
};

extern struct blk_dev_struct blk_dev[MAX_BLKDEV];

struct device_struct {
	const char * name;
	struct file_operations * fops;
};

static struct device_struct chrdevs[MAX_CHRDEV] = {
	{ NULL, NULL },
};

struct device_struct blkdevs[MAX_BLKDEV] = {
        {NULL, NULL},
};


extern int *blk_size[];
extern int *blksize_size[];

int need_resched = 0;

struct wait_queue *wait_for_request;

kdev_t ROOT_DEV = 0;

extern struct gendisk *gendisk_head;

#undef INIT_MM
#define INIT_MM { \
		1, \
		NULL, \
		0,    \
		0, 0, 0, 0, \
		0, 0, 0, 0, \
		0, 0, 0, 0, \
		0, 0, 0, \
		0, \
		NULL, NULL }


mem_map_t * mem_map = NULL;
char dummy1;
struct drive_info_struct { char dummy[32]; } drive_info;
static struct fs_struct init_fs = INIT_FS;
static struct files_struct init_files = INIT_FILES;
static struct mm_struct init_mm = INIT_MM;
static struct signal_struct init_signals = INIT_SIGNALS;
static unsigned long init_kernel_stack[1] = { STACK_MAGIC};
struct exec_domain default_exec_domain ;

struct task_struct init_task = INIT_TASK;
struct task_struct *task[NR_TASKS];
struct task_struct *current_set[NR_CPUS];
#if 1
#undef current			/* defined in sched.h for SMP */
struct task_struct *current = &init_task;
#endif

extern char *floppy_track_buffer;

#ifdef CONFIG_SCSI
int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
				off_t offset, int length, int inout) = NULL ;
struct proc_dir_entry scsi_dir[];
struct proc_dir_entry scsi_hba_dir[];
#endif

/*
 * Align VAL to ALIGN, which must be a power of two.
 */
#define ALIGN(val,align)	(((val) + ((align) - 1)) & ~((align) - 1))

/*
 *  This module implements the dummy device layer in the miniloader.
 *  It must be run after the PCI initialization code is run.
 */
extern void entUna(void);
extern void device_setup(void);
extern long blk_dev_init(long mem_start, long mem_end);

unsigned int current_device = 0;

static unsigned char *	start_mem = 0;
static unsigned char *	end_mem = 0;


/*
 * The the hostid of the INDEX-th SCSI controller according to
 * environment variable SCSI<index>_HOSTID or a default value.  The
 * PCI configuration space register that controls the hostid is
 * located at offset WHERE (this obviously assumes that there is one
 * byte that controls the hostid).
 */
static void scsi_sethostid (unsigned char bus, unsigned char devfn,
			    unsigned char where, int index)
{
    char buf[16], *val;
    int hostid;

    sprintf(buf, "SCSI%d_HOSTID", index);
    hostid = 7;
    val = getenv(buf);
    if (val) {
	hostid = atoi(val);
    }
    printk("dev_config: setting scsi%d host id to %d\n", index, hostid);
    pcibios_write_config_byte(bus, devfn, where, hostid);
}


/*
 * Configure Saturn PCI/ISA bridge (aka 82378).  In the future,
 * support for setting up ISA address decoding etc. should be added.
 */
static void
saturn_config(struct pci_bus * bus, u8 devfn)
{
    struct pci_dev * dev;
    u16 max_devsel;
    u8 old;

    /*
     * Walk through devices on bus and determine subtractive decoding
     * sampling point:
     */
    max_devsel = PCI_STATUS_DEVSEL_FAST;
    for (dev = bus->devices; dev; dev = dev->sibling) {
	u16 status;

	pcibios_read_config_word(bus->number, dev->devfn, PCI_STATUS, &status);
	status &= PCI_STATUS_DEVSEL_MASK;
	if (status > max_devsel) {
	    max_devsel = status;
	}
    }
    max_devsel >>= 9;
    /*
     * Set devsel sampling point, Interrupt Acknowledge Enable,
     * PCI Posted Write Buffer Enable, ISA Master Line Buffer enable,
     * DMA Line Buffer enable.
     */
    pcibios_write_config_byte(bus->number, devfn, 0x40,
			      0x27 | max_devsel << 3);
    /*
     * Enable IRQ12/M mouse function enable bit in the ISA Clock
     * Divisor Register.  This has the effect that a read from port
     * 0x60 will release IRQ12 (see section "4.5.5 Reset UBus IRQ12
     * Register" on page 103 of the Saturn manual.
     */
    pcibios_read_config_byte(bus->number, devfn, 0x4d, &old);
    pcibios_write_config_byte(bus->number, devfn, 0x4d, old | 0x10);
}


/*
 * Do devices configuration that Linux doesn't want to worry
 * about.  This includes setting SCSI hostids and setting up
 * PCI bridges (in particular the PCI-to-ISA bridge).
 */
void dev_config (void)
{
    struct pci_dev *dev;
    int scsi_index = 0;

    /* go through PCI devices and setup things that we know about: */
    for (dev = pci_devices; dev != 0; dev = dev->next) {
	switch (dev->vendor) {
	  case PCI_VENDOR_ID_NCR:
	    switch (dev->device) {
	      case PCI_DEVICE_ID_NCR_53C810:
	      case PCI_DEVICE_ID_NCR_53C815:	/* not sure about this */
	      case PCI_DEVICE_ID_NCR_53C820:	/* not sure about this */
	      case PCI_DEVICE_ID_NCR_53C825:	/* not sure about this */
		scsi_sethostid(dev->bus->number, dev->devfn, 0x84, scsi_index++);
		break;
	      default:
		/*
		 * If NCR ever comes up with something other than
		 * a SCSI controller, this will have to become
		 * silent...
		 */
		printk("dev_config: don't know how to set host id of NCR device %x.\n",
		       dev->device);
	    }
	    break;

	  case PCI_VENDOR_ID_INTEL:
	    switch (dev->device) {
	      case PCI_DEVICE_ID_INTEL_82378:
		saturn_config(dev->bus, dev->devfn);
		break;

	      default:
		if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA) {
		    printk("dev_config: don't know about ISA bridge %x:%x\n",
			   dev->vendor, dev->device);
		}
	    }

	  default:
	    if ((dev->class >> 8) == PCI_CLASS_STORAGE_SCSI) {
		printk("dev_config: don't know how to set hostid of SCSI device (%x:%x).\n",
		       dev->vendor, dev->device);
	    }
	    break;
	}
    }
}



/*
 * dummy routines that device_init() calls
 */
void chr_dev_init(void)
{
    int i;
    for (i= 0; i < MAX_CHRDEV; i++) {
	chrdevs[i].name = NULL;
	chrdevs[i].fops = NULL;
    }
}
void net_dev_init(void) {}

static void block_device_init(void)
{
    int i;
    extern void device_setup(void);

    for (i= 0; i < MAX_BLKDEV; i++) {
	blkdevs[i].name = NULL;
    }

    /* do the setup */
#if defined(BOOT_VERBOSE)
    printk("Setting up devices\n");
#endif
    sti(); 
    device_setup();          /* in genhd.c */

    /*
     * Now tell the world what devices there are.
     */
#if defined(BOOT_VERBOSE)
    printk("Major devices are: ");
    for (i = 0; i < MAX_BLKDEV; i++) {
	if (blkdevs[i].name != NULL) 
	    printk("%s (%04X) ", blkdevs[i].name, i);
    }
    printk("\n");
#endif

#if 0
    cli();
#else
    /* allow interrupts to happen again (just in case they got 
       turned off) */
    sti();
#endif
}

void need_device(const char *devname)
{
    static int	inited = 0;

    if (inited) {
	return;
    }
    inited = 1;
    block_device_init();
}


/*****************************************************************************
 * Initialise minimal set of devices                                         *
 *****************************************************************************/
void device_init(void)
{ 
    unsigned int i;

    /*
     * init
     */
    for (i = 0; i < NR_TASKS; i++) task[i] = 0;
    task[0] = &init_task;
    current = task[0];
    current_set[0]=&init_task;

#if defined(BOOT_VERBOSE)
    printk("Initializing unaligned access traps\n");
#endif
    wrent(entUna, 4);

#if defined(BOOT_VERBOSE)
    printk("Initializing IRQs\n");
#endif
    init_IRQ();

    printk("Initializing timers\n");
    init_timers();

    current->pid = 0;
    current->blocked = 0;
    current->signal = 1;

#if defined(BOOT_VERBOSE)
    printk("Initializing non-block devices\n");
#endif

    /*
     * Get some temporary memory.
     */
    start_mem = kmalloc(8 * PAGE_SIZE, 0);
    end_mem = start_mem + (8 * PAGE_SIZE);
#ifdef DEBUG_DEVICES
    printk("...start_mem @ 0x%p, end_mem @ 0x%p\n", start_mem, end_mem);
#endif

/*
 *  If we are setting up PCI, then do so.   
 */
#ifdef CONFIG_PCI

#ifdef CONFIG_ALPHA_LCA
#ifdef DEBUG_DEVICES
    printk("...LCA specific initialization\n");
#endif
    start_mem = (unsigned char *)lca_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif
#ifdef CONFIG_ALPHA_APECS
#ifdef DEBUG_DEVICES
    printk("...APECS specific initialization\n");
#endif
    start_mem = (unsigned char *)apecs_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif

#ifdef CONFIG_ALPHA_ALCOR
#ifdef DEBUG_DEVICES
    printk("...ALCOR specific initialization\n");
#endif
    start_mem = (unsigned char *)alcor_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif

#if 1
#ifdef DC21066
    printk("Disable parity checking in PCI\n");
    /* disable parity on the LCA */
    *(volatile unsigned long *)LCA_IOC_PAR_DIS = 1UL<<5;
#endif
#endif

#if defined(BOOT_VERBOSE)
    printk("Configuring PCI\n");
#endif
    start_mem = (unsigned char *)pci_init((unsigned long)start_mem, (unsigned long)end_mem);
#endif

    /* init the video */
    video_init();

    /* do the device init that Linux doesn't do */
    dev_config();

    /* initialise the keyboard */
#if defined(BOOT_VERBOSE)
    printk("Initializing keyboard\n");
#endif
    kbd_init();
}

extern struct gendisk *gendisk_head;

void show_devices(void)
{
    extern char *disk_name (struct gendisk *hd, int minor, char *buf) ;
    struct gendisk *p;
    char buf[16] ;
    int i;

    need_device("");
    printk("Devices:\n");
    for (i = 0; i < MAX_BLKDEV; i++) {
	if (blkdevs[i].name != NULL) 
	    printk("    %s (%04X) ", blkdevs[i].name, i << 8);
    }
    printk("\n");

    
    /* for every device */
    for (p = gendisk_head ; p ; p=p->next) {
      int drive;
      
      printk("    ");
      /* for every drive on this device */
      for (drive = 0 ; drive < p->nr_real ; drive++) {
	int first_minor	= drive << p->minor_shift;
	int mask = (1 << p->minor_shift) - 1 ;
	int i ;

	/* eg sda:, sdb: */
	disk_name(p, first_minor, buf) ;
	printk("%s:  ", buf) ;

	/* for every partition */
	for (i = 1 ; i < mask ; i++) {
	  printk(" ") ;
	  if (p->part[first_minor + i].nr_sects) {
	    disk_name(p, first_minor + i, buf) ;
	    printk(buf) ;
	  }
	}
      }
      printk("\n");
    }
}

/*****************************************************************************
 * Device routines                                                           *
 *****************************************************************************/
/*
 *  These device routines do not need locking (via cli() and sti()) as they
 *  are only called by Milo, which is single threaded.
 */
static struct file file;
static struct inode inode;

int device_open(const char *device)
{
    int number, major, minor, result;

    /* parse the device name into a device number, -1 means that we failed */
    number = device_name_to_number(device);
    if (number == -1) return -1;

    /* we need the device */
    need_device(device);

    /* set the device's block size */
    set_blocksize (number, 512);

    /* carry on, major is the index intothe blkdevs[] structure */
    major = (number >> 8) & 0xff;
    minor = (number >> 0) & 0xff;

    /* get the set of things that this device can do for us */
    file.f_op = blkdevs[major].fops;

/*
 *  Fill out the static structures that describe to the block device what we
 *  have opened.
 */
    file.f_pos = 0x0;     
    file.f_inode = &inode;
    file.f_mode = MS_RDONLY;
    inode.i_rdev = number;

/*
 *  If there's a device specific open routine, then go call it.
 *  The two device open routines that I've checked (floppy_open() and 
 *  sd_open() both return 0 for success and -ERROR for failure.
 */
    if (file.f_op->open != NULL) {
	result = (file.f_op->open)(&inode, &file);
	if (result) {
	    printk("ERROR: failed to open device 0x%04X (result = %d)\n",
		   number, result);
	    return -1;         /* error */
	}
    }
    return 0;
}

/*
 *  This called from boot main to mount the device so that it can get an
 *  image from it.
 */
int device_mount(char *device, char *fs_type)
{
    int fs;

#ifdef DEBUG_DEVICES
    printk("device_mount(): called for %s\n", device);
#endif

    fs = fs_name_to_type(fs_type);
    if (fs < 0) {
      printk("MILO: file system %s not supported\n", fs_type);
      return -1;
    }

    if (device_open(device) < 0) return -1;
/*
 *  Now mount the filesystem. 
 */ 
    return __mount(fs_type, inode.i_rdev);
}


/*
 *  Attempt to read an opened device.  fd is the fd returned by device_open()
 *  and is an index into the blkdevs[] array.
 */
int device_read(int fd, char *buffer, int size, unsigned long offset)
{
#ifdef DEBUG_DEVICES
    printk("device_read: called, %d bytes at offset 0x%lx into buffer @ 0x%p\n", size, offset, buffer);
#endif

    if (file.f_op == NULL) {
	printk("...invalid device (f_op = NULL)\n");
	return 0;   /* invalid device */
    }

/*
 *  if there is one, call the read routine.
 */
    if (file.f_op->read != NULL) {
	int count;
#ifdef DEBUG_DEVICES
	int i;
#endif

	file.f_pos = offset;
	count = (file.f_op->read)(&inode, &file, buffer, size); 
	if (count < 0) {
	    printk("device_read: failed (%d)\n", count);
	    count = 0;
	} else {

#ifdef DEBUG_DEVICES		
	    printk("device_read: %d bytes read\n", count);
	    for (i=0; i<20; i++) printk("%02x ", buffer[i] & 0xFF);
	    printk("\n");
#endif
	}
	return count;
    } else {
	printk("...no read routine for this device\n");
	return 0;
    }

}

void device_close(int fd)
{
#ifdef DEBUG_DEVICES
    printk("device_close: called, current_device = 0x%04X\n", current_device);
#endif
    inode.i_rdev = current_device;
#ifdef DEBUG_DEVICES
    printk("device_close: called, release is @0x%p\n", file.f_op->release);
#endif
#if 0
    if (file.f_op->release) (file.f_op->release)(&inode, &file);
#endif
/*    if (file.f_op->revalidate) (file.f_op->revalidate)(current_device);  */
    current_device = -1;
}
/*****************************************************************************
 * Support routines                                                          *
 *****************************************************************************/
/*
 *  These are routines that are required by Linux device drivers and for
 *  various reasons are not taken from the Kernel (as the irq handling
 *  code is).
 */
static inline void __sleep_on(struct wait_queue **p, int state)
{
    int *ptr = (int *)p;

#ifdef DEBUG_DEVICES
    printk("__sleep_on: called, queue = 0x%p, *p = 0x%p\n", p, *p);
#endif
    *ptr = *ptr + 1;
/*
 *  Wait 100us.
 */
    while (*ptr > 0)  {
      schedule() ;
    }
}

void interruptible_sleep_on(struct wait_queue **p)
{
    unsigned long flags;

    save_flags(flags);
    sti();
#ifdef DEBUG_DEVICES		
    printk("interruptible_sleep_on: called, queue = 0x%p, ipl = %lu\n", p, flags);
#endif
    __sleep_on(p,TASK_INTERRUPTIBLE);
    restore_flags(flags);
}

void sleep_on(struct wait_queue ** p)
{
    unsigned long flags;

#ifdef DEBUG_DEVICES		
    printk("sleep_on called for queue @ 0x%p\n", p);
#endif
    save_flags(flags);
    sti();
/*
 *  Wait 100us.
 */
    udelay(100);
    restore_flags(flags);
}

void wake_up(struct wait_queue ** p)
{
    struct wait_queue *tmp;
    int *ptr = (int *)p;
    unsigned long flags;

    /* valid parameters? */
    if (!p || !(tmp = *p))
      return;

    save_flags(flags);

#ifdef DEBUG_DEVICES		
    printk("wake_up called for queue 0x%p, *p = 0x%p\n", p, *p);
#endif
    cli();
    if (*ptr > 0) *ptr = *ptr - 1;
    restore_flags(flags);
}

int check_disk_change(kdev_t dev)
{
    int i;
    struct file_operations * fops;

#ifdef DEBUG_DEVICES		
    printk("check_disk_change called at ipl %lu\n", getipl());
#endif

    /* check for a check_media_change operation from this device */
    i = MAJOR(dev);
    if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL)
      return 0;
    if (fops->check_media_change == NULL)
      return 0;

#ifdef DEBUG_DEVICES
    printk("calling per-device media change routine @ 0x%p\n",
	   fops->check_media_change);
#endif

    sti();
    if (!fops->check_media_change(dev)) {
      return 0;
    }

    printk("VFS: Disk change detected on device %d/%d\n",
	   MAJOR(dev), MINOR(dev));

    if (fops->revalidate) {
      sti();
#ifdef DEBUG_DEVICES
      printk("...calling revalidate routine @ 0x%p\n", fops->revalidate);
#endif
      fops->revalidate(dev);
    }

    /* put things back the way they were and leave */
    return 1;
}

int permission(struct inode * inode,int mask)
{
#ifdef DEBUG_DEVICES		
    printk("permission called\n");
#endif
    return 0;
}

void invalidate_buffers(kdev_t dev)
{
#ifdef DEBUG_DEVICES		
    printk("invalidate_buffers called\n");
#endif
}

void synch_dev(void)
{
#ifdef DEBUG_DEVICES		
    printk("synch_dev called\n");
#endif
}

void sync_dev(kdev_t dev)
{
#ifdef DEBUG_DEVICES		
    printk("sync_dev called\n");
#endif
}

/*
 *  Generic read routine for block devices.  We must set up and call the 
 *  request function for this particular device.
 */
int block_read(struct inode * inode, struct file * filp, char * buf, int count)
{
    int major;

#ifdef DEBUG_DEVICES		
    printk("block_read: called for device 0x%04x, f_pos = %lu\n", inode->i_rdev, 
	   (unsigned long)filp->f_pos);
#endif

    major = MAJOR(inode->i_rdev);
    if (blk_dev[major].request_fn == NULL) {
	printk("...ERROR: request_fn = NULL\n");
	printk("\tmajor = %d\n", major);
	return 0;
    } else {
	int read;
	int blocksize, block;
	struct buffer_head *bh;
/*
 *  Work out what the blocksize is.
 */
	blocksize = BLOCK_SIZE;
	if (blksize_size[MAJOR(inode->i_rdev)]) {
	    if (blksize_size[MAJOR(inode->i_rdev)][MINOR(inode->i_rdev)])
		blocksize = blksize_size[MAJOR(inode->i_rdev)][MINOR(inode->i_rdev)];
	}
#ifdef DEBUG_DEVICES		
	printk("...block size is %d\n", blocksize);
#endif

/*
 *  We need to read all of the blocks that contain the user's buffer.  We
 *  are at filp->f_pos and the block size is blocksize.
 */
	read = 0;
	while (read < count) {

/*
 *  Which block should we read?
 */
	    block = filp->f_pos / blocksize;
/*
 *  Issue the request to read the file a block at a time.
 */
#ifdef DEBUG_DEVICES		
	    printk("block_read: issuing request to read block %d (size = %d)\n", 
		   block, blocksize);
#endif
	    bh = bread(inode->i_rdev, block, blocksize);
	    if ( !bh ) {
/*
 *  Failed, return just how much we managed to read.
 */
		printk("block_read: read failed (after %d bytes)\n", read);
		return read;          
	    } else {
		int size;
/*
 *  Success, so now update the read count and copy over the data into
 *  the request buffer.  We have just read block "block" and filp->f_pos
 *  tells us where we were in this file before we read this block.  
 *  block * blocksize gives us the offset of the first byte we actually have from 
 *  this file.
 */
		if (read == 0) {
		    int first;
		    int offset;
/*
 *  This is the first block that we've read, (read == 0).
 *  We need only the last part of this block (or all of it).
 */
		    first = block * blocksize;
		    offset = filp->f_pos - first;
		    if (count > blocksize)
			size = blocksize;
		    else
			size = count;
		    memcpy(buf, (char *)(bh->b_data) + offset, size);
		} else {         /* read != 0 */
/*
 *  nth block read.
 *  We need either all of this block or only the first part of it.
 */
       		    if ((count - read) > blocksize) 
			size = blocksize;
		    else
			size = count - read;
		    memcpy(buf, (char *)(bh->b_data), size);
		}
		buf += size;
		read += size;
		filp->f_pos += size;
/*
 *  Have we read enough?
 */
		if (read == count) return read;

	    }
	}              /* end while not read everything */
	return read;
    }                  /* else valid device */
}

int block_write(struct inode * inode, struct file *file, const char *buffer, int a)
{
#ifdef DEBUG_DEVICES		
    printk("block_write called\n");
#endif
    return 1;
}

int block_fsync(struct inode *inode, struct file *file)
{
#ifdef DEBUG_DEVICES		
    printk("block_fsync called\n");
#endif
    return 0;
}

int request_dma(void) 
{
#ifdef DEBUG_DEVICES		
    printk("request_dma called\n");
#endif
    return 0;
}

int __verify_write(unsigned long start, unsigned long size)
{
#ifdef DEBUG_DEVICES		
    printk("__verify_write called\n");
#endif
    return 0;
}

void __wait_on_buffer(struct buffer_head * bh)
{
    unsigned long flags; 

#ifdef DEBUG_DEVICES
    printk("__wait_on_buffer called for bh = 0x%p\n", bh);
#endif
    while (buffer_locked(bh)) {
      run_task_queue(&tq_disk) ;
      schedule() ;
      __asm__ __volatile__("": : :"memory");
    }
#ifdef DEBUG_DEVICES
    printk("...returning\n") ;
#endif
}



int verify_area(int type, const void *where, unsigned long size)
{
#ifdef DEBUG_DEVICES		
    printk("verify_area: called\n");
#endif
    return 1;
}

static struct buffer_head *bh = NULL;
static char *buf = NULL;
struct buffer_head * getblk(kdev_t dev, int block, int size)
{
#ifdef DEBUG_DEVICES
    printk("getblk: called (dev = 0x%x), size = %d\n", dev, size);
#endif

    /* allocate some memory for it (if we have to). */
    if (bh == NULL) {
	bh = (struct buffer_head *)kmalloc(sizeof(struct buffer_head), 0);
	/* make sure that the buffer is size aligned */
	buf = kmalloc(size * 2, 0) + size;
	buf = (char *)((unsigned long)buf & ~(size - 1));
#ifdef DEBUG_DEVICES
	printk("getblk: allocating *new* bh (@ 0x%p) and buf (@ 0x%p)\n",
	       bh, buf);
#endif
    }
    memset((void *)bh, 0, sizeof(struct buffer_head));
    memset((void *)buf, 0, size);

    /* now fill it out */
    bh->b_data = buf;
    bh->b_dev = dev;
    bh->b_blocknr = block;
    bh->b_count = 1;
    bh->b_state = 0;	/* Uptodate, Dirty, Lock, Req, Has_aged */
    set_bit(BH_Touched, &bh->b_state);
    bh->b_flushtime = 0;
    bh->b_prev = NULL;
    bh->b_next = NULL;
    bh->b_prev_free = NULL;
    bh->b_next_free = NULL;
    bh->b_this_page = NULL;
    bh->b_reqnext = NULL;
    bh->b_size = size;

    return bh;
}

void __brelse(struct buffer_head * buf)
{
#ifdef DEBUG_DEVICES		
    printk("brelse: called\n");
#endif

    if (buf->b_count) buf->b_count--;
}

/* Added this because the generic SCSI devices need it */
int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
{
	if (major == 0) {
		for (major = MAX_CHRDEV-1; major > 0; major--) {
			if (chrdevs[major].fops == NULL) {
				chrdevs[major].name = name;
				chrdevs[major].fops = fops;
				return major;
			}
		}
		return -EBUSY;
	}
	if (major >= MAX_CHRDEV)
		return -EINVAL;
	if (chrdevs[major].fops && chrdevs[major].fops != fops)
		return -EBUSY;
	chrdevs[major].name = name;
	chrdevs[major].fops = fops;
	return 0;
}

int register_blkdev(unsigned int major, const char * name, struct file_operations *fops)
{
#ifdef DEBUG_DEVICES		
        printk("register_blkdev: called for %s\n", name);
#endif

	if (major >= MAX_BLKDEV)
		return -EINVAL;
	if (blkdevs[major].fops)
		return -EBUSY;
	blkdevs[major].name = name;
	blkdevs[major].fops = fops;
	return 0;
}

int unregister_blkdev(unsigned int major, const char * name)
{
#ifdef DEBUG_DEVICES		
        printk("unregister_blkdev: called for %s\n", name);
#endif

	if (major >= MAX_BLKDEV)
		return -EINVAL;
	if (!blkdevs[major].fops)
		return -EINVAL;
	if (strcmp(blkdevs[major].name, name))
		return -EINVAL;
	blkdevs[major].name = NULL;
	blkdevs[major].fops = NULL;
	return 0;
}
int unregister_chrdev(unsigned int major, const char * name)
{
#ifdef DEBUG_DEVICES		
        printk("unregister_chrdev: called for %s\n", name);
#endif

	if (major >= MAX_BLKDEV)
		return -EINVAL;
	if (!chrdevs[major].fops)
		return -EINVAL;
	if (strcmp(chrdevs[major].name, name))
		return -EINVAL;
	chrdevs[major].name = NULL;
	chrdevs[major].fops = NULL;
	return 0;
}

void free_dma(unsigned int dmanr)
{
#ifdef DEBUG_DEVICES		
    printk("free_dma: called\n");
#endif
}


/* ------------------------------------------------------------ */


asmlinkage void schedule(void)
{
  unsigned long flags;

#ifdef DEBUG_DEVICES		
    printk("[s]");
#endif

    /* drop the ipl for a while so that interrupts can happen */
    save_flags(flags);
    sti();
    udelay(10);
    /* now see what we have to do */
    run_task_queue(&tq_scheduler);    
    restore_flags(flags);
}

void refile_buffer(struct buffer_head * buf)
{
#ifdef DEBUG_DEVICES		
    printk("refile_buffer(): called with buf  = 0x%p\n", buf);
#endif
}

void __down(struct semaphore * sem)
{
#ifdef DEBUG_DEVICES		
    printk("__down(): called\n");
#endif

    while (sem->count <= 0) {
	schedule() ;
    } ;
}

/*
 * bread() reads a specified block and returns the buffer that contains
 * it. It returns NULL if the block was unreadable.
 */
struct buffer_head * bread(kdev_t dev, int block, int size)
{
	struct buffer_head * bh;

#ifdef DEBUG_DEVICES		
	printk("bread(%x, %d, %d): called\n", dev, block, size);
#endif

	/* this is a bit hacky, but someplace during ll_rw_block(), 
	   the ipl gets set low, so always set and reset it during this
	   routine. */
	cli();
	if (!(bh = getblk(dev, block, size))) {
		printk("VFS: bread: READ error on device %d/%d\n",
						MAJOR(dev), MINOR(dev));
		return NULL;
	}
	if (buffer_uptodate(bh))
		return bh;
	cli();
	ll_rw_block(READ, 1, &bh);
	wait_on_buffer(bh);
	if (buffer_uptodate(bh)) {
	  cli();
	  return bh;
	}
	brelse(bh);
	cli();
	return NULL;
}

struct buffer_head * breada(kdev_t dev, int block, int bufsize,
	unsigned int pos, unsigned int filesize)
{
#ifdef DEBUG_DEVICES		
    printk("breada(): called\n");
#endif
    return 0;
}

void wait_for_keypress(void)
{
    uart_getchar(COM1);
}

/*
 * Called every time a block special file is opened
 */
int blkdev_open(struct inode * inode, struct file * filp)
{
	int i;

#ifdef DEBUG_DEVICES		
	printk("blkdev_open: called\n");
#endif

	i = MAJOR(inode->i_rdev);
	if (i >= MAX_BLKDEV || !blkdevs[i].fops)
		return -ENODEV;
	filp->f_op = blkdevs[i].fops;
	if (filp->f_op->open)
		return filp->f_op->open(inode,filp);
	return 0;
}	

void kfree (void *ptr)
{
#ifdef DEBUG_DEVICES		
    printk("kfree(): called\n");
#endif
}

void invalidate_inodes(kdev_t dev)
{
#ifdef DEBUG_DEVICES		
    printk("invalidate_inodes(): called\n");
#endif
}

int fsync_dev(kdev_t dev)
{
#ifdef DEBUG_DEVICES		
    printk("fsync_dev(): called\n");
#endif
    return 0;
}

#if 0
int rd_init(long mem_start, int length)
{
#ifdef DEBUG_DEVICES		
    printk("rd_init: called (why?)\n");
#endif
    return (length);
}
#endif

#if 0
void rd_load(void)
{
#ifdef DEBUG_DEVICES		
    printk("rd_load: called (why?)\n");
#endif
}
#endif

#if 0
unsigned long __get_free_pages(int priority, unsigned long order, unsigned long limit)
#else
unsigned long __get_free_pages(int priority, unsigned long order, int limit)
#endif
{
    int size;
    char *ptr;
    int count, pfn;

#ifdef DEBUG_DEVICES
    printk("__get_free_pages: called, priority = %d, order = %lu, limit = %lu\n",
	   priority, order, limit);
#endif

    /* the size of memory that we want is based on order */
    size = PAGE_SIZE << order;
    count = size / PAGE_SIZE;
    pfn = allocate_many_pfn(count, TEMPORARY_PAGE);
    ptr = (char *) _PFN_TO_PHYSICAL(pfn);
    
#ifdef DEBUG_DEVICES
    printk("__get_free_pages(): returning 0x%p\n", ptr);
#endif
    return (unsigned long)ptr;
}

void free_pages(unsigned long addr, unsigned long order)
{
#ifdef DEBUG_DEVICES
    printk("free_pages: called\n");
#endif
}

void nop(void)
{
#ifdef DEBUG_DEVICES		
    printk("nop: called\n");
#endif
}


int proc_register(struct proc_dir_entry *driver, struct proc_dir_entry *x)
{
    return 0;
}


int proc_unregister(struct proc_dir_entry *driver, int x)
{
    return 0;
}
 

char * kdevname (kdev_t dev)
{
    static char buffer[32];
    sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev));
    return buffer;
}


void console_map_init (void)
{
}


struct inode_operations proc_scsi_inode_operations;
struct proc_dir_entry proc_scsi;


void set_blocksize(kdev_t dev, int size)
{
  if (!blksize_size[MAJOR(dev)])
    return;

  switch(size) {
  default: 
    panic("Invalid blocksize passed to set_blocksize");
  case 512: case 1024: case 2048: case 4096:;
  }

  if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) {
    blksize_size[MAJOR(dev)][MINOR(dev)] = size;
    return;
  }
  if (blksize_size[MAJOR(dev)][MINOR(dev)] == size)
    return;

  blksize_size[MAJOR(dev)][MINOR(dev)] = size;
}

#endif

/*
 * entUna has a different register layout to be reasonably simple. It
 * needs access to all the integer registers (the kernel doesn't use
 * fp-regs), and it needs to have them in order for simpler access.
 *
 * Due to the non-standard register layout (and because we don't want
 * to handle floating-point regs), we disallow user-mode unaligned
 * accesses (we'd need to do "verify_area()" checking, as well as
 * do a full "ret_from_sys_call" return).
 *
 * Oh, btw, we don't handle the "gp" register correctly, but if we fault
 * on a gp-register unaligned load/store, something is _very_ wrong
 * in the kernel anyway..
 */
struct allregs {
	unsigned long regs[32];
	unsigned long ps, pc, gp, a0, a1, a2;
};

asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg,
	unsigned long a3, unsigned long a4, unsigned long a5,
	struct allregs regs)
{
	static int cnt = 0;

	if (regs.ps & 8)
	        panic("do_entUna: failed(1) \n");
	if (++cnt < 5)
		printk("Unaligned trap at %016lx: %p %lx %ld\n",
			regs.pc, va, opcode, reg);
	/* $16-$18 are PAL-saved, and are offset by 19 entries */
	if (reg >= 16 && reg <= 18)
		reg += 19;
	switch (opcode) {
		case 0x28: /* ldl */
			*(reg+regs.regs) = (int) ldl_u(va);
			return;
		case 0x29: /* ldq */
			*(reg+regs.regs) = ldq_u(va);
			return;
		case 0x2c: /* stl */
			stl_u(*(reg+regs.regs), va);
			return;
		case 0x2d: /* stq */
			stq_u(*(reg+regs.regs), va);
			return;
	}
	printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n",
		regs.pc, va, opcode, reg);
	panic("do_entUna: failed(2) \n");
}

void add_interrupt_randomness(int value) {}

void add_blkdev_randomness(int value) {}

void tga_console_init(void) {}

void unlock_buffer(struct buffer_head * bh)
{
  clear_bit(BH_Lock, &bh->b_state);
  wake_up(&bh->b_wait);
}

void mark_buffer_uptodate(struct buffer_head * bh, int on)
{
  if (on) 
    set_bit(BH_Uptodate, &bh->b_state);
  else
    clear_bit(BH_Uptodate, &bh->b_state);
}

int brw_page(int rw, unsigned long address, kdev_t dev, int b[], int size, int bmap)
{
#ifdef DEBUG_DEVICES
  printk("brw_page() called\n") ;
#endif
  return 0 ;
}

/* int register_symtab(struct symbol_table *intab) */
int register_symtab(void *intab)
{
#ifdef DEBUG_DEVICES
  printk("register_symtab() called\n") ;
#endif
  return 0 ;
}

void rand_initialize(void) {}
void rand_initialize_irq(int irq) {}
void rand_initialize_blkdev(int irq, int mode) {}

