/* $NetBSD: sdcd.c,v 1.6 2006/01/25 18:28:28 christos 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 "sdcdvar.h" #include "iocs.h" static int current_id = -1; static int current_blklen, current_devsize, current_npart; static struct boot_partinfo partitions[MAXPARTITIONS]; int sdopen(struct open_file *, int, int); int sdclose(struct open_file*); int sdstrategy(void *devdata, int rw, daddr_t blk, size_t, void*, size_t*); int sd_getbsdpartition(int, int); int cdopen(struct open_file *, int, int); int cdclose(struct open_file*); int cdstrategy(void *devdata, int rw, daddr_t blk, size_t, void*, size_t*); static int readdisklabel(int); static int check_unit(int); #ifdef DEBUG #define DPRINTF(x) printf x #else #define DPRINTF(x) #endif 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(100, id, inqdata); if (error < 0) { /* WHY??? */ error = ENXIO; goto out; } if ((inqdata->unit != 0) && /* direct */ (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_blklen = rcdata->size >> 9; current_devsize = rcdata->block; } { error = IOCS_S_READ(0, 1, id, current_blklen, 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_blklen > 4) { printf ("FATAL: Unsupported block size %d.\n", 256 << current_blklen); return ERDLAB; } /* Try BSD disklabel first */ buffer = alloca(2048); error = IOCS_S_READ(LABELSECTOR, 1, id, current_blklen, 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 */ #if 0 /* assumes 512byte/sec */ error = IOCS_S_READ(DOSPARTOFF, 2, id, current_blklen, buffer); #else error = IOCS_S_READ(8 >> current_blklen, 8 >> current_blklen, id, current_blklen, buffer); #endif 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 = parttbl[i].dp_start * 2; partitions[current_npart].size = parttbl[i].dp_size * 2; 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(8 >> current_blklen, 8 >> current_blklen, id, current_blklen, 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 = parttbl[humanpart].dp_start; parttop = parttop<<(2-current_blklen); 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; int sc_blocksize; }; int sdopen (struct open_file *f, int id, int part) { int error; struct sdcd_softc *sc; 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]; sc->sc_blocksize = current_blklen << 9; f->f_devdata = sc; return 0; } int sdclose (struct open_file *f) { dealloc (f->f_devdata, sizeof (struct sdcd_softc)); return 0; } int sdstrategy (void *arg, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize) { struct sdcd_softc *sc = arg; u_int32_t start = sc->sc_partinfo.start + dblk; size_t nblks; int error; if (size == 0) { if (rsize) *rsize = 0; return 0; } nblks = howmany (size, 256 << current_blklen); if ((dblk & 0x1fffff) == 0x1fffff && (nblks & 0xff) == nblks) { if (rw & F_WRITE) error = IOCS_S_WRITE (start, nblks, current_id, current_blklen, buf); else error = IOCS_S_READ (start, nblks, current_id, current_blklen, buf); } else { if (rw & F_WRITE) error = IOCS_S_WRITEEXT (start, nblks, current_id, current_blklen, buf); else error = IOCS_S_READEXT (start, nblks, current_id, current_blklen, buf); } if (error < 0) return EIO; if (rsize) *rsize = size; return 0; } int cdopen (struct open_file *f, int id, int part) { int error; struct sdcd_softc *sc; 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.size = sc->sc_partinfo.size = current_devsize; sc->sc_blocksize = current_blklen << 9; f->f_devdata = sc; return 0; } int cdclose (struct open_file *f) { dealloc (f->f_devdata, sizeof (struct sdcd_softc)); return 0; } int cdstrategy (void *arg, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize) { struct sdcd_softc *sc = arg; return sdstrategy (arg, rw, dblk * DEV_BSIZE / sc->sc_blocksize, size, buf, rsize); }