Annotation of sys/scsi/ch.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ch.c,v 1.32 2006/11/28 16:56:50 dlg Exp $ */
! 2: /* $NetBSD: ch.c,v 1.26 1997/02/21 22:06:52 thorpej Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
! 6: * All rights reserved.
! 7: *
! 8: * Partially based on an autochanger driver written by Stefan Grefen
! 9: * and on an autochanger driver written by the Systems Programming Group
! 10: * at the University of Utah Computer Science Department.
! 11: *
! 12: * Redistribution and use in source and binary forms, with or without
! 13: * modification, are permitted provided that the following conditions
! 14: * are met:
! 15: * 1. Redistributions of source code must retain the above copyright
! 16: * notice, this list of conditions and the following disclaimer.
! 17: * 2. Redistributions in binary form must reproduce the above copyright
! 18: * notice, this list of conditions and the following disclaimer in the
! 19: * documentation and/or other materials provided with the distribution.
! 20: * 3. All advertising materials mentioning features or use of this software
! 21: * must display the following acknowledgements:
! 22: * This product includes software developed by Jason R. Thorpe
! 23: * for And Communications, http://www.and.com/
! 24: * 4. The name of the author may not be used to endorse or promote products
! 25: * derived from this software without specific prior written permission.
! 26: *
! 27: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 28: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 29: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 30: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 31: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
! 32: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
! 33: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
! 34: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
! 35: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 36: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 37: * SUCH DAMAGE.
! 38: */
! 39:
! 40: #include <sys/param.h>
! 41: #include <sys/systm.h>
! 42: #include <sys/errno.h>
! 43: #include <sys/ioctl.h>
! 44: #include <sys/buf.h>
! 45: #include <sys/proc.h>
! 46: #include <sys/user.h>
! 47: #include <sys/chio.h>
! 48: #include <sys/device.h>
! 49: #include <sys/malloc.h>
! 50: #include <sys/conf.h>
! 51: #include <sys/fcntl.h>
! 52:
! 53: #include <scsi/scsi_all.h>
! 54: #include <scsi/scsi_changer.h>
! 55: #include <scsi/scsiconf.h>
! 56:
! 57: #define CHRETRIES 2
! 58: #define CHUNIT(x) (minor((x)))
! 59:
! 60: struct ch_softc {
! 61: struct device sc_dev; /* generic device info */
! 62: struct scsi_link *sc_link; /* link in the SCSI bus */
! 63:
! 64: int sc_picker; /* current picker */
! 65:
! 66: /*
! 67: * The following information is obtained from the
! 68: * element address assignment page.
! 69: */
! 70: int sc_firsts[4]; /* firsts, indexed by CHET_* */
! 71: int sc_counts[4]; /* counts, indexed by CHET_* */
! 72:
! 73: /*
! 74: * The following mask defines the legal combinations
! 75: * of elements for the MOVE MEDIUM command.
! 76: */
! 77: u_int8_t sc_movemask[4];
! 78:
! 79: /*
! 80: * As above, but for EXCHANGE MEDIUM.
! 81: */
! 82: u_int8_t sc_exchangemask[4];
! 83:
! 84: int flags; /* misc. info */
! 85:
! 86: /*
! 87: * Quirks; see below.
! 88: */
! 89: int sc_settledelay; /* delay for settle */
! 90:
! 91: };
! 92:
! 93: /* sc_flags */
! 94: #define CHF_ROTATE 0x01 /* picker can rotate */
! 95:
! 96: /* Autoconfiguration glue */
! 97: int chmatch(struct device *, void *, void *);
! 98: void chattach(struct device *, struct device *, void *);
! 99:
! 100: struct cfattach ch_ca = {
! 101: sizeof(struct ch_softc), chmatch, chattach
! 102: };
! 103:
! 104: struct cfdriver ch_cd = {
! 105: NULL, "ch", DV_DULL
! 106: };
! 107:
! 108: const struct scsi_inquiry_pattern ch_patterns[] = {
! 109: {T_CHANGER, T_REMOV,
! 110: "", "", ""},
! 111: };
! 112:
! 113: int ch_move(struct ch_softc *, struct changer_move *);
! 114: int ch_exchange(struct ch_softc *, struct changer_exchange *);
! 115: int ch_position(struct ch_softc *, struct changer_position *);
! 116: int ch_usergetelemstatus(struct ch_softc *,
! 117: struct changer_element_status_request *);
! 118: int ch_getelemstatus(struct ch_softc *, int, int, caddr_t, size_t, int);
! 119: int ch_get_params(struct ch_softc *, int);
! 120: int ch_interpret_sense(struct scsi_xfer *xs);
! 121: void ch_get_quirks(struct ch_softc *, struct scsi_inquiry_data *);
! 122:
! 123: /* SCSI glue */
! 124: struct scsi_device ch_switch = {
! 125: ch_interpret_sense,
! 126: NULL,
! 127: NULL,
! 128: NULL
! 129: };
! 130:
! 131: /*
! 132: * SCSI changer quirks.
! 133: */
! 134: struct chquirk {
! 135: struct scsi_inquiry_pattern cq_match; /* device id pattern */
! 136: int cq_settledelay; /* settle delay, in seconds */
! 137: };
! 138:
! 139: struct chquirk chquirks[] = {
! 140: {{T_CHANGER, T_REMOV,
! 141: "SPECTRA", "9000", "0200"},
! 142: 75},
! 143: };
! 144:
! 145: int
! 146: chmatch(parent, match, aux)
! 147: struct device *parent;
! 148: void *match, *aux;
! 149: {
! 150: struct scsi_attach_args *sa = aux;
! 151: int priority;
! 152:
! 153: (void)scsi_inqmatch(sa->sa_inqbuf,
! 154: ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]),
! 155: sizeof(ch_patterns[0]), &priority);
! 156:
! 157: return (priority);
! 158: }
! 159:
! 160: void
! 161: chattach(parent, self, aux)
! 162: struct device *parent, *self;
! 163: void *aux;
! 164: {
! 165: struct ch_softc *sc = (struct ch_softc *)self;
! 166: struct scsi_attach_args *sa = aux;
! 167: struct scsi_link *link = sa->sa_sc_link;
! 168:
! 169: /* Glue into the SCSI bus */
! 170: sc->sc_link = link;
! 171: link->device = &ch_switch;
! 172: link->device_softc = sc;
! 173: link->openings = 1;
! 174:
! 175: printf("\n");
! 176:
! 177: /*
! 178: * Store our our device's quirks.
! 179: */
! 180: ch_get_quirks(sc, sa->sa_inqbuf);
! 181:
! 182: }
! 183:
! 184: int
! 185: chopen(dev, flags, fmt, p)
! 186: dev_t dev;
! 187: int flags, fmt;
! 188: struct proc *p;
! 189: {
! 190: struct ch_softc *sc;
! 191: int oldcounts[4];
! 192: int i, unit, error = 0;
! 193:
! 194: unit = CHUNIT(dev);
! 195: if ((unit >= ch_cd.cd_ndevs) ||
! 196: ((sc = ch_cd.cd_devs[unit]) == NULL))
! 197: return (ENXIO);
! 198:
! 199: /*
! 200: * Only allow one open at a time.
! 201: */
! 202: if (sc->sc_link->flags & SDEV_OPEN)
! 203: return (EBUSY);
! 204:
! 205: sc->sc_link->flags |= SDEV_OPEN;
! 206:
! 207: /*
! 208: * Absorb any unit attention errors. We must notice
! 209: * "Not ready" errors as a changer will report "In the
! 210: * process of getting ready" any time it must rescan
! 211: * itself to determine the state of the changer.
! 212: */
! 213: error = scsi_test_unit_ready(sc->sc_link, TEST_READY_RETRIES,
! 214: SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
! 215: if (error)
! 216: goto bad;
! 217:
! 218: /*
! 219: * Get information about the device. Save old information
! 220: * so we can decide whether to be verbose about new parameters.
! 221: */
! 222: for (i = 0; i < 4; i++) {
! 223: oldcounts[i] = sc->sc_counts[i];
! 224: }
! 225: error = ch_get_params(sc, scsi_autoconf);
! 226: if (error)
! 227: goto bad;
! 228:
! 229: for (i = 0; i < 4; i++) {
! 230: if (oldcounts[i] != sc->sc_counts[i]) {
! 231: break;
! 232: }
! 233: }
! 234: if (i < 4) {
! 235: #ifdef CHANGER_DEBUG
! 236: #define PLURAL(c) (c) == 1 ? "" : "s"
! 237: printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
! 238: sc->sc_dev.dv_xname,
! 239: sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
! 240: sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
! 241: sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
! 242: sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
! 243: #undef PLURAL
! 244: printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
! 245: sc->sc_dev.dv_xname,
! 246: sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
! 247: sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
! 248: printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
! 249: sc->sc_dev.dv_xname,
! 250: sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
! 251: sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
! 252: #endif /* CHANGER_DEBUG */
! 253: }
! 254:
! 255: /* Default the current picker. */
! 256: sc->sc_picker = sc->sc_firsts[CHET_MT];
! 257:
! 258: return (0);
! 259:
! 260: bad:
! 261: sc->sc_link->flags &= ~SDEV_OPEN;
! 262: return (error);
! 263: }
! 264:
! 265: int
! 266: chclose(dev, flags, fmt, p)
! 267: dev_t dev;
! 268: int flags, fmt;
! 269: struct proc *p;
! 270: {
! 271: struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
! 272:
! 273: sc->sc_link->flags &= ~SDEV_OPEN;
! 274: return (0);
! 275: }
! 276:
! 277: int
! 278: chioctl(dev, cmd, data, flags, p)
! 279: dev_t dev;
! 280: u_long cmd;
! 281: caddr_t data;
! 282: int flags;
! 283: struct proc *p;
! 284: {
! 285: struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
! 286: int error = 0;
! 287:
! 288: /*
! 289: * If this command can change the device's state, we must
! 290: * have the device open for writing.
! 291: */
! 292: switch (cmd) {
! 293: case CHIOGPICKER:
! 294: case CHIOGPARAMS:
! 295: case CHIOGSTATUS:
! 296: break;
! 297:
! 298: default:
! 299: if ((flags & FWRITE) == 0)
! 300: return (EBADF);
! 301: }
! 302:
! 303: switch (cmd) {
! 304: case CHIOMOVE:
! 305: error = ch_move(sc, (struct changer_move *)data);
! 306: break;
! 307:
! 308: case CHIOEXCHANGE:
! 309: error = ch_exchange(sc, (struct changer_exchange *)data);
! 310: break;
! 311:
! 312: case CHIOPOSITION:
! 313: error = ch_position(sc, (struct changer_position *)data);
! 314: break;
! 315:
! 316: case CHIOGPICKER:
! 317: *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT];
! 318: break;
! 319:
! 320: case CHIOSPICKER: {
! 321: int new_picker = *(int *)data;
! 322:
! 323: if (new_picker > (sc->sc_counts[CHET_MT] - 1))
! 324: return (EINVAL);
! 325: sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker;
! 326: break; }
! 327:
! 328: case CHIOGPARAMS: {
! 329: struct changer_params *cp = (struct changer_params *)data;
! 330:
! 331: cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT];
! 332: cp->cp_npickers = sc->sc_counts[CHET_MT];
! 333: cp->cp_nslots = sc->sc_counts[CHET_ST];
! 334: cp->cp_nportals = sc->sc_counts[CHET_IE];
! 335: cp->cp_ndrives = sc->sc_counts[CHET_DT];
! 336: break; }
! 337:
! 338: case CHIOGSTATUS: {
! 339: struct changer_element_status_request *cesr =
! 340: (struct changer_element_status_request *)data;
! 341:
! 342: error = ch_usergetelemstatus(sc, cesr);
! 343: break; }
! 344:
! 345: /* Implement prevent/allow? */
! 346:
! 347: default:
! 348: error = scsi_do_ioctl(sc->sc_link, dev, cmd, data,
! 349: flags, p);
! 350: break;
! 351: }
! 352:
! 353: return (error);
! 354: }
! 355:
! 356: int
! 357: ch_move(sc, cm)
! 358: struct ch_softc *sc;
! 359: struct changer_move *cm;
! 360: {
! 361: struct scsi_move_medium cmd;
! 362: u_int16_t fromelem, toelem;
! 363:
! 364: /*
! 365: * Check arguments.
! 366: */
! 367: if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
! 368: return (EINVAL);
! 369: if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
! 370: (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
! 371: return (ENODEV);
! 372:
! 373: /*
! 374: * Check the request against the changer's capabilities.
! 375: */
! 376: if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
! 377: return (EINVAL);
! 378:
! 379: /*
! 380: * Calculate the source and destination elements.
! 381: */
! 382: fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
! 383: toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
! 384:
! 385: /*
! 386: * Build the SCSI command.
! 387: */
! 388: bzero(&cmd, sizeof(cmd));
! 389: cmd.opcode = MOVE_MEDIUM;
! 390: _lto2b(sc->sc_picker, cmd.tea);
! 391: _lto2b(fromelem, cmd.src);
! 392: _lto2b(toelem, cmd.dst);
! 393: if (cm->cm_flags & CM_INVERT)
! 394: cmd.flags |= MOVE_MEDIUM_INVERT;
! 395:
! 396: /*
! 397: * Send command to changer.
! 398: */
! 399: return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
! 400: sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
! 401: }
! 402:
! 403: int
! 404: ch_exchange(sc, ce)
! 405: struct ch_softc *sc;
! 406: struct changer_exchange *ce;
! 407: {
! 408: struct scsi_exchange_medium cmd;
! 409: u_int16_t src, dst1, dst2;
! 410:
! 411: /*
! 412: * Check arguments.
! 413: */
! 414: if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
! 415: (ce->ce_sdsttype > CHET_DT))
! 416: return (EINVAL);
! 417: if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
! 418: (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
! 419: (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
! 420: return (ENODEV);
! 421:
! 422: /*
! 423: * Check the request against the changer's capabilities.
! 424: */
! 425: if (((sc->sc_exchangemask[ce->ce_srctype] &
! 426: (1 << ce->ce_fdsttype)) == 0) ||
! 427: ((sc->sc_exchangemask[ce->ce_fdsttype] &
! 428: (1 << ce->ce_sdsttype)) == 0))
! 429: return (EINVAL);
! 430:
! 431: /*
! 432: * Calculate the source and destination elements.
! 433: */
! 434: src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
! 435: dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
! 436: dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
! 437:
! 438: /*
! 439: * Build the SCSI command.
! 440: */
! 441: bzero(&cmd, sizeof(cmd));
! 442: cmd.opcode = EXCHANGE_MEDIUM;
! 443: _lto2b(sc->sc_picker, cmd.tea);
! 444: _lto2b(src, cmd.src);
! 445: _lto2b(dst1, cmd.fdst);
! 446: _lto2b(dst2, cmd.sdst);
! 447: if (ce->ce_flags & CE_INVERT1)
! 448: cmd.flags |= EXCHANGE_MEDIUM_INV1;
! 449: if (ce->ce_flags & CE_INVERT2)
! 450: cmd.flags |= EXCHANGE_MEDIUM_INV2;
! 451:
! 452: /*
! 453: * Send command to changer.
! 454: */
! 455: return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
! 456: sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
! 457: }
! 458:
! 459: int
! 460: ch_position(sc, cp)
! 461: struct ch_softc *sc;
! 462: struct changer_position *cp;
! 463: {
! 464: struct scsi_position_to_element cmd;
! 465: u_int16_t dst;
! 466:
! 467: /*
! 468: * Check arguments.
! 469: */
! 470: if (cp->cp_type > CHET_DT)
! 471: return (EINVAL);
! 472: if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
! 473: return (ENODEV);
! 474:
! 475: /*
! 476: * Calculate the destination element.
! 477: */
! 478: dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
! 479:
! 480: /*
! 481: * Build the SCSI command.
! 482: */
! 483: bzero(&cmd, sizeof(cmd));
! 484: cmd.opcode = POSITION_TO_ELEMENT;
! 485: _lto2b(sc->sc_picker, cmd.tea);
! 486: _lto2b(dst, cmd.dst);
! 487: if (cp->cp_flags & CP_INVERT)
! 488: cmd.flags |= POSITION_TO_ELEMENT_INVERT;
! 489:
! 490: /*
! 491: * Send command to changer.
! 492: */
! 493: return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
! 494: sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
! 495: }
! 496:
! 497: /*
! 498: * Copy a volume tag to a volume_tag struct, converting SCSI byte order
! 499: * to host native byte order in the volume serial number. The volume
! 500: * label as returned by the changer is transferred to user mode as
! 501: * nul-terminated string. Volume labels are truncated at the first
! 502: * space, as suggested by SCSI-2.
! 503: */
! 504: static void
! 505: copy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
! 506: {
! 507: int i;
! 508:
! 509: for (i=0; i<CH_VOLTAG_MAXLEN; i++) {
! 510: char c = voltag->vif[i];
! 511: if (c && c != ' ')
! 512: uvoltag->cv_volid[i] = c;
! 513: else
! 514: break;
! 515: }
! 516: uvoltag->cv_volid[i] = '\0';
! 517: uvoltag->cv_serial = _2btol(voltag->vsn);
! 518: }
! 519:
! 520: /*
! 521: * Copy an an element status descriptor to a user-mode
! 522: * changer_element_status structure.
! 523: */
! 524: static void
! 525: copy_element_status(int flags, struct read_element_status_descriptor *desc,
! 526: struct changer_element_status *ces)
! 527: {
! 528: ces->ces_flags = desc->flags1;
! 529:
! 530: if (flags & READ_ELEMENT_STATUS_PVOLTAG)
! 531: copy_voltag(&ces->ces_pvoltag, &desc->pvoltag);
! 532: if (flags & READ_ELEMENT_STATUS_AVOLTAG)
! 533: copy_voltag(&ces->ces_avoltag, &desc->avoltag);
! 534: }
! 535:
! 536: /*
! 537: * Perform a READ ELEMENT STATUS on behalf of the user, and return to
! 538: * the user only the data the user is interested in (i.e. an array of
! 539: * changer_element_status structures)
! 540: */
! 541: int
! 542: ch_usergetelemstatus(sc, cesr)
! 543: struct ch_softc *sc;
! 544: struct changer_element_status_request *cesr;
! 545: {
! 546: struct changer_element_status *user_data = NULL;
! 547: struct read_element_status_header *st_hdr;
! 548: struct read_element_status_page_header *pg_hdr;
! 549: struct read_element_status_descriptor *desc;
! 550: caddr_t data = NULL;
! 551: size_t size, desclen, udsize;
! 552: int chet = cesr->cesr_type;
! 553: int avail, i, error = 0;
! 554: int want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;
! 555:
! 556: /*
! 557: * If there are no elements of the requested type in the changer,
! 558: * the request is invalid.
! 559: */
! 560: if (sc->sc_counts[chet] == 0)
! 561: return (EINVAL);
! 562:
! 563: /*
! 564: * Request one descriptor for the given element type. This
! 565: * is used to determine the size of the descriptor so that
! 566: * we can allocate enough storage for all of them. We assume
! 567: * that the first one can fit into 1k.
! 568: */
! 569: data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
! 570: error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024,
! 571: want_voltags);
! 572: if (error)
! 573: goto done;
! 574:
! 575: st_hdr = (struct read_element_status_header *)data;
! 576: pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
! 577: sizeof(struct read_element_status_header));
! 578: desclen = _2btol(pg_hdr->edl);
! 579:
! 580: size = sizeof(struct read_element_status_header) +
! 581: sizeof(struct read_element_status_page_header) +
! 582: (desclen * sc->sc_counts[chet]);
! 583:
! 584: /*
! 585: * Reallocate storage for descriptors and get them from the
! 586: * device.
! 587: */
! 588: free(data, M_DEVBUF);
! 589: data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
! 590: error = ch_getelemstatus(sc, sc->sc_firsts[chet],
! 591: sc->sc_counts[chet], data, size, want_voltags);
! 592: if (error)
! 593: goto done;
! 594:
! 595: /*
! 596: * Fill in the user status array.
! 597: */
! 598: st_hdr = (struct read_element_status_header *)data;
! 599: pg_hdr = (struct read_element_status_page_header *)((u_long)data +
! 600: sizeof(struct read_element_status_header));
! 601:
! 602: avail = _2btol(st_hdr->count);
! 603: if (avail != sc->sc_counts[chet]) {
! 604: error = EINVAL;
! 605: goto done;
! 606: }
! 607: udsize = avail * sizeof(struct changer_element_status);
! 608:
! 609: user_data = malloc(udsize, M_DEVBUF, M_WAITOK);
! 610: bzero(user_data, udsize);
! 611:
! 612: desc = (struct read_element_status_descriptor *)((u_long)data +
! 613: sizeof(struct read_element_status_header) +
! 614: sizeof(struct read_element_status_page_header));
! 615: for (i = 0; i < avail; ++i) {
! 616: struct changer_element_status *ces = &(user_data[i]);
! 617: copy_element_status(pg_hdr->flags, desc, ces);
! 618: (u_long)desc += desclen;
! 619: }
! 620:
! 621: /* Copy array out to userspace. */
! 622: error = copyout(user_data, cesr->cesr_data, udsize);
! 623:
! 624: done:
! 625: if (data != NULL)
! 626: free(data, M_DEVBUF);
! 627: if (user_data != NULL)
! 628: free(user_data, M_DEVBUF);
! 629: return (error);
! 630: }
! 631:
! 632: int
! 633: ch_getelemstatus(sc, first, count, data, datalen, voltag)
! 634: struct ch_softc *sc;
! 635: int first;
! 636: int count;
! 637: caddr_t data;
! 638: size_t datalen;
! 639: int voltag;
! 640: {
! 641: struct scsi_read_element_status cmd;
! 642:
! 643: /*
! 644: * Build SCSI command.
! 645: */
! 646: bzero(&cmd, sizeof(cmd));
! 647: cmd.opcode = READ_ELEMENT_STATUS;
! 648: _lto2b(first, cmd.sea);
! 649: _lto2b(count, cmd.count);
! 650: _lto3b(datalen, cmd.len);
! 651: if (voltag)
! 652: cmd.byte2 |= READ_ELEMENT_STATUS_VOLTAG;
! 653:
! 654: /*
! 655: * Send command to changer.
! 656: */
! 657: return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
! 658: sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, SCSI_DATA_IN));
! 659: }
! 660:
! 661:
! 662: /*
! 663: * Ask the device about itself and fill in the parameters in our
! 664: * softc.
! 665: */
! 666: int
! 667: ch_get_params(sc, flags)
! 668: struct ch_softc *sc;
! 669: int flags;
! 670: {
! 671: union scsi_mode_sense_buf *data;
! 672: struct page_element_address_assignment *ea;
! 673: struct page_device_capabilities *cap;
! 674: int error, from;
! 675: u_int8_t *moves, *exchanges;
! 676:
! 677: data = malloc(sizeof(*data), M_TEMP, M_NOWAIT);
! 678: if (data == NULL)
! 679: return (ENOMEM);
! 680:
! 681: /*
! 682: * Grab info from the element address assignment page (0x1d).
! 683: */
! 684: error = scsi_do_mode_sense(sc->sc_link, 0x1d, data,
! 685: (void **)&ea, NULL, NULL, NULL, sizeof(*ea), flags, NULL);
! 686: if (error == 0 && ea == NULL)
! 687: error = EIO;
! 688: if (error != 0) {
! 689: #ifdef CHANGER_DEBUG
! 690: printf("%s: could not sense element address page\n",
! 691: sc->sc_dev.dv_xname);
! 692: #endif
! 693: free(data, M_TEMP);
! 694: return (error);
! 695: }
! 696:
! 697: sc->sc_firsts[CHET_MT] = _2btol(ea->mtea);
! 698: sc->sc_counts[CHET_MT] = _2btol(ea->nmte);
! 699: sc->sc_firsts[CHET_ST] = _2btol(ea->fsea);
! 700: sc->sc_counts[CHET_ST] = _2btol(ea->nse);
! 701: sc->sc_firsts[CHET_IE] = _2btol(ea->fieea);
! 702: sc->sc_counts[CHET_IE] = _2btol(ea->niee);
! 703: sc->sc_firsts[CHET_DT] = _2btol(ea->fdtea);
! 704: sc->sc_counts[CHET_DT] = _2btol(ea->ndte);
! 705:
! 706: /* XXX Ask for transport geometry page. */
! 707:
! 708: /*
! 709: * Grab info from the capabilities page (0x1f).
! 710: */
! 711: error = scsi_do_mode_sense(sc->sc_link, 0x1f, data,
! 712: (void **)&cap, NULL, NULL, NULL, sizeof(*cap), flags, NULL);
! 713: if (cap == NULL)
! 714: error = EIO;
! 715: if (error != 0) {
! 716: #ifdef CHANGER_DEBUG
! 717: printf("%s: could not sense capabilities page\n",
! 718: sc->sc_dev.dv_xname);
! 719: #endif
! 720: free(data, M_TEMP);
! 721: return (error);
! 722: }
! 723:
! 724: bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
! 725: bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
! 726: moves = &cap->move_from_mt;
! 727: exchanges = &cap->exchange_with_mt;
! 728: for (from = CHET_MT; from <= CHET_DT; ++from) {
! 729: sc->sc_movemask[from] = moves[from];
! 730: sc->sc_exchangemask[from] = exchanges[from];
! 731: }
! 732:
! 733: sc->sc_link->flags |= SDEV_MEDIA_LOADED;
! 734: free(data, M_TEMP);
! 735: return (0);
! 736: }
! 737:
! 738: void
! 739: ch_get_quirks(sc, inqbuf)
! 740: struct ch_softc *sc;
! 741: struct scsi_inquiry_data *inqbuf;
! 742: {
! 743: const struct chquirk *match;
! 744: int priority;
! 745:
! 746: sc->sc_settledelay = 0;
! 747:
! 748: match = (const struct chquirk *)scsi_inqmatch(inqbuf,
! 749: (caddr_t)chquirks,
! 750: sizeof(chquirks) / sizeof(chquirks[0]),
! 751: sizeof(chquirks[0]), &priority);
! 752: if (priority != 0) {
! 753: sc->sc_settledelay = match->cq_settledelay;
! 754: }
! 755: }
! 756:
! 757: /*
! 758: * Look at the returned sense and act on the error and detirmine
! 759: * The unix error number to pass back... (0 = report no error)
! 760: * (-1 = continue processing)
! 761: */
! 762: int
! 763: ch_interpret_sense(xs)
! 764: struct scsi_xfer *xs;
! 765: {
! 766: struct scsi_sense_data *sense = &xs->sense;
! 767: struct scsi_link *sc_link = xs->sc_link;
! 768: u_int8_t serr = sense->error_code & SSD_ERRCODE;
! 769: u_int8_t skey = sense->flags & SSD_KEY;
! 770:
! 771: if (((sc_link->flags & SDEV_OPEN) == 0) ||
! 772: (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
! 773: return (EJUSTRETURN); /* let the generic code handle it */
! 774:
! 775: switch (skey) {
! 776:
! 777: /*
! 778: * We do custom processing in ch for the unit becoming ready case.
! 779: * in this case we do not allow xs->retries to be decremented
! 780: * only on the "Unit Becoming Ready" case. This is because tape
! 781: * changers report "Unit Becoming Ready" when they rescan their
! 782: * state (i.e. when the door got opened) and can take a long time
! 783: * for large units. Rather than having a massive timeout for
! 784: * all operations (which would cause other problems) we allow
! 785: * changers to wait (but be interruptable with Ctrl-C) forever
! 786: * as long as they are reporting that they are becoming ready.
! 787: * all other cases are handled as per the default.
! 788: */
! 789: case SKEY_NOT_READY:
! 790: if ((xs->flags & SCSI_IGNORE_NOT_READY) != 0)
! 791: return (0);
! 792: switch (ASC_ASCQ(sense)) {
! 793: case SENSE_NOT_READY_BECOMING_READY:
! 794: SC_DEBUG(sc_link, SDEV_DB1, ("not ready: busy (%#x)\n",
! 795: sense->add_sense_code_qual));
! 796: /* don't count this as a retry */
! 797: xs->retries++;
! 798: return (scsi_delay(xs, 1));
! 799: default:
! 800: return (EJUSTRETURN);
! 801: }
! 802: default:
! 803: return (EJUSTRETURN);
! 804: }
! 805: }
CVSweb