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