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