This source file includes following definitions.
- chmatch
- chattach
- chopen
- chclose
- chioctl
- ch_move
- ch_exchange
- ch_position
- copy_voltag
- copy_element_status
- ch_usergetelemstatus
- ch_getelemstatus
- ch_get_params
- ch_get_quirks
- ch_interpret_sense
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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;
62 struct scsi_link *sc_link;
63
64 int sc_picker;
65
66
67
68
69
70 int sc_firsts[4];
71 int sc_counts[4];
72
73
74
75
76
77 u_int8_t sc_movemask[4];
78
79
80
81
82 u_int8_t sc_exchangemask[4];
83
84 int flags;
85
86
87
88
89 int sc_settledelay;
90
91 };
92
93
94 #define CHF_ROTATE 0x01
95
96
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
124 struct scsi_device ch_switch = {
125 ch_interpret_sense,
126 NULL,
127 NULL,
128 NULL
129 };
130
131
132
133
134 struct chquirk {
135 struct scsi_inquiry_pattern cq_match;
136 int cq_settledelay;
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
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
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
201
202 if (sc->sc_link->flags & SDEV_OPEN)
203 return (EBUSY);
204
205 sc->sc_link->flags |= SDEV_OPEN;
206
207
208
209
210
211
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
220
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
253 }
254
255
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
290
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
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
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
375
376 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
377 return (EINVAL);
378
379
380
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
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
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
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
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
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
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
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
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
477
478 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
479
480
481
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
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
499
500
501
502
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
522
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
538
539
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
558
559
560 if (sc->sc_counts[chet] == 0)
561 return (EINVAL);
562
563
564
565
566
567
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
586
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
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
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
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
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
664
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
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
707
708
709
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
759
760
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);
774
775 switch (skey) {
776
777
778
779
780
781
782
783
784
785
786
787
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
797 xs->retries++;
798 return (scsi_delay(xs, 1));
799 default:
800 return (EJUSTRETURN);
801 }
802 default:
803 return (EJUSTRETURN);
804 }
805 }