Introduce new "blkdev" layer for 512-byte sector partitioned drives.
authorWill Sowerbutts <will@sowerbutts.com>
Sun, 4 Jan 2015 21:54:01 +0000 (21:54 +0000)
committerWill Sowerbutts <will@sowerbutts.com>
Sun, 4 Jan 2015 22:05:27 +0000 (22:05 +0000)
Hardware drivers are now responsible for the bare mimumum -- identify
the storage devices present, figure out how large they are, and provide
a function to read/write sectors.

The concept of "slices" goes away, instead we will use a separate
partition for each file system.

The blkdev layer handles PC-style MBR partition tables, both primary and
extended partitions, up to a maximum of 15 partitions per drive. The
partition numbering is chosen to be compatible with Linux.

Storage drivers no longer each require a separate entry in the device
switch table.

21 files changed:
Kernel/dev/blkdev.c [new file with mode: 0644]
Kernel/dev/blkdev.h [new file with mode: 0644]
Kernel/dev/devide.c
Kernel/dev/devide.h
Kernel/dev/devsd.c
Kernel/dev/devsd.h
Kernel/dev/mbr.c
Kernel/dev/mbr.h
Kernel/include/kernel.h
Kernel/platform-msx2/Makefile
Kernel/platform-msx2/config.h
Kernel/platform-msx2/devices.c
Kernel/platform-msx2/fuzix.lnk
Kernel/platform-n8vem-mark4/Makefile
Kernel/platform-n8vem-mark4/config.h
Kernel/platform-n8vem-mark4/devices.c
Kernel/platform-n8vem-mark4/fuzix.lnk
Kernel/platform-p112/Makefile
Kernel/platform-p112/config.h
Kernel/platform-p112/devices.c
Kernel/platform-p112/fuzix.lnk

diff --git a/Kernel/dev/blkdev.c b/Kernel/dev/blkdev.c
new file mode 100644 (file)
index 0000000..675e6df
--- /dev/null
@@ -0,0 +1,149 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <blkdev.h>
+#include <mbr.h>
+
+/*
+   Minor numbers
+
+   The kernel identifies our storage devices with an 8-bit minor number.
+
+   The top four bits of the minor identify the drive, allowing a maximum of
+   sixteen drives.
+
+   The bottom four bits of the minor identify the partition number. Partition
+   zero addresses "the whole drive", with no translation. Due to limitations
+   within the kernel only the first 32MB can currently be accessed through
+   partition zero.
+
+   Partition zero is intended to be used primarily for writing a partition
+   table to the drive, although it can also be used to store a single Fuzix
+   filesystem on an unpartitioned disk.
+
+   Partitions 1 through 15 are identified by reading a PC-style MBR partition
+   table from the drive. The first four partitions are always the primary
+   partitions on the drive, the remainder are logical partitions stored within
+   an extended partition.
+
+   To create the required device nodes, use:
+
+       mknod /dev/hda 60660 0
+       mknod /dev/hdb 60660 16
+       mknod /dev/hdc 60660 32
+       mknod /dev/hdd 60660 48
+       ... (etc) ...
+       mknod /dev/hdp 60660 240
+
+       mknod /dev/hda1 60660 1
+       mknod /dev/hda2 60660 2
+       mknod /dev/hda3 60660 3
+       ... (etc) ...
+       mknod /dev/hda15 60660 15
+
+       mknod /dev/hdb1 60660 17
+       mknod /dev/hdb2 60660 18
+       mknod /dev/hdb3 60660 19
+       ... (etc) ...
+       mknod /dev/hdb15 60660 31
+*/
+
+static blkdev_t blkdev_table[MAX_BLKDEV];
+
+int blkdev_open(uint8_t minor, uint16_t flags)
+{
+    uint8_t drive, partition;
+
+    flags; /* unused */
+
+    drive = minor >> 4;
+    partition = minor & 0x0F;
+
+    if(drive < MAX_BLKDEV){
+       if(blkdev_table[drive].drive_lba_count && (partition == 0 || blkdev_table[drive].lba_count[partition-1]))
+           return 0; /* that is a valid minor number */
+    }
+
+    /* sorry, can't find it */
+    udata.u_error = ENODEV;
+    return -1;
+}
+
+static int blkdev_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+    void *target;
+    uint32_t lba;
+    uint8_t partition;
+    blkdev_t *blk;
+
+    /* we trust that blkdev_open() has already verified that this minor number is valid */
+    blk = &blkdev_table[minor >> 4];
+    partition = minor & 0x0F;
+
+    if(rawflag == 0) {
+        target = udata.u_buf->bf_data;
+        lba = udata.u_buf->bf_blk;
+    }else
+        goto xferfail;
+
+    if(partition == 0){
+       /* partition 0 is the whole disk and requires no translation */
+       if(lba >= blk->drive_lba_count)
+           goto xferfail;
+    }else{
+       /* partitions 1+ require us to add in an offset */
+       if(lba >= blk->lba_count[partition-1])
+           goto xferfail;
+
+       lba += blk->lba_first[partition-1];
+    }
+
+    if(blk->transfer(blk->drive_number, lba, target, is_read))
+       return 1; /* 10/10, would transfer sectors again */
+
+xferfail:
+    udata.u_error = EIO;
+    return -1;
+}
+
+int blkdev_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag; /* not used */
+    return blkdev_transfer(minor, true, rawflag);
+}
+
+int blkdev_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag; /* not used */
+    return blkdev_transfer(minor, false, rawflag);
+}
+
+void blkdev_add(transfer_function_t transfer, uint8_t drive_number, uint32_t lba_count)
+{
+    char letter;
+    blkdev_t *blk = NULL;
+
+    letter = 'a';
+    blk = &blkdev_table[0];
+
+    /* find an empty slot */
+    while(blk <= &blkdev_table[MAX_BLKDEV-1]){
+       if(blk->drive_lba_count == 0){
+           blk->drive_lba_count = lba_count;
+           blk->drive_number = drive_number;
+           blk->transfer = transfer;
+
+           /* now we parse the partition table; might need a hook for platforms to
+              parse their platform-specific partition table first? eg P112 has its
+              own partitioning scheme */
+           kprintf("hd%c: ", letter);
+           mbr_parse(blk, letter);
+           kputchar('\n');
+           return;
+       }
+       blk++;
+       letter++;
+    }
+
+    kputs("blkdev: full!\n");
+}
diff --git a/Kernel/dev/blkdev.h b/Kernel/dev/blkdev.h
new file mode 100644 (file)
index 0000000..f9acbae
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __BLKDEV_DOT_H__
+#define __BLKDEV_DOT_H__
+
+/* block device drives should call blkdev_add() for each block device found,
+   and implement a sector transfer function matching the following prototype. */
+typedef bool (*transfer_function_t)(uint8_t drive, uint32_t lba, void *buffer, bool read_notwrite);
+
+/* public interface */
+void blkdev_add(transfer_function_t transfer, uint8_t drive_number, uint32_t lba_count);
+int blkdev_open(uint8_t minor, uint16_t flags);
+int blkdev_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int blkdev_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+
+/* the following details should be required only by partition parsing code */
+#define MAX_PARTITIONS 15                  /* must be at least 4, at most 15 */
+typedef struct {
+    uint32_t drive_lba_count;              /* count of sectors on raw disk device */
+    uint8_t drive_number;                  /* driver's drive number */
+    uint32_t lba_first[MAX_PARTITIONS];            /* LBA of first sector of each partition; 0 if partition absent */
+    uint32_t lba_count[MAX_PARTITIONS];            /* count of sectors in each partition; 0 if partition absent */
+    transfer_function_t transfer;          /* function to read and write sectors */
+} blkdev_t;
+
+#endif
index 49d0e4f..ced4c89 100644 (file)
@@ -3,75 +3,18 @@
 /* 2014-11-02 Will Sowerbutts (unreleased UZI-mark4)                     */
 /* 2014-12-22 WRS ported to Fuzix                                        */
 /* 2014-12-25 WRS updated to also support P112 GIDE                      */
+/* 2015-01-04 WRS updated to new blkdev API                              */
 /*-----------------------------------------------------------------------*/
 
-/* Minor numbers
-
-   The kernel identifies our storage devices with an 8-bit minor number.
-
-   The top two bits of the minor identify the drive, allowing a maximum of four
-   drives, the current hardware supports only two but future controllers may
-   support more.
-   
-   The bottom six bits of the minor identify the slice number. Slice 0
-   addresses "the whole drive", with no translation. Due to limitations within
-   the kernel only the first 32MB can currently be accessed through slice 0.
-
-   Slice 0 is intended to be used primarily for writing a partition table to
-   the drive, although it can also be used to store a single Fuzix filesystem
-   on an unpartitioned disk.
-   
-   Slices 1+ are stored in a partition on the drive. The partition is stored in
-   a PC-style MBR partition table and must be a primary partition of type 0x5A.
-   Only the first suitable partition is found. Multiple filesystems are stored
-   in this partition by dividing it into 32MB slices.
-
-   To create the required device nodes, use:
-
-       mknod /dev/hda 60660 0
-       mknod /dev/hdb 60660 64
-       mknod /dev/hdc 60660 128
-       mknod /dev/hdd 60660 192
-
-       mknod /dev/hda1 60660 1
-       mknod /dev/hda2 60660 2
-       mknod /dev/hda3 60660 3
-       ...
-       mknod /dev/hda63 60660 63
-
-       mknod /dev/hdb1 60660 65
-       mknod /dev/hdb2 60660 66
-       mknod /dev/hdb3 60660 67
-       ...
-       mknod /dev/hdb63 60660 127
-
-       mknod /dev/hdc1 60660 129
-       mknod /dev/hdc2 60660 130
-       mknod /dev/hdc3 60660 131
-       ...
-       mknod /dev/hdc63 60660 191
-
-       mknod /dev/hdd1 60660 193
-       mknod /dev/hdd2 60660 194
-       mknod /dev/hdd3 60660 195
-       ...
-       mknod /dev/hdd63 60660 255
-*/
-
 #include <kernel.h>
 #include <kdata.h>
 #include <printf.h>
 #include <stdbool.h>
 #include <timer.h>
 #include <devide.h>
-#include <mbr.h>
+#include <blkdev.h>
 
 #define DRIVE_COUNT 2   /* range 1 -- 4 */
-#define MAX_SLICES 63
-
-static uint8_t  ide_drives_present; /* bitmap */
-static uint32_t ide_partition_start[DRIVE_COUNT];
-static uint8_t  ide_slice_count[DRIVE_COUNT];
 
 #ifdef IDE_REG_ALTSTATUS
 __sfr __at IDE_REG_ALTSTATUS ide_reg_altstatus;
@@ -127,16 +70,6 @@ static void devide_write_data(void *buffer, uint8_t ioport) __naked
     __endasm;
 }
 
-static void devide_delay(void)
-{
-    timer_t timeout;
-
-    timeout = set_timer_ms(25);
-
-    while(!timer_expired(timeout))
-        platform_idle();
-}
-
 static bool devide_wait(uint8_t bits)
 {
     uint8_t status;
@@ -162,122 +95,71 @@ static bool devide_wait(uint8_t bits)
     };
 }
 
-static bool devide_read_sector(uint8_t *buffer)
-{
-    if(!devide_wait(IDE_STATUS_DATAREQUEST))
-        return false;
-
-    devide_read_data(buffer, IDE_REG_DATA);
-
-    return true;
-}
-
-static bool devide_write_sector(uint8_t *buffer)
+static bool devide_transfer_sector(uint8_t drive, uint32_t lba, void *buffer, bool read_notwrite)
 {
-    if(!devide_wait(IDE_STATUS_DATAREQUEST))
-        return false;
-
-    devide_write_data(buffer, IDE_REG_DATA);
-
-    if(!devide_wait(IDE_STATUS_READY))
-        return false;
-
-    return true;
-}
-
-static int devide_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
-{
-    uint8_t *target, *p;
-    uint32_t lba;
-    uint8_t drive;
-
-    drive = minor >> 6;
-    minor = minor & 0x3F;
-
-    if(rawflag == 0) {
-        target = udata.u_buf->bf_data;
-        lba = udata.u_buf->bf_blk;
-    }else
-        goto xferfail;
-
-    /* minor 0 is the whole disk and requires no translation */
-    if(minor > 0){
-        /* minor 1+ are slices located within the partition */
-        lba += (ide_partition_start[drive]);
-        lba += ((uint32_t)(minor-1) << SLICE_SIZE_LOG2_SECTORS);
-    }
-
 #if 0
     ide_reg_lba_3 = ((lba >> 24) & 0xF) | ((drive == 0) ? 0xE0 : 0xF0); // select drive, start loading LBA
     ide_reg_lba_2 = (lba >> 16);
     ide_reg_lba_1 = (lba >> 8);
     ide_reg_lba_0 = lba;
-#else
+#else 
     /* sdcc sadly unable to figure this out for itself yet */
-    p = (uint8_t *)&lba;
+    uint8_t *p = (uint8_t *)&lba;
     ide_reg_lba_3 = (p[3] & 0x0F) | ((drive == 0) ? 0xE0 : 0xF0); // select drive, start loading LBA
     ide_reg_lba_2 = p[2];
     ide_reg_lba_1 = p[1];
     ide_reg_lba_0 = p[0];
 #endif
-    ide_reg_sec_count = 1;
-
-    if(is_read){
-        ide_reg_command = IDE_CMD_READ_SECTOR;
-        if(!devide_read_sector(target))
-            goto xferfail;
-    }else{
-        ide_reg_command = IDE_CMD_WRITE_SECTOR;
-        if(!devide_write_sector(target))
-            goto xferfail;
-    }
 
-    return 1;
-xferfail:
-    udata.u_error = EIO;
-    return -1;
-}
+    if(!devide_wait(IDE_STATUS_READY))
+       return false;
 
-int devide_open(uint8_t minor, uint16_t flags)
-{
-    uint8_t drive;
-    flags; /* not used */
+    ide_reg_sec_count = 1;
+    ide_reg_command = read_notwrite ? IDE_CMD_READ_SECTOR : IDE_CMD_WRITE_SECTOR;
 
-    drive = minor >> 6;
-    minor = minor & 0x3F;
+    if(!devide_wait(IDE_STATUS_DATAREQUEST))
+        return false;
 
-    if(ide_drives_present & (1 << drive) && (minor == 0 || minor < ide_slice_count[drive]))
-        return 0;
+    if(read_notwrite)
+       devide_read_data(buffer, IDE_REG_DATA);
+    else{
+       devide_write_data(buffer, IDE_REG_DATA);
+       if(!devide_wait(IDE_STATUS_READY))
+           return false;
+    }
 
-    udata.u_error = ENODEV;
-    return -1;
+    return true;
 }
 
-int devide_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
-    flag; /* not used */
-    return devide_transfer(minor, true, rawflag);
-}
+/****************************************************************************/
+/* Code below this point used only once, at startup, so we want it to live  */
+/* in the DISCARD segment. sdcc only allows us to specify one segment for   */
+/* each source file. This "solution" is a bit (well, very) hacky ...        */
+/****************************************************************************/
+static void DISCARDSEG(void) __naked { __asm .area _DISCARD __endasm; }
 
-int devide_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+static void devide_delay(void)
 {
-    flag; /* not used */
-    return devide_transfer(minor, false, rawflag);
+    timer_t timeout;
+
+    timeout = set_timer_ms(25);
+
+    while(!timer_expired(timeout))
+        platform_idle();
 }
 
-void devide_init_drive(uint8_t drive)
+static void devide_init_drive(uint8_t drive)
 {
     uint8_t *buffer, select;
+    uint32_t size;
 
     switch(drive){
-        case 0: select = 0xE0; break;
-        case 1: select = 0xF0; break;
+       case 0: select = 0xE0; break;
+       case 1: select = 0xF0; break;
         default: return;
     }
 
-    kprintf("hd%c: ", 'a' + drive);
-    ide_partition_start[drive] = 0;
-    ide_slice_count[drive] = 0;
+    kprintf("IDE drive %d: ", drive);
 
     /* Reset depends upon the presence of alt control, which is optional */
 #ifdef IDE_REG_CONTROL
@@ -303,38 +185,32 @@ void devide_init_drive(uint8_t drive)
     /* confirm drive has LBA support */
     if(!devide_wait(IDE_STATUS_READY))
         return;
+
+    /* send identify command */
     ide_reg_command = IDE_CMD_IDENTIFY;
 
     /* allocate temporary sector buffer memory */
     buffer = (uint8_t *)tmpbuf();
 
-    if(!devide_read_sector(buffer))
-        goto failout;
+    if(!devide_wait(IDE_STATUS_DATAREQUEST))
+       goto failout;
+
+    devide_read_data(buffer, IDE_REG_DATA);
 
     if(!(buffer[99] & 0x02)){
         kputs("LBA unsupported.\n");
         goto failout;
     }
 
-    /* read first sector (looking for the partition table) */
-    if(!devide_wait(IDE_STATUS_READY))
-        goto failout;
-
-    ide_reg_devhead = select;
-    ide_reg_lba_2 = 0;
-    ide_reg_lba_1 = 0;
-    ide_reg_lba_0 = 0;
-    ide_reg_sec_count = 1;
-    ide_reg_command = IDE_CMD_READ_SECTOR;
+    /* read out the drive's sector count */
+    size = *((uint32_t*)&buffer[120]);
 
-    if(!devide_read_sector(buffer))
-        goto failout;
-
-    /* if we get this far the drive is apparently present and functioning */
-    ide_drives_present |= (1 << drive);
+    /* done with our temporary memory */
+    brelse((bufptr)buffer);
 
-    parse_partition_table(buffer, &ide_partition_start[drive], &ide_slice_count[drive], MAX_SLICES);
+    blkdev_add(devide_transfer_sector, drive, size);
 
+    return;
 failout:
     brelse((bufptr)buffer);
 }
@@ -343,8 +219,6 @@ void devide_init(void)
 {
     uint8_t d;
 
-    ide_drives_present = 0;
-
     for(d=0; d<DRIVE_COUNT; d++)
         devide_init_drive(d);
 }
index 75409f0..560cb93 100644 (file)
@@ -21,9 +21,6 @@
 */
 
 void devide_init(void);
-int  devide_open(uint8_t minor, uint16_t flags);
-int  devide_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
-int  devide_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
 
 #ifdef IDE_REG_BASE
 #ifdef IDE_REG_CS0_FIRST
index 0dc0ccf..66ad9a2 100644 (file)
@@ -1,6 +1,7 @@
 /*-----------------------------------------------------------------------*/
 /* Fuzix SD card driver                                                  */
 /* 2014-12-28 Will Sowerbutts                                            */
+/* 2015-01-04 WRS updated to new blkdev API                              */
 /*                                                                       */
 /* Based on UZI-socz80 SD card driver, which was itself based on:        */
 /*   MMCv3/SDv1/SDv2 (in SPI mode) control module  (C)ChaN, 2007         */
@@ -8,28 +9,18 @@
 /* and http://elm-chan.org/docs/mmc/mmc_e.html                           */
 /*-----------------------------------------------------------------------*/
 
-/* Minor numbers: See comments in devide.c, exactly the same scheme is used here. */
-
 #include <kernel.h>
 #include <kdata.h>
 #include <printf.h>
 #include <timer.h>
 #include <devsd.h>
 #include <stdbool.h>
-#include <mbr.h>
-#include "config.h"
-
-#define MAX_SLICES 63
+#include <blkdev.h>
 
-/* keep track of current card type, UZI partition offset */
-static uint8_t  sd_drive_present; /* bitmap */
-static uint8_t  sd_card_type[SD_DRIVE_COUNT];
-static uint32_t sd_partition_start[SD_DRIVE_COUNT];
-static uint8_t  sd_slice_count[SD_DRIVE_COUNT];
+static uint8_t sd_card_type[SD_DRIVE_COUNT];
 
 /* internal functions */
 static void sd_init_drive(uint8_t drive);
-static int sd_readwrite(uint8_t minor, uint8_t rawflag, bool do_write);
 static int sd_spi_init(uint8_t drive);
 static void sd_spi_release(uint8_t drive);
 static int sd_spi_wait_ready(uint8_t drive);
@@ -37,178 +28,36 @@ static bool sd_spi_transmit_sector(uint8_t drive, void *ptr, unsigned int length
 static bool sd_spi_receive_sector(uint8_t drive, void *ptr, unsigned int length);
 static int sd_send_command(uint8_t drive, unsigned char cmd, uint32_t arg);
 static uint32_t sd_get_size_sectors(uint8_t drive);
-static bool sd_read_sector(uint8_t drive, void *ptr, uint32_t lba);
-static bool sd_write_sector(uint8_t drive, void *ptr, uint32_t lba);
-
-int devsd_open(uint8_t minor, uint16_t flags)
-{
-    uint8_t drive;
-    flags; /* not used */
-
-    drive = minor >> 6;
-    minor = minor & 0x3F;
-
-    if(sd_drive_present & (1 << drive) && (minor == 0 || minor < sd_slice_count[drive]))
-        return 0;
-
-    udata.u_error = ENODEV;
-    return -1;
-}
-
-int devsd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
-    flag; /* not used */
-    return sd_readwrite(minor, rawflag, false);
-}
-
-int devsd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
-    flag; /* not used */
-    return sd_readwrite(minor, rawflag, true);
-}
 
-void devsd_init(void)
+static bool devsd_transfer_sector(uint8_t drive, uint32_t lba, void *buffer, bool read_notwrite)
 {
-    uint8_t d;
+    uint8_t attempt;
+    bool success;
 
-    sd_drive_present = 0;
-    for(d=0; d<SD_DRIVE_COUNT; d++)
-        sd_init_drive(d);
-}
+    if(!(sd_card_type[drive] & CT_BLOCK))
+       lba <<= 9; /* multiply by 512 to convert block to byte address */
 
-static int sd_readwrite(uint8_t minor, uint8_t rawflag, bool do_write)
-{
-    uint32_t lba;
-    int attempt;
-    void *target;
-    uint8_t drive;
-
-    drive = minor >> 6;
-    minor = minor & 0x3F;
+    for(attempt=0; attempt<8; attempt++){
+       if(sd_send_command(drive, read_notwrite ? CMD17 : CMD24, lba) == 0){
+           if(read_notwrite)
+               success = sd_spi_receive_sector(drive, buffer, 512);
+           else
+               success = sd_spi_transmit_sector(drive, buffer, 512);
+       }else
+           success = false;
 
-    if(rawflag == 0){
-        target = udata.u_buf->bf_data;
+       sd_spi_release(drive);
 
-        for(attempt=0; attempt<5; attempt++){
-            lba = udata.u_buf->bf_blk;
+       if(success)
+           return 1;
 
-            /* minor 0 is the whole drive, without translation */
-            if(minor > 0){
-                lba += sd_partition_start[drive];
-                lba += ((unsigned long)(minor-1) << SLICE_SIZE_LOG2_SECTORS);
-            }
-            if(do_write){
-                if(sd_write_sector(drive, udata.u_buf->bf_data, lba))
-                    return 1;
-            }else{
-                if(sd_read_sector(drive, udata.u_buf->bf_data, lba))
-                    return 1;
-            }
-            kputs("sd: failed, retrying.\n");
-        }
+       kputs("sd: failed, retrying.\n");
     }
 
     udata.u_error = EIO;
     return -1;
 }
 
-static void sd_init_drive(uint8_t drive)
-{
-    uint32_t sector_count;
-    unsigned char *sector;
-
-    sd_partition_start[drive] = 0;
-    sd_slice_count[drive] = 0;
-
-    kprintf("sd%c: ", 'a'+drive);
-    sd_card_type[drive] = sd_spi_init(drive);
-
-    if(!(sd_card_type[drive] & (~CT_BLOCK))){
-        kprintf("no card found\n");
-        return;
-    }
-    
-    /* read and compute card size */
-    sector_count = sd_get_size_sectors(drive);
-    if(!sector_count){
-        kputs("weird card\n");
-        return;
-    }
-
-    /* read partition table, locate the UZI partition */
-    sector = (unsigned char*)tmpbuf();
-
-    if(!sd_read_sector(drive, sector, 0)){
-        kprintf("failed to read partition table\n");
-        goto failout;
-    }
-
-    /* if we get this far the drive is probably ok */
-    sd_drive_present |= (1 << drive);
-
-    /* look for our partition */
-    parse_partition_table(sector, &sd_partition_start[drive], &sd_slice_count[drive], MAX_SLICES);
-
-failout:
-    brelse((bufptr)sector);
-}
-
-static int sd_spi_init(uint8_t drive)
-{
-    unsigned char n, cmd, card_type, ocr[4];
-    timer_t timer;
-
-    sd_spi_raise_cs(drive);
-
-    sd_spi_clock(drive, false);
-    for (n = 20; n; n--)
-        sd_spi_receive_byte(drive); /* 160 dummy clocks */
-
-    card_type = 0;
-    /* Enter Idle state */
-    if (sd_send_command(drive, CMD0, 0) == 1) {
-        /* initialisation timeout 1 second */
-        timer = set_timer_sec(1);
-        if (sd_send_command(drive, CMD8, (uint32_t)0x1AA) == 1) {    /* SDHC */
-            /* Get trailing return value of R7 resp */
-            for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte(drive);
-            /* The card can work at vdd range of 2.7-3.6V */
-            if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
-                /* Wait for leaving idle state (ACMD41 with HCS bit) */
-                while(!timer_expired(timer) && sd_send_command(drive, ACMD41, (uint32_t)1 << 30));
-                /* Check CCS bit in the OCR */
-                if (!timer_expired(timer) && sd_send_command(drive, CMD58, 0) == 0) {
-                    for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte(drive);
-                    card_type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;   /* SDv2 */
-                }
-            }
-        } else { /* SDSC or MMC */
-            if (sd_send_command(drive, ACMD41, 0) <= 1) {
-                /* SDv1 */
-                card_type = CT_SD1;
-                cmd = ACMD41;
-            } else {
-                /* MMCv3 */
-                card_type = CT_MMC;
-                cmd = CMD1;
-            }
-            /* Wait for leaving idle state */
-            while(!timer_expired(timer) && sd_send_command(drive, cmd, 0));
-            /* Set R/W block length to 512 */
-            if(timer_expired || sd_send_command(drive, CMD16, 512) != 0)
-                card_type = 0;
-        }
-    }
-    sd_spi_release(drive);
-
-    if (card_type) {
-        sd_spi_clock(drive, true); /* up to 20MHz should be OK */
-        return card_type;
-    }
-
-    return 0; /* failed */
-}
-
 static void sd_spi_release(uint8_t drive)
 {
     sd_spi_raise_cs(drive);
@@ -320,54 +169,113 @@ static int sd_send_command(uint8_t drive, unsigned char cmd, uint32_t arg)
     return res;         /* Return with the response value */
 }
 
-static uint32_t sd_get_size_sectors(uint8_t drive)
+/****************************************************************************/
+/* Code below this point used only once, at startup, so we want it to live  */
+/* in the DISCARD segment. sdcc only allows us to specify one segment for   */
+/* each source file. This "solution" is a bit (well, very) hacky ...        */
+/****************************************************************************/
+static void DISCARDSEG(void) __naked { __asm .area _DISCARD __endasm; }
+
+void devsd_init(void)
 {
-    unsigned char csd[16], n;
-    uint32_t sectors = 0;
+    uint8_t d;
 
-    if(sd_send_command(drive, CMD9, 0) == 0 && sd_spi_receive_sector(drive, csd, 16)){
-        if ((csd[0] >> 6) == 1) {      /* SDC ver 2.00 */
-            sectors = ((uint32_t)csd[9] + (uint32_t)((unsigned int)csd[8] << 8) + 1) << 10;
-        } else {                                       /* SDC ver 1.XX or MMC*/
-            n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
-            sectors = (csd[8] >> 6) + ((unsigned int)csd[7] << 2) + ((unsigned int)(csd[6] & 3) << 10) + 1;
-            sectors = sectors << (n - 9);
-        }
-    }
-    sd_spi_release(drive);
-    return sectors;
+    for(d=0; d<SD_DRIVE_COUNT; d++)
+        sd_init_drive(d);
 }
 
-static bool sd_read_sector(uint8_t drive, void *ptr, uint32_t lba)
+static void sd_init_drive(uint8_t drive)
 {
-    bool r = false;
-
-    if(sd_card_type[drive] != CT_NONE){
-        if(!(sd_card_type[drive] & CT_BLOCK))
-            lba = lba << 9; /* multiply by 512 to get byte address */
+    uint32_t sector_count;
 
-        if(sd_send_command(drive, CMD17, lba) == 0)
-            r = sd_spi_receive_sector(drive, ptr, 512);
+    kprintf("SD drive %d: ", drive);
+    sd_card_type[drive] = sd_spi_init(drive);
 
-        sd_spi_release(drive);
+    if(!(sd_card_type[drive] & (~CT_BLOCK))){
+        kprintf("no card found\n");
+        return;
+    }
+    
+    /* read and compute card size */
+    sector_count = sd_get_size_sectors(drive);
+    if(!sector_count){
+        kputs("weird card\n");
+        return;
     }
 
-    return r;
+    blkdev_add(devsd_transfer_sector, drive, sector_count);
 }
 
-static bool sd_write_sector(uint8_t drive, void *ptr, uint32_t lba)
+static int sd_spi_init(uint8_t drive)
 {
-    bool r = false;
+    unsigned char n, cmd, card_type, ocr[4];
+    timer_t timer;
 
-    if(sd_card_type[drive] != CT_NONE){
-        if(!(sd_card_type[drive] & CT_BLOCK))
-            lba = lba << 9; /* multiply by 512 to get byte address */
+    sd_spi_raise_cs(drive);
 
-        if(sd_send_command(drive, CMD24, lba) == 0)
-            r = sd_spi_transmit_sector(drive, ptr, 512);
+    sd_spi_clock(drive, false);
+    for (n = 20; n; n--)
+        sd_spi_receive_byte(drive); /* 160 dummy clocks */
 
-        sd_spi_release(drive);
+    card_type = 0;
+    /* Enter Idle state */
+    if (sd_send_command(drive, CMD0, 0) == 1) {
+        /* initialisation timeout 1 second */
+        timer = set_timer_sec(1);
+        if (sd_send_command(drive, CMD8, (uint32_t)0x1AA) == 1) {    /* SDHC */
+            /* Get trailing return value of R7 resp */
+            for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte(drive);
+            /* The card can work at vdd range of 2.7-3.6V */
+            if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
+                /* Wait for leaving idle state (ACMD41 with HCS bit) */
+                while(!timer_expired(timer) && sd_send_command(drive, ACMD41, (uint32_t)1 << 30));
+                /* Check CCS bit in the OCR */
+                if (!timer_expired(timer) && sd_send_command(drive, CMD58, 0) == 0) {
+                    for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte(drive);
+                    card_type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;   /* SDv2 */
+                }
+            }
+        } else { /* SDSC or MMC */
+            if (sd_send_command(drive, ACMD41, 0) <= 1) {
+                /* SDv1 */
+                card_type = CT_SD1;
+                cmd = ACMD41;
+            } else {
+                /* MMCv3 */
+                card_type = CT_MMC;
+                cmd = CMD1;
+            }
+            /* Wait for leaving idle state */
+            while(!timer_expired(timer) && sd_send_command(drive, cmd, 0));
+            /* Set R/W block length to 512 */
+            if(timer_expired || sd_send_command(drive, CMD16, 512) != 0)
+                card_type = 0;
+        }
     }
+    sd_spi_release(drive);
 
-    return r;
+    if (card_type) {
+        sd_spi_clock(drive, true); /* up to 20MHz should be OK */
+        return card_type;
+    }
+
+    return 0; /* failed */
+}
+
+static uint32_t sd_get_size_sectors(uint8_t drive)
+{
+    unsigned char csd[16], n;
+    uint32_t sectors = 0;
+
+    if(sd_send_command(drive, CMD9, 0) == 0 && sd_spi_receive_sector(drive, csd, 16)){
+        if ((csd[0] >> 6) == 1) {      /* SDC ver 2.00 */
+            sectors = ((uint32_t)csd[9] + (uint32_t)((unsigned int)csd[8] << 8) + 1) << 10;
+        } else {                                       /* SDC ver 1.XX or MMC*/
+            n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
+            sectors = (csd[8] >> 6) + ((unsigned int)csd[7] << 2) + ((unsigned int)(csd[6] & 3) << 10) + 1;
+            sectors = sectors << (n - 9);
+        }
+    }
+    sd_spi_release(drive);
+    return sectors;
 }
index 5ab232b..840015b 100644 (file)
@@ -15,9 +15,6 @@
 
 
 /* public interface */
-int devsd_open(uint8_t minor, uint16_t flags);
-int devsd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
-int devsd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
 void devsd_init(void);
 
 /* platform-specific SPI functions */
index 9985b64..1a90c80 100644 (file)
@@ -1,46 +1,80 @@
 #include <kernel.h>
 #include <kdata.h>
 #include <printf.h>
+#include <blkdev.h>
 
 typedef struct {
-    unsigned char status;
-    unsigned char chs_first[3];
-    unsigned char type;
-    unsigned char chs_last[3];
-    unsigned long lba_first;
-    unsigned long lba_count;
+    uint8_t  status;
+    uint8_t  chs_first[3];
+    uint8_t  type;
+    uint8_t  chs_last[3];
+    uint32_t lba_first;
+    uint32_t lba_count;
 } partition_table_entry_t;
 
 #define MBR_ENTRY_COUNT 4
+#define MBR_SIGNATURE 0xAA55
 typedef struct {
-    unsigned char bootcode[446];
+    uint8_t bootcode[446];
     partition_table_entry_t partition[MBR_ENTRY_COUNT];
-    unsigned int signature;
-} master_boot_record_t;
+    uint16_t signature;
+} boot_record_t;
 
-void parse_partition_table(void *buffer, uint32_t *first_lba, uint8_t *slice_count, uint8_t max_slices)
+void mbr_parse(blkdev_t *blk, char letter)
 {
-    master_boot_record_t *mbr = (master_boot_record_t*)buffer;
-    uint16_t slices = 0;
+    boot_record_t *br;
     uint8_t i;
+    uint32_t lba = 0, ep_offset = 0, br_offset = 0;
+    uint8_t next = 0;
 
-    /* check for MBR table signature */
-    if(mbr->signature != 0xaa55){
-        kputs("no partition table\n");
-        return;
-    }
-
-    /* look for a fuzix partition (type 0x5A) */
-    for(i=0; i<MBR_ENTRY_COUNT; i++){
-        if(mbr->partition[i].type == 0x5A){
-            *first_lba = mbr->partition[i].lba_first;
-            slices = mbr->partition[i].lba_count >> SLICE_SIZE_LOG2_SECTORS;
-            if(slices > max_slices)
-                slices = max_slices;
-            *slice_count = slices;
-            break;
-        }
-    }
-
-    kprintf("%d slices\n", slices);
+    /* allocate temporary memory */
+    br = (boot_record_t *)tmpbuf();
+
+    do{
+       if(!blk->transfer(blk->drive_number, lba, br, true) || br->signature != MBR_SIGNATURE)
+           break;
+
+       if(next < 4 && lba != 0){ 
+           /* we just loaded the first extended boot record */
+           ep_offset = lba;
+           next = 4;
+           kputs("< ");
+       }
+
+       br_offset = lba;
+       lba = 0;
+
+       for(i=0; i<MBR_ENTRY_COUNT && next < MAX_PARTITIONS; i++){
+           switch(br->partition[i].type){
+               case 0:
+                   break;
+               case 0x05:
+               case 0x0f:
+               case 0x85:
+                   /* Extended boot record, or chained table; in principle a drive should contain
+                      at most one extended partition so this code is OK even for parsing the MBR.
+                      Chained EBR addresses are relative to the start of the extended partiton. */
+                   lba = ep_offset + br->partition[i].lba_first;
+                   if(next >= 4)
+                       break;
+                   /* we include all primary partitions but we deliberately knobble the size in 
+                      order to prevent catastrophic accidents */
+                   br->partition[i].lba_count = 2;
+                   /* fall through */
+               default:
+                   /* Regular partition: In EBRs these are relative to the EBR (not the disk, nor
+                      the extended partition) */
+                   blk->lba_first[next] = br_offset + br->partition[i].lba_first;
+                   blk->lba_count[next] = br->partition[i].lba_count;
+                   kprintf("hd%c%d ", letter, 1+next);
+                   next++;
+           }
+       }
+    }while(lba);
+
+    if(next >= 4)
+       kputs("> ");
+
+    /* release temporary memory */
+    brelse((bufptr)br);
 }
index 1bb5c01..8286160 100644 (file)
@@ -1,6 +1,6 @@
 #ifndef __MBR_DOT_H__
 #define __MBR_DOT_H__
 
-void parse_partition_table(void *buffer, uint32_t *first_lba, uint8_t *slice_count, uint8_t max_slices);
+void mbr_parse(blkdev_t *blk, char letter);
 
 #endif
index 601b979..30c8df3 100644 (file)
@@ -88,8 +88,6 @@ typedef uint16_t blkno_t;    /* Can have 65536 512-byte blocks in filesystem */
 #define BLKSHIFT       9
 #define BLKMASK                511
 
-#define SLICE_SIZE_LOG2_SECTORS 16    /* 32MB slices */
-
 /* FIXME: if we could split the data and the header we could keep blocks
    outside of our kernel data (as ELKS does) which would be a win, but need
    some more care on copies, block indexes and directory ops */
index 7f22686..5867070 100644 (file)
@@ -1,5 +1,5 @@
 
-CSRCS = ../dev/devsd.c ../dev/mbr.c devfd.c devhd.c devlpr.c
+CSRCS = ../dev/devsd.c ../dev/mbr.c ../dev/blkdev.c devfd.c devhd.c devlpr.c
 CSRCS += devices.c main.c devtty.c
 DSRCS = devmegasd.c
 ASRCS = msx2.s crt0.s vdp.s
index ae55549..0554315 100644 (file)
@@ -43,3 +43,5 @@
 
 #define DEVICE_SD
 #define SD_DRIVE_COUNT 1
+
+#define MAX_BLKDEV 1      /* Single SD drive */
index 1fd53bf..98bf22c 100644 (file)
@@ -8,7 +8,8 @@
 #include <tty.h>
 #include <vt.h>
 #include <devtty.h>
-#include <../dev/devsd.h>
+#include <devsd.h>
+#include <blkdev.h>
 
 extern int megasd_probe();
 
@@ -16,8 +17,8 @@ struct devsw dev_tab[] =  /* The device driver switch table */
 {
   /* 0: /dev/fd                Floppy disc block devices */
   {  no_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
-  /* 1: /dev/sd                MegaSD Interface */
-  {  devsd_open,     no_close,    devsd_read,   devsd_write,   no_ioctl },
+  /* 1: /dev/hd                MegaSD Interface */
+  {  blkdev_open,    no_close,   blkdev_read,  blkdev_write,   no_ioctl },
   /* 2: /dev/tty       TTY devices */
   {  tty_open,     tty_close,   tty_read,  tty_write,  vt_ioctl },
   /* 3: /dev/lpr       Printer devices */
index 3d1bad0..7ca30f2 100644 (file)
@@ -36,6 +36,7 @@ vt.rel
 devsys.rel
 usermem.rel
 usermem_std-z80.rel
+platform-msx2/blkdev.rel
 platform-msx2/devsd.rel
 platform-msx2/devmegasd.rel
 platform-msx2/mbr.rel
index db10613..84942f9 100644 (file)
@@ -1,5 +1,5 @@
 CSRCS += devices.c main.c devtty.c devsdspi.c
-DSRCS = ../dev/devide.c ../dev/devsd.c ../dev/mbr.c ../dev/ds1302.c
+DSRCS = ../dev/devide.c ../dev/devsd.c ../dev/mbr.c ../dev/blkdev.c ../dev/ds1302.c
 ASRCS = crt0.s z180.s commonmem.s mark4.s ds1302-mark4.s
 
 AOBJS = $(ASRCS:.s=.rel)
index 28a47cf..5a85b73 100644 (file)
@@ -50,6 +50,8 @@
 #define Z180_IO_BASE       0x40
 #define MARK4_IO_BASE      0x80
 
+#define MAX_BLKDEV 3       /* 2 IDE drives, 1 SD drive */
+
 #define DEVICE_IDE                  /* enable if IDE interface present */
 #define IDE_REG_BASE       MARK4_IO_BASE
 #define IDE_8BIT_ONLY
index c1d72b1..e5c493d 100644 (file)
@@ -6,23 +6,17 @@
 #include <devtty.h>
 #include <devide.h>
 #include <devsd.h>
+#include <blkdev.h>
 #include <ds1302.h>
 
 struct devsw dev_tab[] =  /* The device driver switch table */
 {
-// minor    open         close        read      write       ioctl
-// -----------------------------------------------------------------
-  /* 0: /dev/hd                IDE-8 interface */
-  {  devide_open, no_close, devide_read, devide_write, no_ioctl },
-  /* 1: /dev/sd                SD interface */
-  {  devsd_open,  no_close, devsd_read,  devsd_write,  no_ioctl },
-  /* 2: /dev/tty       serial ports */
-  {  tty_open,    tty_close,   tty_read,  tty_write,  tty_ioctl },
-  /* 3: /dev/lpr       Unused slot (pad to keep system devices at index 4) */
-  {  no_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
-  /* 4: /dev/mem etc   System devices (one offs) */
-  {  no_open,      no_close,    sys_read, sys_write, sys_ioctl  },
-  /* Pack to 7 with nxio if adding private devices and start at 8 */
+/*   open          close       read            write           ioctl */
+  {  blkdev_open,   no_close,  blkdev_read,    blkdev_write,   no_ioctl },     /* 0: /dev/hd -- standard block device interface */
+  {  no_open,      no_close,   no_rdwr,        no_rdwr,        no_ioctl },     /* 1: unused slot */
+  {  tty_open,     tty_close,  tty_read,       tty_write,      tty_ioctl },    /* 2: /dev/tty -- serial ports */
+  {  no_open,      no_close,   no_rdwr,        no_rdwr,        no_ioctl },     /* 3: unused slot */
+  {  no_open,      no_close,   sys_read,       sys_write,      sys_ioctl  },   /* 4: /dev/mem etc      System devices (one offs) */
 };
 
 bool validdev(uint16_t dev)
index 2b4e801..dcce185 100644 (file)
@@ -36,6 +36,7 @@ platform-n8vem-mark4/devide.rel
 platform-n8vem-mark4/devsd.rel
 platform-n8vem-mark4/devsdspi.rel
 platform-n8vem-mark4/mbr.rel
+platform-n8vem-mark4/blkdev.rel
 platform-n8vem-mark4/ds1302.rel
 platform-n8vem-mark4/ds1302-mark4.rel
 -e
index c132819..9f195fd 100644 (file)
@@ -1,5 +1,5 @@
 CSRCS += devices.c main.c devtty.c
-DSRCS = ../dev/devide.c ../dev/mbr.c ../dev/ds1302.c
+DSRCS = ../dev/blkdev.c ../dev/devide.c ../dev/mbr.c ../dev/ds1302.c
 ASRCS = crt0.s z180.s commonmem.s p112.s ds1302-p112.s
 
 AOBJS = $(ASRCS:.s=.rel)
index dc46476..f664412 100644 (file)
@@ -49,6 +49,8 @@
 /* Hardware parameters */
 #define Z180_IO_BASE       0x00
 
+#define MAX_BLKDEV  2              /* 2 IDE drives */
+
 #define DEVICE_IDE                  /* enable if IDE interface present */
 #define IDE_REG_BASE       0x50
 #define IDE_REG_CS0_FIRST
index 9f69e36..f36711c 100644 (file)
@@ -5,23 +5,17 @@
 #include <devsys.h>
 #include <devtty.h>
 #include <devide.h>
+#include <blkdev.h>
 #include <ds1302.h>
 
 struct devsw dev_tab[] =  /* The device driver switch table */
 {
-// minor    open         close        read      write       ioctl
-// -----------------------------------------------------------------
-  /* 0: /dev/hd                IDE-8 interface */
-  {  devide_open, no_close, devide_read, devide_write, no_ioctl },
-  /* 1: /dev/sd                SD interface */
-  {  no_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
-  /* 2: /dev/tty       serial ports */
-  {  tty_open,     tty_close,   tty_read,  tty_write,  tty_ioctl },
-  /* 3: /dev/lpr       Unused slot (pad to keep system devices at index 4) */
-  {  no_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
-  /* 4: /dev/mem etc   System devices (one offs) */
-  {  no_open,      no_close,    sys_read, sys_write, sys_ioctl  },
-  /* Pack to 7 with nxio if adding private devices and start at 8 */
+/*   open          close       read            write           ioctl */
+  {  blkdev_open,   no_close,  blkdev_read,    blkdev_write,   no_ioctl },     /* 0: /dev/hd -- standard block device interface */
+  {  no_open,      no_close,   no_rdwr,        no_rdwr,        no_ioctl },     /* 1: unused slot */
+  {  tty_open,     tty_close,  tty_read,       tty_write,      tty_ioctl },    /* 2: /dev/tty -- serial ports */
+  {  no_open,      no_close,   no_rdwr,        no_rdwr,        no_ioctl },     /* 3: unused slot */
+  {  no_open,      no_close,   sys_read,       sys_write,      sys_ioctl  },   /* 4: /dev/mem etc      System devices (one offs) */
 };
 
 bool validdev(uint16_t dev)
index 94a4f48..00fe4cf 100644 (file)
@@ -33,6 +33,7 @@ swap.rel
 devsys.rel
 platform-p112/devtty.rel
 platform-p112/devide.rel
+platform-p112/blkdev.rel
 platform-p112/mbr.rel
 platform-p112/ds1302.rel
 platform-p112/ds1302-p112.rel