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 39static int current_id = -1; 40static int current_blkbytes; 41static int current_blkshift; 42static int current_devsize, current_npart; 43static struct boot_partinfo partitions[MAXPARTITIONS]; 44 45static uint human2blk(uint); 46static uint human2bsd(uint); 47static uint bsd2blk(uint); 48static int readdisklabel(int); 49static 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 */ 61static uint 62human2blk(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 */ 77static uint 78human2bsd(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 */ 88static uint 89bsd2blk(uint n) 90{ 91 92 return ((DEV_BSIZE / 256) * n) >> current_blkshift; 93} 94 95static int 96check_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 158static int 159readdisklabel(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 } 217done: 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 230int 231sd_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 267struct sdcd_softc { 268 int sc_part; 269 struct boot_partinfo sc_partinfo; 270}; 271 272/* sdopen(struct open_file *f, int id, int part) */ 273int 274sdopen(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 303int 304sdcdclose(struct open_file *f) 305{ 306 307 dealloc(f->f_devdata, sizeof(struct sdcd_softc)); 308 return 0; 309} 310 311int 312sdcdstrategy(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) */ 352int 353cdopen(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