ch.c revision 1.44
1/* $NetBSD: ch.c,v 1.44 2001/04/25 17:53:39 bouyer 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 bzero(&cmd, 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 bzero(&cmd, 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 bzero(&cmd, 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 (u_long)desc += desclen; 762 } 763 764 done: 765 if (data != NULL) 766 free(data, M_DEVBUF); 767 return (error); 768} 769 770/* 771 * Perform a READ ELEMENT STATUS on behalf of the user. This returns 772 * the new (more complete) data format. 773 */ 774int 775ch_usergetelemstatus(sc, cesr) 776 struct ch_softc *sc; 777 struct changer_element_status_request *cesr; 778{ 779 struct scsipi_channel *chan = sc->sc_periph->periph_channel; 780 struct scsipi_periph *dtperiph; 781 struct read_element_status_header *st_hdrp, st_hdr; 782 struct read_element_status_page_header *pg_hdrp; 783 struct read_element_status_descriptor *desc; 784 struct changer_volume_tag *avol, *pvol; 785 size_t size, desclen, stddesclen, offset; 786 int first, avail, i, error = 0; 787 caddr_t data; 788 void *uvendptr; 789 struct changer_element_status ces; 790 791 /* 792 * Check arguments. 793 */ 794 if (cesr->cesr_type > CHET_DT) 795 return (EINVAL); 796 if (sc->sc_counts[cesr->cesr_type] == 0) 797 return (ENODEV); 798 if (cesr->cesr_unit > (sc->sc_counts[cesr->cesr_type] - 1)) 799 return (ENODEV); 800 if (cesr->cesr_count > 801 (sc->sc_counts[cesr->cesr_type] + cesr->cesr_unit)) 802 return (EINVAL); 803 804 /* 805 * Do the request the user wants, but only read the status header. 806 * This will tell us the amount of storage we must allocate 807 * in order to read all the data. 808 */ 809 error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + 810 cesr->cesr_unit, cesr->cesr_count, &st_hdr, sizeof(st_hdr), 0, 811 cesr->cesr_flags); 812 if (error) 813 return (error); 814 815 size = sizeof(struct read_element_status_header) + 816 _3btol(st_hdr.nbytes); 817 818 /* 819 * We must have at least room for the status header and 820 * one page header (since we only ask for oen element type 821 * at a time). 822 */ 823 if (size < (sizeof(struct read_element_status_header) + 824 sizeof(struct read_element_status_page_header))) 825 return (EIO); 826 827 /* 828 * Allocate the storage and do the request again. 829 */ 830 data = malloc(size, M_DEVBUF, M_WAITOK); 831 error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + 832 cesr->cesr_unit, cesr->cesr_count, data, size, 0, 833 cesr->cesr_flags); 834 if (error) 835 goto done; 836 837 st_hdrp = (struct read_element_status_header *)data; 838 pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp + 839 sizeof(struct read_element_status_header)); 840 desclen = _2btol(pg_hdrp->edl); 841 842 /* 843 * Fill in the user status array. 844 */ 845 first = _2btol(st_hdrp->fear); 846 if (first < (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit) || 847 first >= (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit + 848 cesr->cesr_count)) { 849 error = EIO; 850 goto done; 851 } 852 first -= sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit; 853 854 avail = _2btol(st_hdrp->count); 855 if (avail <= 0 || avail > cesr->cesr_count) { 856 error = EIO; 857 goto done; 858 } 859 860 offset = sizeof(struct read_element_status_header) + 861 sizeof(struct read_element_status_page_header); 862 863 for (i = 0; i < cesr->cesr_count; i++) { 864 memset(&ces, 0, sizeof(ces)); 865 if (i < first || i >= (first + avail)) { 866 error = copyout(&ces, &cesr->cesr_data[i], 867 sizeof(ces)); 868 if (error) 869 goto done; 870 } 871 872 desc = (struct read_element_status_descriptor *) 873 (data + offset); 874 stddesclen = sizeof(struct read_element_status_descriptor); 875 offset += desclen; 876 877 ces.ces_flags = CESTATUS_STATUS_VALID; 878 879 /* 880 * The SCSI flags conveniently map directly to the 881 * chio API flags. 882 */ 883 ces.ces_flags |= (desc->flags1 & 0x3f); 884 885 ces.ces_asc = desc->sense_code; 886 ces.ces_ascq = desc->sense_qual; 887 888 /* 889 * For Data Transport elemenets, get the SCSI ID and LUN, 890 * and attempt to map them to a device name if they're 891 * on the same SCSI bus. 892 */ 893 if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) { 894 ces.ces_target = desc->dt_scsi_addr; 895 ces.ces_flags |= CESTATUS_TARGET_VALID; 896 } 897 if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUVALID) { 898 ces.ces_lun = desc->dt_scsi_flags & 899 READ_ELEMENT_STATUS_DT_LUNMASK; 900 ces.ces_flags |= CESTATUS_LUN_VALID; 901 } 902 if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_NOTBUS) 903 ces.ces_flags |= CESTATUS_NOTBUS; 904 else if ((ces.ces_flags & 905 (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) == 906 (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) { 907 if (ces.ces_target < chan->chan_ntargets && 908 ces.ces_lun < chan->chan_nluns && 909 (dtperiph = scsipi_lookup_periph(chan, 910 ces.ces_target, ces.ces_lun)) != NULL && 911 dtperiph->periph_dev != NULL) { 912 strcpy(ces.ces_xname, 913 dtperiph->periph_dev->dv_xname); 914 ces.ces_flags |= CESTATUS_XNAME_VALID; 915 } 916 } 917 918 if (desc->flags2 & READ_ELEMENT_STATUS_INVERT) 919 ces.ces_flags |= CESTATUS_INVERTED; 920 921 if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) { 922 if (ch_map_element(sc, _2btol(desc->ssea), 923 &ces.ces_from_type, &ces.ces_from_unit)) 924 ces.ces_flags |= CESTATUS_FROM_VALID; 925 } 926 927 /* 928 * Extract volume tag information. 929 */ 930 switch (pg_hdrp->flags & 931 (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG)) { 932 case (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG): 933 pvol = (struct changer_volume_tag *)(desc + 1); 934 avol = pvol + 1; 935 break; 936 937 case READ_ELEMENT_STATUS_PVOLTAG: 938 pvol = (struct changer_volume_tag *)(desc + 1); 939 avol = NULL; 940 break; 941 942 case READ_ELEMENT_STATUS_AVOLTAG: 943 pvol = NULL; 944 avol = (struct changer_volume_tag *)(desc + 1); 945 break; 946 947 default: 948 avol = pvol = NULL; 949 break; 950 } 951 952 if (pvol != NULL) { 953 ch_voltag_convert_in(pvol, &ces.ces_pvoltag); 954 ces.ces_flags |= CESTATUS_PVOL_VALID; 955 stddesclen += sizeof(struct changer_volume_tag); 956 } 957 if (avol != NULL) { 958 ch_voltag_convert_in(avol, &ces.ces_avoltag); 959 ces.ces_flags |= CESTATUS_AVOL_VALID; 960 stddesclen += sizeof(struct changer_volume_tag); 961 } 962 963 /* 964 * Compute vendor-specific length. Note the 4 reserved 965 * bytes between the volume tags and the vendor-specific 966 * data. Copy it out of the user wants it. 967 */ 968 stddesclen += 4; 969 if (desclen > stddesclen) 970 ces.ces_vendor_len = desclen - stddesclen; 971 972 if (ces.ces_vendor_len != 0 && cesr->cesr_vendor_data != NULL) { 973 error = copyin(&cesr->cesr_vendor_data[i], &uvendptr, 974 sizeof(uvendptr)); 975 if (error) 976 goto done; 977 error = copyout((void *)((u_long)desc + stddesclen), 978 uvendptr, ces.ces_vendor_len); 979 if (error) 980 goto done; 981 } 982 983 /* 984 * Now copy out the status descriptor we've constructed. 985 */ 986 error = copyout(&ces, &cesr->cesr_data[i], sizeof(ces)); 987 if (error) 988 goto done; 989 } 990 991 done: 992 if (data != NULL) 993 free(data, M_DEVBUF); 994 return (error); 995} 996 997int 998ch_getelemstatus(sc, first, count, data, datalen, scsiflags, flags) 999 struct ch_softc *sc; 1000 int first, count; 1001 void *data; 1002 size_t datalen; 1003 int scsiflags; 1004 int flags; 1005{ 1006 struct scsi_read_element_status cmd; 1007 1008 /* 1009 * Build SCSI command. 1010 */ 1011 bzero(&cmd, sizeof(cmd)); 1012 cmd.opcode = READ_ELEMENT_STATUS; 1013 cmd.byte2 = ELEMENT_TYPE_ALL; 1014 if (flags & CESR_VOLTAGS) 1015 cmd.byte2 |= READ_ELEMENT_STATUS_VOLTAG; 1016 _lto2b(first, cmd.sea); 1017 _lto2b(count, cmd.count); 1018 _lto3b(datalen, cmd.len); 1019 1020 /* 1021 * Send command to changer. 1022 */ 1023 return (scsipi_command(sc->sc_periph, 1024 (struct scsipi_generic *)&cmd, sizeof(cmd), 1025 (u_char *)data, datalen, CHRETRIES, 100000, NULL, 1026 scsiflags | XS_CTL_DATA_IN)); 1027} 1028 1029int 1030ch_setvoltag(sc, csvr) 1031 struct ch_softc *sc; 1032 struct changer_set_voltag_request *csvr; 1033{ 1034 struct scsi_send_volume_tag cmd; 1035 struct changer_volume_tag voltag; 1036 void *data = NULL; 1037 size_t datalen = 0; 1038 int error; 1039 u_int16_t dst; 1040 1041 /* 1042 * Check arguments. 1043 */ 1044 if (csvr->csvr_type > CHET_DT) 1045 return (EINVAL); 1046 if (csvr->csvr_unit > (sc->sc_counts[csvr->csvr_type] - 1)) 1047 return (ENODEV); 1048 1049 dst = sc->sc_firsts[csvr->csvr_type] + csvr->csvr_unit; 1050 1051 /* 1052 * Build the SCSI command. 1053 */ 1054 bzero(&cmd, sizeof(cmd)); 1055 cmd.opcode = SEND_VOLUME_TAG; 1056 _lto2b(dst, cmd.eaddr); 1057 1058#define ALTERNATE (csvr->csvr_flags & CSVR_ALTERNATE) 1059 1060 switch (csvr->csvr_flags & CSVR_MODE_MASK) { 1061 case CSVR_MODE_SET: 1062 cmd.sac = ALTERNATE ? SAC_ASSERT_ALT : SAC_ASSERT_PRIMARY; 1063 break; 1064 1065 case CSVR_MODE_REPLACE: 1066 cmd.sac = ALTERNATE ? SAC_REPLACE_ALT : SAC_REPLACE_PRIMARY; 1067 break; 1068 1069 case CSVR_MODE_CLEAR: 1070 cmd.sac = ALTERNATE ? SAC_UNDEFINED_ALT : SAC_UNDEFINED_PRIMARY; 1071 break; 1072 1073 default: 1074 return (EINVAL); 1075 } 1076 1077#undef ALTERNATE 1078 1079 if (cmd.sac < SAC_UNDEFINED_PRIMARY) { 1080 error = ch_voltag_convert_out(&csvr->csvr_voltag, &voltag); 1081 if (error) 1082 return (error); 1083 data = &voltag; 1084 datalen = sizeof(voltag); 1085 _lto2b(datalen, cmd.length); 1086 } 1087 1088 /* 1089 * Send command to changer. 1090 */ 1091 return (scsipi_command(sc->sc_periph, 1092 (struct scsipi_generic *)&cmd, sizeof(cmd), 1093 (u_char *)data, datalen, CHRETRIES, 100000, NULL, 1094 datalen ? XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK : 0)); 1095} 1096 1097int 1098ch_ielem(sc) 1099 struct ch_softc *sc; 1100{ 1101 int tmo; 1102 struct scsi_initialize_element_status cmd; 1103 1104 /* 1105 * Build SCSI command. 1106 */ 1107 bzero(&cmd, sizeof(cmd)); 1108 cmd.opcode = INITIALIZE_ELEMENT_STATUS; 1109 1110 /* 1111 * Send command to changer. 1112 * 1113 * The problem is, how long to allow for the command? 1114 * It can take a *really* long time, and also depends 1115 * on unknowable factors such as whether there are 1116 * *almost* readable labels on tapes that a barcode 1117 * reader is trying to decipher. 1118 * 1119 * I'm going to make this long enough to allow 5 minutes 1120 * per element plus an initial 10 minute wait. 1121 */ 1122 tmo = sc->sc_counts[CHET_MT] + 1123 sc->sc_counts[CHET_ST] + 1124 sc->sc_counts[CHET_IE] + 1125 sc->sc_counts[CHET_DT]; 1126 tmo *= 5 * 60 * 1000; 1127 tmo += (10 * 60 * 1000); 1128 1129 return (scsipi_command(sc->sc_periph, 1130 (struct scsipi_generic *)&cmd, sizeof(cmd), 1131 NULL, 0, CHRETRIES, tmo, NULL, XS_CTL_IGNORE_ILLEGAL_REQUEST)); 1132} 1133 1134/* 1135 * Ask the device about itself and fill in the parameters in our 1136 * softc. 1137 */ 1138int 1139ch_get_params(sc, scsiflags) 1140 struct ch_softc *sc; 1141 int scsiflags; 1142{ 1143 struct scsi_mode_sense cmd; 1144 struct scsi_mode_sense_data { 1145 struct scsi_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 bzero(&cmd, sizeof(cmd)); 1159 bzero(&sense_data, sizeof(sense_data)); 1160 cmd.opcode = SCSI_MODE_SENSE; 1161 cmd.byte2 |= 0x08; /* disable block descriptors */ 1162 cmd.page = 0x1d; 1163 cmd.length = (sizeof(sense_data) & 0xff); 1164 error = scsipi_command(sc->sc_periph, 1165 (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data, 1166 sizeof(sense_data), CHRETRIES, 6000, NULL, 1167 scsiflags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK); 1168 if (error) { 1169 printf("%s: could not sense element address page\n", 1170 sc->sc_dev.dv_xname); 1171 return (error); 1172 } 1173 1174 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); 1175 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); 1176 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); 1177 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); 1178 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); 1179 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); 1180 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); 1181 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); 1182 1183 /* XXX ask for transport geometry page XXX */ 1184 1185 /* 1186 * Grab info from the capabilities page. 1187 */ 1188 bzero(&cmd, sizeof(cmd)); 1189 bzero(&sense_data, sizeof(sense_data)); 1190 cmd.opcode = SCSI_MODE_SENSE; 1191 /* 1192 * XXX: Note: not all changers can deal with disabled block descriptors 1193 */ 1194 cmd.byte2 = 0x08; /* disable block descriptors */ 1195 cmd.page = 0x1f; 1196 cmd.length = (sizeof(sense_data) & 0xff); 1197 error = scsipi_command(sc->sc_periph, 1198 (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data, 1199 sizeof(sense_data), CHRETRIES, 6000, NULL, 1200 scsiflags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK); 1201 if (error) { 1202 printf("%s: could not sense capabilities page\n", 1203 sc->sc_dev.dv_xname); 1204 return (error); 1205 } 1206 1207 bzero(sc->sc_movemask, sizeof(sc->sc_movemask)); 1208 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask)); 1209 moves = &sense_data.pages.cap.move_from_mt; 1210 exchanges = &sense_data.pages.cap.exchange_with_mt; 1211 for (from = CHET_MT; from <= CHET_DT; ++from) { 1212 sc->sc_movemask[from] = moves[from]; 1213 sc->sc_exchangemask[from] = exchanges[from]; 1214 } 1215 1216#ifdef CH_AUTOMATIC_IELEM_POLICY 1217 /* 1218 * If we need to do an Init-Element-Status, 1219 * do that now that we know what's in the changer. 1220 */ 1221 if ((scsiflags & XS_CTL_IGNORE_MEDIA_CHANGE) == 0) { 1222 if ((sc->sc_periph->periph_flags & PERIPH_MEDIA_LOADED) == 0) 1223 error = ch_ielem(sc); 1224 if (error == 0) 1225 sc->sc_periph->periph_flags |= PERIPH_MEDIA_LOADED; 1226 else 1227 sc->sc_periph->periph_flags &= ~PERIPH_MEDIA_LOADED; 1228 } 1229#endif 1230 return (error); 1231} 1232 1233void 1234ch_get_quirks(sc, inqbuf) 1235 struct ch_softc *sc; 1236 struct scsipi_inquiry_pattern *inqbuf; 1237{ 1238 struct chquirk *match; 1239 int priority; 1240 1241 sc->sc_settledelay = 0; 1242 1243 match = (struct chquirk *)scsipi_inqmatch(inqbuf, 1244 (caddr_t)chquirks, 1245 sizeof(chquirks) / sizeof(chquirks[0]), 1246 sizeof(chquirks[0]), &priority); 1247 if (priority != 0) 1248 sc->sc_settledelay = match->cq_settledelay; 1249} 1250 1251int 1252ch_map_element(sc, elem, typep, unitp) 1253 struct ch_softc *sc; 1254 u_int16_t elem; 1255 int *typep, *unitp; 1256{ 1257 int chet; 1258 1259 for (chet = CHET_MT; chet <= CHET_DT; chet++) { 1260 if (elem >= sc->sc_firsts[chet] && 1261 elem < (sc->sc_firsts[chet] + sc->sc_counts[chet])) { 1262 *typep = chet; 1263 *unitp = elem - sc->sc_firsts[chet]; 1264 return (1); 1265 } 1266 } 1267 return (0); 1268} 1269 1270void 1271ch_voltag_convert_in(sv, cv) 1272 const struct changer_volume_tag *sv; 1273 struct changer_voltag *cv; 1274{ 1275 int i; 1276 1277 memset(cv, 0, sizeof(struct changer_voltag)); 1278 1279 /* 1280 * Copy the volume tag string from the SCSI representation. 1281 * Per the SCSI-2 spec, we stop at the first blank character. 1282 */ 1283 for (i = 0; i < sizeof(sv->volid); i++) { 1284 if (sv->volid[i] == ' ') 1285 break; 1286 cv->cv_tag[i] = sv->volid[i]; 1287 } 1288 cv->cv_tag[i] = '\0'; 1289 1290 cv->cv_serial = _2btol(sv->volseq); 1291} 1292 1293int 1294ch_voltag_convert_out(cv, sv) 1295 const struct changer_voltag *cv; 1296 struct changer_volume_tag *sv; 1297{ 1298 int i; 1299 1300 memset(sv, ' ', sizeof(struct changer_volume_tag)); 1301 1302 for (i = 0; i < sizeof(sv->volid); i++) { 1303 if (cv->cv_tag[i] == '\0') 1304 break; 1305 /* 1306 * Limit the character set to what is suggested in 1307 * the SCSI-2 spec. 1308 */ 1309 if ((cv->cv_tag[i] < '0' || cv->cv_tag[i] > '9') && 1310 (cv->cv_tag[i] < 'A' || cv->cv_tag[i] > 'Z') && 1311 (cv->cv_tag[i] != '_')) 1312 return (EINVAL); 1313 sv->volid[i] = cv->cv_tag[i]; 1314 } 1315 1316 _lto2b(cv->cv_serial, sv->volseq); 1317 1318 return (0); 1319} 1320