ch.c revision 1.47
1/* $NetBSD: ch.c,v 1.47 2001/07/18 18:21:05 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 1996, 1997, 1998, 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 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 acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/kernel.h> 43#include <sys/errno.h> 44#include <sys/ioctl.h> 45#include <sys/buf.h> 46#include <sys/proc.h> 47#include <sys/user.h> 48#include <sys/chio.h> 49#include <sys/device.h> 50#include <sys/malloc.h> 51#include <sys/conf.h> 52#include <sys/fcntl.h> 53#include <sys/vnode.h> 54#include <sys/time.h> 55#include <sys/select.h> 56#include <sys/poll.h> 57 58#include <dev/scsipi/scsipi_all.h> 59#include <dev/scsipi/scsi_all.h> 60#include <dev/scsipi/scsi_changer.h> 61#include <dev/scsipi/scsiconf.h> 62 63#define CHRETRIES 2 64#define CHUNIT(x) (minor((x))) 65 66struct ch_softc { 67 struct device sc_dev; /* generic device info */ 68 struct scsipi_periph *sc_periph;/* our periph data */ 69 70 u_int sc_events; /* event bitmask */ 71 struct selinfo sc_selq; /* select/poll queue for events */ 72 73 int sc_flags; /* misc. info */ 74 75 int sc_picker; /* current picker */ 76 77 /* 78 * The following information is obtained from the 79 * element address assignment page. 80 */ 81 int sc_firsts[4]; /* firsts, indexed by CHET_* */ 82 int sc_counts[4]; /* counts, indexed by CHET_* */ 83 84 /* 85 * The following mask defines the legal combinations 86 * of elements for the MOVE MEDIUM command. 87 */ 88 u_int8_t sc_movemask[4]; 89 90 /* 91 * As above, but for EXCHANGE MEDIUM. 92 */ 93 u_int8_t sc_exchangemask[4]; 94 95 /* 96 * Quirks; see below. 97 */ 98 int sc_settledelay; /* delay for settle */ 99 100}; 101 102/* sc_flags */ 103#define CHF_ROTATE 0x01 /* picker can rotate */ 104 105/* Autoconfiguration glue */ 106int chmatch __P((struct device *, struct cfdata *, void *)); 107void chattach __P((struct device *, struct device *, void *)); 108 109struct cfattach ch_ca = { 110 sizeof(struct ch_softc), chmatch, chattach 111}; 112 113extern struct cfdriver ch_cd; 114 115struct scsipi_inquiry_pattern ch_patterns[] = { 116 {T_CHANGER, T_REMOV, 117 "", "", ""}, 118}; 119 120/* SCSI glue */ 121int ch_interpret_sense __P((struct scsipi_xfer *)); 122 123const struct scsipi_periphsw ch_switch = { 124 ch_interpret_sense, /* check our error handler first */ 125 NULL, /* no queue; our commands are synchronous */ 126 NULL, /* have no async handler */ 127 NULL, /* nothing to be done when xfer is done */ 128}; 129 130int ch_move __P((struct ch_softc *, struct changer_move_request *)); 131int ch_exchange __P((struct ch_softc *, struct changer_exchange_request *)); 132int ch_position __P((struct ch_softc *, struct changer_position_request *)); 133int ch_ielem __P((struct ch_softc *)); 134int ch_ousergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); 135int ch_usergetelemstatus __P((struct ch_softc *, 136 struct changer_element_status_request *)); 137int ch_getelemstatus __P((struct ch_softc *, int, int, void *, 138 size_t, int, int)); 139int ch_setvoltag __P((struct ch_softc *, 140 struct changer_set_voltag_request *)); 141int ch_get_params __P((struct ch_softc *, int)); 142void ch_get_quirks __P((struct ch_softc *, 143 struct scsipi_inquiry_pattern *)); 144void ch_event __P((struct ch_softc *, u_int)); 145int ch_map_element __P((struct ch_softc *, u_int16_t, int *, int *)); 146 147void ch_voltag_convert_in __P((const struct changer_volume_tag *, 148 struct changer_voltag *)); 149int ch_voltag_convert_out __P((const struct changer_voltag *, 150 struct changer_volume_tag *)); 151 152/* 153 * SCSI changer quirks. 154 */ 155struct chquirk { 156 struct scsipi_inquiry_pattern cq_match; /* device id pattern */ 157 int cq_settledelay; /* settle delay, in seconds */ 158}; 159 160struct chquirk chquirks[] = { 161 {{T_CHANGER, T_REMOV, 162 "SPECTRA", "9000", "0200"}, 163 75}, 164}; 165 166int 167chmatch(parent, match, aux) 168 struct device *parent; 169 struct cfdata *match; 170 void *aux; 171{ 172 struct scsipibus_attach_args *sa = aux; 173 int priority; 174 175 (void)scsipi_inqmatch(&sa->sa_inqbuf, 176 (caddr_t)ch_patterns, sizeof(ch_patterns) / sizeof(ch_patterns[0]), 177 sizeof(ch_patterns[0]), &priority); 178 179 return (priority); 180} 181 182void 183chattach(parent, self, aux) 184 struct device *parent, *self; 185 void *aux; 186{ 187 struct ch_softc *sc = (struct ch_softc *)self; 188 struct scsipibus_attach_args *sa = aux; 189 struct scsipi_periph *periph = sa->sa_periph; 190 191 /* Glue into the SCSI bus */ 192 sc->sc_periph = periph; 193 periph->periph_dev = &sc->sc_dev; 194 periph->periph_switch = &ch_switch; 195 196 printf("\n"); 197 198 /* 199 * Find out our device's quirks. 200 */ 201 ch_get_quirks(sc, &sa->sa_inqbuf); 202 203 /* 204 * Some changers require a long time to settle out, to do 205 * tape inventory, for instance. 206 */ 207 if (sc->sc_settledelay) { 208 printf("%s: waiting %d seconds for changer to settle...\n", 209 sc->sc_dev.dv_xname, sc->sc_settledelay); 210 delay(1000000 * sc->sc_settledelay); 211 } 212 213 /* 214 * Get information about the device. Note we can't use 215 * interrupts yet. 216 */ 217 if (ch_get_params(sc, XS_CTL_DISCOVERY|XS_CTL_IGNORE_MEDIA_CHANGE)) 218 printf("%s: offline\n", sc->sc_dev.dv_xname); 219 else { 220#define PLURAL(c) (c) == 1 ? "" : "s" 221 printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n", 222 sc->sc_dev.dv_xname, 223 sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]), 224 sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]), 225 sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]), 226 sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE])); 227#undef PLURAL 228#ifdef CHANGER_DEBUG 229 printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", 230 sc->sc_dev.dv_xname, 231 sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], 232 sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); 233 printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", 234 sc->sc_dev.dv_xname, 235 sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], 236 sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); 237#endif /* CHANGER_DEBUG */ 238 } 239 240 /* Default the current picker. */ 241 sc->sc_picker = sc->sc_firsts[CHET_MT]; 242} 243 244int 245chopen(dev, flags, fmt, p) 246 dev_t dev; 247 int flags, fmt; 248 struct proc *p; 249{ 250 struct ch_softc *sc; 251 struct scsipi_periph *periph; 252 struct scsipi_adapter *adapt; 253 int unit, error; 254 255 unit = CHUNIT(dev); 256 if ((unit >= ch_cd.cd_ndevs) || 257 ((sc = ch_cd.cd_devs[unit]) == NULL)) 258 return (ENXIO); 259 260 periph = sc->sc_periph; 261 adapt = periph->periph_channel->chan_adapter; 262 263 /* 264 * Only allow one open at a time. 265 */ 266 if (periph->periph_flags & PERIPH_OPEN) 267 return (EBUSY); 268 269 if ((error = scsipi_adapter_addref(adapt)) != 0) 270 return (error); 271 272 periph->periph_flags |= PERIPH_OPEN; 273 274 /* 275 * Make sure the unit is on-line. If a UNIT ATTENTION 276 * occurs, we will mark that an Init-Element-Status is 277 * needed in ch_get_params(). 278 * 279 * We ignore NOT READY in case e.g a magazine isn't actually 280 * loaded into the changer or a tape isn't in the drive. 281 */ 282 error = scsipi_test_unit_ready(periph, XS_CTL_IGNORE_NOT_READY); 283 if (error) 284 goto bad; 285 286 /* 287 * Make sure our parameters are up to date. 288 */ 289 if ((error = ch_get_params(sc, 0)) != 0) 290 goto bad; 291 292 return (0); 293 294 bad: 295 scsipi_adapter_delref(adapt); 296 periph->periph_flags &= ~PERIPH_OPEN; 297 return (error); 298} 299 300int 301chclose(dev, flags, fmt, p) 302 dev_t dev; 303 int flags, fmt; 304 struct proc *p; 305{ 306 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 307 struct scsipi_periph *periph = sc->sc_periph; 308 struct scsipi_adapter *adapt = periph->periph_channel->chan_adapter; 309 310 scsipi_wait_drain(periph); 311 312 scsipi_adapter_delref(adapt); 313 314 sc->sc_events = 0; 315 316 periph->periph_flags &= ~PERIPH_OPEN; 317 return (0); 318} 319 320int 321chread(dev, uio, flags) 322 dev_t dev; 323 struct uio *uio; 324 int flags; 325{ 326 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 327 int error; 328 329 if (uio->uio_resid != CHANGER_EVENT_SIZE) 330 return (EINVAL); 331 332 /* 333 * Read never blocks; if there are no events pending, we just 334 * return an all-clear bitmask. 335 */ 336 error = uiomove(&sc->sc_events, CHANGER_EVENT_SIZE, uio); 337 if (error == 0) 338 sc->sc_events = 0; 339 return (error); 340} 341 342int 343chioctl(dev, cmd, data, flags, p) 344 dev_t dev; 345 u_long cmd; 346 caddr_t data; 347 int flags; 348 struct proc *p; 349{ 350 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 351 int error = 0; 352 353 /* 354 * If this command can change the device's state, we must 355 * have the device open for writing. 356 */ 357 switch (cmd) { 358 case CHIOGPICKER: 359 case CHIOGPARAMS: 360 case OCHIOGSTATUS: 361 break; 362 363 default: 364 if ((flags & FWRITE) == 0) 365 return (EBADF); 366 } 367 368 switch (cmd) { 369 case CHIOMOVE: 370 error = ch_move(sc, (struct changer_move_request *)data); 371 break; 372 373 case CHIOEXCHANGE: 374 error = ch_exchange(sc, 375 (struct changer_exchange_request *)data); 376 break; 377 378 case CHIOPOSITION: 379 error = ch_position(sc, 380 (struct changer_position_request *)data); 381 break; 382 383 case CHIOGPICKER: 384 *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; 385 break; 386 387 case CHIOSPICKER: 388 { 389 int new_picker = *(int *)data; 390 391 if (new_picker > (sc->sc_counts[CHET_MT] - 1)) 392 return (EINVAL); 393 sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; 394 break; 395 } 396 397 case CHIOGPARAMS: 398 { 399 struct changer_params *cp = (struct changer_params *)data; 400 401 cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; 402 cp->cp_npickers = sc->sc_counts[CHET_MT]; 403 cp->cp_nslots = sc->sc_counts[CHET_ST]; 404 cp->cp_nportals = sc->sc_counts[CHET_IE]; 405 cp->cp_ndrives = sc->sc_counts[CHET_DT]; 406 break; 407 } 408 409 case CHIOIELEM: 410 error = ch_ielem(sc); 411 if (error == 0) { 412 sc->sc_periph->periph_flags |= PERIPH_MEDIA_LOADED; 413 } 414 break; 415 416 case OCHIOGSTATUS: 417 { 418 struct ochanger_element_status_request *cesr = 419 (struct ochanger_element_status_request *)data; 420 421 error = ch_ousergetelemstatus(sc, cesr->cesr_type, 422 cesr->cesr_data); 423 break; 424 } 425 426 case CHIOGSTATUS: 427 error = ch_usergetelemstatus(sc, 428 (struct changer_element_status_request *)data); 429 break; 430 431 case CHIOSVOLTAG: 432 error = ch_setvoltag(sc, 433 (struct changer_set_voltag_request *)data); 434 break; 435 436 /* Implement prevent/allow? */ 437 438 default: 439 error = scsipi_do_ioctl(sc->sc_periph, dev, cmd, data, 440 flags, p); 441 break; 442 } 443 444 return (error); 445} 446 447int 448chpoll(dev, events, p) 449 dev_t dev; 450 int events; 451 struct proc *p; 452{ 453 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 454 int revents; 455 456 revents = events & (POLLOUT | POLLWRNORM); 457 458 if ((events & (POLLIN | POLLRDNORM)) == 0) 459 return (revents); 460 461 if (sc->sc_events == 0) 462 revents |= events & (POLLIN | POLLRDNORM); 463 else 464 selrecord(p, &sc->sc_selq); 465 466 return (revents); 467} 468 469int 470ch_interpret_sense(xs) 471 struct scsipi_xfer *xs; 472{ 473 struct scsipi_periph *periph = xs->xs_periph; 474 struct scsipi_sense_data *sense = &xs->sense.scsi_sense; 475 struct ch_softc *sc = (void *)periph->periph_dev; 476 u_int16_t asc_ascq; 477 478 /* 479 * If the periph is already recovering, just do the 480 * normal error recovering. 481 */ 482 if (periph->periph_flags & PERIPH_RECOVERING) 483 return (EJUSTRETURN); 484 485 /* 486 * If it isn't an extended or extended/deferred error, let 487 * the generic code handle it. 488 */ 489 if ((sense->error_code & SSD_ERRCODE) != 0x70 && 490 (sense->error_code & SSD_ERRCODE) != 0x71) 491 return (EJUSTRETURN); 492 493 /* 494 * We're only interested in condtions that 495 * indicate potential inventory violation. 496 * 497 * We use ASC/ASCQ codes for this. 498 */ 499 500 asc_ascq = (((u_int16_t) sense->add_sense_code) << 8) | 501 sense->add_sense_code_qual; 502 503 switch (asc_ascq) { 504 case 0x2800: 505 /* "Not Ready To Ready Transition (Medium May Have Changed)" */ 506 case 0x2900: 507 /* "Power On, Reset, or Bus Device Reset Occurred" */ 508 sc->sc_periph->periph_flags &= ~PERIPH_MEDIA_LOADED; 509 /* 510 * Enqueue an Element-Status-Changed event, and 511 * wake up any processes waiting for them. 512 */ 513 /* 514 * Enqueue an Element-Status-Changed event, and wake up 515 * any processes waiting for them. 516 */ 517 if ((xs->xs_control & XS_CTL_IGNORE_MEDIA_CHANGE) == 0) 518 ch_event(sc, CHEV_ELEMENT_STATUS_CHANGED); 519 break; 520 default: 521 break; 522 } 523 524 return (EJUSTRETURN); 525} 526 527void 528ch_event(sc, event) 529 struct ch_softc *sc; 530 u_int event; 531{ 532 533 sc->sc_events |= event; 534 selwakeup(&sc->sc_selq); 535} 536 537int 538ch_move(sc, cm) 539 struct ch_softc *sc; 540 struct changer_move_request *cm; 541{ 542 struct scsi_move_medium cmd; 543 u_int16_t fromelem, toelem; 544 545 /* 546 * Check arguments. 547 */ 548 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) 549 return (EINVAL); 550 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || 551 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) 552 return (ENODEV); 553 554 /* 555 * Check the request against the changer's capabilities. 556 */ 557 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) 558 return (ENODEV); 559 560 /* 561 * Calculate the source and destination elements. 562 */ 563 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; 564 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; 565 566 /* 567 * Build the SCSI command. 568 */ 569 memset(&cmd, 0, sizeof(cmd)); 570 cmd.opcode = MOVE_MEDIUM; 571 _lto2b(sc->sc_picker, cmd.tea); 572 _lto2b(fromelem, cmd.src); 573 _lto2b(toelem, cmd.dst); 574 if (cm->cm_flags & CM_INVERT) 575 cmd.flags |= MOVE_MEDIUM_INVERT; 576 577 /* 578 * Send command to changer. 579 */ 580 return (scsipi_command(sc->sc_periph, 581 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES, 582 100000, NULL, 0)); 583} 584 585int 586ch_exchange(sc, ce) 587 struct ch_softc *sc; 588 struct changer_exchange_request *ce; 589{ 590 struct scsi_exchange_medium cmd; 591 u_int16_t src, dst1, dst2; 592 593 /* 594 * Check arguments. 595 */ 596 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || 597 (ce->ce_sdsttype > CHET_DT)) 598 return (EINVAL); 599 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || 600 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || 601 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) 602 return (ENODEV); 603 604 /* 605 * Check the request against the changer's capabilities. 606 */ 607 if (((sc->sc_exchangemask[ce->ce_srctype] & 608 (1 << ce->ce_fdsttype)) == 0) || 609 ((sc->sc_exchangemask[ce->ce_fdsttype] & 610 (1 << ce->ce_sdsttype)) == 0)) 611 return (ENODEV); 612 613 /* 614 * Calculate the source and destination elements. 615 */ 616 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; 617 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; 618 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; 619 620 /* 621 * Build the SCSI command. 622 */ 623 memset(&cmd, 0, sizeof(cmd)); 624 cmd.opcode = EXCHANGE_MEDIUM; 625 _lto2b(sc->sc_picker, cmd.tea); 626 _lto2b(src, cmd.src); 627 _lto2b(dst1, cmd.fdst); 628 _lto2b(dst2, cmd.sdst); 629 if (ce->ce_flags & CE_INVERT1) 630 cmd.flags |= EXCHANGE_MEDIUM_INV1; 631 if (ce->ce_flags & CE_INVERT2) 632 cmd.flags |= EXCHANGE_MEDIUM_INV2; 633 634 /* 635 * Send command to changer. 636 */ 637 return (scsipi_command(sc->sc_periph, 638 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES, 639 100000, NULL, 0)); 640} 641 642int 643ch_position(sc, cp) 644 struct ch_softc *sc; 645 struct changer_position_request *cp; 646{ 647 struct scsi_position_to_element cmd; 648 u_int16_t dst; 649 650 /* 651 * Check arguments. 652 */ 653 if (cp->cp_type > CHET_DT) 654 return (EINVAL); 655 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) 656 return (ENODEV); 657 658 /* 659 * Calculate the destination element. 660 */ 661 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; 662 663 /* 664 * Build the SCSI command. 665 */ 666 memset(&cmd, 0, sizeof(cmd)); 667 cmd.opcode = POSITION_TO_ELEMENT; 668 _lto2b(sc->sc_picker, cmd.tea); 669 _lto2b(dst, cmd.dst); 670 if (cp->cp_flags & CP_INVERT) 671 cmd.flags |= POSITION_TO_ELEMENT_INVERT; 672 673 /* 674 * Send command to changer. 675 */ 676 return (scsipi_command(sc->sc_periph, 677 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES, 678 100000, NULL, 0)); 679} 680 681/* 682 * Perform a READ ELEMENT STATUS on behalf of the user, and return to 683 * the user only the data the user is interested in. This returns the 684 * old data format. 685 */ 686int 687ch_ousergetelemstatus(sc, chet, uptr) 688 struct ch_softc *sc; 689 int chet; 690 u_int8_t *uptr; 691{ 692 struct read_element_status_header *st_hdrp, st_hdr; 693 struct read_element_status_page_header *pg_hdrp; 694 struct read_element_status_descriptor *desc; 695 size_t size, desclen; 696 caddr_t data; 697 int avail, i, error = 0; 698 u_int8_t user_data; 699 700 /* 701 * If there are no elements of the requested type in the changer, 702 * the request is invalid. 703 */ 704 if (sc->sc_counts[chet] == 0) 705 return (EINVAL); 706 707 /* 708 * Do the request the user wants, but only read the status header. 709 * This will tell us the amount of storage we must allocate in 710 * order to read all data. 711 */ 712 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 713 sc->sc_counts[chet], &st_hdr, sizeof(st_hdr), 714 XS_CTL_DATA_ONSTACK, 0); 715 if (error) 716 return (error); 717 718 size = sizeof(struct read_element_status_header) + 719 _3btol(st_hdr.nbytes); 720 721 /* 722 * We must have at least room for the status header and 723 * one page header (since we only ask for one element type 724 * at a time). 725 */ 726 if (size < (sizeof(struct read_element_status_header) + 727 sizeof(struct read_element_status_page_header))) 728 return (EIO); 729 730 /* 731 * Allocate the storage and do the request again. 732 */ 733 data = malloc(size, M_DEVBUF, M_WAITOK); 734 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 735 sc->sc_counts[chet], data, size, 0, 0); 736 if (error) 737 goto done; 738 739 st_hdrp = (struct read_element_status_header *)data; 740 pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp + 741 sizeof(struct read_element_status_header)); 742 desclen = _2btol(pg_hdrp->edl); 743 744 /* 745 * Fill in the user status array. 746 */ 747 avail = _2btol(st_hdrp->count); 748 749 if (avail != sc->sc_counts[chet]) 750 printf("%s: warning, READ ELEMENT STATUS avail != count\n", 751 sc->sc_dev.dv_xname); 752 753 desc = (struct read_element_status_descriptor *)((u_long)data + 754 sizeof(struct read_element_status_header) + 755 sizeof(struct read_element_status_page_header)); 756 for (i = 0; i < avail; ++i) { 757 user_data = desc->flags1; 758 error = copyout(&user_data, &uptr[i], avail); 759 if (error) 760 break; 761 desc = (struct read_element_status_descriptor *)((u_long)desc 762 + desclen); 763 } 764 765 done: 766 if (data != NULL) 767 free(data, M_DEVBUF); 768 return (error); 769} 770 771/* 772 * Perform a READ ELEMENT STATUS on behalf of the user. This returns 773 * the new (more complete) data format. 774 */ 775int 776ch_usergetelemstatus(sc, cesr) 777 struct ch_softc *sc; 778 struct changer_element_status_request *cesr; 779{ 780 struct scsipi_channel *chan = sc->sc_periph->periph_channel; 781 struct scsipi_periph *dtperiph; 782 struct read_element_status_header *st_hdrp, st_hdr; 783 struct read_element_status_page_header *pg_hdrp; 784 struct read_element_status_descriptor *desc; 785 struct changer_volume_tag *avol, *pvol; 786 size_t size, desclen, stddesclen, offset; 787 int first, avail, i, error = 0; 788 caddr_t data; 789 void *uvendptr; 790 struct changer_element_status ces; 791 792 /* 793 * Check arguments. 794 */ 795 if (cesr->cesr_type > CHET_DT) 796 return (EINVAL); 797 if (sc->sc_counts[cesr->cesr_type] == 0) 798 return (ENODEV); 799 if (cesr->cesr_unit > (sc->sc_counts[cesr->cesr_type] - 1)) 800 return (ENODEV); 801 if (cesr->cesr_count > 802 (sc->sc_counts[cesr->cesr_type] + cesr->cesr_unit)) 803 return (EINVAL); 804 805 /* 806 * Do the request the user wants, but only read the status header. 807 * This will tell us the amount of storage we must allocate 808 * in order to read all the data. 809 */ 810 error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + 811 cesr->cesr_unit, cesr->cesr_count, &st_hdr, sizeof(st_hdr), 0, 812 cesr->cesr_flags); 813 if (error) 814 return (error); 815 816 size = sizeof(struct read_element_status_header) + 817 _3btol(st_hdr.nbytes); 818 819 /* 820 * We must have at least room for the status header and 821 * one page header (since we only ask for oen element type 822 * at a time). 823 */ 824 if (size < (sizeof(struct read_element_status_header) + 825 sizeof(struct read_element_status_page_header))) 826 return (EIO); 827 828 /* 829 * Allocate the storage and do the request again. 830 */ 831 data = malloc(size, M_DEVBUF, M_WAITOK); 832 error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + 833 cesr->cesr_unit, cesr->cesr_count, data, size, 0, 834 cesr->cesr_flags); 835 if (error) 836 goto done; 837 838 st_hdrp = (struct read_element_status_header *)data; 839 pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp + 840 sizeof(struct read_element_status_header)); 841 desclen = _2btol(pg_hdrp->edl); 842 843 /* 844 * Fill in the user status array. 845 */ 846 first = _2btol(st_hdrp->fear); 847 if (first < (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit) || 848 first >= (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit + 849 cesr->cesr_count)) { 850 error = EIO; 851 goto done; 852 } 853 first -= sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit; 854 855 avail = _2btol(st_hdrp->count); 856 if (avail <= 0 || avail > cesr->cesr_count) { 857 error = EIO; 858 goto done; 859 } 860 861 offset = sizeof(struct read_element_status_header) + 862 sizeof(struct read_element_status_page_header); 863 864 for (i = 0; i < cesr->cesr_count; i++) { 865 memset(&ces, 0, sizeof(ces)); 866 if (i < first || i >= (first + avail)) { 867 error = copyout(&ces, &cesr->cesr_data[i], 868 sizeof(ces)); 869 if (error) 870 goto done; 871 } 872 873 desc = (struct read_element_status_descriptor *) 874 (data + offset); 875 stddesclen = sizeof(struct read_element_status_descriptor); 876 offset += desclen; 877 878 ces.ces_flags = CESTATUS_STATUS_VALID; 879 880 /* 881 * The SCSI flags conveniently map directly to the 882 * chio API flags. 883 */ 884 ces.ces_flags |= (desc->flags1 & 0x3f); 885 886 ces.ces_asc = desc->sense_code; 887 ces.ces_ascq = desc->sense_qual; 888 889 /* 890 * For Data Transport elemenets, get the SCSI ID and LUN, 891 * and attempt to map them to a device name if they're 892 * on the same SCSI bus. 893 */ 894 if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) { 895 ces.ces_target = desc->dt_scsi_addr; 896 ces.ces_flags |= CESTATUS_TARGET_VALID; 897 } 898 if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUVALID) { 899 ces.ces_lun = desc->dt_scsi_flags & 900 READ_ELEMENT_STATUS_DT_LUNMASK; 901 ces.ces_flags |= CESTATUS_LUN_VALID; 902 } 903 if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_NOTBUS) 904 ces.ces_flags |= CESTATUS_NOTBUS; 905 else if ((ces.ces_flags & 906 (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) == 907 (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) { 908 if (ces.ces_target < chan->chan_ntargets && 909 ces.ces_lun < chan->chan_nluns && 910 (dtperiph = scsipi_lookup_periph(chan, 911 ces.ces_target, ces.ces_lun)) != NULL && 912 dtperiph->periph_dev != NULL) { 913 strcpy(ces.ces_xname, 914 dtperiph->periph_dev->dv_xname); 915 ces.ces_flags |= CESTATUS_XNAME_VALID; 916 } 917 } 918 919 if (desc->flags2 & READ_ELEMENT_STATUS_INVERT) 920 ces.ces_flags |= CESTATUS_INVERTED; 921 922 if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) { 923 if (ch_map_element(sc, _2btol(desc->ssea), 924 &ces.ces_from_type, &ces.ces_from_unit)) 925 ces.ces_flags |= CESTATUS_FROM_VALID; 926 } 927 928 /* 929 * Extract volume tag information. 930 */ 931 switch (pg_hdrp->flags & 932 (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG)) { 933 case (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG): 934 pvol = (struct changer_volume_tag *)(desc + 1); 935 avol = pvol + 1; 936 break; 937 938 case READ_ELEMENT_STATUS_PVOLTAG: 939 pvol = (struct changer_volume_tag *)(desc + 1); 940 avol = NULL; 941 break; 942 943 case READ_ELEMENT_STATUS_AVOLTAG: 944 pvol = NULL; 945 avol = (struct changer_volume_tag *)(desc + 1); 946 break; 947 948 default: 949 avol = pvol = NULL; 950 break; 951 } 952 953 if (pvol != NULL) { 954 ch_voltag_convert_in(pvol, &ces.ces_pvoltag); 955 ces.ces_flags |= CESTATUS_PVOL_VALID; 956 stddesclen += sizeof(struct changer_volume_tag); 957 } 958 if (avol != NULL) { 959 ch_voltag_convert_in(avol, &ces.ces_avoltag); 960 ces.ces_flags |= CESTATUS_AVOL_VALID; 961 stddesclen += sizeof(struct changer_volume_tag); 962 } 963 964 /* 965 * Compute vendor-specific length. Note the 4 reserved 966 * bytes between the volume tags and the vendor-specific 967 * data. Copy it out of the user wants it. 968 */ 969 stddesclen += 4; 970 if (desclen > stddesclen) 971 ces.ces_vendor_len = desclen - stddesclen; 972 973 if (ces.ces_vendor_len != 0 && cesr->cesr_vendor_data != NULL) { 974 error = copyin(&cesr->cesr_vendor_data[i], &uvendptr, 975 sizeof(uvendptr)); 976 if (error) 977 goto done; 978 error = copyout((void *)((u_long)desc + stddesclen), 979 uvendptr, ces.ces_vendor_len); 980 if (error) 981 goto done; 982 } 983 984 /* 985 * Now copy out the status descriptor we've constructed. 986 */ 987 error = copyout(&ces, &cesr->cesr_data[i], sizeof(ces)); 988 if (error) 989 goto done; 990 } 991 992 done: 993 if (data != NULL) 994 free(data, M_DEVBUF); 995 return (error); 996} 997 998int 999ch_getelemstatus(sc, first, count, data, datalen, scsiflags, flags) 1000 struct ch_softc *sc; 1001 int first, count; 1002 void *data; 1003 size_t datalen; 1004 int scsiflags; 1005 int flags; 1006{ 1007 struct scsi_read_element_status cmd; 1008 1009 /* 1010 * Build SCSI command. 1011 */ 1012 memset(&cmd, 0, sizeof(cmd)); 1013 cmd.opcode = READ_ELEMENT_STATUS; 1014 cmd.byte2 = ELEMENT_TYPE_ALL; 1015 if (flags & CESR_VOLTAGS) 1016 cmd.byte2 |= READ_ELEMENT_STATUS_VOLTAG; 1017 _lto2b(first, cmd.sea); 1018 _lto2b(count, cmd.count); 1019 _lto3b(datalen, cmd.len); 1020 1021 /* 1022 * Send command to changer. 1023 */ 1024 return (scsipi_command(sc->sc_periph, 1025 (struct scsipi_generic *)&cmd, sizeof(cmd), 1026 (u_char *)data, datalen, CHRETRIES, 100000, NULL, 1027 scsiflags | XS_CTL_DATA_IN)); 1028} 1029 1030int 1031ch_setvoltag(sc, csvr) 1032 struct ch_softc *sc; 1033 struct changer_set_voltag_request *csvr; 1034{ 1035 struct scsi_send_volume_tag cmd; 1036 struct changer_volume_tag voltag; 1037 void *data = NULL; 1038 size_t datalen = 0; 1039 int error; 1040 u_int16_t dst; 1041 1042 /* 1043 * Check arguments. 1044 */ 1045 if (csvr->csvr_type > CHET_DT) 1046 return (EINVAL); 1047 if (csvr->csvr_unit > (sc->sc_counts[csvr->csvr_type] - 1)) 1048 return (ENODEV); 1049 1050 dst = sc->sc_firsts[csvr->csvr_type] + csvr->csvr_unit; 1051 1052 /* 1053 * Build the SCSI command. 1054 */ 1055 memset(&cmd, 0, sizeof(cmd)); 1056 cmd.opcode = SEND_VOLUME_TAG; 1057 _lto2b(dst, cmd.eaddr); 1058 1059#define ALTERNATE (csvr->csvr_flags & CSVR_ALTERNATE) 1060 1061 switch (csvr->csvr_flags & CSVR_MODE_MASK) { 1062 case CSVR_MODE_SET: 1063 cmd.sac = ALTERNATE ? SAC_ASSERT_ALT : SAC_ASSERT_PRIMARY; 1064 break; 1065 1066 case CSVR_MODE_REPLACE: 1067 cmd.sac = ALTERNATE ? SAC_REPLACE_ALT : SAC_REPLACE_PRIMARY; 1068 break; 1069 1070 case CSVR_MODE_CLEAR: 1071 cmd.sac = ALTERNATE ? SAC_UNDEFINED_ALT : SAC_UNDEFINED_PRIMARY; 1072 break; 1073 1074 default: 1075 return (EINVAL); 1076 } 1077 1078#undef ALTERNATE 1079 1080 if (cmd.sac < SAC_UNDEFINED_PRIMARY) { 1081 error = ch_voltag_convert_out(&csvr->csvr_voltag, &voltag); 1082 if (error) 1083 return (error); 1084 data = &voltag; 1085 datalen = sizeof(voltag); 1086 _lto2b(datalen, cmd.length); 1087 } 1088 1089 /* 1090 * Send command to changer. 1091 */ 1092 return (scsipi_command(sc->sc_periph, 1093 (struct scsipi_generic *)&cmd, sizeof(cmd), 1094 (u_char *)data, datalen, CHRETRIES, 100000, NULL, 1095 datalen ? XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK : 0)); 1096} 1097 1098int 1099ch_ielem(sc) 1100 struct ch_softc *sc; 1101{ 1102 int tmo; 1103 struct scsi_initialize_element_status cmd; 1104 1105 /* 1106 * Build SCSI command. 1107 */ 1108 memset(&cmd, 0, sizeof(cmd)); 1109 cmd.opcode = INITIALIZE_ELEMENT_STATUS; 1110 1111 /* 1112 * Send command to changer. 1113 * 1114 * The problem is, how long to allow for the command? 1115 * It can take a *really* long time, and also depends 1116 * on unknowable factors such as whether there are 1117 * *almost* readable labels on tapes that a barcode 1118 * reader is trying to decipher. 1119 * 1120 * I'm going to make this long enough to allow 5 minutes 1121 * per element plus an initial 10 minute wait. 1122 */ 1123 tmo = sc->sc_counts[CHET_MT] + 1124 sc->sc_counts[CHET_ST] + 1125 sc->sc_counts[CHET_IE] + 1126 sc->sc_counts[CHET_DT]; 1127 tmo *= 5 * 60 * 1000; 1128 tmo += (10 * 60 * 1000); 1129 1130 return (scsipi_command(sc->sc_periph, 1131 (struct scsipi_generic *)&cmd, sizeof(cmd), 1132 NULL, 0, CHRETRIES, tmo, NULL, XS_CTL_IGNORE_ILLEGAL_REQUEST)); 1133} 1134 1135/* 1136 * Ask the device about itself and fill in the parameters in our 1137 * softc. 1138 */ 1139int 1140ch_get_params(sc, scsiflags) 1141 struct ch_softc *sc; 1142 int scsiflags; 1143{ 1144 struct scsi_mode_sense_data { 1145 struct scsipi_mode_header header; 1146 union { 1147 struct page_element_address_assignment ea; 1148 struct page_transport_geometry_parameters tg; 1149 struct page_device_capabilities cap; 1150 } pages; 1151 } sense_data; 1152 int error, from; 1153 u_int8_t *moves, *exchanges; 1154 1155 /* 1156 * Grab info from the element address assignment page. 1157 */ 1158 memset(&sense_data, 0, sizeof(sense_data)); 1159 error = scsipi_mode_sense(sc->sc_periph, SMS_DBD, 0x1d, 1160 &sense_data.header, sizeof(sense_data), 1161 scsiflags | XS_CTL_DATA_ONSTACK, CHRETRIES, 6000); 1162 if (error) { 1163 printf("%s: could not sense element address page\n", 1164 sc->sc_dev.dv_xname); 1165 return (error); 1166 } 1167 1168 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); 1169 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); 1170 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); 1171 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); 1172 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); 1173 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); 1174 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); 1175 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); 1176 1177 /* XXX ask for transport geometry page XXX */ 1178 1179 /* 1180 * Grab info from the capabilities page. 1181 */ 1182 memset(&sense_data, 0, sizeof(sense_data)); 1183 /* 1184 * XXX: Note: not all changers can deal with disabled block descriptors 1185 */ 1186 error = scsipi_mode_sense(sc->sc_periph, SMS_DBD, 0x1f, 1187 &sense_data.header, sizeof(sense_data), 1188 scsiflags | XS_CTL_DATA_ONSTACK, CHRETRIES, 6000); 1189 if (error) { 1190 printf("%s: could not sense capabilities page\n", 1191 sc->sc_dev.dv_xname); 1192 return (error); 1193 } 1194 1195 memset(sc->sc_movemask, 0, sizeof(sc->sc_movemask)); 1196 memset(sc->sc_exchangemask, 0, sizeof(sc->sc_exchangemask)); 1197 moves = &sense_data.pages.cap.move_from_mt; 1198 exchanges = &sense_data.pages.cap.exchange_with_mt; 1199 for (from = CHET_MT; from <= CHET_DT; ++from) { 1200 sc->sc_movemask[from] = moves[from]; 1201 sc->sc_exchangemask[from] = exchanges[from]; 1202 } 1203 1204#ifdef CH_AUTOMATIC_IELEM_POLICY 1205 /* 1206 * If we need to do an Init-Element-Status, 1207 * do that now that we know what's in the changer. 1208 */ 1209 if ((scsiflags & XS_CTL_IGNORE_MEDIA_CHANGE) == 0) { 1210 if ((sc->sc_periph->periph_flags & PERIPH_MEDIA_LOADED) == 0) 1211 error = ch_ielem(sc); 1212 if (error == 0) 1213 sc->sc_periph->periph_flags |= PERIPH_MEDIA_LOADED; 1214 else 1215 sc->sc_periph->periph_flags &= ~PERIPH_MEDIA_LOADED; 1216 } 1217#endif 1218 return (error); 1219} 1220 1221void 1222ch_get_quirks(sc, inqbuf) 1223 struct ch_softc *sc; 1224 struct scsipi_inquiry_pattern *inqbuf; 1225{ 1226 struct chquirk *match; 1227 int priority; 1228 1229 sc->sc_settledelay = 0; 1230 1231 match = (struct chquirk *)scsipi_inqmatch(inqbuf, 1232 (caddr_t)chquirks, 1233 sizeof(chquirks) / sizeof(chquirks[0]), 1234 sizeof(chquirks[0]), &priority); 1235 if (priority != 0) 1236 sc->sc_settledelay = match->cq_settledelay; 1237} 1238 1239int 1240ch_map_element(sc, elem, typep, unitp) 1241 struct ch_softc *sc; 1242 u_int16_t elem; 1243 int *typep, *unitp; 1244{ 1245 int chet; 1246 1247 for (chet = CHET_MT; chet <= CHET_DT; chet++) { 1248 if (elem >= sc->sc_firsts[chet] && 1249 elem < (sc->sc_firsts[chet] + sc->sc_counts[chet])) { 1250 *typep = chet; 1251 *unitp = elem - sc->sc_firsts[chet]; 1252 return (1); 1253 } 1254 } 1255 return (0); 1256} 1257 1258void 1259ch_voltag_convert_in(sv, cv) 1260 const struct changer_volume_tag *sv; 1261 struct changer_voltag *cv; 1262{ 1263 int i; 1264 1265 memset(cv, 0, sizeof(struct changer_voltag)); 1266 1267 /* 1268 * Copy the volume tag string from the SCSI representation. 1269 * Per the SCSI-2 spec, we stop at the first blank character. 1270 */ 1271 for (i = 0; i < sizeof(sv->volid); i++) { 1272 if (sv->volid[i] == ' ') 1273 break; 1274 cv->cv_tag[i] = sv->volid[i]; 1275 } 1276 cv->cv_tag[i] = '\0'; 1277 1278 cv->cv_serial = _2btol(sv->volseq); 1279} 1280 1281int 1282ch_voltag_convert_out(cv, sv) 1283 const struct changer_voltag *cv; 1284 struct changer_volume_tag *sv; 1285{ 1286 int i; 1287 1288 memset(sv, ' ', sizeof(struct changer_volume_tag)); 1289 1290 for (i = 0; i < sizeof(sv->volid); i++) { 1291 if (cv->cv_tag[i] == '\0') 1292 break; 1293 /* 1294 * Limit the character set to what is suggested in 1295 * the SCSI-2 spec. 1296 */ 1297 if ((cv->cv_tag[i] < '0' || cv->cv_tag[i] > '9') && 1298 (cv->cv_tag[i] < 'A' || cv->cv_tag[i] > 'Z') && 1299 (cv->cv_tag[i] != '_')) 1300 return (EINVAL); 1301 sv->volid[i] = cv->cv_tag[i]; 1302 } 1303 1304 _lto2b(cv->cv_serial, sv->volseq); 1305 1306 return (0); 1307} 1308