1 /* $OpenBSD: ch.c,v 1.9 2000/04/08 19:19:33 csapuntz Exp $ */ 2 /* $NetBSD: ch.c,v 1.26 1997/02/21 22:06:52 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com> 6 * All rights reserved. 7 * 8 * Partially based on an autochanger driver written by Stefan Grefen 9 * and on an autochanger driver written by the Systems Programming Group 10 * at the University of Utah Computer Science Department. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgements: 22 * This product includes software developed by Jason R. Thorpe 23 * for And Communications, http://www.and.com/ 24 * 4. The name of the author may not be used to endorse or promote products 25 * derived from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 32 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 33 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 34 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 35 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/errno.h> 43 #include <sys/ioctl.h> 44 #include <sys/buf.h> 45 #include <sys/proc.h> 46 #include <sys/user.h> 47 #include <sys/chio.h> 48 #include <sys/device.h> 49 #include <sys/malloc.h> 50 #include <sys/conf.h> 51 #include <sys/fcntl.h> 52 53 #include <scsi/scsi_all.h> 54 #include <scsi/scsi_changer.h> 55 #include <scsi/scsiconf.h> 56 57 #define CHRETRIES 2 58 #define CHUNIT(x) (minor((x))) 59 60 struct ch_softc { 61 struct device sc_dev; /* generic device info */ 62 struct scsi_link *sc_link; /* link in the SCSI bus */ 63 64 int sc_picker; /* current picker */ 65 66 /* 67 * The following information is obtained from the 68 * element address assignment page. 69 */ 70 int sc_firsts[4]; /* firsts, indexed by CHET_* */ 71 int sc_counts[4]; /* counts, indexed by CHET_* */ 72 73 /* 74 * The following mask defines the legal combinations 75 * of elements for the MOVE MEDIUM command. 76 */ 77 u_int8_t sc_movemask[4]; 78 79 /* 80 * As above, but for EXCHANGE MEDIUM. 81 */ 82 u_int8_t sc_exchangemask[4]; 83 84 int flags; /* misc. info */ 85 86 /* 87 * Quirks; see below. 88 */ 89 int sc_settledelay; /* delay for settle */ 90 91 }; 92 93 /* sc_flags */ 94 #define CHF_ROTATE 0x01 /* picker can rotate */ 95 96 /* Autoconfiguration glue */ 97 int chmatch __P((struct device *, void *, void *)); 98 void chattach __P((struct device *, struct device *, void *)); 99 100 struct cfattach ch_ca = { 101 sizeof(struct ch_softc), chmatch, chattach 102 }; 103 104 struct cfdriver ch_cd = { 105 NULL, "ch", DV_DULL 106 }; 107 108 struct scsi_inquiry_pattern ch_patterns[] = { 109 {T_CHANGER, T_REMOV, 110 "", "", ""}, 111 }; 112 113 /* SCSI glue */ 114 struct scsi_device ch_switch = { 115 NULL, NULL, NULL, NULL 116 }; 117 118 int ch_move __P((struct ch_softc *, struct changer_move *)); 119 int ch_exchange __P((struct ch_softc *, struct changer_exchange *)); 120 int ch_position __P((struct ch_softc *, struct changer_position *)); 121 int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); 122 int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t)); 123 int ch_get_params __P((struct ch_softc *, int)); 124 void ch_get_quirks __P((struct ch_softc *, struct scsi_inquiry_data *)); 125 126 /* 127 * SCSI changer quirks. 128 */ 129 struct chquirk { 130 struct scsi_inquiry_pattern cq_match; /* device id pattern */ 131 int cq_settledelay; /* settle delay, in seconds */ 132 }; 133 134 struct chquirk chquirks[] = { 135 {{T_CHANGER, T_REMOV, 136 "SPECTRA", "9000", "0200"}, 137 75}, 138 }; 139 140 int 141 chmatch(parent, match, aux) 142 struct device *parent; 143 void *match, *aux; 144 { 145 struct scsibus_attach_args *sa = aux; 146 int priority; 147 148 (void)scsi_inqmatch(sa->sa_inqbuf, 149 (caddr_t)ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]), 150 sizeof(ch_patterns[0]), &priority); 151 152 return (priority); 153 } 154 155 void 156 chattach(parent, self, aux) 157 struct device *parent, *self; 158 void *aux; 159 { 160 struct ch_softc *sc = (struct ch_softc *)self; 161 struct scsibus_attach_args *sa = aux; 162 struct scsi_link *link = sa->sa_sc_link; 163 164 /* Glue into the SCSI bus */ 165 sc->sc_link = link; 166 link->device = &ch_switch; 167 link->device_softc = sc; 168 link->openings = 1; 169 170 printf("\n"); 171 172 /* 173 * Find out our device's quirks. 174 */ 175 ch_get_quirks(sc, sa->sa_inqbuf); 176 177 /* 178 * Some changers require a long time to settle out, to do 179 * tape inventory, for instance. 180 */ 181 if (sc->sc_settledelay) { 182 printf("%s: waiting %d seconds for changer to settle...\n", 183 sc->sc_dev.dv_xname, sc->sc_settledelay); 184 delay(1000000 * sc->sc_settledelay); 185 } 186 187 /* 188 * Get information about the device. Note we can't use 189 * interrupts yet. 190 */ 191 if (ch_get_params(sc, scsi_autoconf)) 192 printf("%s: offline\n", sc->sc_dev.dv_xname); 193 else { 194 #define PLURAL(c) (c) == 1 ? "" : "s" 195 printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n", 196 sc->sc_dev.dv_xname, 197 sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]), 198 sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]), 199 sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]), 200 sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE])); 201 #undef PLURAL 202 #ifdef CHANGER_DEBUG 203 printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", 204 sc->sc_dev.dv_xname, 205 sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], 206 sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); 207 printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", 208 sc->sc_dev.dv_xname, 209 sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], 210 sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); 211 #endif /* CHANGER_DEBUG */ 212 } 213 214 /* Default the current picker. */ 215 sc->sc_picker = sc->sc_firsts[CHET_MT]; 216 } 217 218 int 219 chopen(dev, flags, fmt, p) 220 dev_t dev; 221 int flags, fmt; 222 struct proc *p; 223 { 224 struct ch_softc *sc; 225 int unit, error = 0; 226 227 unit = CHUNIT(dev); 228 if ((unit >= ch_cd.cd_ndevs) || 229 ((sc = ch_cd.cd_devs[unit]) == NULL)) 230 return (ENXIO); 231 232 /* 233 * Only allow one open at a time. 234 */ 235 if (sc->sc_link->flags & SDEV_OPEN) 236 return (EBUSY); 237 238 sc->sc_link->flags |= SDEV_OPEN; 239 240 /* 241 * Absorb any unit attention errors. Ignore "not ready" 242 * since this might occur if e.g. a tape isn't actually 243 * loaded in the drive. 244 */ 245 error = scsi_test_unit_ready(sc->sc_link, 246 SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE); 247 if (error) 248 goto bad; 249 250 /* 251 * Make sure our parameters are up to date. 252 */ 253 if ((error = ch_get_params(sc, 0)) != 0) 254 goto bad; 255 256 return (0); 257 258 bad: 259 sc->sc_link->flags &= ~SDEV_OPEN; 260 return (error); 261 } 262 263 int 264 chclose(dev, flags, fmt, p) 265 dev_t dev; 266 int flags, fmt; 267 struct proc *p; 268 { 269 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 270 271 sc->sc_link->flags &= ~SDEV_OPEN; 272 return (0); 273 } 274 275 int 276 chioctl(dev, cmd, data, flags, p) 277 dev_t dev; 278 u_long cmd; 279 caddr_t data; 280 int flags; 281 struct proc *p; 282 { 283 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 284 int error = 0; 285 286 /* 287 * If this command can change the device's state, we must 288 * have the device open for writing. 289 */ 290 switch (cmd) { 291 case CHIOGPICKER: 292 case CHIOGPARAMS: 293 case CHIOGSTATUS: 294 break; 295 296 default: 297 if ((flags & FWRITE) == 0) 298 return (EBADF); 299 } 300 301 switch (cmd) { 302 case CHIOMOVE: 303 error = ch_move(sc, (struct changer_move *)data); 304 break; 305 306 case CHIOEXCHANGE: 307 error = ch_exchange(sc, (struct changer_exchange *)data); 308 break; 309 310 case CHIOPOSITION: 311 error = ch_position(sc, (struct changer_position *)data); 312 break; 313 314 case CHIOGPICKER: 315 *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; 316 break; 317 318 case CHIOSPICKER: { 319 int new_picker = *(int *)data; 320 321 if (new_picker > (sc->sc_counts[CHET_MT] - 1)) 322 return (EINVAL); 323 sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; 324 break; } 325 326 case CHIOGPARAMS: { 327 struct changer_params *cp = (struct changer_params *)data; 328 329 cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; 330 cp->cp_npickers = sc->sc_counts[CHET_MT]; 331 cp->cp_nslots = sc->sc_counts[CHET_ST]; 332 cp->cp_nportals = sc->sc_counts[CHET_IE]; 333 cp->cp_ndrives = sc->sc_counts[CHET_DT]; 334 break; } 335 336 case CHIOGSTATUS: { 337 struct changer_element_status *ces = 338 (struct changer_element_status *)data; 339 340 error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data); 341 break; } 342 343 /* Implement prevent/allow? */ 344 345 default: 346 error = scsi_do_safeioctl(sc->sc_link, dev, cmd, data, 347 flags, p); 348 break; 349 } 350 351 return (error); 352 } 353 354 int 355 ch_move(sc, cm) 356 struct ch_softc *sc; 357 struct changer_move *cm; 358 { 359 struct scsi_move_medium cmd; 360 u_int16_t fromelem, toelem; 361 362 /* 363 * Check arguments. 364 */ 365 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) 366 return (EINVAL); 367 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || 368 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) 369 return (ENODEV); 370 371 /* 372 * Check the request against the changer's capabilities. 373 */ 374 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) 375 return (EINVAL); 376 377 /* 378 * Calculate the source and destination elements. 379 */ 380 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; 381 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; 382 383 /* 384 * Build the SCSI command. 385 */ 386 bzero(&cmd, sizeof(cmd)); 387 cmd.opcode = MOVE_MEDIUM; 388 _lto2b(sc->sc_picker, cmd.tea); 389 _lto2b(fromelem, cmd.src); 390 _lto2b(toelem, cmd.dst); 391 if (cm->cm_flags & CM_INVERT) 392 cmd.flags |= MOVE_MEDIUM_INVERT; 393 394 /* 395 * Send command to changer. 396 */ 397 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 398 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 399 } 400 401 int 402 ch_exchange(sc, ce) 403 struct ch_softc *sc; 404 struct changer_exchange *ce; 405 { 406 struct scsi_exchange_medium cmd; 407 u_int16_t src, dst1, dst2; 408 409 /* 410 * Check arguments. 411 */ 412 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || 413 (ce->ce_sdsttype > CHET_DT)) 414 return (EINVAL); 415 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || 416 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || 417 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) 418 return (ENODEV); 419 420 /* 421 * Check the request against the changer's capabilities. 422 */ 423 if (((sc->sc_exchangemask[ce->ce_srctype] & 424 (1 << ce->ce_fdsttype)) == 0) || 425 ((sc->sc_exchangemask[ce->ce_fdsttype] & 426 (1 << ce->ce_sdsttype)) == 0)) 427 return (EINVAL); 428 429 /* 430 * Calculate the source and destination elements. 431 */ 432 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; 433 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; 434 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; 435 436 /* 437 * Build the SCSI command. 438 */ 439 bzero(&cmd, sizeof(cmd)); 440 cmd.opcode = EXCHANGE_MEDIUM; 441 _lto2b(sc->sc_picker, cmd.tea); 442 _lto2b(src, cmd.src); 443 _lto2b(dst1, cmd.fdst); 444 _lto2b(dst2, cmd.sdst); 445 if (ce->ce_flags & CE_INVERT1) 446 cmd.flags |= EXCHANGE_MEDIUM_INV1; 447 if (ce->ce_flags & CE_INVERT2) 448 cmd.flags |= EXCHANGE_MEDIUM_INV2; 449 450 /* 451 * Send command to changer. 452 */ 453 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 454 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 455 } 456 457 int 458 ch_position(sc, cp) 459 struct ch_softc *sc; 460 struct changer_position *cp; 461 { 462 struct scsi_position_to_element cmd; 463 u_int16_t dst; 464 465 /* 466 * Check arguments. 467 */ 468 if (cp->cp_type > CHET_DT) 469 return (EINVAL); 470 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) 471 return (ENODEV); 472 473 /* 474 * Calculate the destination element. 475 */ 476 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; 477 478 /* 479 * Build the SCSI command. 480 */ 481 bzero(&cmd, sizeof(cmd)); 482 cmd.opcode = POSITION_TO_ELEMENT; 483 _lto2b(sc->sc_picker, cmd.tea); 484 _lto2b(dst, cmd.dst); 485 if (cp->cp_flags & CP_INVERT) 486 cmd.flags |= POSITION_TO_ELEMENT_INVERT; 487 488 /* 489 * Send command to changer. 490 */ 491 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 492 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 493 } 494 495 /* 496 * Perform a READ ELEMENT STATUS on behalf of the user, and return to 497 * the user only the data the user is interested in (i.e. an array of 498 * flags bytes). 499 */ 500 int 501 ch_usergetelemstatus(sc, chet, uptr) 502 struct ch_softc *sc; 503 int chet; 504 u_int8_t *uptr; 505 { 506 struct read_element_status_header *st_hdr; 507 struct read_element_status_page_header *pg_hdr; 508 struct read_element_status_descriptor *desc; 509 caddr_t data = NULL; 510 size_t size, desclen; 511 int avail, i, error = 0; 512 u_int8_t *user_data = NULL; 513 514 /* 515 * If there are no elements of the requested type in the changer, 516 * the request is invalid. 517 */ 518 if (sc->sc_counts[chet] == 0) 519 return (EINVAL); 520 521 /* 522 * Request one descriptor for the given element type. This 523 * is used to determine the size of the descriptor so that 524 * we can allocate enough storage for all of them. We assume 525 * that the first one can fit into 1k. 526 */ 527 data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK); 528 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024); 529 if (error) 530 goto done; 531 532 st_hdr = (struct read_element_status_header *)data; 533 pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr + 534 sizeof(struct read_element_status_header)); 535 desclen = _2btol(pg_hdr->edl); 536 537 size = sizeof(struct read_element_status_header) + 538 sizeof(struct read_element_status_page_header) + 539 (desclen * sc->sc_counts[chet]); 540 541 /* 542 * Reallocate storage for descriptors and get them from the 543 * device. 544 */ 545 free(data, M_DEVBUF); 546 data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK); 547 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 548 sc->sc_counts[chet], data, size); 549 if (error) 550 goto done; 551 552 /* 553 * Fill in the user status array. 554 */ 555 st_hdr = (struct read_element_status_header *)data; 556 avail = _2btol(st_hdr->count); 557 if (avail != sc->sc_counts[chet]) 558 printf("%s: warning, READ ELEMENT STATUS avail != count\n", 559 sc->sc_dev.dv_xname); 560 561 user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK); 562 563 desc = (struct read_element_status_descriptor *)((u_long)data + 564 sizeof(struct read_element_status_header) + 565 sizeof(struct read_element_status_page_header)); 566 for (i = 0; i < avail; ++i) { 567 user_data[i] = desc->flags1; 568 (u_long)desc += desclen; 569 } 570 571 /* Copy flags array out to userspace. */ 572 error = copyout(user_data, uptr, avail); 573 574 done: 575 if (data != NULL) 576 free(data, M_DEVBUF); 577 if (user_data != NULL) 578 free(user_data, M_DEVBUF); 579 return (error); 580 } 581 582 int 583 ch_getelemstatus(sc, first, count, data, datalen) 584 struct ch_softc *sc; 585 int first, count; 586 caddr_t data; 587 size_t datalen; 588 { 589 struct scsi_read_element_status cmd; 590 591 /* 592 * Build SCSI command. 593 */ 594 bzero(&cmd, sizeof(cmd)); 595 cmd.opcode = READ_ELEMENT_STATUS; 596 _lto2b(first, cmd.sea); 597 _lto2b(count, cmd.count); 598 _lto3b(datalen, cmd.len); 599 600 /* 601 * Send command to changer. 602 */ 603 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 604 sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, SCSI_DATA_IN)); 605 } 606 607 608 /* 609 * Ask the device about itself and fill in the parameters in our 610 * softc. 611 */ 612 int 613 ch_get_params(sc, scsiflags) 614 struct ch_softc *sc; 615 int scsiflags; 616 { 617 struct scsi_mode_sense cmd; 618 struct scsi_mode_sense_data { 619 struct scsi_mode_header header; 620 union { 621 struct page_element_address_assignment ea; 622 struct page_transport_geometry_parameters tg; 623 struct page_device_capabilities cap; 624 } pages; 625 } sense_data; 626 int error, from; 627 u_int8_t *moves, *exchanges; 628 629 /* 630 * Grab info from the element address assignment page. 631 */ 632 bzero(&cmd, sizeof(cmd)); 633 bzero(&sense_data, sizeof(sense_data)); 634 cmd.opcode = MODE_SENSE; 635 cmd.byte2 |= 0x08; /* disable block descriptors */ 636 cmd.page = 0x1d; 637 cmd.length = (sizeof(sense_data) & 0xff); 638 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 639 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, 640 6000, NULL, scsiflags | SCSI_DATA_IN); 641 if (error) { 642 printf("%s: could not sense element address page\n", 643 sc->sc_dev.dv_xname); 644 return (error); 645 } 646 647 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); 648 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); 649 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); 650 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); 651 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); 652 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); 653 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); 654 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); 655 656 /* XXX ask for page trasport geom */ 657 658 /* 659 * Grab info from the capabilities page. 660 */ 661 bzero(&cmd, sizeof(cmd)); 662 bzero(&sense_data, sizeof(sense_data)); 663 cmd.opcode = MODE_SENSE; 664 cmd.byte2 |= 0x08; /* disable block descriptors */ 665 cmd.page = 0x1f; 666 cmd.length = (sizeof(sense_data) & 0xff); 667 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 668 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, 669 6000, NULL, scsiflags | SCSI_DATA_IN); 670 if (error) { 671 printf("%s: could not sense capabilities page\n", 672 sc->sc_dev.dv_xname); 673 return (error); 674 } 675 676 bzero(sc->sc_movemask, sizeof(sc->sc_movemask)); 677 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask)); 678 moves = &sense_data.pages.cap.move_from_mt; 679 exchanges = &sense_data.pages.cap.exchange_with_mt; 680 for (from = CHET_MT; from <= CHET_DT; ++from) { 681 sc->sc_movemask[from] = moves[from]; 682 sc->sc_exchangemask[from] = exchanges[from]; 683 } 684 685 sc->sc_link->flags |= SDEV_MEDIA_LOADED; 686 return (0); 687 } 688 689 void 690 ch_get_quirks(sc, inqbuf) 691 struct ch_softc *sc; 692 struct scsi_inquiry_data *inqbuf; 693 { 694 struct chquirk *match; 695 int priority; 696 697 sc->sc_settledelay = 0; 698 699 match = (struct chquirk *)scsi_inqmatch(inqbuf, 700 (caddr_t)chquirks, 701 sizeof(chquirks) / sizeof(chquirks[0]), 702 sizeof(chquirks[0]), &priority); 703 if (priority != 0) { 704 sc->sc_settledelay = match->cq_settledelay; 705 } 706 } 707