1 /* $NetBSD: ch.c,v 1.21 1996/04/19 00:02:29 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> 5 * All rights reserved. 6 * 7 * Partially based on an autochanger driver written by Stefan Grefen 8 * and on an autochanger driver written by the Systems Programming Group 9 * at the University of Utah Computer Science Department. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgements: 21 * This product includes software developed by Jason R. Thorpe 22 * for And Communications, http://www.and.com/ 23 * 4. The name of the author may not be used to endorse or promote products 24 * derived from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/errno.h> 42 #include <sys/ioctl.h> 43 #include <sys/buf.h> 44 #include <sys/proc.h> 45 #include <sys/user.h> 46 #include <sys/chio.h> 47 #include <sys/device.h> 48 #include <sys/malloc.h> 49 #include <sys/conf.h> 50 51 #include <scsi/scsi_all.h> 52 #include <scsi/scsi_changer.h> 53 #include <scsi/scsiconf.h> 54 55 #define CHRETRIES 2 56 #define CHUNIT(x) (minor((x))) 57 58 struct ch_softc { 59 struct device sc_dev; /* generic device info */ 60 struct scsi_link *sc_link; /* link in the SCSI bus */ 61 62 int sc_picker; /* current picker */ 63 64 /* 65 * The following information is obtained from the 66 * element address assignment page. 67 */ 68 int sc_firsts[4]; /* firsts, indexed by CHET_* */ 69 int sc_counts[4]; /* counts, indexed by CHET_* */ 70 71 /* 72 * The following mask defines the legal combinations 73 * of elements for the MOVE MEDIUM command. 74 */ 75 u_int8_t sc_movemask[4]; 76 77 /* 78 * As above, but for EXCHANGE MEDIUM. 79 */ 80 u_int8_t sc_exchangemask[4]; 81 82 int flags; /* misc. info */ 83 }; 84 85 /* sc_flags */ 86 #define CHF_ROTATE 0x01 /* picker can rotate */ 87 88 /* Autoconfiguration glue */ 89 int chmatch __P((struct device *, void *, void *)); 90 void chattach __P((struct device *, struct device *, void *)); 91 92 struct cfattach ch_ca = { 93 sizeof(struct ch_softc), chmatch, chattach 94 }; 95 96 struct cfdriver ch_cd = { 97 NULL, "ch", DV_DULL 98 }; 99 100 struct scsi_inquiry_pattern ch_patterns[] = { 101 {T_CHANGER, T_REMOV, 102 "", "", ""}, 103 }; 104 105 /* SCSI glue */ 106 struct scsi_device ch_switch = { 107 NULL, NULL, NULL, NULL 108 }; 109 110 int ch_move __P((struct ch_softc *, struct changer_move *)); 111 int ch_exchange __P((struct ch_softc *, struct changer_exchange *)); 112 int ch_position __P((struct ch_softc *, struct changer_position *)); 113 int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); 114 int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t)); 115 int ch_get_params __P((struct ch_softc *, int)); 116 117 int 118 chmatch(parent, match, aux) 119 struct device *parent; 120 void *match, *aux; 121 { 122 struct scsibus_attach_args *sa = aux; 123 int priority; 124 125 (void)scsi_inqmatch(sa->sa_inqbuf, 126 (caddr_t)ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]), 127 sizeof(ch_patterns[0]), &priority); 128 129 return (priority); 130 } 131 132 void 133 chattach(parent, self, aux) 134 struct device *parent, *self; 135 void *aux; 136 { 137 struct ch_softc *sc = (struct ch_softc *)self; 138 struct scsibus_attach_args *sa = aux; 139 struct scsi_link *link = sa->sa_sc_link; 140 141 /* Glue into the SCSI bus */ 142 sc->sc_link = link; 143 link->device = &ch_switch; 144 link->device_softc = sc; 145 link->openings = 1; 146 147 printf("\n"); 148 149 /* 150 * Get information about the device. Note we can't use 151 * interrupts yet. 152 */ 153 if (ch_get_params(sc, SCSI_AUTOCONF)) 154 printf("%s: offline\n", sc->sc_dev.dv_xname); 155 else { 156 printf("%s: %d slot%s, %d drive%s, %d picker%s", 157 sc->sc_dev.dv_xname, 158 sc->sc_counts[CHET_ST], (sc->sc_counts[CHET_ST] > 1) ? 159 "s" : "", 160 sc->sc_counts[CHET_DT], (sc->sc_counts[CHET_DT] > 1) ? 161 "s" : "", 162 sc->sc_counts[CHET_MT], (sc->sc_counts[CHET_MT] > 1) ? 163 "s" : ""); 164 if (sc->sc_counts[CHET_IE]) 165 printf(", %d portal%s", sc->sc_counts[CHET_IE], 166 (sc->sc_counts[CHET_IE] > 1) ? "s" : ""); 167 printf("\n"); 168 #ifdef CHANGER_DEBUG 169 printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", 170 sc->sc_dev.dv_xname, 171 sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], 172 sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); 173 printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", 174 sc->sc_dev.dv_xname, 175 sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], 176 sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); 177 #endif /* CHANGER_DEBUG */ 178 } 179 180 /* Default the current picker. */ 181 sc->sc_picker = sc->sc_firsts[CHET_MT]; 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 unit, error = 0; 192 193 unit = CHUNIT(dev); 194 if ((unit >= ch_cd.cd_ndevs) || 195 ((sc = ch_cd.cd_devs[unit]) == NULL)) 196 return (ENXIO); 197 198 /* 199 * Only allow one open at a time. 200 */ 201 if (sc->sc_link->flags & SDEV_OPEN) 202 return (EBUSY); 203 204 sc->sc_link->flags |= SDEV_OPEN; 205 206 /* 207 * Absorb any unit attention errors. Ignore "not ready" 208 * since this might occur if e.g. a tape isn't actually 209 * loaded in the drive. 210 */ 211 error = scsi_test_unit_ready(sc->sc_link, 212 SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE); 213 if (error) 214 goto bad; 215 216 /* 217 * Make sure our parameters are up to date. 218 */ 219 if ((error = ch_get_params(sc, 0)) != 0) 220 goto bad; 221 222 return (0); 223 224 bad: 225 sc->sc_link->flags &= ~SDEV_OPEN; 226 return (error); 227 } 228 229 int 230 chclose(dev, flags, fmt, p) 231 dev_t dev; 232 int flags, fmt; 233 struct proc *p; 234 { 235 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 236 237 sc->sc_link->flags &= ~SDEV_OPEN; 238 return (0); 239 } 240 241 int 242 chioctl(dev, cmd, data, flags, p) 243 dev_t dev; 244 u_long cmd; 245 caddr_t data; 246 int flags; 247 struct proc *p; 248 { 249 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 250 int error = 0; 251 252 switch (cmd) { 253 case CHIOMOVE: 254 error = ch_move(sc, (struct changer_move *)data); 255 break; 256 257 case CHIOEXCHANGE: 258 error = ch_exchange(sc, (struct changer_exchange *)data); 259 break; 260 261 case CHIOPOSITION: 262 error = ch_position(sc, (struct changer_position *)data); 263 break; 264 265 case CHIOGPICKER: 266 *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; 267 break; 268 269 case CHIOSPICKER: { 270 int new_picker = *(int *)data; 271 272 if (new_picker > (sc->sc_counts[CHET_MT] - 1)) 273 return (EINVAL); 274 sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; 275 break; } 276 277 case CHIOGPARAMS: { 278 struct changer_params *cp = (struct changer_params *)data; 279 280 cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; 281 cp->cp_npickers = sc->sc_counts[CHET_MT]; 282 cp->cp_nslots = sc->sc_counts[CHET_ST]; 283 cp->cp_nportals = sc->sc_counts[CHET_IE]; 284 cp->cp_ndrives = sc->sc_counts[CHET_DT]; 285 break; } 286 287 case CHIOGSTATUS: { 288 struct changer_element_status *ces = 289 (struct changer_element_status *)data; 290 291 error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data); 292 break; } 293 294 /* Implement prevent/allow? */ 295 296 default: 297 error = scsi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p); 298 break; 299 } 300 301 return (error); 302 } 303 304 int 305 ch_move(sc, cm) 306 struct ch_softc *sc; 307 struct changer_move *cm; 308 { 309 struct scsi_move_medium cmd; 310 u_int16_t fromelem, toelem; 311 312 /* 313 * Check arguments. 314 */ 315 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) 316 return (EINVAL); 317 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || 318 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) 319 return (ENODEV); 320 321 /* 322 * Check the request against the changer's capabilities. 323 */ 324 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) 325 return (EINVAL); 326 327 /* 328 * Calculate the source and destination elements. 329 */ 330 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; 331 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; 332 333 /* 334 * Build the SCSI command. 335 */ 336 bzero(&cmd, sizeof(cmd)); 337 cmd.opcode = MOVE_MEDIUM; 338 _lto2b(sc->sc_picker, cmd.tea); 339 _lto2b(fromelem, cmd.src); 340 _lto2b(toelem, cmd.dst); 341 if (cm->cm_flags & CM_INVERT) 342 cmd.flags |= MOVE_MEDIUM_INVERT; 343 344 /* 345 * Send command to changer. 346 */ 347 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 348 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 349 } 350 351 int 352 ch_exchange(sc, ce) 353 struct ch_softc *sc; 354 struct changer_exchange *ce; 355 { 356 struct scsi_exchange_medium cmd; 357 u_int16_t src, dst1, dst2; 358 359 /* 360 * Check arguments. 361 */ 362 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || 363 (ce->ce_sdsttype > CHET_DT)) 364 return (EINVAL); 365 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || 366 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || 367 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) 368 return (ENODEV); 369 370 /* 371 * Check the request against the changer's capabilities. 372 */ 373 if (((sc->sc_exchangemask[ce->ce_srctype] & 374 (1 << ce->ce_fdsttype)) == 0) || 375 ((sc->sc_exchangemask[ce->ce_fdsttype] & 376 (1 << ce->ce_sdsttype)) == 0)) 377 return (EINVAL); 378 379 /* 380 * Calculate the source and destination elements. 381 */ 382 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; 383 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; 384 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; 385 386 /* 387 * Build the SCSI command. 388 */ 389 bzero(&cmd, sizeof(cmd)); 390 cmd.opcode = EXCHANGE_MEDIUM; 391 _lto2b(sc->sc_picker, cmd.tea); 392 _lto2b(src, cmd.src); 393 _lto2b(dst1, cmd.fdst); 394 _lto2b(dst2, cmd.sdst); 395 if (ce->ce_flags & CE_INVERT1) 396 cmd.flags |= EXCHANGE_MEDIUM_INV1; 397 if (ce->ce_flags & CE_INVERT2) 398 cmd.flags |= EXCHANGE_MEDIUM_INV2; 399 400 /* 401 * Send command to changer. 402 */ 403 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 404 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 405 } 406 407 int 408 ch_position(sc, cp) 409 struct ch_softc *sc; 410 struct changer_position *cp; 411 { 412 struct scsi_position_to_element cmd; 413 u_int16_t dst; 414 415 /* 416 * Check arguments. 417 */ 418 if (cp->cp_type > CHET_DT) 419 return (EINVAL); 420 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) 421 return (ENODEV); 422 423 /* 424 * Calculate the destination element. 425 */ 426 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; 427 428 /* 429 * Build the SCSI command. 430 */ 431 bzero(&cmd, sizeof(cmd)); 432 cmd.opcode = POSITION_TO_ELEMENT; 433 _lto2b(sc->sc_picker, cmd.tea); 434 _lto2b(dst, cmd.dst); 435 if (cp->cp_flags & CP_INVERT) 436 cmd.flags |= POSITION_TO_ELEMENT_INVERT; 437 438 /* 439 * Send command to changer. 440 */ 441 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 442 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 443 } 444 445 /* 446 * Perform a READ ELEMENT STATUS on behalf of the user, and return to 447 * the user only the data the user is interested in (i.e. an array of 448 * flags bytes). 449 */ 450 int 451 ch_usergetelemstatus(sc, chet, uptr) 452 struct ch_softc *sc; 453 int chet; 454 u_int8_t *uptr; 455 { 456 struct read_element_status_header *st_hdr; 457 struct read_element_status_page_header *pg_hdr; 458 struct read_element_status_descriptor *desc; 459 caddr_t data = NULL; 460 size_t size, desclen; 461 int avail, i, error = 0; 462 u_int8_t *user_data = NULL; 463 464 /* 465 * If there are no elements of the requested type in the changer, 466 * the request is invalid. 467 */ 468 if (sc->sc_counts[chet] == 0) 469 return (EINVAL); 470 471 /* 472 * Request one descriptor for the given element type. This 473 * is used to determine the size of the descriptor so that 474 * we can allocate enough storage for all of them. We assume 475 * that the first one can fit into 1k. 476 */ 477 data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK); 478 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024); 479 if (error) 480 goto done; 481 482 st_hdr = (struct read_element_status_header *)data; 483 pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr + 484 sizeof(struct read_element_status_header)); 485 desclen = _2btol(pg_hdr->edl); 486 487 size = sizeof(struct read_element_status_header) + 488 sizeof(struct read_element_status_page_header) + 489 (desclen * sc->sc_counts[chet]); 490 491 /* 492 * Reallocate storage for descriptors and get them from the 493 * device. 494 */ 495 free(data, M_DEVBUF); 496 data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK); 497 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 498 sc->sc_counts[chet], data, size); 499 if (error) 500 goto done; 501 502 /* 503 * Fill in the user status array. 504 */ 505 st_hdr = (struct read_element_status_header *)data; 506 avail = _2btol(st_hdr->count); 507 if (avail != sc->sc_counts[chet]) 508 printf("%s: warning, READ ELEMENT STATUS avail != count\n", 509 sc->sc_dev.dv_xname); 510 511 user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK); 512 513 desc = (struct read_element_status_descriptor *)((u_long)data + 514 sizeof(struct read_element_status_header) + 515 sizeof(struct read_element_status_page_header)); 516 for (i = 0; i < avail; ++i) { 517 user_data[i] = desc->flags1; 518 (u_long)desc += desclen; 519 } 520 521 /* Copy flags array out to userspace. */ 522 error = copyout(user_data, uptr, avail); 523 524 done: 525 if (data != NULL) 526 free(data, M_DEVBUF); 527 if (user_data != NULL) 528 free(user_data, M_DEVBUF); 529 return (error); 530 } 531 532 int 533 ch_getelemstatus(sc, first, count, data, datalen) 534 struct ch_softc *sc; 535 int first, count; 536 caddr_t data; 537 size_t datalen; 538 { 539 struct scsi_read_element_status cmd; 540 541 /* 542 * Build SCSI command. 543 */ 544 bzero(&cmd, sizeof(cmd)); 545 cmd.opcode = READ_ELEMENT_STATUS; 546 _lto2b(first, cmd.sea); 547 _lto2b(count, cmd.count); 548 _lto3b(datalen, cmd.len); 549 550 /* 551 * Send command to changer. 552 */ 553 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 554 sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, 0)); 555 } 556 557 558 /* 559 * Ask the device about itself and fill in the parameters in our 560 * softc. 561 */ 562 int 563 ch_get_params(sc, scsiflags) 564 struct ch_softc *sc; 565 int scsiflags; 566 { 567 struct scsi_mode_sense cmd; 568 struct scsi_mode_sense_data { 569 struct scsi_mode_header header; 570 union { 571 struct page_element_address_assignment ea; 572 struct page_transport_geometry_parameters tg; 573 struct page_device_capabilities cap; 574 } pages; 575 } sense_data; 576 int error, from; 577 u_int8_t *moves, *exchanges; 578 579 /* 580 * Grab info from the element address assignment page. 581 */ 582 bzero(&cmd, sizeof(cmd)); 583 bzero(&sense_data, sizeof(sense_data)); 584 cmd.opcode = MODE_SENSE; 585 cmd.byte2 |= 0x08; /* disable block descriptors */ 586 cmd.page = 0x1d; 587 cmd.length = (sizeof(sense_data) & 0xff); 588 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 589 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, 590 6000, NULL, scsiflags | SCSI_DATA_IN); 591 if (error) { 592 printf("%s: could not sense element address page\n", 593 sc->sc_dev.dv_xname); 594 return (error); 595 } 596 597 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); 598 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); 599 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); 600 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); 601 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); 602 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); 603 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); 604 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); 605 606 /* XXX ask for page trasport geom */ 607 608 /* 609 * Grab info from the capabilities page. 610 */ 611 bzero(&cmd, sizeof(cmd)); 612 bzero(&sense_data, sizeof(sense_data)); 613 cmd.opcode = MODE_SENSE; 614 cmd.byte2 |= 0x08; /* disable block descriptors */ 615 cmd.page = 0x1f; 616 cmd.length = (sizeof(sense_data) & 0xff); 617 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 618 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, 619 6000, NULL, scsiflags | SCSI_DATA_IN); 620 if (error) { 621 printf("%s: could not sense capabilities page\n", 622 sc->sc_dev.dv_xname); 623 return (error); 624 } 625 626 bzero(sc->sc_movemask, sizeof(sc->sc_movemask)); 627 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask)); 628 moves = &sense_data.pages.cap.move_from_mt; 629 exchanges = &sense_data.pages.cap.exchange_with_mt; 630 for (from = CHET_MT; from <= CHET_DT; ++from) { 631 sc->sc_movemask[from] = moves[from]; 632 sc->sc_exchangemask[from] = exchanges[from]; 633 } 634 635 sc->sc_link->flags |= SDEV_MEDIA_LOADED; 636 return (0); 637 } 638