ch.c revision 1.22 1 /* $NetBSD: ch.c,v 1.22 1996/09/18 02:34:31 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1996 Jason R. Thorpe <thorpej (at) 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
59 struct 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 */
90 int chmatch __P((struct device *, void *, void *));
91 void chattach __P((struct device *, struct device *, void *));
92
93 struct cfattach ch_ca = {
94 sizeof(struct ch_softc), chmatch, chattach
95 };
96
97 struct cfdriver ch_cd = {
98 NULL, "ch", DV_DULL
99 };
100
101 struct scsi_inquiry_pattern ch_patterns[] = {
102 {T_CHANGER, T_REMOV,
103 "", "", ""},
104 };
105
106 /* SCSI glue */
107 struct scsi_device ch_switch = {
108 NULL, NULL, NULL, NULL
109 };
110
111 int ch_move __P((struct ch_softc *, struct changer_move *));
112 int ch_exchange __P((struct ch_softc *, struct changer_exchange *));
113 int ch_position __P((struct ch_softc *, struct changer_position *));
114 int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *));
115 int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t));
116 int ch_get_params __P((struct ch_softc *, int));
117
118 int
119 chmatch(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
133 void
134 chattach(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
185 int
186 chopen(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
230 int
231 chclose(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
242 int
243 chioctl(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
320 int
321 ch_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
367 int
368 ch_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
423 int
424 ch_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 */
466 int
467 ch_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
548 int
549 ch_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 */
578 int
579 ch_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