Annotation of sys/arch/i386/stand/libsa/biosdev.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: biosdev.c,v 1.72 2007/05/27 00:57:17 tom Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1996 Michael Shalayeff
! 5: * Copyright (c) 2003 Tobias Weingartner
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 18: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
! 19: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 20: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 27: * SUCH DAMAGE.
! 28: *
! 29: */
! 30:
! 31: #include <sys/param.h>
! 32: #include <sys/reboot.h>
! 33: #include <sys/disklabel.h>
! 34: #include <machine/tss.h>
! 35: #include <machine/biosvar.h>
! 36: #include <lib/libsa/saerrno.h>
! 37: #include <isofs/cd9660/iso.h>
! 38: #include "disk.h"
! 39: #include "debug.h"
! 40: #include "libsa.h"
! 41: #include "biosdev.h"
! 42:
! 43: static const char *biosdisk_err(u_int);
! 44: static int biosdisk_errno(u_int);
! 45:
! 46: static int CHS_rw (int, int, int, int, int, int, void *);
! 47: static int EDD_rw (int, int, u_int64_t, u_int32_t, void *);
! 48:
! 49: extern int debug;
! 50: int bios_bootdev;
! 51: int bios_cddev = -1; /* Set by srt0 if coming from CD */
! 52:
! 53: #if 0
! 54: struct biosdisk {
! 55: bios_diskinfo_t *bios_info;
! 56: dev_t bsddev;
! 57: struct disklabel disklabel;
! 58: };
! 59: #endif
! 60:
! 61: struct EDD_CB {
! 62: u_int8_t edd_len; /* size of packet */
! 63: u_int8_t edd_res1; /* reserved */
! 64: u_int8_t edd_nblk; /* # of blocks to transfer */
! 65: u_int8_t edd_res2; /* reserved */
! 66: u_int16_t edd_off; /* address of buffer (offset) */
! 67: u_int16_t edd_seg; /* address of buffer (segment) */
! 68: u_int64_t edd_daddr; /* starting block */
! 69: };
! 70:
! 71: /*
! 72: * reset disk system
! 73: */
! 74: static int
! 75: biosdreset(int dev)
! 76: {
! 77: int rv;
! 78:
! 79: __asm __volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
! 80: : "0" (0), "d" (dev) : "%ecx", "cc");
! 81:
! 82: return ((rv & 0xff)? rv >> 8 : 0);
! 83: }
! 84:
! 85: /*
! 86: * Fill out a bios_diskinfo_t for this device.
! 87: * Return 0 if all ok.
! 88: * Return 1 if not ok.
! 89: */
! 90: int
! 91: bios_getdiskinfo(int dev, bios_diskinfo_t *pdi)
! 92: {
! 93: u_int rv;
! 94:
! 95: /* Just reset, don't check return code */
! 96: rv = biosdreset(dev);
! 97:
! 98: #ifdef BIOS_DEBUG
! 99: if (debug)
! 100: printf("getinfo: try #8, 0x%x, %p\n", dev, pdi);
! 101: #endif
! 102: __asm __volatile (DOINT(0x13) "\n\t"
! 103: "setc %b0; movzbl %h1, %1\n\t"
! 104: "movzbl %%cl, %3; andb $0x3f, %b3\n\t"
! 105: "xchgb %%cl, %%ch; rolb $2, %%ch"
! 106: : "=a" (rv), "=d" (pdi->bios_heads),
! 107: "=c" (pdi->bios_cylinders),
! 108: "=b" (pdi->bios_sectors)
! 109: : "0" (0x0800), "1" (dev) : "cc");
! 110:
! 111: #ifdef BIOS_DEBUG
! 112: if (debug) {
! 113: printf("getinfo: got #8\n");
! 114: printf("disk 0x%x: %d,%d,%d\n", dev, pdi->bios_cylinders,
! 115: pdi->bios_heads, pdi->bios_sectors);
! 116: }
! 117: #endif
! 118: if (rv & 0xff)
! 119: return 1;
! 120:
! 121: /* Fix up info */
! 122: pdi->bios_number = dev;
! 123: pdi->bios_heads++;
! 124: pdi->bios_cylinders &= 0x3ff;
! 125: pdi->bios_cylinders++;
! 126:
! 127: /* Sanity check */
! 128: if (!pdi->bios_cylinders || !pdi->bios_heads || !pdi->bios_sectors)
! 129: return 1;
! 130:
! 131: /* CD-ROMs sometimes return heads == 1 */
! 132: if (pdi->bios_heads < 2)
! 133: return 1;
! 134:
! 135: /* NOTE:
! 136: * This currently hangs/reboots some machines
! 137: * The IBM ThinkPad 750ED for one.
! 138: *
! 139: * Funny that an IBM/MS extension would not be
! 140: * implemented by an IBM system...
! 141: *
! 142: * Future hangs (when reported) can be "fixed"
! 143: * with getSYSCONFaddr() and an exceptions list.
! 144: */
! 145: if (dev & 0x80 && (dev == 0x80 || dev == 0x81 || dev == bios_bootdev)) {
! 146: int bm;
! 147:
! 148: #ifdef BIOS_DEBUG
! 149: if (debug)
! 150: printf("getinfo: try #41, 0x%x\n", dev);
! 151: #endif
! 152: /* EDD support check */
! 153: __asm __volatile(DOINT(0x13) "; setc %b0"
! 154: : "=a" (rv), "=c" (bm)
! 155: : "0" (0x4100), "b" (0x55aa), "d" (dev) : "cc");
! 156: if (!(rv & 0xff) && (BIOS_regs.biosr_bx & 0xffff) == 0xaa55)
! 157: pdi->bios_edd = (bm & 0xffff) | ((rv & 0xff) << 16);
! 158: else
! 159: pdi->bios_edd = -1;
! 160:
! 161: #ifdef BIOS_DEBUG
! 162: if (debug) {
! 163: printf("getinfo: got #41\n");
! 164: printf("disk 0x%x: 0x%x\n", dev, bm);
! 165: }
! 166: #endif
! 167: /*
! 168: * If extended disk access functions are not supported
! 169: * there is not much point on doing EDD.
! 170: */
! 171: if (!(pdi->bios_edd & EXT_BM_EDA))
! 172: pdi->bios_edd = -1;
! 173: } else
! 174: pdi->bios_edd = -1;
! 175:
! 176: return 0;
! 177: }
! 178:
! 179: /*
! 180: * Read/Write a block from given place using the BIOS.
! 181: */
! 182: static __inline int
! 183: CHS_rw(int rw, int dev, int cyl, int head, int sect, int nsect, void *buf)
! 184: {
! 185: int rv;
! 186:
! 187: rw = rw == F_READ ? 2 : 3;
! 188: BIOS_regs.biosr_es = (u_int32_t)buf >> 4;
! 189: __asm __volatile ("movb %b7, %h1\n\t"
! 190: "movb %b6, %%dh\n\t"
! 191: "andl $0xf, %4\n\t"
! 192: /* cylinder; the highest 2 bits of cyl is in %cl */
! 193: "xchgb %%ch, %%cl\n\t"
! 194: "rorb $2, %%cl\n\t"
! 195: "orb %b5, %%cl\n\t"
! 196: "inc %%cx\n\t"
! 197: DOINT(0x13) "\n\t"
! 198: "setc %b0"
! 199: : "=a" (rv)
! 200: : "0" (nsect), "d" (dev), "c" (cyl),
! 201: "b" (buf), "m" (sect), "m" (head),
! 202: "m" (rw)
! 203: : "cc", "memory");
! 204:
! 205: return ((rv & 0xff)? rv >> 8 : 0);
! 206: }
! 207:
! 208: static __inline int
! 209: EDD_rw(int rw, int dev, u_int64_t daddr, u_int32_t nblk, void *buf)
! 210: {
! 211: int rv;
! 212: volatile static struct EDD_CB cb;
! 213:
! 214: /* Zero out reserved stuff */
! 215: cb.edd_res1 = 0;
! 216: cb.edd_res2 = 0;
! 217:
! 218: /* Fill in parameters */
! 219: cb.edd_len = sizeof(cb);
! 220: cb.edd_nblk = nblk;
! 221: cb.edd_seg = ((u_int32_t)buf >> 4) & 0xffff;
! 222: cb.edd_off = (u_int32_t)buf & 0xf;
! 223: cb.edd_daddr = daddr;
! 224:
! 225: /* if offset/segment are zero, punt */
! 226: if (!cb.edd_seg && !cb.edd_off)
! 227: return 1;
! 228:
! 229: /* Call extended read/write (with disk packet) */
! 230: BIOS_regs.biosr_ds = (u_int32_t)&cb >> 4;
! 231: __asm __volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
! 232: : "0" ((rw == F_READ)? 0x4200: 0x4300),
! 233: "d" (dev), "S" ((int) (&cb) & 0xf) : "%ecx", "cc");
! 234: return ((rv & 0xff)? rv >> 8 : 0);
! 235: }
! 236:
! 237: /*
! 238: * Read given sector, handling retry/errors/etc.
! 239: */
! 240: int
! 241: biosd_io(int rw, bios_diskinfo_t *bd, daddr_t off, int nsect, void *buf)
! 242: {
! 243: int dev = bd->bios_number;
! 244: int j, error;
! 245: void *bb;
! 246: int bbsize = nsect * DEV_BSIZE;
! 247:
! 248: if (bd->flags & BDI_EL_TORITO) { /* It's a CD device */
! 249: dev &= 0xff; /* Mask out this flag bit */
! 250:
! 251: /*
! 252: * sys/lib/libsa/cd9600.c converts 2,048-byte CD sectors
! 253: * to DEV_BSIZE blocks before calling the device strategy
! 254: * routine. However, the El Torito spec says that the
! 255: * BIOS will work in 2,048-byte sectors. So shift back.
! 256: */
! 257: off >>= (ISO_DEFAULT_BLOCK_SHIFT - DEV_BSHIFT);
! 258: nsect >>= (ISO_DEFAULT_BLOCK_SHIFT - DEV_BSHIFT);
! 259: }
! 260:
! 261: /*
! 262: * Use a bounce buffer to not cross 64k DMA boundary, and to
! 263: * not access 1 MB or above.
! 264: */
! 265: if (((((u_int32_t)buf) & ~0xffff) !=
! 266: (((u_int32_t)buf + bbsize) & ~0xffff)) ||
! 267: (((u_int32_t)buf) >= 0x100000)) {
! 268: /*
! 269: * XXX we believe that all the io is buffered
! 270: * by fs routines, so no big reads anyway
! 271: */
! 272: bb = alloca(bbsize);
! 273: if (rw != F_READ)
! 274: bcopy(buf, bb, bbsize);
! 275: } else
! 276: bb = buf;
! 277:
! 278: /* Try to do operation up to 5 times */
! 279: for (error = 1, j = 5; j-- && error; ) {
! 280: /* CHS or LBA access? */
! 281: if (bd->bios_edd != -1) {
! 282: error = EDD_rw(rw, dev, off, nsect, bb);
! 283: } else {
! 284: int cyl, head, sect;
! 285: size_t i, n;
! 286: char *p = bb;
! 287:
! 288: /* Handle track boundaries */
! 289: for (error = i = 0; error == 0 && i < nsect;
! 290: i += n, off += n, p += n * DEV_BSIZE) {
! 291:
! 292: btochs(off, cyl, head, sect, bd->bios_heads,
! 293: bd->bios_sectors);
! 294:
! 295: if ((sect + (nsect - i)) >= bd->bios_sectors)
! 296: n = bd->bios_sectors - sect;
! 297: else
! 298: n = nsect - i;
! 299:
! 300: error = CHS_rw(rw, dev, cyl, head, sect, n, p);
! 301:
! 302: /* ECC corrected */
! 303: if (error == 0x11)
! 304: error = 0;
! 305: }
! 306: }
! 307: switch (error) {
! 308: case 0x00: /* No errors */
! 309: case 0x11: /* ECC corrected */
! 310: error = 0;
! 311: break;
! 312:
! 313: default: /* All other errors */
! 314: #ifdef BIOS_DEBUG
! 315: if (debug)
! 316: printf("\nBIOS error 0x%x (%s)\n",
! 317: error, biosdisk_err(error));
! 318: #endif
! 319: biosdreset(dev);
! 320: break;
! 321: }
! 322: }
! 323:
! 324: if (bb != buf && rw == F_READ)
! 325: bcopy(bb, buf, bbsize);
! 326:
! 327: #ifdef BIOS_DEBUG
! 328: if (debug) {
! 329: if (error != 0)
! 330: printf("=0x%x(%s)", error, biosdisk_err(error));
! 331: putchar('\n');
! 332: }
! 333: #endif
! 334:
! 335: return error;
! 336: }
! 337:
! 338: /*
! 339: * Try to read the bsd label on the given BIOS device
! 340: */
! 341: const char *
! 342: bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label)
! 343: {
! 344: daddr_t off = LABELSECTOR;
! 345: char *buf;
! 346: struct dos_mbr mbr;
! 347: int error, i;
! 348:
! 349: /* Sanity check */
! 350: if (bd->bios_heads == 0 || bd->bios_sectors == 0)
! 351: return "failed to read disklabel";
! 352:
! 353: /* MBR is a harddisk thing */
! 354: if (bd->bios_number & 0x80) {
! 355: /* Read MBR */
! 356: error = biosd_io(F_READ, bd, DOSBBSECTOR, 1, &mbr);
! 357: if (error)
! 358: return (biosdisk_err(error));
! 359:
! 360: /* check mbr signature */
! 361: if (mbr.dmbr_sign != DOSMBR_SIGNATURE)
! 362: return "bad MBR signature\n";
! 363:
! 364: /* Search for OpenBSD partition */
! 365: for (off = 0, i = 0; off == 0 && i < NDOSPART; i++)
! 366: if (mbr.dmbr_parts[i].dp_typ == DOSPTYP_OPENBSD)
! 367: off = mbr.dmbr_parts[i].dp_start + LABELSECTOR;
! 368: if (off == 0)
! 369: return "no OpenBSD partition\n";
! 370: } else
! 371: off = LABELSECTOR;
! 372:
! 373: /* Load BSD disklabel */
! 374: buf = alloca(DEV_BSIZE);
! 375: #ifdef BIOS_DEBUG
! 376: if (debug)
! 377: printf("loading disklabel @ %u\n", off);
! 378: #endif
! 379: /* read disklabel */
! 380: error = biosd_io(F_READ, bd, off, 1, buf);
! 381:
! 382: if (error)
! 383: return "failed to read disklabel";
! 384:
! 385: /* Fill in disklabel */
! 386: return (getdisklabel(buf, label));
! 387: }
! 388:
! 389: int
! 390: biosopen(struct open_file *f, ...)
! 391: {
! 392: va_list ap;
! 393: register char *cp, **file;
! 394: dev_t maj, unit, part;
! 395: struct diskinfo *dip;
! 396: int biosdev;
! 397:
! 398: va_start(ap, f);
! 399: cp = *(file = va_arg(ap, char **));
! 400: va_end(ap);
! 401:
! 402: #ifdef BIOS_DEBUG
! 403: if (debug)
! 404: printf("%s\n", cp);
! 405: #endif
! 406:
! 407: f->f_devdata = NULL;
! 408: /* search for device specification */
! 409: cp += 2;
! 410: if (cp[2] != ':') {
! 411: if (cp[3] != ':')
! 412: return ENOENT;
! 413: else
! 414: cp++;
! 415: }
! 416:
! 417: for (maj = 0; maj < nbdevs && strncmp(*file, bdevs[maj], cp - *file); )
! 418: maj++;
! 419: if (maj >= nbdevs) {
! 420: printf("Unknown device: ");
! 421: for (cp = *file; *cp != ':'; cp++)
! 422: putchar(*cp);
! 423: putchar('\n');
! 424: return EADAPT;
! 425: }
! 426:
! 427: /* get unit */
! 428: if ('0' <= *cp && *cp <= '9')
! 429: unit = *cp++ - '0';
! 430: else {
! 431: printf("Bad unit number\n");
! 432: return EUNIT;
! 433: }
! 434: /* get partition */
! 435: if ('a' <= *cp && *cp <= 'p')
! 436: part = *cp++ - 'a';
! 437: else {
! 438: printf("Bad partition id\n");
! 439: return EPART;
! 440: }
! 441:
! 442: cp++; /* skip ':' */
! 443: if (*cp != 0)
! 444: *file = cp;
! 445: else
! 446: f->f_flags |= F_RAW;
! 447:
! 448: biosdev = unit;
! 449: switch (maj) {
! 450: case 0: /* wd */
! 451: case 4: /* sd */
! 452: case 17: /* hd */
! 453: biosdev |= 0x80;
! 454: break;
! 455: case 2: /* fd */
! 456: break;
! 457: case 6: /* cd */
! 458: biosdev = bios_bootdev & 0xff;
! 459: break;
! 460: default:
! 461: return ENXIO;
! 462: }
! 463:
! 464: /* Find device */
! 465: bootdev_dip = dip = dklookup(biosdev);
! 466:
! 467: /* Fix up bootdev */
! 468: { dev_t bsd_dev;
! 469: bsd_dev = dip->bios_info.bsd_dev;
! 470: dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
! 471: B_CONTROLLER(bsd_dev), unit, part);
! 472: dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
! 473: B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
! 474: }
! 475:
! 476: #if 0
! 477: dip->bios_info.bsd_dev = dip->bootdev;
! 478: bootdev = dip->bootdev;
! 479: #endif
! 480:
! 481: #ifdef BIOS_DEBUG
! 482: if (debug) {
! 483: printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
! 484: dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
! 485: dip->bios_info.bios_edd);
! 486: }
! 487: #endif
! 488:
! 489: /* Try for disklabel again (might be removable media) */
! 490: if (dip->bios_info.flags & BDI_BADLABEL){
! 491: const char *st = bios_getdisklabel(&dip->bios_info,
! 492: &dip->disklabel);
! 493: #ifdef BIOS_DEBUG
! 494: if (debug && st)
! 495: printf("%s\n", st);
! 496: #endif
! 497: if (!st) {
! 498: dip->bios_info.flags &= ~BDI_BADLABEL;
! 499: dip->bios_info.flags |= BDI_GOODLABEL;
! 500: } else
! 501: return ERDLAB;
! 502: }
! 503:
! 504: f->f_devdata = dip;
! 505:
! 506: return 0;
! 507: }
! 508:
! 509: const u_char bidos_errs[] =
! 510: /* ignored "\x00" "successful completion\0" */
! 511: "\x01" "invalid function/parameter\0"
! 512: "\x02" "address mark not found\0"
! 513: "\x03" "write-protected\0"
! 514: "\x04" "sector not found\0"
! 515: "\x05" "reset failed\0"
! 516: "\x06" "disk changed\0"
! 517: "\x07" "drive parameter activity failed\0"
! 518: "\x08" "DMA overrun\0"
! 519: "\x09" "data boundary error\0"
! 520: "\x0A" "bad sector detected\0"
! 521: "\x0B" "bad track detected\0"
! 522: "\x0C" "invalid media\0"
! 523: "\x0E" "control data address mark detected\0"
! 524: "\x0F" "DMA arbitration level out of range\0"
! 525: "\x10" "uncorrectable CRC or ECC error on read\0"
! 526: /* ignored "\x11" "data ECC corrected\0" */
! 527: "\x20" "controller failure\0"
! 528: "\x31" "no media in drive\0"
! 529: "\x32" "incorrect drive type in CMOS\0"
! 530: "\x40" "seek failed\0"
! 531: "\x80" "operation timed out\0"
! 532: "\xAA" "drive not ready\0"
! 533: "\xB0" "volume not locked in drive\0"
! 534: "\xB1" "volume locked in drive\0"
! 535: "\xB2" "volume not removable\0"
! 536: "\xB3" "volume in use\0"
! 537: "\xB4" "lock count exceeded\0"
! 538: "\xB5" "valid eject request failed\0"
! 539: "\xBB" "undefined error\0"
! 540: "\xCC" "write fault\0"
! 541: "\xE0" "status register error\0"
! 542: "\xFF" "sense operation failed\0"
! 543: "\x00" "\0";
! 544:
! 545: static const char *
! 546: biosdisk_err(u_int error)
! 547: {
! 548: register const u_char *p = bidos_errs;
! 549:
! 550: while (*p && *p != error)
! 551: while (*p++);
! 552:
! 553: return ++p;
! 554: }
! 555:
! 556: const struct biosdisk_errors {
! 557: u_char error;
! 558: u_char errno;
! 559: } tab[] = {
! 560: { 0x01, EINVAL },
! 561: { 0x03, EROFS },
! 562: { 0x08, EINVAL },
! 563: { 0x09, EINVAL },
! 564: { 0x0A, EBSE },
! 565: { 0x0B, EBSE },
! 566: { 0x0C, ENXIO },
! 567: { 0x0D, EINVAL },
! 568: { 0x10, EECC },
! 569: { 0x20, EHER },
! 570: { 0x31, ENXIO },
! 571: { 0x32, ENXIO },
! 572: { 0x00, EIO }
! 573: };
! 574:
! 575: static int
! 576: biosdisk_errno(u_int error)
! 577: {
! 578: register const struct biosdisk_errors *p;
! 579:
! 580: if (error == 0)
! 581: return 0;
! 582:
! 583: for (p = tab; p->error && p->error != error; p++);
! 584:
! 585: return p->errno;
! 586: }
! 587:
! 588: int
! 589: biosstrategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
! 590: size_t *rsize)
! 591: {
! 592: struct diskinfo *dip = (struct diskinfo *)devdata;
! 593: bios_diskinfo_t *bd = &dip->bios_info;
! 594: u_int8_t error = 0;
! 595: size_t nsect;
! 596:
! 597: nsect = (size + DEV_BSIZE-1) / DEV_BSIZE;
! 598: if (rsize != NULL)
! 599: blk += dip->disklabel.
! 600: d_partitions[B_PARTITION(dip->bsddev)].p_offset;
! 601:
! 602: /* Read all, sub-functions handle track boundaries */
! 603: error = biosd_io(rw, bd, blk, nsect, buf);
! 604:
! 605: #ifdef BIOS_DEBUG
! 606: if (debug) {
! 607: if (error != 0)
! 608: printf("=0x%x(%s)", error, biosdisk_err(error));
! 609: putchar('\n');
! 610: }
! 611: #endif
! 612:
! 613: if (rsize != NULL)
! 614: *rsize = nsect * DEV_BSIZE;
! 615:
! 616: return (biosdisk_errno(error));
! 617: }
! 618:
! 619: int
! 620: biosclose(struct open_file *f)
! 621: {
! 622: f->f_devdata = NULL;
! 623:
! 624: return 0;
! 625: }
! 626:
! 627: int
! 628: biosioctl(struct open_file *f, u_long cmd, void *data)
! 629: {
! 630: return 0;
! 631: }
CVSweb