ch.c revision 1.24
1/* $NetBSD: ch.c,v 1.24 1996/10/12 23:23:14 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#include <sys/fcntl.h> 51 52#include <scsi/scsi_all.h> 53#include <scsi/scsi_changer.h> 54#include <scsi/scsiconf.h> 55 56#define CHRETRIES 2 57#define CHUNIT(x) (minor((x))) 58 59struct ch_softc { 60 struct device sc_dev; /* generic device info */ 61 struct scsi_link *sc_link; /* link in the SCSI bus */ 62 63 int sc_picker; /* current picker */ 64 65 /* 66 * The following information is obtained from the 67 * element address assignment page. 68 */ 69 int sc_firsts[4]; /* firsts, indexed by CHET_* */ 70 int sc_counts[4]; /* counts, indexed by CHET_* */ 71 72 /* 73 * The following mask defines the legal combinations 74 * of elements for the MOVE MEDIUM command. 75 */ 76 u_int8_t sc_movemask[4]; 77 78 /* 79 * As above, but for EXCHANGE MEDIUM. 80 */ 81 u_int8_t sc_exchangemask[4]; 82 83 int flags; /* misc. info */ 84}; 85 86/* sc_flags */ 87#define CHF_ROTATE 0x01 /* picker can rotate */ 88 89/* Autoconfiguration glue */ 90int chmatch __P((struct device *, void *, void *)); 91void chattach __P((struct device *, struct device *, void *)); 92 93struct cfattach ch_ca = { 94 sizeof(struct ch_softc), chmatch, chattach 95}; 96 97struct cfdriver ch_cd = { 98 NULL, "ch", DV_DULL 99}; 100 101struct scsi_inquiry_pattern ch_patterns[] = { 102 {T_CHANGER, T_REMOV, 103 "", "", ""}, 104}; 105 106/* SCSI glue */ 107struct scsi_device ch_switch = { 108 NULL, NULL, NULL, NULL 109}; 110 111int ch_move __P((struct ch_softc *, struct changer_move *)); 112int ch_exchange __P((struct ch_softc *, struct changer_exchange *)); 113int ch_position __P((struct ch_softc *, struct changer_position *)); 114int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); 115int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t)); 116int ch_get_params __P((struct ch_softc *, int)); 117 118int 119chmatch(parent, match, aux) 120 struct device *parent; 121 void *match, *aux; 122{ 123 struct scsibus_attach_args *sa = aux; 124 int priority; 125 126 (void)scsi_inqmatch(sa->sa_inqbuf, 127 (caddr_t)ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]), 128 sizeof(ch_patterns[0]), &priority); 129 130 return (priority); 131} 132 133void 134chattach(parent, self, aux) 135 struct device *parent, *self; 136 void *aux; 137{ 138 struct ch_softc *sc = (struct ch_softc *)self; 139 struct scsibus_attach_args *sa = aux; 140 struct scsi_link *link = sa->sa_sc_link; 141 142 /* Glue into the SCSI bus */ 143 sc->sc_link = link; 144 link->device = &ch_switch; 145 link->device_softc = sc; 146 link->openings = 1; 147 148 printf("\n"); 149 150 /* 151 * Get information about the device. Note we can't use 152 * interrupts yet. 153 */ 154 if (ch_get_params(sc, SCSI_AUTOCONF)) 155 printf("%s: offline\n", sc->sc_dev.dv_xname); 156 else { 157 printf("%s: %d slot%s, %d drive%s, %d picker%s", 158 sc->sc_dev.dv_xname, 159 sc->sc_counts[CHET_ST], (sc->sc_counts[CHET_ST] > 1) ? 160 "s" : "", 161 sc->sc_counts[CHET_DT], (sc->sc_counts[CHET_DT] > 1) ? 162 "s" : "", 163 sc->sc_counts[CHET_MT], (sc->sc_counts[CHET_MT] > 1) ? 164 "s" : ""); 165 if (sc->sc_counts[CHET_IE]) 166 printf(", %d portal%s", sc->sc_counts[CHET_IE], 167 (sc->sc_counts[CHET_IE] > 1) ? "s" : ""); 168 printf("\n"); 169#ifdef CHANGER_DEBUG 170 printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", 171 sc->sc_dev.dv_xname, 172 sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], 173 sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); 174 printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", 175 sc->sc_dev.dv_xname, 176 sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], 177 sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); 178#endif /* CHANGER_DEBUG */ 179 } 180 181 /* Default the current picker. */ 182 sc->sc_picker = sc->sc_firsts[CHET_MT]; 183} 184 185int 186chopen(dev, flags, fmt, p) 187 dev_t dev; 188 int flags, fmt; 189 struct proc *p; 190{ 191 struct ch_softc *sc; 192 int 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. Ignore "not ready" 209 * since this might occur if e.g. a tape isn't actually 210 * loaded in the drive. 211 */ 212 error = scsi_test_unit_ready(sc->sc_link, 213 SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE); 214 if (error) 215 goto bad; 216 217 /* 218 * Make sure our parameters are up to date. 219 */ 220 if ((error = ch_get_params(sc, 0)) != 0) 221 goto bad; 222 223 return (0); 224 225 bad: 226 sc->sc_link->flags &= ~SDEV_OPEN; 227 return (error); 228} 229 230int 231chclose(dev, flags, fmt, p) 232 dev_t dev; 233 int flags, fmt; 234 struct proc *p; 235{ 236 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 237 238 sc->sc_link->flags &= ~SDEV_OPEN; 239 return (0); 240} 241 242int 243chioctl(dev, cmd, data, flags, p) 244 dev_t dev; 245 u_long cmd; 246 caddr_t data; 247 int flags; 248 struct proc *p; 249{ 250 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; 251 int error = 0; 252 253 /* 254 * If this command can change the device's state, we must 255 * have the device open for writing. 256 */ 257 switch (cmd) { 258 case CHIOGPICKER: 259 case CHIOGPARAMS: 260 case CHIOGSTATUS: 261 break; 262 263 default: 264 if ((flags & FWRITE) == 0) 265 return (EBADF); 266 } 267 268 switch (cmd) { 269 case CHIOMOVE: 270 error = ch_move(sc, (struct changer_move *)data); 271 break; 272 273 case CHIOEXCHANGE: 274 error = ch_exchange(sc, (struct changer_exchange *)data); 275 break; 276 277 case CHIOPOSITION: 278 error = ch_position(sc, (struct changer_position *)data); 279 break; 280 281 case CHIOGPICKER: 282 *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; 283 break; 284 285 case CHIOSPICKER: { 286 int new_picker = *(int *)data; 287 288 if (new_picker > (sc->sc_counts[CHET_MT] - 1)) 289 return (EINVAL); 290 sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; 291 break; } 292 293 case CHIOGPARAMS: { 294 struct changer_params *cp = (struct changer_params *)data; 295 296 cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; 297 cp->cp_npickers = sc->sc_counts[CHET_MT]; 298 cp->cp_nslots = sc->sc_counts[CHET_ST]; 299 cp->cp_nportals = sc->sc_counts[CHET_IE]; 300 cp->cp_ndrives = sc->sc_counts[CHET_DT]; 301 break; } 302 303 case CHIOGSTATUS: { 304 struct changer_element_status *ces = 305 (struct changer_element_status *)data; 306 307 error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data); 308 break; } 309 310 /* Implement prevent/allow? */ 311 312 default: 313 error = scsi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p); 314 break; 315 } 316 317 return (error); 318} 319 320int 321ch_move(sc, cm) 322 struct ch_softc *sc; 323 struct changer_move *cm; 324{ 325 struct scsi_move_medium cmd; 326 u_int16_t fromelem, toelem; 327 328 /* 329 * Check arguments. 330 */ 331 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) 332 return (EINVAL); 333 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || 334 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) 335 return (ENODEV); 336 337 /* 338 * Check the request against the changer's capabilities. 339 */ 340 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) 341 return (EINVAL); 342 343 /* 344 * Calculate the source and destination elements. 345 */ 346 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; 347 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; 348 349 /* 350 * Build the SCSI command. 351 */ 352 bzero(&cmd, sizeof(cmd)); 353 cmd.opcode = MOVE_MEDIUM; 354 _lto2b(sc->sc_picker, cmd.tea); 355 _lto2b(fromelem, cmd.src); 356 _lto2b(toelem, cmd.dst); 357 if (cm->cm_flags & CM_INVERT) 358 cmd.flags |= MOVE_MEDIUM_INVERT; 359 360 /* 361 * Send command to changer. 362 */ 363 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 364 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 365} 366 367int 368ch_exchange(sc, ce) 369 struct ch_softc *sc; 370 struct changer_exchange *ce; 371{ 372 struct scsi_exchange_medium cmd; 373 u_int16_t src, dst1, dst2; 374 375 /* 376 * Check arguments. 377 */ 378 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || 379 (ce->ce_sdsttype > CHET_DT)) 380 return (EINVAL); 381 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || 382 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || 383 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) 384 return (ENODEV); 385 386 /* 387 * Check the request against the changer's capabilities. 388 */ 389 if (((sc->sc_exchangemask[ce->ce_srctype] & 390 (1 << ce->ce_fdsttype)) == 0) || 391 ((sc->sc_exchangemask[ce->ce_fdsttype] & 392 (1 << ce->ce_sdsttype)) == 0)) 393 return (EINVAL); 394 395 /* 396 * Calculate the source and destination elements. 397 */ 398 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; 399 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; 400 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; 401 402 /* 403 * Build the SCSI command. 404 */ 405 bzero(&cmd, sizeof(cmd)); 406 cmd.opcode = EXCHANGE_MEDIUM; 407 _lto2b(sc->sc_picker, cmd.tea); 408 _lto2b(src, cmd.src); 409 _lto2b(dst1, cmd.fdst); 410 _lto2b(dst2, cmd.sdst); 411 if (ce->ce_flags & CE_INVERT1) 412 cmd.flags |= EXCHANGE_MEDIUM_INV1; 413 if (ce->ce_flags & CE_INVERT2) 414 cmd.flags |= EXCHANGE_MEDIUM_INV2; 415 416 /* 417 * Send command to changer. 418 */ 419 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 420 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 421} 422 423int 424ch_position(sc, cp) 425 struct ch_softc *sc; 426 struct changer_position *cp; 427{ 428 struct scsi_position_to_element cmd; 429 u_int16_t dst; 430 431 /* 432 * Check arguments. 433 */ 434 if (cp->cp_type > CHET_DT) 435 return (EINVAL); 436 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) 437 return (ENODEV); 438 439 /* 440 * Calculate the destination element. 441 */ 442 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; 443 444 /* 445 * Build the SCSI command. 446 */ 447 bzero(&cmd, sizeof(cmd)); 448 cmd.opcode = POSITION_TO_ELEMENT; 449 _lto2b(sc->sc_picker, cmd.tea); 450 _lto2b(dst, cmd.dst); 451 if (cp->cp_flags & CP_INVERT) 452 cmd.flags |= POSITION_TO_ELEMENT_INVERT; 453 454 /* 455 * Send command to changer. 456 */ 457 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 458 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); 459} 460 461/* 462 * Perform a READ ELEMENT STATUS on behalf of the user, and return to 463 * the user only the data the user is interested in (i.e. an array of 464 * flags bytes). 465 */ 466int 467ch_usergetelemstatus(sc, chet, uptr) 468 struct ch_softc *sc; 469 int chet; 470 u_int8_t *uptr; 471{ 472 struct read_element_status_header *st_hdr; 473 struct read_element_status_page_header *pg_hdr; 474 struct read_element_status_descriptor *desc; 475 caddr_t data = NULL; 476 size_t size, desclen; 477 int avail, i, error = 0; 478 u_int8_t *user_data = NULL; 479 480 /* 481 * If there are no elements of the requested type in the changer, 482 * the request is invalid. 483 */ 484 if (sc->sc_counts[chet] == 0) 485 return (EINVAL); 486 487 /* 488 * Request one descriptor for the given element type. This 489 * is used to determine the size of the descriptor so that 490 * we can allocate enough storage for all of them. We assume 491 * that the first one can fit into 1k. 492 */ 493 data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK); 494 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024); 495 if (error) 496 goto done; 497 498 st_hdr = (struct read_element_status_header *)data; 499 pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr + 500 sizeof(struct read_element_status_header)); 501 desclen = _2btol(pg_hdr->edl); 502 503 size = sizeof(struct read_element_status_header) + 504 sizeof(struct read_element_status_page_header) + 505 (desclen * sc->sc_counts[chet]); 506 507 /* 508 * Reallocate storage for descriptors and get them from the 509 * device. 510 */ 511 free(data, M_DEVBUF); 512 data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK); 513 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 514 sc->sc_counts[chet], data, size); 515 if (error) 516 goto done; 517 518 /* 519 * Fill in the user status array. 520 */ 521 st_hdr = (struct read_element_status_header *)data; 522 avail = _2btol(st_hdr->count); 523 if (avail != sc->sc_counts[chet]) 524 printf("%s: warning, READ ELEMENT STATUS avail != count\n", 525 sc->sc_dev.dv_xname); 526 527 user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK); 528 529 desc = (struct read_element_status_descriptor *)((u_long)data + 530 sizeof(struct read_element_status_header) + 531 sizeof(struct read_element_status_page_header)); 532 for (i = 0; i < avail; ++i) { 533 user_data[i] = desc->flags1; 534 (u_long)desc += desclen; 535 } 536 537 /* Copy flags array out to userspace. */ 538 error = copyout(user_data, uptr, avail); 539 540 done: 541 if (data != NULL) 542 free(data, M_DEVBUF); 543 if (user_data != NULL) 544 free(user_data, M_DEVBUF); 545 return (error); 546} 547 548int 549ch_getelemstatus(sc, first, count, data, datalen) 550 struct ch_softc *sc; 551 int first, count; 552 caddr_t data; 553 size_t datalen; 554{ 555 struct scsi_read_element_status cmd; 556 557 /* 558 * Build SCSI command. 559 */ 560 bzero(&cmd, sizeof(cmd)); 561 cmd.opcode = READ_ELEMENT_STATUS; 562 _lto2b(first, cmd.sea); 563 _lto2b(count, cmd.count); 564 _lto3b(datalen, cmd.len); 565 566 /* 567 * Send command to changer. 568 */ 569 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 570 sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, 0)); 571} 572 573 574/* 575 * Ask the device about itself and fill in the parameters in our 576 * softc. 577 */ 578int 579ch_get_params(sc, scsiflags) 580 struct ch_softc *sc; 581 int scsiflags; 582{ 583 struct scsi_mode_sense cmd; 584 struct scsi_mode_sense_data { 585 struct scsi_mode_header header; 586 union { 587 struct page_element_address_assignment ea; 588 struct page_transport_geometry_parameters tg; 589 struct page_device_capabilities cap; 590 } pages; 591 } sense_data; 592 int error, from; 593 u_int8_t *moves, *exchanges; 594 595 /* 596 * Grab info from the element address assignment page. 597 */ 598 bzero(&cmd, sizeof(cmd)); 599 bzero(&sense_data, sizeof(sense_data)); 600 cmd.opcode = MODE_SENSE; 601 cmd.byte2 |= 0x08; /* disable block descriptors */ 602 cmd.page = 0x1d; 603 cmd.length = (sizeof(sense_data) & 0xff); 604 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 605 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, 606 6000, NULL, scsiflags | SCSI_DATA_IN); 607 if (error) { 608 printf("%s: could not sense element address page\n", 609 sc->sc_dev.dv_xname); 610 return (error); 611 } 612 613 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); 614 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); 615 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); 616 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); 617 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); 618 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); 619 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); 620 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); 621 622 /* XXX ask for page trasport geom */ 623 624 /* 625 * Grab info from the capabilities page. 626 */ 627 bzero(&cmd, sizeof(cmd)); 628 bzero(&sense_data, sizeof(sense_data)); 629 cmd.opcode = MODE_SENSE; 630 cmd.byte2 |= 0x08; /* disable block descriptors */ 631 cmd.page = 0x1f; 632 cmd.length = (sizeof(sense_data) & 0xff); 633 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, 634 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, 635 6000, NULL, scsiflags | SCSI_DATA_IN); 636 if (error) { 637 printf("%s: could not sense capabilities page\n", 638 sc->sc_dev.dv_xname); 639 return (error); 640 } 641 642 bzero(sc->sc_movemask, sizeof(sc->sc_movemask)); 643 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask)); 644 moves = &sense_data.pages.cap.move_from_mt; 645 exchanges = &sense_data.pages.cap.exchange_with_mt; 646 for (from = CHET_MT; from <= CHET_DT; ++from) { 647 sc->sc_movemask[from] = moves[from]; 648 sc->sc_exchangemask[from] = exchanges[from]; 649 } 650 651 sc->sc_link->flags |= SDEV_MEDIA_LOADED; 652 return (0); 653} 654