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