ch.c revision 1.30 1 /* $NetBSD: ch.c,v 1.30 1997/10/01 05:39:56 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1996, 1997 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 <dev/scsipi/scsipi_all.h>
53 #include <dev/scsipi/scsi_all.h>
54 #include <dev/scsipi/scsi_changer.h>
55 #include <dev/scsipi/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 scsipi_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 #ifdef __BROKEN_INDIRECT_CONFIG
98 int chmatch __P((struct device *, void *, void *));
99 #else
100 int chmatch __P((struct device *, struct cfdata *, void *));
101 #endif
102 void chattach __P((struct device *, struct device *, void *));
103
104 struct cfattach ch_ca = {
105 sizeof(struct ch_softc), chmatch, chattach
106 };
107
108 struct cfdriver ch_cd = {
109 NULL, "ch", DV_DULL
110 };
111
112 struct scsipi_inquiry_pattern ch_patterns[] = {
113 {T_CHANGER, T_REMOV,
114 "", "", ""},
115 };
116
117 /* SCSI glue */
118 struct scsipi_device ch_switch = {
119 NULL, NULL, NULL, NULL
120 };
121
122 int ch_move __P((struct ch_softc *, struct changer_move *));
123 int ch_exchange __P((struct ch_softc *, struct changer_exchange *));
124 int ch_position __P((struct ch_softc *, struct changer_position *));
125 int ch_ielem __P((struct ch_softc *));
126 int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *));
127 int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t));
128 int ch_get_params __P((struct ch_softc *, int));
129 void ch_get_quirks __P((struct ch_softc *,
130 struct scsipi_inquiry_pattern *));
131
132 /*
133 * SCSI changer quirks.
134 */
135 struct chquirk {
136 struct scsipi_inquiry_pattern cq_match; /* device id pattern */
137 int cq_settledelay; /* settle delay, in seconds */
138 };
139
140 struct chquirk chquirks[] = {
141 {{T_CHANGER, T_REMOV,
142 "SPECTRA", "9000", "0200"},
143 75},
144 };
145
146 int
147 chmatch(parent, match, aux)
148 struct device *parent;
149 #ifdef __BROKEN_INDIRECT_CONFIG
150 void *match;
151 #else
152 struct cfdata *match;
153 #endif
154 void *aux;
155 {
156 struct scsipibus_attach_args *sa = aux;
157 int priority;
158
159 (void)scsipi_inqmatch(&sa->sa_inqbuf,
160 (caddr_t)ch_patterns, sizeof(ch_patterns) / sizeof(ch_patterns[0]),
161 sizeof(ch_patterns[0]), &priority);
162
163 return (priority);
164 }
165
166 void
167 chattach(parent, self, aux)
168 struct device *parent, *self;
169 void *aux;
170 {
171 struct ch_softc *sc = (struct ch_softc *)self;
172 struct scsipibus_attach_args *sa = aux;
173 struct scsipi_link *link = sa->sa_sc_link;
174
175 /* Glue into the SCSI bus */
176 sc->sc_link = link;
177 link->device = &ch_switch;
178 link->device_softc = sc;
179 link->openings = 1;
180
181 printf("\n");
182
183 /*
184 * Find out our device's quirks.
185 */
186 ch_get_quirks(sc, &sa->sa_inqbuf);
187
188 /*
189 * Some changers require a long time to settle out, to do
190 * tape inventory, for instance.
191 */
192 if (sc->sc_settledelay) {
193 printf("%s: waiting %d seconds for changer to settle...\n",
194 sc->sc_dev.dv_xname, sc->sc_settledelay);
195 delay(1000000 * sc->sc_settledelay);
196 }
197
198 /*
199 * Get information about the device. Note we can't use
200 * interrupts yet.
201 */
202 if (ch_get_params(sc, SCSI_AUTOCONF))
203 printf("%s: offline\n", sc->sc_dev.dv_xname);
204 else {
205 #define PLURAL(c) (c) == 1 ? "" : "s"
206 printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
207 sc->sc_dev.dv_xname,
208 sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
209 sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
210 sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
211 sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
212 #undef PLURAL
213 #ifdef CHANGER_DEBUG
214 printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
215 sc->sc_dev.dv_xname,
216 sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
217 sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
218 printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
219 sc->sc_dev.dv_xname,
220 sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
221 sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
222 #endif /* CHANGER_DEBUG */
223 }
224
225 /* Default the current picker. */
226 sc->sc_picker = sc->sc_firsts[CHET_MT];
227 }
228
229 int
230 chopen(dev, flags, fmt, p)
231 dev_t dev;
232 int flags, fmt;
233 struct proc *p;
234 {
235 struct ch_softc *sc;
236 int unit, error = 0;
237
238 unit = CHUNIT(dev);
239 if ((unit >= ch_cd.cd_ndevs) ||
240 ((sc = ch_cd.cd_devs[unit]) == NULL))
241 return (ENXIO);
242
243 /*
244 * Only allow one open at a time.
245 */
246 if (sc->sc_link->flags & SDEV_OPEN)
247 return (EBUSY);
248
249 sc->sc_link->flags |= SDEV_OPEN;
250
251 /*
252 * Absorb any unit attention errors. Ignore "not ready"
253 * since this might occur if e.g. a tape isn't actually
254 * loaded in the drive.
255 */
256 error = scsipi_test_unit_ready(sc->sc_link,
257 SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE);
258 if (error)
259 goto bad;
260
261 /*
262 * Make sure our parameters are up to date.
263 */
264 if ((error = ch_get_params(sc, 0)) != 0)
265 goto bad;
266
267 return (0);
268
269 bad:
270 sc->sc_link->flags &= ~SDEV_OPEN;
271 return (error);
272 }
273
274 int
275 chclose(dev, flags, fmt, p)
276 dev_t dev;
277 int flags, fmt;
278 struct proc *p;
279 {
280 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
281
282 sc->sc_link->flags &= ~SDEV_OPEN;
283 return (0);
284 }
285
286 int
287 chioctl(dev, cmd, data, flags, p)
288 dev_t dev;
289 u_long cmd;
290 caddr_t data;
291 int flags;
292 struct proc *p;
293 {
294 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
295 int error = 0;
296
297 /*
298 * If this command can change the device's state, we must
299 * have the device open for writing.
300 */
301 switch (cmd) {
302 case CHIOGPICKER:
303 case CHIOGPARAMS:
304 case CHIOGSTATUS:
305 break;
306
307 default:
308 if ((flags & FWRITE) == 0)
309 return (EBADF);
310 }
311
312 switch (cmd) {
313 case CHIOMOVE:
314 error = ch_move(sc, (struct changer_move *)data);
315 break;
316
317 case CHIOEXCHANGE:
318 error = ch_exchange(sc, (struct changer_exchange *)data);
319 break;
320
321 case CHIOPOSITION:
322 error = ch_position(sc, (struct changer_position *)data);
323 break;
324
325 case CHIOGPICKER:
326 *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT];
327 break;
328
329 case CHIOSPICKER: {
330 int new_picker = *(int *)data;
331
332 if (new_picker > (sc->sc_counts[CHET_MT] - 1))
333 return (EINVAL);
334 sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker;
335 break; }
336
337 case CHIOGPARAMS: {
338 struct changer_params *cp = (struct changer_params *)data;
339
340 cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT];
341 cp->cp_npickers = sc->sc_counts[CHET_MT];
342 cp->cp_nslots = sc->sc_counts[CHET_ST];
343 cp->cp_nportals = sc->sc_counts[CHET_IE];
344 cp->cp_ndrives = sc->sc_counts[CHET_DT];
345 break; }
346
347 case CHIOIELEM:
348 error = ch_ielem(sc);
349 break;
350
351 case CHIOGSTATUS: {
352 struct changer_element_status *ces =
353 (struct changer_element_status *)data;
354
355 error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data);
356 break; }
357
358 /* Implement prevent/allow? */
359
360 default:
361 error = scsipi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p);
362 break;
363 }
364
365 return (error);
366 }
367
368 int
369 ch_move(sc, cm)
370 struct ch_softc *sc;
371 struct changer_move *cm;
372 {
373 struct scsi_move_medium cmd;
374 u_int16_t fromelem, toelem;
375
376 /*
377 * Check arguments.
378 */
379 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
380 return (EINVAL);
381 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
382 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
383 return (ENODEV);
384
385 /*
386 * Check the request against the changer's capabilities.
387 */
388 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
389 return (EINVAL);
390
391 /*
392 * Calculate the source and destination elements.
393 */
394 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
395 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
396
397 /*
398 * Build the SCSI command.
399 */
400 bzero(&cmd, sizeof(cmd));
401 cmd.opcode = MOVE_MEDIUM;
402 _lto2b(sc->sc_picker, cmd.tea);
403 _lto2b(fromelem, cmd.src);
404 _lto2b(toelem, cmd.dst);
405 if (cm->cm_flags & CM_INVERT)
406 cmd.flags |= MOVE_MEDIUM_INVERT;
407
408 /*
409 * Send command to changer.
410 */
411 return ((*sc->sc_link->scsipi_cmd)(sc->sc_link,
412 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
413 100000, NULL, 0));
414 }
415
416 int
417 ch_exchange(sc, ce)
418 struct ch_softc *sc;
419 struct changer_exchange *ce;
420 {
421 struct scsi_exchange_medium cmd;
422 u_int16_t src, dst1, dst2;
423
424 /*
425 * Check arguments.
426 */
427 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
428 (ce->ce_sdsttype > CHET_DT))
429 return (EINVAL);
430 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
431 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
432 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
433 return (ENODEV);
434
435 /*
436 * Check the request against the changer's capabilities.
437 */
438 if (((sc->sc_exchangemask[ce->ce_srctype] &
439 (1 << ce->ce_fdsttype)) == 0) ||
440 ((sc->sc_exchangemask[ce->ce_fdsttype] &
441 (1 << ce->ce_sdsttype)) == 0))
442 return (EINVAL);
443
444 /*
445 * Calculate the source and destination elements.
446 */
447 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
448 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
449 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
450
451 /*
452 * Build the SCSI command.
453 */
454 bzero(&cmd, sizeof(cmd));
455 cmd.opcode = EXCHANGE_MEDIUM;
456 _lto2b(sc->sc_picker, cmd.tea);
457 _lto2b(src, cmd.src);
458 _lto2b(dst1, cmd.fdst);
459 _lto2b(dst2, cmd.sdst);
460 if (ce->ce_flags & CE_INVERT1)
461 cmd.flags |= EXCHANGE_MEDIUM_INV1;
462 if (ce->ce_flags & CE_INVERT2)
463 cmd.flags |= EXCHANGE_MEDIUM_INV2;
464
465 /*
466 * Send command to changer.
467 */
468 return ((*sc->sc_link->scsipi_cmd)(sc->sc_link,
469 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
470 100000, NULL, 0));
471 }
472
473 int
474 ch_position(sc, cp)
475 struct ch_softc *sc;
476 struct changer_position *cp;
477 {
478 struct scsi_position_to_element cmd;
479 u_int16_t dst;
480
481 /*
482 * Check arguments.
483 */
484 if (cp->cp_type > CHET_DT)
485 return (EINVAL);
486 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
487 return (ENODEV);
488
489 /*
490 * Calculate the destination element.
491 */
492 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
493
494 /*
495 * Build the SCSI command.
496 */
497 bzero(&cmd, sizeof(cmd));
498 cmd.opcode = POSITION_TO_ELEMENT;
499 _lto2b(sc->sc_picker, cmd.tea);
500 _lto2b(dst, cmd.dst);
501 if (cp->cp_flags & CP_INVERT)
502 cmd.flags |= POSITION_TO_ELEMENT_INVERT;
503
504 /*
505 * Send command to changer.
506 */
507 return ((*sc->sc_link->scsipi_cmd)(sc->sc_link,
508 (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
509 100000, NULL, 0));
510 }
511
512 /*
513 * Perform a READ ELEMENT STATUS on behalf of the user, and return to
514 * the user only the data the user is interested in (i.e. an array of
515 * flags bytes).
516 */
517 int
518 ch_usergetelemstatus(sc, chet, uptr)
519 struct ch_softc *sc;
520 int chet;
521 u_int8_t *uptr;
522 {
523 struct read_element_status_header *st_hdr;
524 struct read_element_status_page_header *pg_hdr;
525 struct read_element_status_descriptor *desc;
526 caddr_t data = NULL;
527 size_t size, desclen;
528 int avail, i, error = 0;
529 u_int8_t *user_data = NULL;
530
531 /*
532 * If there are no elements of the requested type in the changer,
533 * the request is invalid.
534 */
535 if (sc->sc_counts[chet] == 0)
536 return (EINVAL);
537
538 /*
539 * Request one descriptor for the given element type. This
540 * is used to determine the size of the descriptor so that
541 * we can allocate enough storage for all of them. We assume
542 * that the first one can fit into 1k.
543 */
544 data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
545 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024);
546 if (error)
547 goto done;
548
549 st_hdr = (struct read_element_status_header *)data;
550 pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
551 sizeof(struct read_element_status_header));
552 desclen = _2btol(pg_hdr->edl);
553
554 size = sizeof(struct read_element_status_header) +
555 sizeof(struct read_element_status_page_header) +
556 (desclen * sc->sc_counts[chet]);
557
558 /*
559 * Reallocate storage for descriptors and get them from the
560 * device.
561 */
562 free(data, M_DEVBUF);
563 data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
564 error = ch_getelemstatus(sc, sc->sc_firsts[chet],
565 sc->sc_counts[chet], data, size);
566 if (error)
567 goto done;
568
569 /*
570 * Fill in the user status array.
571 */
572 st_hdr = (struct read_element_status_header *)data;
573 avail = _2btol(st_hdr->count);
574
575 if (avail != sc->sc_counts[chet])
576 printf("%s: warning, READ ELEMENT STATUS avail != count\n",
577 sc->sc_dev.dv_xname);
578
579 user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK);
580
581 desc = (struct read_element_status_descriptor *)((u_long)data +
582 sizeof(struct read_element_status_header) +
583 sizeof(struct read_element_status_page_header));
584 for (i = 0; i < avail; ++i) {
585 user_data[i] = desc->flags1;
586 (u_long)desc += desclen;
587 }
588
589 /* Copy flags array out to userspace. */
590 error = copyout(user_data, uptr, avail);
591
592 done:
593 if (data != NULL)
594 free(data, M_DEVBUF);
595 if (user_data != NULL)
596 free(user_data, M_DEVBUF);
597 return (error);
598 }
599
600 int
601 ch_getelemstatus(sc, first, count, data, datalen)
602 struct ch_softc *sc;
603 int first, count;
604 caddr_t data;
605 size_t datalen;
606 {
607 struct scsi_read_element_status cmd;
608
609 /*
610 * Build SCSI command.
611 */
612 bzero(&cmd, sizeof(cmd));
613 cmd.opcode = READ_ELEMENT_STATUS;
614 _lto2b(first, cmd.sea);
615 _lto2b(count, cmd.count);
616 _lto3b(datalen, cmd.len);
617
618 /*
619 * Send command to changer.
620 */
621 return ((*sc->sc_link->scsipi_cmd)(sc->sc_link,
622 (struct scsipi_generic *)&cmd, sizeof(cmd),
623 (u_char *)data, datalen, CHRETRIES, 100000, NULL, SCSI_DATA_IN));
624 }
625
626
627 int
628 ch_ielem(sc)
629 struct ch_softc *sc;
630 {
631 struct scsi_initialize_element_status cmd;
632
633 /*
634 * Build SCSI command.
635 */
636 bzero(&cmd, sizeof(cmd));
637 cmd.opcode = INITIALIZE_ELEMENT_STATUS;
638
639 /*
640 * Send command to changer.
641 */
642 return ((*sc->sc_link->scsipi_cmd)(sc->sc_link,
643 (struct scsipi_generic *)&cmd, sizeof(cmd),
644 NULL, 0, CHRETRIES, 100000, NULL, 0));
645 }
646
647 /*
648 * Ask the device about itself and fill in the parameters in our
649 * softc.
650 */
651 int
652 ch_get_params(sc, scsiflags)
653 struct ch_softc *sc;
654 int scsiflags;
655 {
656 struct scsi_mode_sense cmd;
657 struct scsi_mode_sense_data {
658 struct scsi_mode_header header;
659 union {
660 struct page_element_address_assignment ea;
661 struct page_transport_geometry_parameters tg;
662 struct page_device_capabilities cap;
663 } pages;
664 } sense_data;
665 int error, from;
666 u_int8_t *moves, *exchanges;
667
668 /*
669 * Grab info from the element address assignment page.
670 */
671 bzero(&cmd, sizeof(cmd));
672 bzero(&sense_data, sizeof(sense_data));
673 cmd.opcode = SCSI_MODE_SENSE;
674 cmd.byte2 |= 0x08; /* disable block descriptors */
675 cmd.page = 0x1d;
676 cmd.length = (sizeof(sense_data) & 0xff);
677 error = (*sc->sc_link->scsipi_cmd)(sc->sc_link,
678 (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data,
679 sizeof(sense_data), CHRETRIES, 6000, NULL,
680 scsiflags | SCSI_DATA_IN);
681 if (error) {
682 printf("%s: could not sense element address page\n",
683 sc->sc_dev.dv_xname);
684 return (error);
685 }
686
687 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea);
688 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte);
689 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea);
690 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse);
691 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea);
692 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee);
693 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea);
694 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte);
695
696 /* XXX ask for page trasport geom */
697
698 /*
699 * Grab info from the capabilities page.
700 */
701 bzero(&cmd, sizeof(cmd));
702 bzero(&sense_data, sizeof(sense_data));
703 cmd.opcode = SCSI_MODE_SENSE;
704 /*
705 * XXX: Note: not all changers can deal with disabled block descriptors
706 */
707 cmd.byte2 = 0x08; /* disable block descriptors */
708 cmd.page = 0x1f;
709 cmd.length = (sizeof(sense_data) & 0xff);
710 error = (*sc->sc_link->scsipi_cmd)(sc->sc_link,
711 (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data,
712 sizeof(sense_data), CHRETRIES, 6000, NULL,
713 scsiflags | SCSI_DATA_IN);
714 if (error) {
715 printf("%s: could not sense capabilities page\n",
716 sc->sc_dev.dv_xname);
717 return (error);
718 }
719
720 bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
721 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
722 moves = &sense_data.pages.cap.move_from_mt;
723 exchanges = &sense_data.pages.cap.exchange_with_mt;
724 for (from = CHET_MT; from <= CHET_DT; ++from) {
725 sc->sc_movemask[from] = moves[from];
726 sc->sc_exchangemask[from] = exchanges[from];
727 }
728
729 sc->sc_link->flags |= SDEV_MEDIA_LOADED;
730 return (0);
731 }
732
733 void
734 ch_get_quirks(sc, inqbuf)
735 struct ch_softc *sc;
736 struct scsipi_inquiry_pattern *inqbuf;
737 {
738 struct chquirk *match;
739 int priority;
740
741 sc->sc_settledelay = 0;
742
743 match = (struct chquirk *)scsipi_inqmatch(inqbuf,
744 (caddr_t)chquirks,
745 sizeof(chquirks) / sizeof(chquirks[0]),
746 sizeof(chquirks[0]), &priority);
747 if (priority != 0)
748 sc->sc_settledelay = match->cq_settledelay;
749 }
750