--- /dev/null
+#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");
+}
--- /dev/null
+#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
/* 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;
__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;
};
}
-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
/* 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);
}
{
uint8_t d;
- ide_drives_present = 0;
-
for(d=0; d<DRIVE_COUNT; d++)
devide_init_drive(d);
}
*/
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
/*-----------------------------------------------------------------------*/
/* 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 */
/* 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);
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);
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;
}
/* 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 */
#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);
}
#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
#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 */
-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
#define DEVICE_SD
#define SD_DRIVE_COUNT 1
+
+#define MAX_BLKDEV 1 /* Single SD drive */
#include <tty.h>
#include <vt.h>
#include <devtty.h>
-#include <../dev/devsd.h>
+#include <devsd.h>
+#include <blkdev.h>
extern int megasd_probe();
{
/* 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 */
devsys.rel
usermem.rel
usermem_std-z80.rel
+platform-msx2/blkdev.rel
platform-msx2/devsd.rel
platform-msx2/devmegasd.rel
platform-msx2/mbr.rel
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)
#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
#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)
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
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)
/* 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
#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)
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