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