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