/* $NetBSD: sdcd.c,v 1.19 2024/01/07 07:58:34 isaki Exp $ */ /* * Copyright (c) 2001 MINOURA Makoto. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "libx68k.h" #include "sdcdvar.h" #include "iocs.h" static int current_id = -1; static int current_blkbytes; static int current_blkshift; static int current_devsize, current_npart; static struct boot_partinfo partitions[MAXPARTITIONS]; static uint human2blk(uint); static uint human2bsd(uint); static uint bsd2blk(uint); static int readdisklabel(int); static int check_unit(int); #ifdef DEBUG #define DPRINTF(x) printf x #else #define DPRINTF(x) #endif /* * Convert the number of sectors on Human68k * into the number of blocks on the current device. */ static uint human2blk(uint n) { uint blk_per_sect; /* Human68k uses 1024 byte/sector. */ blk_per_sect = 4 >> current_blkshift; if (blk_per_sect == 0) blk_per_sect = 1; return blk_per_sect * n; } /* * Convert the number of sectors on Human68k * into the number of DEV_BSIZE sectors. */ static uint human2bsd(uint n) { return n * (1024 / DEV_BSIZE); } /* * Convert the number of DEV_BSIZE sectors * into the number of blocks on the current device. */ static uint bsd2blk(uint n) { return ((DEV_BSIZE / 256) * n) >> current_blkshift; } static int check_unit(int id) { #define BUFFER_SIZE 8192 int error; void *buffer = alloca(BUFFER_SIZE); if (current_id == id) return 0; current_id = -1; error = IOCS_S_TESTUNIT(id); if (error < 0) { /* not ready */ error = ENXIO; goto out; } { struct iocs_inquiry *inqdata = buffer; error = IOCS_S_INQUIRY(sizeof(*inqdata), id, inqdata); if (error < 0) { /* WHY??? */ error = ENXIO; goto out; } if ((inqdata->unit != 0) && /* direct */ (inqdata->unit != 5) && /* cdrom */ (inqdata->unit != 7)) { /* optical */ error = EUNIT; goto out; } } { struct iocs_readcap *rcdata = buffer; error = IOCS_S_READCAP(id, rcdata); if (error < 0) { /* WHY??? */ error = EUNIT; goto out; } current_blkbytes = rcdata->size; current_blkshift = fls32(current_blkbytes) - 9; current_devsize = rcdata->block; } { error = IOCS_S_READ(0, 1, id, current_blkshift, buffer); if (error < 0) { error = EIO; goto out; } if (strncmp((char *)buffer, "X68SCSI1", 8) != 0) { error = EUNLAB; goto out; } } out: return error; } static int readdisklabel(int id) { int error, i; char *buffer; struct disklabel *label; struct dos_partition *parttbl; if (current_id == id) return 0; current_id = -1; error = check_unit(id); if (error) return error; if (current_blkbytes > 2048) { printf("FATAL: Unsupported block size %d.\n", current_blkbytes); return ERDLAB; } /* Try BSD disklabel first */ buffer = alloca(2048); error = IOCS_S_READ(LABELSECTOR, 1, id, current_blkshift, buffer); if (error < 0) return EIO; label = (void *)(buffer + LABELOFFSET); if (label->d_magic == DISKMAGIC && label->d_magic2 == DISKMAGIC) { for (i = 0; i < label->d_npartitions; i++) { partitions[i].start = label->d_partitions[i].p_offset; partitions[i].size = label->d_partitions[i].p_size; } current_npart = label->d_npartitions; goto done; } /* Try Human68K-style partition table */ error = IOCS_S_READ(human2blk(2), 1, id, current_blkshift, buffer); if (error < 0) return EIO; parttbl = (void *)(buffer + DOSBBSECTOR); if (strncmp(buffer, "X68K", 4) != 0) return EUNLAB; parttbl++; for (current_npart = 0, i = 0; current_npart < MAXPARTITIONS && i < 15 && parttbl[i].dp_size; i++) { partitions[current_npart].start = human2bsd(parttbl[i].dp_start); partitions[current_npart].size = human2bsd(parttbl[i].dp_size); if (++current_npart == RAW_PART) { partitions[current_npart].start = 0; partitions[current_npart].size = -1; /* XXX */ current_npart++; } } done: #ifdef DEBUG for (i = 0; i < current_npart; i++) { printf ("%d: starts %d, size %d\n", i, partitions[i].start, partitions[i].size); } #endif current_id = id; return 0; } int sd_getbsdpartition(int id, int humanpart) { int error, i; char *buffer; struct dos_partition *parttbl; unsigned parttop; if (humanpart < 2) humanpart++; error = readdisklabel(id); if (error) { printf("Reading disklabel: %s\n", strerror(error)); return -1; } buffer = alloca(2048); error = IOCS_S_READ(human2blk(2), 1, id, current_blkshift, buffer); if (error < 0) { printf("Reading partition table: %s\n", strerror(error)); return -1; } parttbl = (void *)(buffer + DOSBBSECTOR); if (strncmp(buffer, "X68K", 4) != 0) return 0; parttop = human2bsd(parttbl[humanpart].dp_start); for (i = 0; i < current_npart; i++) { if (partitions[i].start == parttop) return i; } printf("Could not determine the boot partition.\n"); return -1; } struct sdcd_softc { int sc_part; struct boot_partinfo sc_partinfo; }; /* sdopen(struct open_file *f, int id, int part) */ int sdopen(struct open_file *f, ...) { int error; struct sdcd_softc *sc; int id, part; va_list ap; va_start(ap, f); id = va_arg(ap, int); part = va_arg(ap, int); va_end(ap); if (id < 0 || id > 7) return ENXIO; if (current_id != id) { error = readdisklabel(id); if (error) return error; } if (part >= current_npart) return ENXIO; sc = alloc(sizeof(struct sdcd_softc)); sc->sc_part = part; sc->sc_partinfo = partitions[part]; f->f_devdata = sc; return 0; } int sdcdclose(struct open_file *f) { dealloc(f->f_devdata, sizeof(struct sdcd_softc)); return 0; } int sdcdstrategy(void *arg, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize) { struct sdcd_softc *sc = arg; uint32_t start = sc->sc_partinfo.start + dblk; size_t nblks; int error; if (size == 0) { if (rsize) *rsize = 0; return 0; } start = bsd2blk(start); nblks = howmany(size, current_blkbytes); if (start < 0x200000 && nblks < 256) { if (rw & F_WRITE) error = IOCS_S_WRITE(start, nblks, current_id, current_blkshift, buf); else error = IOCS_S_READ(start, nblks, current_id, current_blkshift, buf); } else { if (rw & F_WRITE) error = IOCS_S_WRITEEXT(start, nblks, current_id, current_blkshift, buf); else error = IOCS_S_READEXT(start, nblks, current_id, current_blkshift, buf); } if (error < 0) return EIO; if (rsize) *rsize = size; return 0; } /* cdopen(struct open_file *f, int id, int part) */ int cdopen(struct open_file *f, ...) { int error; struct sdcd_softc *sc; int id, part; va_list ap; va_start(ap, f); id = va_arg(ap, int); part = va_arg(ap, int); va_end(ap); if (id < 0 || id > 7) return ENXIO; if (part != 0 && part != 2) return ENXIO; if (current_id != id) { error = check_unit(id); if (error) return error; } sc = alloc(sizeof(struct sdcd_softc)); current_npart = 3; sc->sc_part = 0; sc->sc_partinfo.start = 0; sc->sc_partinfo.size = current_devsize; f->f_devdata = sc; current_id = id; return 0; }