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