ch.c revision 1.25.6.1 1 /* $NetBSD: ch.c,v 1.25.6.1 1997/03/12 21:25:40 is 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 <scsi/scsi_all.h>
53 #include <scsi/scsi_changer.h>
54 #include <scsi/scsiconf.h>
55
56 #define CHRETRIES 2
57 #define CHUNIT(x) (minor((x)))
58
59 struct ch_softc {
60 struct device sc_dev; /* generic device info */
61 struct scsi_link *sc_link; /* link in the SCSI bus */
62
63 int sc_picker; /* current picker */
64
65 /*
66 * The following information is obtained from the
67 * element address assignment page.
68 */
69 int sc_firsts[4]; /* firsts, indexed by CHET_* */
70 int sc_counts[4]; /* counts, indexed by CHET_* */
71
72 /*
73 * The following mask defines the legal combinations
74 * of elements for the MOVE MEDIUM command.
75 */
76 u_int8_t sc_movemask[4];
77
78 /*
79 * As above, but for EXCHANGE MEDIUM.
80 */
81 u_int8_t sc_exchangemask[4];
82
83 int flags; /* misc. info */
84
85 /*
86 * Quirks; see below.
87 */
88 int sc_settledelay; /* delay for settle */
89
90 };
91
92 /* sc_flags */
93 #define CHF_ROTATE 0x01 /* picker can rotate */
94
95 /* Autoconfiguration glue */
96 #ifdef __BROKEN_INDIRECT_CONFIG
97 int chmatch __P((struct device *, void *, void *));
98 #else
99 int chmatch __P((struct device *, struct cfdata *, void *));
100 #endif
101 void chattach __P((struct device *, struct device *, void *));
102
103 struct cfattach ch_ca = {
104 sizeof(struct ch_softc), chmatch, chattach
105 };
106
107 struct cfdriver ch_cd = {
108 NULL, "ch", DV_DULL
109 };
110
111 struct scsi_inquiry_pattern ch_patterns[] = {
112 {T_CHANGER, T_REMOV,
113 "", "", ""},
114 };
115
116 /* SCSI glue */
117 struct scsi_device ch_switch = {
118 NULL, NULL, NULL, NULL
119 };
120
121 int ch_move __P((struct ch_softc *, struct changer_move *));
122 int ch_exchange __P((struct ch_softc *, struct changer_exchange *));
123 int ch_position __P((struct ch_softc *, struct changer_position *));
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 *, struct scsi_inquiry_data *));
128
129 /*
130 * SCSI changer quirks.
131 */
132 struct chquirk {
133 struct scsi_inquiry_pattern cq_match; /* device id pattern */
134 int cq_settledelay; /* settle delay, in seconds */
135 };
136
137 struct chquirk chquirks[] = {
138 {{T_CHANGER, T_REMOV,
139 "SPECTRA", "9000", "0200"},
140 75},
141 };
142
143 int
144 chmatch(parent, match, aux)
145 struct device *parent;
146 #ifdef __BROKEN_INDIRECT_CONFIG
147 void *match;
148 #else
149 struct cfdata *match;
150 #endif
151 void *aux;
152 {
153 struct scsibus_attach_args *sa = aux;
154 int priority;
155
156 (void)scsi_inqmatch(sa->sa_inqbuf,
157 (caddr_t)ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]),
158 sizeof(ch_patterns[0]), &priority);
159
160 return (priority);
161 }
162
163 void
164 chattach(parent, self, aux)
165 struct device *parent, *self;
166 void *aux;
167 {
168 struct ch_softc *sc = (struct ch_softc *)self;
169 struct scsibus_attach_args *sa = aux;
170 struct scsi_link *link = sa->sa_sc_link;
171
172 /* Glue into the SCSI bus */
173 sc->sc_link = link;
174 link->device = &ch_switch;
175 link->device_softc = sc;
176 link->openings = 1;
177
178 printf("\n");
179
180 /*
181 * Find out our device's quirks.
182 */
183 ch_get_quirks(sc, sa->sa_inqbuf);
184
185 /*
186 * Some changers require a long time to settle out, to do
187 * tape inventory, for instance.
188 */
189 if (sc->sc_settledelay) {
190 printf("%s: waiting %d seconds for changer to settle...\n",
191 sc->sc_dev.dv_xname, sc->sc_settledelay);
192 delay(1000000 * sc->sc_settledelay);
193 }
194
195 /*
196 * Get information about the device. Note we can't use
197 * interrupts yet.
198 */
199 if (ch_get_params(sc, SCSI_AUTOCONF))
200 printf("%s: offline\n", sc->sc_dev.dv_xname);
201 else {
202 #define PLURAL(c) (c) == 1 ? "" : "s"
203 printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
204 sc->sc_dev.dv_xname,
205 sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
206 sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
207 sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
208 sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
209 #undef PLURAL
210 #ifdef CHANGER_DEBUG
211 printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
212 sc->sc_dev.dv_xname,
213 sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
214 sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
215 printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
216 sc->sc_dev.dv_xname,
217 sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
218 sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
219 #endif /* CHANGER_DEBUG */
220 }
221
222 /* Default the current picker. */
223 sc->sc_picker = sc->sc_firsts[CHET_MT];
224 }
225
226 int
227 chopen(dev, flags, fmt, p)
228 dev_t dev;
229 int flags, fmt;
230 struct proc *p;
231 {
232 struct ch_softc *sc;
233 int unit, error = 0;
234
235 unit = CHUNIT(dev);
236 if ((unit >= ch_cd.cd_ndevs) ||
237 ((sc = ch_cd.cd_devs[unit]) == NULL))
238 return (ENXIO);
239
240 /*
241 * Only allow one open at a time.
242 */
243 if (sc->sc_link->flags & SDEV_OPEN)
244 return (EBUSY);
245
246 sc->sc_link->flags |= SDEV_OPEN;
247
248 /*
249 * Absorb any unit attention errors. Ignore "not ready"
250 * since this might occur if e.g. a tape isn't actually
251 * loaded in the drive.
252 */
253 error = scsi_test_unit_ready(sc->sc_link,
254 SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE);
255 if (error)
256 goto bad;
257
258 /*
259 * Make sure our parameters are up to date.
260 */
261 if ((error = ch_get_params(sc, 0)) != 0)
262 goto bad;
263
264 return (0);
265
266 bad:
267 sc->sc_link->flags &= ~SDEV_OPEN;
268 return (error);
269 }
270
271 int
272 chclose(dev, flags, fmt, p)
273 dev_t dev;
274 int flags, fmt;
275 struct proc *p;
276 {
277 struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
278
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 CHIOGSTATUS: {
345 struct changer_element_status *ces =
346 (struct changer_element_status *)data;
347
348 error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data);
349 break; }
350
351 /* Implement prevent/allow? */
352
353 default:
354 error = scsi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p);
355 break;
356 }
357
358 return (error);
359 }
360
361 int
362 ch_move(sc, cm)
363 struct ch_softc *sc;
364 struct changer_move *cm;
365 {
366 struct scsi_move_medium cmd;
367 u_int16_t fromelem, toelem;
368
369 /*
370 * Check arguments.
371 */
372 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
373 return (EINVAL);
374 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
375 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
376 return (ENODEV);
377
378 /*
379 * Check the request against the changer's capabilities.
380 */
381 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
382 return (EINVAL);
383
384 /*
385 * Calculate the source and destination elements.
386 */
387 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
388 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
389
390 /*
391 * Build the SCSI command.
392 */
393 bzero(&cmd, sizeof(cmd));
394 cmd.opcode = MOVE_MEDIUM;
395 _lto2b(sc->sc_picker, cmd.tea);
396 _lto2b(fromelem, cmd.src);
397 _lto2b(toelem, cmd.dst);
398 if (cm->cm_flags & CM_INVERT)
399 cmd.flags |= MOVE_MEDIUM_INVERT;
400
401 /*
402 * Send command to changer.
403 */
404 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
405 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
406 }
407
408 int
409 ch_exchange(sc, ce)
410 struct ch_softc *sc;
411 struct changer_exchange *ce;
412 {
413 struct scsi_exchange_medium cmd;
414 u_int16_t src, dst1, dst2;
415
416 /*
417 * Check arguments.
418 */
419 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
420 (ce->ce_sdsttype > CHET_DT))
421 return (EINVAL);
422 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
423 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
424 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
425 return (ENODEV);
426
427 /*
428 * Check the request against the changer's capabilities.
429 */
430 if (((sc->sc_exchangemask[ce->ce_srctype] &
431 (1 << ce->ce_fdsttype)) == 0) ||
432 ((sc->sc_exchangemask[ce->ce_fdsttype] &
433 (1 << ce->ce_sdsttype)) == 0))
434 return (EINVAL);
435
436 /*
437 * Calculate the source and destination elements.
438 */
439 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
440 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
441 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
442
443 /*
444 * Build the SCSI command.
445 */
446 bzero(&cmd, sizeof(cmd));
447 cmd.opcode = EXCHANGE_MEDIUM;
448 _lto2b(sc->sc_picker, cmd.tea);
449 _lto2b(src, cmd.src);
450 _lto2b(dst1, cmd.fdst);
451 _lto2b(dst2, cmd.sdst);
452 if (ce->ce_flags & CE_INVERT1)
453 cmd.flags |= EXCHANGE_MEDIUM_INV1;
454 if (ce->ce_flags & CE_INVERT2)
455 cmd.flags |= EXCHANGE_MEDIUM_INV2;
456
457 /*
458 * Send command to changer.
459 */
460 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
461 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
462 }
463
464 int
465 ch_position(sc, cp)
466 struct ch_softc *sc;
467 struct changer_position *cp;
468 {
469 struct scsi_position_to_element cmd;
470 u_int16_t dst;
471
472 /*
473 * Check arguments.
474 */
475 if (cp->cp_type > CHET_DT)
476 return (EINVAL);
477 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
478 return (ENODEV);
479
480 /*
481 * Calculate the destination element.
482 */
483 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
484
485 /*
486 * Build the SCSI command.
487 */
488 bzero(&cmd, sizeof(cmd));
489 cmd.opcode = POSITION_TO_ELEMENT;
490 _lto2b(sc->sc_picker, cmd.tea);
491 _lto2b(dst, cmd.dst);
492 if (cp->cp_flags & CP_INVERT)
493 cmd.flags |= POSITION_TO_ELEMENT_INVERT;
494
495 /*
496 * Send command to changer.
497 */
498 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
499 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
500 }
501
502 /*
503 * Perform a READ ELEMENT STATUS on behalf of the user, and return to
504 * the user only the data the user is interested in (i.e. an array of
505 * flags bytes).
506 */
507 int
508 ch_usergetelemstatus(sc, chet, uptr)
509 struct ch_softc *sc;
510 int chet;
511 u_int8_t *uptr;
512 {
513 struct read_element_status_header *st_hdr;
514 struct read_element_status_page_header *pg_hdr;
515 struct read_element_status_descriptor *desc;
516 caddr_t data = NULL;
517 size_t size, desclen;
518 int avail, i, error = 0;
519 u_int8_t *user_data = NULL;
520
521 /*
522 * If there are no elements of the requested type in the changer,
523 * the request is invalid.
524 */
525 if (sc->sc_counts[chet] == 0)
526 return (EINVAL);
527
528 /*
529 * Request one descriptor for the given element type. This
530 * is used to determine the size of the descriptor so that
531 * we can allocate enough storage for all of them. We assume
532 * that the first one can fit into 1k.
533 */
534 data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
535 error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024);
536 if (error)
537 goto done;
538
539 st_hdr = (struct read_element_status_header *)data;
540 pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
541 sizeof(struct read_element_status_header));
542 desclen = _2btol(pg_hdr->edl);
543
544 size = sizeof(struct read_element_status_header) +
545 sizeof(struct read_element_status_page_header) +
546 (desclen * sc->sc_counts[chet]);
547
548 /*
549 * Reallocate storage for descriptors and get them from the
550 * device.
551 */
552 free(data, M_DEVBUF);
553 data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
554 error = ch_getelemstatus(sc, sc->sc_firsts[chet],
555 sc->sc_counts[chet], data, size);
556 if (error)
557 goto done;
558
559 /*
560 * Fill in the user status array.
561 */
562 st_hdr = (struct read_element_status_header *)data;
563 avail = _2btol(st_hdr->count);
564 if (avail != sc->sc_counts[chet])
565 printf("%s: warning, READ ELEMENT STATUS avail != count\n",
566 sc->sc_dev.dv_xname);
567
568 user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK);
569
570 desc = (struct read_element_status_descriptor *)((u_long)data +
571 sizeof(struct read_element_status_header) +
572 sizeof(struct read_element_status_page_header));
573 for (i = 0; i < avail; ++i) {
574 user_data[i] = desc->flags1;
575 (u_long)desc += desclen;
576 }
577
578 /* Copy flags array out to userspace. */
579 error = copyout(user_data, uptr, avail);
580
581 done:
582 if (data != NULL)
583 free(data, M_DEVBUF);
584 if (user_data != NULL)
585 free(user_data, M_DEVBUF);
586 return (error);
587 }
588
589 int
590 ch_getelemstatus(sc, first, count, data, datalen)
591 struct ch_softc *sc;
592 int first, count;
593 caddr_t data;
594 size_t datalen;
595 {
596 struct scsi_read_element_status cmd;
597
598 /*
599 * Build SCSI command.
600 */
601 bzero(&cmd, sizeof(cmd));
602 cmd.opcode = READ_ELEMENT_STATUS;
603 _lto2b(first, cmd.sea);
604 _lto2b(count, cmd.count);
605 _lto3b(datalen, cmd.len);
606
607 /*
608 * Send command to changer.
609 */
610 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
611 sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, 0));
612 }
613
614
615 /*
616 * Ask the device about itself and fill in the parameters in our
617 * softc.
618 */
619 int
620 ch_get_params(sc, scsiflags)
621 struct ch_softc *sc;
622 int scsiflags;
623 {
624 struct scsi_mode_sense cmd;
625 struct scsi_mode_sense_data {
626 struct scsi_mode_header header;
627 union {
628 struct page_element_address_assignment ea;
629 struct page_transport_geometry_parameters tg;
630 struct page_device_capabilities cap;
631 } pages;
632 } sense_data;
633 int error, from;
634 u_int8_t *moves, *exchanges;
635
636 /*
637 * Grab info from the element address assignment page.
638 */
639 bzero(&cmd, sizeof(cmd));
640 bzero(&sense_data, sizeof(sense_data));
641 cmd.opcode = MODE_SENSE;
642 cmd.byte2 |= 0x08; /* disable block descriptors */
643 cmd.page = 0x1d;
644 cmd.length = (sizeof(sense_data) & 0xff);
645 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
646 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES,
647 6000, NULL, scsiflags | SCSI_DATA_IN);
648 if (error) {
649 printf("%s: could not sense element address page\n",
650 sc->sc_dev.dv_xname);
651 return (error);
652 }
653
654 sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea);
655 sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte);
656 sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea);
657 sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse);
658 sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea);
659 sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee);
660 sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea);
661 sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte);
662
663 /* XXX ask for page trasport geom */
664
665 /*
666 * Grab info from the capabilities page.
667 */
668 bzero(&cmd, sizeof(cmd));
669 bzero(&sense_data, sizeof(sense_data));
670 cmd.opcode = MODE_SENSE;
671 cmd.byte2 |= 0x08; /* disable block descriptors */
672 cmd.page = 0x1f;
673 cmd.length = (sizeof(sense_data) & 0xff);
674 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
675 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES,
676 6000, NULL, scsiflags | SCSI_DATA_IN);
677 if (error) {
678 printf("%s: could not sense capabilities page\n",
679 sc->sc_dev.dv_xname);
680 return (error);
681 }
682
683 bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
684 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
685 moves = &sense_data.pages.cap.move_from_mt;
686 exchanges = &sense_data.pages.cap.exchange_with_mt;
687 for (from = CHET_MT; from <= CHET_DT; ++from) {
688 sc->sc_movemask[from] = moves[from];
689 sc->sc_exchangemask[from] = exchanges[from];
690 }
691
692 sc->sc_link->flags |= SDEV_MEDIA_LOADED;
693 return (0);
694 }
695
696 void
697 ch_get_quirks(sc, inqbuf)
698 struct ch_softc *sc;
699 struct scsi_inquiry_data *inqbuf;
700 {
701 struct chquirk *match;
702 int priority;
703
704 sc->sc_settledelay = 0;
705
706 match = (struct chquirk *)scsi_inqmatch(inqbuf,
707 (caddr_t)chquirks,
708 sizeof(chquirks) / sizeof(chquirks[0]),
709 sizeof(chquirks[0]), &priority);
710 if (priority != 0) {
711 sc->sc_settledelay = match->cq_settledelay;
712 }
713 }
714