Home | History | Annotate | Line # | Download | only in libsa
      1 /*	$NetBSD: sdcd.c,v 1.19 2024/01/07 07:58:34 isaki Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2001 MINOURA Makoto.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/param.h>
     29 #include <sys/bitops.h>
     30 #include <sys/disklabel.h>
     31 #include <lib/libkern/libkern.h>
     32 #include <lib/libsa/stand.h>
     33 
     34 #include "libx68k.h"
     35 #include "sdcdvar.h"
     36 #include "iocs.h"
     37 
     38 
     39 static int current_id = -1;
     40 static int current_blkbytes;
     41 static int current_blkshift;
     42 static int current_devsize, current_npart;
     43 static struct boot_partinfo partitions[MAXPARTITIONS];
     44 
     45 static uint human2blk(uint);
     46 static uint human2bsd(uint);
     47 static uint bsd2blk(uint);
     48 static int readdisklabel(int);
     49 static int check_unit(int);
     50 
     51 #ifdef DEBUG
     52 #define DPRINTF(x)	printf x
     53 #else
     54 #define DPRINTF(x)
     55 #endif
     56 
     57 /*
     58  * Convert the number of sectors on Human68k
     59  * into the number of blocks on the current device.
     60  */
     61 static uint
     62 human2blk(uint n)
     63 {
     64 	uint blk_per_sect;
     65 
     66 	/* Human68k uses 1024 byte/sector. */
     67 	blk_per_sect = 4 >> current_blkshift;
     68 	if (blk_per_sect == 0)
     69 		blk_per_sect = 1;
     70 	return blk_per_sect * n;
     71 }
     72 
     73 /*
     74  * Convert the number of sectors on Human68k
     75  * into the number of DEV_BSIZE sectors.
     76  */
     77 static uint
     78 human2bsd(uint n)
     79 {
     80 
     81 	return n * (1024 / DEV_BSIZE);
     82 }
     83 
     84 /*
     85  * Convert the number of DEV_BSIZE sectors
     86  * into the number of blocks on the current device.
     87  */
     88 static uint
     89 bsd2blk(uint n)
     90 {
     91 
     92 	return ((DEV_BSIZE / 256) * n) >> current_blkshift;
     93 }
     94 
     95 static int
     96 check_unit(int id)
     97 {
     98 #define BUFFER_SIZE	8192
     99 	int error;
    100 	void *buffer = alloca(BUFFER_SIZE);
    101 
    102 	if (current_id == id)
    103 		return 0;
    104 
    105 	current_id = -1;
    106 
    107 	error = IOCS_S_TESTUNIT(id);
    108 	if (error < 0) {			/* not ready */
    109 		error = ENXIO;
    110 		goto out;
    111 	}
    112 
    113 	{
    114 		struct iocs_inquiry *inqdata = buffer;
    115 
    116 		error = IOCS_S_INQUIRY(sizeof(*inqdata), id, inqdata);
    117 		if (error < 0) {		/* WHY??? */
    118 			error = ENXIO;
    119 			goto out;
    120 		}
    121 		if ((inqdata->unit != 0) &&	/* direct */
    122 		    (inqdata->unit != 5) &&	/* cdrom */
    123 		    (inqdata->unit != 7)) {	/* optical */
    124 			error = EUNIT;
    125 			goto out;
    126 		}
    127 	}
    128 
    129 	{
    130 		struct iocs_readcap *rcdata = buffer;
    131 
    132 		error = IOCS_S_READCAP(id, rcdata);
    133 		if (error < 0) {		/* WHY??? */
    134 			error = EUNIT;
    135 			goto out;
    136 		}
    137 		current_blkbytes = rcdata->size;
    138 		current_blkshift = fls32(current_blkbytes) - 9;
    139 		current_devsize = rcdata->block;
    140 	}
    141 
    142 	{
    143 		error = IOCS_S_READ(0, 1, id, current_blkshift, buffer);
    144 		if (error < 0) {
    145 			error =  EIO;
    146 			goto out;
    147 		}
    148 		if (strncmp((char *)buffer, "X68SCSI1", 8) != 0) {
    149 			error = EUNLAB;
    150 			goto out;
    151 		}
    152 	}
    153 
    154  out:
    155 	return error;
    156 }
    157 
    158 static int
    159 readdisklabel(int id)
    160 {
    161 	int error, i;
    162 	char *buffer;
    163 	struct disklabel *label;
    164 	struct dos_partition *parttbl;
    165 
    166 	if (current_id == id)
    167 		return 0;
    168 	current_id = -1;
    169 
    170 	error = check_unit(id);
    171 	if (error)
    172 		return error;
    173 	if (current_blkbytes > 2048) {
    174 		printf("FATAL: Unsupported block size %d.\n",
    175 		    current_blkbytes);
    176 		return ERDLAB;
    177 	}
    178 
    179 	/* Try BSD disklabel first */
    180 	buffer = alloca(2048);
    181 	error = IOCS_S_READ(LABELSECTOR, 1, id, current_blkshift, buffer);
    182 	if (error < 0)
    183 		return EIO;
    184 	label = (void *)(buffer + LABELOFFSET);
    185 	if (label->d_magic == DISKMAGIC &&
    186 	    label->d_magic2 == DISKMAGIC) {
    187 		for (i = 0; i < label->d_npartitions; i++) {
    188 			partitions[i].start = label->d_partitions[i].p_offset;
    189 			partitions[i].size  = label->d_partitions[i].p_size;
    190 		}
    191 		current_npart = label->d_npartitions;
    192 
    193 		goto done;
    194 	}
    195 
    196 	/* Try Human68K-style partition table */
    197 	error = IOCS_S_READ(human2blk(2), 1, id, current_blkshift, buffer);
    198 	if (error < 0)
    199 		return EIO;
    200 	parttbl = (void *)(buffer + DOSBBSECTOR);
    201 	if (strncmp(buffer, "X68K", 4) != 0)
    202 		return EUNLAB;
    203 	parttbl++;
    204 	for (current_npart = 0, i = 0;
    205 	     current_npart < MAXPARTITIONS && i < 15 && parttbl[i].dp_size;
    206 	     i++) {
    207 		partitions[current_npart].start
    208 			= human2bsd(parttbl[i].dp_start);
    209 		partitions[current_npart].size
    210 			= human2bsd(parttbl[i].dp_size);
    211 		if (++current_npart == RAW_PART) {
    212 			partitions[current_npart].start = 0;
    213 			partitions[current_npart].size = -1; /* XXX */
    214 			current_npart++;
    215 		}
    216 	}
    217 done:
    218 #ifdef DEBUG
    219 	for (i = 0; i < current_npart; i++) {
    220 		printf ("%d: starts %d, size %d\n", i,
    221 			partitions[i].start,
    222 			partitions[i].size);
    223 	}
    224 #endif
    225 	current_id = id;
    226 
    227 	return 0;
    228 }
    229 
    230 int
    231 sd_getbsdpartition(int id, int humanpart)
    232 {
    233 	int error, i;
    234 	char *buffer;
    235 	struct dos_partition *parttbl;
    236 	unsigned parttop;
    237 
    238 	if (humanpart < 2)
    239 		humanpart++;
    240 
    241 	error = readdisklabel(id);
    242 	if (error) {
    243 		printf("Reading disklabel: %s\n", strerror(error));
    244 		return -1;
    245 	}
    246 	buffer = alloca(2048);
    247 	error = IOCS_S_READ(human2blk(2), 1, id, current_blkshift, buffer);
    248 	if (error < 0) {
    249 		printf("Reading partition table: %s\n", strerror(error));
    250 		return -1;
    251 	}
    252 	parttbl = (void *)(buffer + DOSBBSECTOR);
    253 	if (strncmp(buffer, "X68K", 4) != 0)
    254 		return 0;
    255 	parttop = human2bsd(parttbl[humanpart].dp_start);
    256 
    257 	for (i = 0; i < current_npart; i++) {
    258 		if (partitions[i].start == parttop)
    259 			return i;
    260 	}
    261 
    262 	printf("Could not determine the boot partition.\n");
    263 
    264 	return -1;
    265 }
    266 
    267 struct sdcd_softc {
    268 	int			sc_part;
    269 	struct boot_partinfo	sc_partinfo;
    270 };
    271 
    272 /* sdopen(struct open_file *f, int id, int part) */
    273 int
    274 sdopen(struct open_file *f, ...)
    275 {
    276 	int error;
    277 	struct sdcd_softc *sc;
    278 	int id, part;
    279 	va_list ap;
    280 
    281 	va_start(ap, f);
    282 	id   = va_arg(ap, int);
    283 	part = va_arg(ap, int);
    284 	va_end(ap);
    285 
    286 	if (id < 0 || id > 7)
    287 		return ENXIO;
    288 	if (current_id != id) {
    289 		error = readdisklabel(id);
    290 		if (error)
    291 			return error;
    292 	}
    293 	if (part >= current_npart)
    294 		return ENXIO;
    295 
    296 	sc = alloc(sizeof(struct sdcd_softc));
    297 	sc->sc_part = part;
    298 	sc->sc_partinfo = partitions[part];
    299 	f->f_devdata = sc;
    300 	return 0;
    301 }
    302 
    303 int
    304 sdcdclose(struct open_file *f)
    305 {
    306 
    307 	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
    308 	return 0;
    309 }
    310 
    311 int
    312 sdcdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
    313            void *buf, size_t *rsize)
    314 {
    315 	struct sdcd_softc *sc = arg;
    316 	uint32_t	start = sc->sc_partinfo.start + dblk;
    317 	size_t		nblks;
    318 	int		error;
    319 
    320 	if (size == 0) {
    321 		if (rsize)
    322 			*rsize = 0;
    323 		return 0;
    324 	}
    325 	start = bsd2blk(start);
    326 	nblks = howmany(size, current_blkbytes);
    327 
    328 	if (start < 0x200000 && nblks < 256) {
    329 		if (rw & F_WRITE)
    330 			error = IOCS_S_WRITE(start, nblks, current_id,
    331 			                     current_blkshift, buf);
    332 		else
    333 			error = IOCS_S_READ(start, nblks, current_id,
    334 			                    current_blkshift, buf);
    335 	} else {
    336 		if (rw & F_WRITE)
    337 			error = IOCS_S_WRITEEXT(start, nblks, current_id,
    338 			                        current_blkshift, buf);
    339 		else
    340 			error = IOCS_S_READEXT(start, nblks, current_id,
    341 			                       current_blkshift, buf);
    342 	}
    343 	if (error < 0)
    344 		return EIO;
    345 
    346 	if (rsize)
    347 		*rsize = size;
    348 	return 0;
    349 }
    350 
    351 /* cdopen(struct open_file *f, int id, int part) */
    352 int
    353 cdopen(struct open_file *f, ...)
    354 {
    355 	int error;
    356 	struct sdcd_softc *sc;
    357 	int id, part;
    358 	va_list ap;
    359 
    360 	va_start(ap, f);
    361 	id   = va_arg(ap, int);
    362 	part = va_arg(ap, int);
    363 	va_end(ap);
    364 
    365 	if (id < 0 || id > 7)
    366 		return ENXIO;
    367 	if (part != 0 && part != 2)
    368 		return ENXIO;
    369 	if (current_id != id) {
    370 		error = check_unit(id);
    371 		if (error)
    372 			return error;
    373 	}
    374 
    375 	sc = alloc(sizeof(struct sdcd_softc));
    376 	current_npart = 3;
    377 	sc->sc_part = 0;
    378 	sc->sc_partinfo.start = 0;
    379 	sc->sc_partinfo.size = current_devsize;
    380 	f->f_devdata = sc;
    381 	current_id = id;
    382 
    383 	return 0;
    384 }
    385