1 /* $NetBSD: ch.c,v 1.31 1997/10/18 19:50:57 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1996, 1997 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 #ifdef __BROKEN_INDIRECT_CONFIG 98 int chmatch __P((struct device *, void *, void *)); 99 #else 100 int chmatch __P((struct device *, struct cfdata *, void *)); 101 #endif 102 void chattach __P((struct device *, struct device *, void *)); 103 104 struct cfattach ch_ca = { 105 sizeof(struct ch_softc), chmatch, chattach 106 }; 107 108 struct cfdriver ch_cd = { 109 NULL, "ch", DV_DULL 110 }; 111 112 struct scsipi_inquiry_pattern ch_patterns[] = { 113 {T_CHANGER, T_REMOV, 114 "", "", ""}, 115 }; 116 117 /* SCSI glue */ 118 struct scsipi_device ch_switch = { 119 NULL, NULL, NULL, NULL 120 }; 121 122 int ch_move __P((struct ch_softc *, struct changer_move *)); 123 int ch_exchange __P((struct ch_softc *, struct changer_exchange *)); 124 int ch_position __P((struct ch_softc *, struct changer_position *)); 125 int ch_ielem __P((struct ch_softc *)); 126 int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); 127 int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t)); 128 int ch_get_params __P((struct ch_softc *, int)); 129 void ch_get_quirks __P((struct ch_softc *, 130 struct scsipi_inquiry_pattern *)); 131 132 /* 133 * SCSI changer quirks. 134 */ 135 struct chquirk { 136 struct scsipi_inquiry_pattern cq_match; /* device id pattern */ 137 int cq_settledelay; /* settle delay, in seconds */ 138 }; 139 140 struct chquirk chquirks[] = { 141 {{T_CHANGER, T_REMOV, 142 "SPECTRA", "9000", "0200"}, 143 75}, 144 }; 145 146 int 147 chmatch(parent, match, aux) 148 struct device *parent; 149 #ifdef __BROKEN_INDIRECT_CONFIG 150 void *match; 151 #else 152 struct cfdata *match; 153 #endif 154 void *aux; 155 { 156 struct scsipibus_attach_args *sa = aux; 157 int priority; 158 159 (void)scsipi_inqmatch(&sa->sa_inqbuf, 160 (caddr_t)ch_patterns, sizeof(ch_patterns) / sizeof(ch_patterns[0]), 161 sizeof(ch_patterns[0]), &priority); 162 163 return (priority); 164 } 165 166 void 167 chattach(parent, self, aux) 168 struct device *parent, *self; 169 void *aux; 170 { 171 struct ch_softc *sc = (struct ch_softc *)self; 172 struct scsipibus_attach_args *sa = aux; 173 struct scsipi_link *link = sa->sa_sc_link; 174 175 /* Glue into the SCSI bus */ 176 sc->sc_link = link; 177 link->device = &ch_switch; 178 link->device_softc = sc; 179 link->openings = 1; 180 181 printf("\n"); 182 183 /* 184 * Find out our device's quirks. 185 */ 186 ch_get_quirks(sc, &sa->sa_inqbuf); 187 188 /* 189 * Some changers require a long time to settle out, to do 190 * tape inventory, for instance. 191 */ 192 if (sc->sc_settledelay) { 193 printf("%s: waiting %d seconds for changer to settle...\n", 194 sc->sc_dev.dv_xname, sc->sc_settledelay); 195 delay(1000000 * sc->sc_settledelay); 196 } 197 198 /* 199 * Get information about the device. Note we can't use 200 * interrupts yet. 201 */ 202 if (ch_get_params(sc, SCSI_AUTOCONF)) 203 printf("%s: offline\n", sc->sc_dev.dv_xname); 204 else { 205 #define PLURAL(c) (c) == 1 ? "" : "s" 206 printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n", 207 sc->sc_dev.dv_xname, 208 sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]), 209 sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]), 210 sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]), 211 sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE])); 212 #undef PLURAL 213 #ifdef CHANGER_DEBUG 214 printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", 215 sc->sc_dev.dv_xname, 216 sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], 217 sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); 218 printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", 219 sc->sc_dev.dv_xname, 220 sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], 221 sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); 222 #endif /* CHANGER_DEBUG */ 223 } 224 225 /* Default the current picker. */ 226 sc->sc_picker = sc->sc_firsts[CHET_MT]; 227 } 228 229 int 230 chopen(dev, flags, fmt, p) 231 dev_t dev; 232 int flags, fmt; 233 struct proc *p; 234 { 235 struct ch_softc *sc; 236 int unit, error = 0; 237 238 unit = CHUNIT(dev); 239 if ((unit >= ch_cd.cd_ndevs) || 240 ((sc = ch_cd.cd_devs[unit]) == NULL)) 241 return (ENXIO); 242 243 /* 244 * Only allow one open at a time. 245 */ 246 if (sc->sc_link->flags & SDEV_OPEN) 247 return (EBUSY); 248 249 sc->sc_link->flags |= SDEV_OPEN; 250 251 /* 252 * Absorb any unit attention errors. Ignore "not ready" 253 * since this might occur if e.g. a tape isn't actually 254 * loaded in the drive. 255 */ 256 error = scsipi_test_unit_ready(sc->sc_link, 257 SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE); 258 if (error) 259 goto bad; 260 261 /* 262 * Make sure our parameters are up to date. 263 */ 264 if ((error = ch_get_params(sc, 0)) != 0) 265 goto bad; 266 267 return (0); 268 269 bad: 270 sc->sc_link->flags &= ~SDEV_OPEN; 271 return (error); 272 } 273 274 int 275 chclose(dev, flags, fmt, p) 276 dev_t dev; 277 int flags, fmt; 278 struct proc *p; 279 { 280 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 281 282 sc->sc_link->flags &= ~SDEV_OPEN; 283 return (0); 284 } 285 286 int 287 chioctl(dev, cmd, data, flags, p) 288 dev_t dev; 289 u_long cmd; 290 caddr_t data; 291 int flags; 292 struct proc *p; 293 { 294 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 295 int error = 0; 296 297 /* 298 * If this command can change the device's state, we must 299 * have the device open for writing. 300 */ 301 switch (cmd) { 302 case CHIOGPICKER: 303 case CHIOGPARAMS: 304 case CHIOGSTATUS: 305 break; 306 307 default: 308 if ((flags & FWRITE) == 0) 309 return (EBADF); 310 } 311 312 switch (cmd) { 313 case CHIOMOVE: 314 error = ch_move(sc, (struct changer_move *)data); 315 break; 316 317 case CHIOEXCHANGE: 318 error = ch_exchange(sc, (struct changer_exchange *)data); 319 break; 320 321 case CHIOPOSITION: 322 error = ch_position(sc, (struct changer_position *)data); 323 break; 324 325 case CHIOGPICKER: 326 *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; 327 break; 328 329 case CHIOSPICKER: { 330 int new_picker = *(int *)data; 331 332 if (new_picker > (sc->sc_counts[CHET_MT] - 1)) 333 return (EINVAL); 334 sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; 335 break; } 336 337 case CHIOGPARAMS: { 338 struct changer_params *cp = (struct changer_params *)data; 339 340 cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; 341 cp->cp_npickers = sc->sc_counts[CHET_MT]; 342 cp->cp_nslots = sc->sc_counts[CHET_ST]; 343 cp->cp_nportals = sc->sc_counts[CHET_IE]; 344 cp->cp_ndrives = sc->sc_counts[CHET_DT]; 345 break; } 346 347 case CHIOIELEM: 348 error = ch_ielem(sc); 349 break; 350 351 case CHIOGSTATUS: { 352 struct changer_element_status *ces = 353 (struct changer_element_status *)data; 354 355 error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data); 356 break; } 357 358 /* Implement prevent/allow? */ 359 360 default: 361 error = scsipi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p); 362 break; 363 } 364 365 return (error); 366 } 367 368 int 369 ch_move(sc, cm) 370 struct ch_softc *sc; 371 struct changer_move *cm; 372 { 373 struct scsi_move_medium cmd; 374 u_int16_t fromelem, toelem; 375 376 /* 377 * Check arguments. 378 */ 379 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) 380 return (EINVAL); 381 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || 382 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) 383 return (ENODEV); 384 385 /* 386 * Check the request against the changer's capabilities. 387 */ 388 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) 389 return (EINVAL); 390 391 /* 392 * Calculate the source and destination elements. 393 */ 394 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; 395 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; 396 397 /* 398 * Build the SCSI command. 399 */ 400 bzero(&cmd, sizeof(cmd)); 401 cmd.opcode = MOVE_MEDIUM; 402 _lto2b(sc->sc_picker, cmd.tea); 403 _lto2b(fromelem, cmd.src); 404 _lto2b(toelem, cmd.dst); 405 if (cm->cm_flags & CM_INVERT) 406 cmd.flags |= MOVE_MEDIUM_INVERT; 407 408 /* 409 * Send command to changer. 410 */ 411 return (scsipi_command(sc->sc_link, 412 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES, 413 100000, NULL, 0)); 414 } 415 416 int 417 ch_exchange(sc, ce) 418 struct ch_softc *sc; 419 struct changer_exchange *ce; 420 { 421 struct scsi_exchange_medium cmd; 422 u_int16_t src, dst1, dst2; 423 424 /* 425 * Check arguments. 426 */ 427 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || 428 (ce->ce_sdsttype > CHET_DT)) 429 return (EINVAL); 430 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || 431 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || 432 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) 433 return (ENODEV); 434 435 /* 436 * Check the request against the changer's capabilities. 437 */ 438 if (((sc->sc_exchangemask[ce->ce_srctype] & 439 (1 << ce->ce_fdsttype)) == 0) || 440 ((sc->sc_exchangemask[ce->ce_fdsttype] & 441 (1 << ce->ce_sdsttype)) == 0)) 442 return (EINVAL); 443 444 /* 445 * Calculate the source and destination elements. 446 */ 447 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; 448 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; 449 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; 450 451 /* 452 * Build the SCSI command. 453 */ 454 bzero(&cmd, sizeof(cmd)); 455 cmd.opcode = EXCHANGE_MEDIUM; 456 _lto2b(sc->sc_picker, cmd.tea); 457 _lto2b(src, cmd.src); 458 _lto2b(dst1, cmd.fdst); 459 _lto2b(dst2, cmd.sdst); 460 if (ce->ce_flags & CE_INVERT1) 461 cmd.flags |= EXCHANGE_MEDIUM_INV1; 462 if (ce->ce_flags & CE_INVERT2) 463 cmd.flags |= EXCHANGE_MEDIUM_INV2; 464 465 /* 466 * Send command to changer. 467 */ 468 return (scsipi_command(sc->sc_link, 469 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES, 470 100000, NULL, 0)); 471 } 472 473 int 474 ch_position(sc, cp) 475 struct ch_softc *sc; 476 struct changer_position *cp; 477 { 478 struct scsi_position_to_element cmd; 479 u_int16_t dst; 480 481 /* 482 * Check arguments. 483 */ 484 if (cp->cp_type > CHET_DT) 485 return (EINVAL); 486 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) 487 return (ENODEV); 488 489 /* 490 * Calculate the destination element. 491 */ 492 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; 493 494 /* 495 * Build the SCSI command. 496 */ 497 bzero(&cmd, sizeof(cmd)); 498 cmd.opcode = POSITION_TO_ELEMENT; 499 _lto2b(sc->sc_picker, cmd.tea); 500 _lto2b(dst, cmd.dst); 501 if (cp->cp_flags & CP_INVERT) 502 cmd.flags |= POSITION_TO_ELEMENT_INVERT; 503 504 /* 505 * Send command to changer. 506 */ 507 return (scsipi_command(sc->sc_link, 508 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES, 509 100000, NULL, 0)); 510 } 511 512 /* 513 * Perform a READ ELEMENT STATUS on behalf of the user, and return to 514 * the user only the data the user is interested in (i.e. an array of 515 * flags bytes). 516 */ 517 int 518 ch_usergetelemstatus(sc, chet, uptr) 519 struct ch_softc *sc; 520 int chet; 521 u_int8_t *uptr; 522 { 523 struct read_element_status_header *st_hdr; 524 struct read_element_status_page_header *pg_hdr; 525 struct read_element_status_descriptor *desc; 526 caddr_t data = NULL; 527 size_t size, desclen; 528 int avail, i, error = 0; 529 u_int8_t *user_data = NULL; 530 531 /* 532 * If there are no elements of the requested type in the changer, 533 * the request is invalid. 534 */ 535 if (sc->sc_counts[chet] == 0) 536 return (EINVAL); 537 538 /* 539 * Request one descriptor for the given element type. This 540 * is used to determine the size of the descriptor so that 541 * we can allocate enough storage for all of them. We assume 542 * that the first one can fit into 1k. 543 */ 544 data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK); 545 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024); 546 if (error) 547 goto done; 548 549 st_hdr = (struct read_element_status_header *)data; 550 pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr + 551 sizeof(struct read_element_status_header)); 552 desclen = _2btol(pg_hdr->edl); 553 554 size = sizeof(struct read_element_status_header) + 555 sizeof(struct read_element_status_page_header) + 556 (desclen * sc->sc_counts[chet]); 557 558 /* 559 * Reallocate storage for descriptors and get them from the 560 * device. 561 */ 562 free(data, M_DEVBUF); 563 data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK); 564 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 565 sc->sc_counts[chet], data, size); 566 if (error) 567 goto done; 568 569 /* 570 * Fill in the user status array. 571 */ 572 st_hdr = (struct read_element_status_header *)data; 573 avail = _2btol(st_hdr->count); 574 575 if (avail != sc->sc_counts[chet]) 576 printf("%s: warning, READ ELEMENT STATUS avail != count\n", 577 sc->sc_dev.dv_xname); 578 579 user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK); 580 581 desc = (struct read_element_status_descriptor *)((u_long)data + 582 sizeof(struct read_element_status_header) + 583 sizeof(struct read_element_status_page_header)); 584 for (i = 0; i < avail; ++i) { 585 user_data[i] = desc->flags1; 586 (u_long)desc += desclen; 587 } 588 589 /* Copy flags array out to userspace. */ 590 error = copyout(user_data, uptr, avail); 591 592 done: 593 if (data != NULL) 594 free(data, M_DEVBUF); 595 if (user_data != NULL) 596 free(user_data, M_DEVBUF); 597 return (error); 598 } 599 600 int 601 ch_getelemstatus(sc, first, count, data, datalen) 602 struct ch_softc *sc; 603 int first, count; 604 caddr_t data; 605 size_t datalen; 606 { 607 struct scsi_read_element_status cmd; 608 609 /* 610 * Build SCSI command. 611 */ 612 bzero(&cmd, sizeof(cmd)); 613 cmd.opcode = READ_ELEMENT_STATUS; 614 _lto2b(first, cmd.sea); 615 _lto2b(count, cmd.count); 616 _lto3b(datalen, cmd.len); 617 618 /* 619 * Send command to changer. 620 */ 621 return (scsipi_command(sc->sc_link, 622 (struct scsipi_generic *)&cmd, sizeof(cmd), 623 (u_char *)data, datalen, CHRETRIES, 100000, NULL, SCSI_DATA_IN)); 624 } 625 626 627 int 628 ch_ielem(sc) 629 struct ch_softc *sc; 630 { 631 struct scsi_initialize_element_status cmd; 632 633 /* 634 * Build SCSI command. 635 */ 636 bzero(&cmd, sizeof(cmd)); 637 cmd.opcode = INITIALIZE_ELEMENT_STATUS; 638 639 /* 640 * Send command to changer. 641 */ 642 return (scsipi_command(sc->sc_link, 643 (struct scsipi_generic *)&cmd, sizeof(cmd), 644 NULL, 0, CHRETRIES, 100000, NULL, 0)); 645 } 646 647 /* 648 * Ask the device about itself and fill in the parameters in our 649 * softc. 650 */ 651 int 652 ch_get_params(sc, scsiflags) 653 struct ch_softc *sc; 654 int scsiflags; 655 { 656 struct scsi_mode_sense cmd; 657 struct scsi_mode_sense_data { 658 struct scsi_mode_header header; 659 union { 660 struct page_element_address_assignment ea; 661 struct page_transport_geometry_parameters tg; 662 struct page_device_capabilities cap; 663 } pages; 664 } sense_data; 665 int error, from; 666 u_int8_t *moves, *exchanges; 667 668 /* 669 * Grab info from the element address assignment page. 670 */ 671 bzero(&cmd, sizeof(cmd)); 672 bzero(&sense_data, sizeof(sense_data)); 673 cmd.opcode = SCSI_MODE_SENSE; 674 cmd.byte2 |= 0x08; /* disable block descriptors */ 675 cmd.page = 0x1d; 676 cmd.length = (sizeof(sense_data) & 0xff); 677 error = scsipi_command(sc->sc_link, 678 (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data, 679 sizeof(sense_data), CHRETRIES, 6000, NULL, 680 scsiflags | SCSI_DATA_IN); 681 if (error) { 682 printf("%s: could not sense element address page\n", 683 sc->sc_dev.dv_xname); 684 return (error); 685 } 686 687 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); 688 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); 689 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); 690 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); 691 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); 692 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); 693 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); 694 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); 695 696 /* XXX ask for page trasport geom */ 697 698 /* 699 * Grab info from the capabilities page. 700 */ 701 bzero(&cmd, sizeof(cmd)); 702 bzero(&sense_data, sizeof(sense_data)); 703 cmd.opcode = SCSI_MODE_SENSE; 704 /* 705 * XXX: Note: not all changers can deal with disabled block descriptors 706 */ 707 cmd.byte2 = 0x08; /* disable block descriptors */ 708 cmd.page = 0x1f; 709 cmd.length = (sizeof(sense_data) & 0xff); 710 error = scsipi_command(sc->sc_link, 711 (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data, 712 sizeof(sense_data), CHRETRIES, 6000, NULL, 713 scsiflags | SCSI_DATA_IN); 714 if (error) { 715 printf("%s: could not sense capabilities page\n", 716 sc->sc_dev.dv_xname); 717 return (error); 718 } 719 720 bzero(sc->sc_movemask, sizeof(sc->sc_movemask)); 721 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask)); 722 moves = &sense_data.pages.cap.move_from_mt; 723 exchanges = &sense_data.pages.cap.exchange_with_mt; 724 for (from = CHET_MT; from <= CHET_DT; ++from) { 725 sc->sc_movemask[from] = moves[from]; 726 sc->sc_exchangemask[from] = exchanges[from]; 727 } 728 729 sc->sc_link->flags |= SDEV_MEDIA_LOADED; 730 return (0); 731 } 732 733 void 734 ch_get_quirks(sc, inqbuf) 735 struct ch_softc *sc; 736 struct scsipi_inquiry_pattern *inqbuf; 737 { 738 struct chquirk *match; 739 int priority; 740 741 sc->sc_settledelay = 0; 742 743 match = (struct chquirk *)scsipi_inqmatch(inqbuf, 744 (caddr_t)chquirks, 745 sizeof(chquirks) / sizeof(chquirks[0]), 746 sizeof(chquirks[0]), &priority); 747 if (priority != 0) 748 sc->sc_settledelay = match->cq_settledelay; 749 } 750