1/* $NetBSD: sd.c,v 1.1 2010/10/14 06:58:22 kiyohara Exp $ */ 2/* 3 * Copyright (c) 2010 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include <sys/param.h> 29#include <sys/types.h> 30#include <sys/stdint.h> 31 32#include <lib/libsa/stand.h> 33#include <lib/libkern/libkern.h> 34 35#include <machine/param.h> 36 37#include "boot.h" 38#include "sdvar.h" 39 40#ifdef DEBUG 41#define DPRINTF(x) printf x 42#else 43#define DPRINTF(x) 44#endif 45 46#define SD_DEFAULT_BLKSIZE 512 47 48 49struct sd_mode_sense_data { 50 struct scsi_mode_parameter_header_6 header; 51 struct scsi_general_block_descriptor blk_desc; 52 union scsi_disk_pages pages; 53}; 54 55static int sd_validate_blksize(int); 56static uint64_t sd_read_capacity(struct sd_softc *, int *); 57static int sd_get_simplifiedparms(struct sd_softc *); 58static int sd_get_capacity(struct sd_softc *); 59static int sd_get_parms_page4(struct sd_softc *, struct disk_parms *); 60static int sd_get_parms_page5(struct sd_softc *, struct disk_parms *); 61static int sd_get_parms(struct sd_softc *); 62static void sdgetdefaultlabel(struct sd_softc *, struct disklabel *); 63static int sdgetdisklabel(struct sd_softc *); 64 65int sdopen(struct open_file *, ...); 66int sdclose(struct open_file *); 67int sdstrategy(void *, int, daddr_t, size_t, void *, size_t *); 68 69 70static int 71sd_validate_blksize(int len) 72{ 73 74 switch (len) { 75 case 256: 76 case 512: 77 case 1024: 78 case 2048: 79 case 4096: 80 return 1; 81 } 82 return 0; 83} 84 85/* 86 * sd_read_capacity: 87 * 88 * Find out from the device what its capacity is. 89 */ 90static uint64_t 91sd_read_capacity(struct sd_softc *sd, int *blksize) 92{ 93 union { 94 struct scsipi_read_capacity_10 cmd; 95 struct scsipi_read_capacity_16 cmd16; 96 } cmd; 97 union { 98 struct scsipi_read_capacity_10_data data; 99 struct scsipi_read_capacity_16_data data16; 100 } data; 101 uint64_t rv; 102 103 memset(&cmd, 0, sizeof(cmd)); 104 cmd.cmd.opcode = READ_CAPACITY_10; 105 106 /* 107 * If the command works, interpret the result as a 4 byte 108 * number of blocks 109 */ 110 rv = 0; 111 memset(&data, 0, sizeof(data.data)); 112 if (scsi_command(sd, (void *)&cmd.cmd, sizeof(cmd.cmd), 113 (void *)&data, sizeof(data.data)) != 0) 114 goto out; 115 116 if (_4btol(data.data.addr) != 0xffffffff) { 117 *blksize = _4btol(data.data.length); 118 rv = _4btol(data.data.addr) + 1; 119 goto out; 120 } 121 122 /* 123 * Device is larger than can be reflected by READ CAPACITY (10). 124 * Try READ CAPACITY (16). 125 */ 126 127 memset(&cmd, 0, sizeof(cmd)); 128 cmd.cmd16.opcode = READ_CAPACITY_16; 129 cmd.cmd16.byte2 = SRC16_SERVICE_ACTION; 130 _lto4b(sizeof(data.data16), cmd.cmd16.len); 131 132 memset(&data, 0, sizeof(data.data16)); 133 if (scsi_command(sd, (void *)&cmd.cmd16, sizeof(cmd.cmd16), 134 (void *)&data, sizeof(data.data16)) != 0) 135 goto out; 136 137 *blksize = _4btol(data.data16.length); 138 rv = _8btol(data.data16.addr) + 1; 139 140 out: 141 return rv; 142} 143 144static int 145sd_get_simplifiedparms(struct sd_softc *sd) 146{ 147 struct { 148 struct scsi_mode_parameter_header_6 header; 149 /* no block descriptor */ 150 uint8_t pg_code; /* page code (should be 6) */ 151 uint8_t pg_length; /* page length (should be 11) */ 152 uint8_t wcd; /* bit0: cache disable */ 153 uint8_t lbs[2]; /* logical block size */ 154 uint8_t size[5]; /* number of log. blocks */ 155 uint8_t pp; /* power/performance */ 156 uint8_t flags; 157 uint8_t resvd; 158 } scsipi_sense; 159 struct disk_parms *dp = &sd->sc_params; 160 uint64_t blocks; 161 int error, blksize; 162 163 /* 164 * sd_read_capacity (ie "read capacity") and mode sense page 6 165 * give the same information. Do both for now, and check 166 * for consistency. 167 * XXX probably differs for removable media 168 */ 169 dp->blksize = SD_DEFAULT_BLKSIZE; 170 if ((blocks = sd_read_capacity(sd, &blksize)) == 0) 171 return SDGP_RESULT_OFFLINE; /* XXX? */ 172 173 error = scsi_mode_sense(sd, SMS_DBD, 6, 174 &scsipi_sense.header, sizeof(scsipi_sense)); 175 176 if (error != 0) 177 return SDGP_RESULT_OFFLINE; /* XXX? */ 178 179 dp->blksize = blksize; 180 if (!sd_validate_blksize(dp->blksize)) 181 dp->blksize = _2btol(scsipi_sense.lbs); 182 if (!sd_validate_blksize(dp->blksize)) 183 dp->blksize = SD_DEFAULT_BLKSIZE; 184 185 /* 186 * Create a pseudo-geometry. 187 */ 188 dp->heads = 64; 189 dp->sectors = 32; 190 dp->cyls = blocks / (dp->heads * dp->sectors); 191 dp->disksize = _5btol(scsipi_sense.size); 192 if (dp->disksize <= UINT32_MAX && dp->disksize != blocks) { 193 printf("RBC size: mode sense=%llu, get cap=%llu\n", 194 (unsigned long long)dp->disksize, 195 (unsigned long long)blocks); 196 dp->disksize = blocks; 197 } 198 dp->disksize512 = (dp->disksize * dp->blksize) / DEV_BSIZE; 199 200 return SDGP_RESULT_OK; 201} 202 203/* 204 * Get the scsi driver to send a full inquiry to the * device and use the 205 * results to fill out the disk parameter structure. 206 */ 207static int 208sd_get_capacity(struct sd_softc *sd) 209{ 210 struct disk_parms *dp = &sd->sc_params; 211 uint64_t blocks; 212 int error, blksize; 213 214 dp->disksize = blocks = sd_read_capacity(sd, &blksize); 215 if (blocks == 0) { 216 struct scsipi_read_format_capacities cmd; 217 struct { 218 struct scsipi_capacity_list_header header; 219 struct scsipi_capacity_descriptor desc; 220 } __packed data; 221 222 memset(&cmd, 0, sizeof(cmd)); 223 memset(&data, 0, sizeof(data)); 224 cmd.opcode = READ_FORMAT_CAPACITIES; 225 _lto2b(sizeof(data), cmd.length); 226 227 error = scsi_command(sd, 228 (void *)&cmd, sizeof(cmd), (void *)&data, sizeof(data)); 229 if (error == EFTYPE) 230 /* Medium Format Corrupted, handle as not formatted */ 231 return SDGP_RESULT_UNFORMATTED; 232 if (error || data.header.length == 0) 233 return SDGP_RESULT_OFFLINE; 234 235 switch (data.desc.byte5 & SCSIPI_CAP_DESC_CODE_MASK) { 236 case SCSIPI_CAP_DESC_CODE_RESERVED: 237 case SCSIPI_CAP_DESC_CODE_FORMATTED: 238 break; 239 240 case SCSIPI_CAP_DESC_CODE_UNFORMATTED: 241 return SDGP_RESULT_UNFORMATTED; 242 243 case SCSIPI_CAP_DESC_CODE_NONE: 244 return SDGP_RESULT_OFFLINE; 245 } 246 247 dp->disksize = blocks = _4btol(data.desc.nblks); 248 if (blocks == 0) 249 return SDGP_RESULT_OFFLINE; /* XXX? */ 250 251 blksize = _3btol(data.desc.blklen); 252 253 } else if (!sd_validate_blksize(blksize)) { 254 struct sd_mode_sense_data scsipi_sense; 255 int bsize; 256 257 memset(&scsipi_sense, 0, sizeof(scsipi_sense)); 258 error = scsi_mode_sense(sd, 0, 0, &scsipi_sense.header, 259 sizeof(struct scsi_mode_parameter_header_6) + 260 sizeof(scsipi_sense.blk_desc)); 261 if (!error) { 262 bsize = scsipi_sense.header.blk_desc_len; 263 264 if (bsize >= 8) 265 blksize = _3btol(scsipi_sense.blk_desc.blklen); 266 } 267 } 268 269 if (!sd_validate_blksize(blksize)) 270 blksize = SD_DEFAULT_BLKSIZE; 271 272 dp->blksize = blksize; 273 dp->disksize512 = (blocks * dp->blksize) / DEV_BSIZE; 274 return 0; 275} 276 277static int 278sd_get_parms_page4(struct sd_softc *sd, struct disk_parms *dp) 279{ 280 struct sd_mode_sense_data scsipi_sense; 281 union scsi_disk_pages *pages; 282 size_t poffset; 283 int byte2, error; 284 285 byte2 = SMS_DBD; 286again: 287 memset(&scsipi_sense, 0, sizeof(scsipi_sense)); 288 error = scsi_mode_sense(sd, byte2, 4, &scsipi_sense.header, 289 (byte2 ? 0 : sizeof(scsipi_sense.blk_desc)) + 290 sizeof(scsipi_sense.pages.rigid_geometry)); 291 if (error) { 292 if (byte2 == SMS_DBD) { 293 /* No result; try once more with DBD off */ 294 byte2 = 0; 295 goto again; 296 } 297 return error; 298 } 299 300 poffset = sizeof(scsipi_sense.header); 301 poffset += scsipi_sense.header.blk_desc_len; 302 303 if (poffset > sizeof(scsipi_sense) - sizeof(pages->rigid_geometry)) 304 return ERESTART; 305 306 pages = (void *)((u_long)&scsipi_sense + poffset); 307#if 0 308 { 309 size_t i; 310 u_int8_t *p; 311 312 printf("page 4 sense:"); 313 for (i = sizeof(scsipi_sense), p = (void *)&scsipi_sense; i; 314 i--, p++) 315 printf(" %02x", *p); 316 printf("\n"); 317 printf("page 4 pg_code=%d sense=%p/%p\n", 318 pages->rigid_geometry.pg_code, &scsipi_sense, pages); 319 } 320#endif 321 322 if ((pages->rigid_geometry.pg_code & PGCODE_MASK) != 4) 323 return ERESTART; 324 325 /* 326 * KLUDGE!! (for zone recorded disks) 327 * give a number of sectors so that sec * trks * cyls 328 * is <= disk_size 329 * can lead to wasted space! THINK ABOUT THIS ! 330 */ 331 dp->heads = pages->rigid_geometry.nheads; 332 dp->cyls = _3btol(pages->rigid_geometry.ncyl); 333 if (dp->heads == 0 || dp->cyls == 0) 334 return ERESTART; 335 dp->sectors = dp->disksize / (dp->heads * dp->cyls); /* XXX */ 336 337 dp->rot_rate = _2btol(pages->rigid_geometry.rpm); 338 if (dp->rot_rate == 0) 339 dp->rot_rate = 3600; 340 341#if 0 342printf("page 4 ok\n"); 343#endif 344 return 0; 345} 346 347static int 348sd_get_parms_page5(struct sd_softc *sd, struct disk_parms *dp) 349{ 350 struct sd_mode_sense_data scsipi_sense; 351 union scsi_disk_pages *pages; 352 size_t poffset; 353 int byte2, error; 354 355 byte2 = SMS_DBD; 356again: 357 memset(&scsipi_sense, 0, sizeof(scsipi_sense)); 358 error = scsi_mode_sense(sd, 0, 5, &scsipi_sense.header, 359 (byte2 ? 0 : sizeof(scsipi_sense.blk_desc)) + 360 sizeof(scsipi_sense.pages.flex_geometry)); 361 if (error) { 362 if (byte2 == SMS_DBD) { 363 /* No result; try once more with DBD off */ 364 byte2 = 0; 365 goto again; 366 } 367 return error; 368 } 369 370 poffset = sizeof(scsipi_sense.header); 371 poffset += scsipi_sense.header.blk_desc_len; 372 373 if (poffset > sizeof(scsipi_sense) - sizeof(pages->flex_geometry)) 374 return ERESTART; 375 376 pages = (void *)((u_long)&scsipi_sense + poffset); 377#if 0 378 { 379 size_t i; 380 u_int8_t *p; 381 382 printf("page 5 sense:"); 383 for (i = sizeof(scsipi_sense), p = (void *)&scsipi_sense; i; 384 i--, p++) 385 printf(" %02x", *p); 386 printf("\n"); 387 printf("page 5 pg_code=%d sense=%p/%p\n", 388 pages->flex_geometry.pg_code, &scsipi_sense, pages); 389 } 390#endif 391 392 if ((pages->flex_geometry.pg_code & PGCODE_MASK) != 5) 393 return ERESTART; 394 395 dp->heads = pages->flex_geometry.nheads; 396 dp->cyls = _2btol(pages->flex_geometry.ncyl); 397 dp->sectors = pages->flex_geometry.ph_sec_tr; 398 if (dp->heads == 0 || dp->cyls == 0 || dp->sectors == 0) 399 return ERESTART; 400 401 dp->rot_rate = _2btol(pages->rigid_geometry.rpm); 402 if (dp->rot_rate == 0) 403 dp->rot_rate = 3600; 404 405#if 0 406printf("page 5 ok\n"); 407#endif 408 return 0; 409} 410 411static int 412sd_get_parms(struct sd_softc *sd) 413{ 414 struct disk_parms *dp = &sd->sc_params; 415 int error; 416 417 /* 418 * If offline, the SDEV_MEDIA_LOADED flag will be 419 * cleared by the caller if necessary. 420 */ 421 if (sd->sc_type == T_SIMPLE_DIRECT) { 422 error = sd_get_simplifiedparms(sd); 423 if (error) 424 return error; 425 goto ok; 426 } 427 428 error = sd_get_capacity(sd); 429 if (error) 430 return error; 431 432 if (sd->sc_type == T_OPTICAL) 433 goto page0; 434 435 if (sd->sc_flags & FLAGS_REMOVABLE) { 436 if (!sd_get_parms_page5(sd, dp) || 437 !sd_get_parms_page4(sd, dp)) 438 goto ok; 439 } else { 440 if (!sd_get_parms_page4(sd, dp) || 441 !sd_get_parms_page5(sd, dp)) 442 goto ok; 443 } 444 445page0: 446 printf("fabricating a geometry\n"); 447 /* Try calling driver's method for figuring out geometry. */ 448 /* 449 * Use adaptec standard fictitious geometry 450 * this depends on which controller (e.g. 1542C is 451 * different. but we have to put SOMETHING here..) 452 */ 453 dp->heads = 64; 454 dp->sectors = 32; 455 dp->cyls = dp->disksize / (64 * 32); 456 dp->rot_rate = 3600; 457 458ok: 459 DPRINTF(("disksize = %" PRId64 ", disksize512 = %" PRId64 ".\n", 460 dp->disksize, dp->disksize512)); 461 462 return 0; 463} 464 465static void 466sdgetdefaultlabel(struct sd_softc *sd, struct disklabel *lp) 467{ 468 469 memset(lp, 0, sizeof(struct disklabel)); 470 471 lp->d_secsize = sd->sc_params.blksize; 472 lp->d_ntracks = sd->sc_params.heads; 473 lp->d_nsectors = sd->sc_params.sectors; 474 lp->d_ncylinders = sd->sc_params.cyls; 475 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 476 477 lp->d_type = DTYPE_SCSI; 478 479 strncpy(lp->d_packname, "fictitious", 16); 480 lp->d_secperunit = sd->sc_params.disksize; 481 lp->d_rpm = sd->sc_params.rot_rate; 482 lp->d_interleave = 1; 483 lp->d_flags = (sd->sc_flags & FLAGS_REMOVABLE) ? D_REMOVABLE : 0; 484 485 lp->d_partitions[RAW_PART].p_offset = 0; 486 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 487 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 488 lp->d_npartitions = RAW_PART + 1; 489 490 lp->d_magic = DISKMAGIC; 491 lp->d_magic2 = DISKMAGIC; 492 lp->d_checksum = dkcksum(lp); 493} 494 495/* 496 * Load the label information on the named device. 497 */ 498static int 499sdgetdisklabel(struct sd_softc *sd) 500{ 501 struct mbr_sector *mbr; 502 struct mbr_partition *mp; 503 struct disklabel *lp = &sd->sc_label; 504 size_t rsize; 505 int sector, i; 506 char *msg; 507 uint8_t buf[DEV_BSIZE]; 508 509 sdgetdefaultlabel(sd, lp); 510 511 if (lp->d_secpercyl == 0) { 512 lp->d_secpercyl = 100; 513 /* as long as it's not 0 - readdisklabel divides by it (?) */ 514 } 515 516 /* 517 * Find NetBSD Partition in DOS partition table. 518 */ 519 sector = 0; 520 if (sdstrategy(sd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf, &rsize)) 521 return EOFFSET; 522 523 mbr = (struct mbr_sector *)buf; 524 if (mbr->mbr_magic == htole16(MBR_MAGIC)) { 525 /* 526 * Lookup NetBSD slice. If there is none, go ahead 527 * and try to read the disklabel off sector #0. 528 */ 529 mp = mbr->mbr_parts; 530 for (i = 0; i < MBR_PART_COUNT; i++) { 531 if (mp[i].mbrp_type == MBR_PTYPE_NETBSD) { 532 sector = le32toh(mp[i].mbrp_start); 533 break; 534 } 535 } 536 } 537 538 if (sdstrategy(sd, F_READ, sector + LABELSECTOR, DEV_BSIZE, 539 buf, &rsize)) 540 return EOFFSET; 541 542 msg = getdisklabel((const char *)buf + LABELOFFSET, &sd->sc_label); 543 if (msg) 544 printf("scsi/%d/%d/%d: getdisklabel: %s\n", 545 sd->sc_bus, sd->sc_target, sd->sc_lun, msg); 546 547 /* check partition */ 548 if ((sd->sc_part >= lp->d_npartitions) || 549 (lp->d_partitions[sd->sc_part].p_fstype == FS_UNUSED)) { 550 DPRINTF(("illegal partition\n")); 551 return EPART; 552 } 553 554 DPRINTF(("label info: d_secsize %d, d_nsectors %d, d_ncylinders %d," 555 " d_ntracks %d, d_secpercyl %d\n", 556 sd->sc_label.d_secsize, 557 sd->sc_label.d_nsectors, 558 sd->sc_label.d_ncylinders, 559 sd->sc_label.d_ntracks, 560 sd->sc_label.d_secpercyl)); 561 562 return 0; 563} 564 565/* 566 * Open device (read drive parameters and disklabel) 567 */ 568int 569sdopen(struct open_file *f, ...) 570{ 571 struct sd_softc *sd; 572 struct scsi_test_unit_ready cmd; 573 struct scsipi_inquiry_data *inqbuf; 574 u_int bus, target, lun, part; 575 int error; 576 char buf[SCSIPI_INQUIRY_LENGTH_SCSI2]; 577 va_list ap; 578 579 va_start(ap, f); 580 bus = va_arg(ap, u_int); 581 target = va_arg(ap, u_int); 582 lun = va_arg(ap, u_int); 583 part = va_arg(ap, u_int); 584 va_end(ap); 585 586 DPRINTF(("sdopen: scsi/%d/%d/%d_%d\n", bus, target, lun, part)); 587 588 sd = alloc(sizeof(struct sd_softc)); 589 if (sd == NULL) 590 return ENOMEM; 591 592 memset(sd, 0, sizeof(struct sd_softc)); 593 594 sd->sc_part = part; 595 sd->sc_lun = lun; 596 sd->sc_target = target; 597 sd->sc_bus = bus; 598 599 if ((error = scsi_inquire(sd, sizeof(buf), buf)) != 0) 600 return error; 601 602 inqbuf = (struct scsipi_inquiry_data *)buf; 603 604 sd->sc_type = inqbuf->device & SID_TYPE; 605 606 /* 607 * Determine the operating mode capabilities of the device. 608 */ 609 if ((inqbuf->version & SID_ANSII) >= 2) { 610// if ((inqbuf->flags3 & SID_CmdQue) != 0) 611// sd->sc_cap |= PERIPH_CAP_TQING; 612 if ((inqbuf->flags3 & SID_Sync) != 0) 613 sd->sc_cap |= PERIPH_CAP_SYNC; 614 615 /* SPC-2 */ 616 if ((inqbuf->version & SID_ANSII) >= 3) { 617 /* 618 * Report ST clocking though CAP_WIDExx/CAP_SYNC. 619 * If the device only supports DT, clear these 620 * flags (DT implies SYNC and WIDE) 621 */ 622 switch (inqbuf->flags4 & SID_Clocking) { 623 case SID_CLOCKING_DT_ONLY: 624 sd->sc_cap &= ~PERIPH_CAP_SYNC; 625 break; 626 } 627 } 628 } 629 sd->sc_flags = 630 (inqbuf->dev_qual2 & SID_REMOVABLE) ? FLAGS_REMOVABLE : 0; 631 632 memset(&cmd, 0, sizeof(cmd)); 633 cmd.opcode = SCSI_TEST_UNIT_READY; 634 if ((error = scsi_command(sd, (void *)&cmd, sizeof(cmd), NULL, 0)) != 0) 635 return error; 636 637 if (sd->sc_flags & FLAGS_REMOVABLE) { 638 printf("XXXXX: removable device found. will not support\n"); 639 } 640 if (!(sd->sc_flags & FLAGS_MEDIA_LOADED)) 641 sd->sc_flags |= FLAGS_MEDIA_LOADED; 642 643 if ((error = sd_get_parms(sd)) != 0) 644 return error; 645 646 strncpy(sd->sc_label.d_typename, inqbuf->product, 16); 647 if ((error = sdgetdisklabel(sd)) != 0) 648 return error; 649 650 f->f_devdata = sd; 651 return 0; 652} 653 654/* 655 * Close device. 656 */ 657int 658sdclose(struct open_file *f) 659{ 660 661 return 0; 662} 663 664/* 665 * Read some data. 666 */ 667int 668sdstrategy(void *f, int rw, daddr_t dblk, size_t size, void *p, size_t *rsize) 669{ 670 struct sd_softc *sd; 671 struct disklabel *lp; 672 struct partition *pp; 673 struct scsipi_generic *cmdp; 674 struct scsipi_rw_16 cmd16; 675 struct scsipi_rw_10 cmd_big; 676 struct scsi_rw_6 cmd_small; 677 daddr_t blkno; 678 int cmdlen, nsect, i; 679 uint8_t *buf; 680 681 if (size == 0) 682 return 0; 683 684 if (rw != F_READ) 685 return EOPNOTSUPP; 686 687 buf = p; 688 sd = f; 689 lp = &sd->sc_label; 690 pp = &lp->d_partitions[sd->sc_part]; 691 692 if (!(sd->sc_flags & FLAGS_MEDIA_LOADED)) 693 return EIO; 694 695 nsect = howmany(size, lp->d_secsize); 696 blkno = dblk + pp->p_offset; 697 698 for (i = 0; i < nsect; i++, blkno++) { 699 int error; 700 701 /* 702 * Fill out the scsi command. Use the smallest CDB possible 703 * (6-byte, 10-byte, or 16-byte). 704 */ 705 if ((blkno & 0x1fffff) == blkno) { 706 /* 6-byte CDB */ 707 memset(&cmd_small, 0, sizeof(cmd_small)); 708 cmd_small.opcode = SCSI_READ_6_COMMAND; 709 _lto3b(blkno, cmd_small.addr); 710 cmd_small.length = 1; 711 cmdlen = sizeof(cmd_small); 712 cmdp = (struct scsipi_generic *)&cmd_small; 713 } else if ((blkno & 0xffffffff) == blkno) { 714 /* 10-byte CDB */ 715 memset(&cmd_big, 0, sizeof(cmd_big)); 716 cmd_small.opcode = READ_10; 717 _lto4b(blkno, cmd_big.addr); 718 _lto2b(1, cmd_big.length); 719 cmdlen = sizeof(cmd_big); 720 cmdp = (struct scsipi_generic *)&cmd_big; 721 } else { 722 /* 16-byte CDB */ 723 memset(&cmd16, 0, sizeof(cmd16)); 724 cmd_small.opcode = READ_16; 725 _lto8b(blkno, cmd16.addr); 726 _lto4b(1, cmd16.length); 727 cmdlen = sizeof(cmd16); 728 cmdp = (struct scsipi_generic *)&cmd16; 729 } 730 731 error = scsi_command(sd, cmdp, cmdlen, buf, lp->d_secsize); 732 if (error) 733 return error; 734 735 buf += lp->d_secsize; 736 } 737 738 *rsize = size; 739 return 0; 740} 741