353 lines
7.9 KiB
C
353 lines
7.9 KiB
C
/* $NetBSD: sdcd.c,v 1.5 2002/01/07 04:00:30 minoura 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 <sys/param.h>
|
|
#include <sys/disklabel.h>
|
|
#include <lib/libkern/libkern.h>
|
|
#include <lib/libsa/stand.h>
|
|
|
|
#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)
|
|
{
|
|
free (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)
|
|
{
|
|
free (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);
|
|
}
|