FreeBSD/Linux Kernel Cross Reference
sys/scsi/ch.c
1 /*
2 * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
3 * All rights reserved.
4 *
5 * Partially based on an autochanger driver written by Stefan Grefen
6 * and on an autochanger driver written by the Systems Programming Group
7 * at the University of Utah Computer Science Department.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgements:
19 * This product includes software developed by Jason R. Thorpe
20 * for And Communications, http://www.and.com/
21 * 4. The name of the author may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * $FreeBSD: src/sys/scsi/ch.c,v 1.34.2.3 1999/09/05 08:21:34 peter Exp $
37 */
38
39 #include "opt_scsi.h"
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/errno.h>
44 #include <sys/ioctl.h>
45 #include <sys/buf.h>
46 #include <sys/proc.h>
47 #include <sys/chio.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #ifdef DEVFS
51 #include <sys/devfsext.h>
52 #endif /*DEVFS*/
53
54 #include <scsi/scsi_all.h>
55 #include <scsi/scsi_changer.h>
56 #include <scsi/scsiconf.h>
57
58 #include "ch.h"
59
60 #define CHRETRIES 2
61 #define CHUNIT(x) (minor((x)))
62 #define CHSETUNIT(x, y) makedev(major(x), y)
63
64 struct ch_softc {
65 /*
66 * Human-readable external name. FreeBSD doesn't have a
67 * generic hook for this, so we make it look NetBSD-like. See
68 * comment in chattach().
69 */
70 struct {
71 char dv_xname[16];
72 } sc_dev;
73
74 /*
75 * Pointer back to the scsi_link. See comment in chattach().
76 */
77 struct scsi_link *sc_link;
78
79 int sc_picker; /* current picker */
80
81 /*
82 * The following information is obtained from the
83 * element address assignment page.
84 */
85 int sc_firsts[4]; /* firsts, indexed by CHET_* */
86 int sc_counts[4]; /* counts, indexed by CHET_* */
87
88 /*
89 * The following mask defines the legal combinations
90 * of elements for the MOVE MEDIUM command.
91 */
92 u_int8_t sc_movemask[4];
93
94 /*
95 * As above, but for EXCHANGE MEDIUM.
96 */
97 u_int8_t sc_exchangemask[4];
98
99 int flags; /* misc. info */
100 #ifdef DEVFS
101 void *c_devfs_token;
102 void *ctl_devfs_token;
103 #endif
104 };
105
106 /* sc_flags */
107 #define CHF_ROTATE 0x01 /* picker can rotate */
108
109 static d_open_t chopen;
110 static d_close_t chclose;
111 static d_ioctl_t chioctl;
112
113 #define CDEV_MAJOR 17
114 static struct cdevsw ch_cdevsw =
115 { chopen, chclose, noread, nowrite, /*17*/
116 chioctl, nostop, nullreset, nodevtotty,/* ch */
117 noselect, nommap, nostrat, "ch", NULL, -1 };
118
119 /*
120 * SCSI glue.
121 */
122
123 /*
124 * Under FreeBSD, this macro sets up a bunch of trampoline
125 * functions that indirect through the SCSI subsystem.
126 */
127 SCSI_DEVICE_ENTRIES(ch)
128
129 static int chunit __P((dev_t));
130 static dev_t chsetunit __P((dev_t, int));
131
132 /* So, like, why not "int"? */
133 static errval ch_devopen __P((dev_t, int, int, struct proc *,
134 struct scsi_link *));
135 static errval ch_devioctl __P((dev_t, int, caddr_t, int, struct proc *,
136 struct scsi_link *));
137 static errval ch_devclose __P((dev_t, int, int, struct proc *,
138 struct scsi_link *));
139
140 static struct scsi_device ch_switch = {
141 NULL, /* (*err_handler) */
142 NULL, /* (*start) */
143 NULL, /* (*async) */
144 NULL, /* (*done) */
145 "ch", /* name */
146 0, /* flags */
147 { 0, 0 }, /* spare[2] */
148 0, /* link_flags */
149 chattach, /* (*attach) */
150 "Medium-Changer", /* desc */
151 chopen, /* (*open) */
152 sizeof(struct ch_softc), /* sizeof_scsi_data */
153 T_CHANGER, /* type */
154 chunit, /* (*getunit) */
155 chsetunit, /* (*setunit) */
156 ch_devopen, /* (*dev_open) */
157 ch_devioctl, /* (*dev_ioctl) */
158 ch_devclose, /* (*dev_close) */
159 };
160
161 static int ch_move __P((struct ch_softc *, struct changer_move *));
162 static int ch_exchange __P((struct ch_softc *, struct changer_exchange *));
163 static int ch_position __P((struct ch_softc *, struct changer_position *));
164 static int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *));
165 static int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t));
166 static int ch_get_params __P((struct ch_softc *, int));
167
168 static errval
169 chattach(link)
170 struct scsi_link *link;
171 {
172 struct ch_softc *sc = (struct ch_softc *)(link->sd);
173 u_int32_t unit = link->dev_unit;
174
175 /*
176 * FreeBSD doesn't have any common way of carrying
177 * around a device's external name (i.e. <name><unit>),
178 * so emulate the structure used by NetBSD to keep the
179 * diffs lower.
180 */
181 bzero(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname));
182 sprintf(sc->sc_dev.dv_xname, "%s%d", ch_switch.name, unit);
183
184 /*
185 * FreeBSD gets "softc" info for a device from the
186 * scsi_link argument passed to indirect entry point functions.
187 * NetBSD get scsi_link info from softcs that are
188 * obtained from indexes passed to direct entry point functions.
189 * We emulate the NetBSD behavior here to keep the diffs
190 * lower.
191 */
192 sc->sc_link = link;
193
194 /*
195 * Get information about the device. Note we can't use
196 * interrupts yet.
197 */
198 if (ch_get_params(sc, SCSI_NOSLEEP|SCSI_NOMASK))
199 printf("offline");
200 else {
201 printf("%d slot%s, %d drive%s, %d picker%s",
202 sc->sc_counts[CHET_ST], (sc->sc_counts[CHET_ST] > 1) ?
203 "s" : "",
204 sc->sc_counts[CHET_DT], (sc->sc_counts[CHET_DT] > 1) ?
205 "s" : "",
206 sc->sc_counts[CHET_MT], (sc->sc_counts[CHET_MT] > 1) ?
207 "s" : "");
208 if (sc->sc_counts[CHET_IE])
209 printf(", %d portal%s", sc->sc_counts[CHET_IE],
210 (sc->sc_counts[CHET_IE] > 1) ? "s" : "");
211 if (bootverbose) {
212 printf("\n"); /* This will probably look ugly ... bummer. */
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 }
222 }
223
224 /* Default the current picker. */
225 sc->sc_picker = sc->sc_firsts[CHET_MT];
226
227 #ifdef DEVFS
228 sc->c_devfs_token = devfs_add_devswf(&ch_cdevsw, unit << 4, DV_CHR,
229 UID_ROOT, GID_OPERATOR, 0600,
230 "ch%d", unit);
231 sc->ctl_devfs_token = devfs_add_devswf(&ch_cdevsw,
232 (unit << 4) | SCSI_CONTROL_MASK,
233 DV_CHR,
234 UID_ROOT, GID_OPERATOR, 0600,
235 "ch%d.ctl", unit);
236 #endif
237 return (0);
238 }
239
240 static errval
241 ch_devopen(dev, flags, fmt, p, link)
242 dev_t dev;
243 int flags, fmt;
244 struct proc *p;
245 struct scsi_link *link;
246 {
247 struct ch_softc *sc = (struct ch_softc *)(link->sd);
248 errval error = 0;
249 int unit;
250
251 unit = CHUNIT(dev);
252
253 /*
254 * Only allow one open at a time.
255 */
256 if (link->flags & SDEV_OPEN)
257 return (EBUSY);
258
259 link->flags |= SDEV_OPEN;
260
261 /*
262 * Absorb any unit attention errors. Ignore "not ready"
263 * since this might occur if e.g. a tape isn't actually
264 * loaded in the drive.
265 */
266 (void)scsi_test_unit_ready(link, SCSI_SILENT);
267
268 /*
269 * Make sure our parameters are up to date.
270 */
271 if (error = ch_get_params(sc, 0))
272 goto bad;
273
274 return (0);
275
276 bad:
277 link->flags &= ~SDEV_OPEN;
278 return (error);
279 }
280
281 static errval
282 ch_devclose(dev, flags, fmt, p, link)
283 dev_t dev;
284 int flags, fmt;
285 struct proc *p;
286 struct scsi_link *link;
287 {
288
289 link->flags &= ~SDEV_OPEN;
290 return (0);
291 }
292
293 static errval
294 ch_devioctl(dev, cmd, data, flags, p, link)
295 dev_t dev;
296 int cmd;
297 caddr_t data;
298 int flags;
299 struct proc *p;
300 struct scsi_link *link;
301 {
302 struct ch_softc *sc = (struct ch_softc *)(link->sd);
303 caddr_t elemdata;
304 int error = 0;
305
306 switch (cmd) {
307 case CHIOMOVE:
308 error = ch_move(sc, (struct changer_move *)data);
309 break;
310
311 case CHIOEXCHANGE:
312 error = ch_exchange(sc, (struct changer_exchange *)data);
313 break;
314
315 case CHIOPOSITION:
316 error = ch_position(sc, (struct changer_position *)data);
317 break;
318
319 case CHIOGPICKER:
320 *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT];
321 break;
322
323 case CHIOSPICKER: {
324 int new_picker = *(int *)data;
325
326 if (new_picker > (sc->sc_counts[CHET_MT] - 1))
327 return (EINVAL);
328 sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker;
329 break; }
330
331 case CHIOGPARAMS: {
332 struct changer_params *cp = (struct changer_params *)data;
333
334 cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT];
335 cp->cp_npickers = sc->sc_counts[CHET_MT];
336 cp->cp_nslots = sc->sc_counts[CHET_ST];
337 cp->cp_nportals = sc->sc_counts[CHET_IE];
338 cp->cp_ndrives = sc->sc_counts[CHET_DT];
339 break; }
340
341 case CHIOGSTATUS: {
342 struct changer_element_status *ces =
343 (struct changer_element_status *)data;
344
345 error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data);
346 break; }
347
348 /* Implement prevent/allow? */
349
350 default:
351 error = scsi_do_ioctl(dev, cmd, data, flags, p, link);
352 break;
353 }
354
355 return (error);
356 }
357
358 static int
359 ch_move(sc, cm)
360 struct ch_softc *sc;
361 struct changer_move *cm;
362 {
363 struct scsi_move_medium cmd;
364 u_int16_t fromelem, toelem;
365
366 /*
367 * Check arguments.
368 */
369 if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
370 return (EINVAL);
371 if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
372 (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
373 return (ENODEV);
374
375 /*
376 * Check the request against the changer's capabilities.
377 */
378 if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
379 return (EINVAL);
380
381 /*
382 * Calculate the source and destination elements.
383 */
384 fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
385 toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
386
387 /*
388 * Build the SCSI command.
389 */
390 bzero(&cmd, sizeof(cmd));
391 cmd.opcode = MOVE_MEDIUM;
392 scsi_uto2b(sc->sc_picker, cmd.tea);
393 scsi_uto2b(fromelem, cmd.src);
394 scsi_uto2b(toelem, cmd.dst);
395 if (cm->cm_flags & CM_INVERT)
396 cmd.flags |= MOVE_MEDIUM_INVERT;
397
398 /*
399 * Send command to changer.
400 */
401 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
402 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
403 }
404
405 static int
406 ch_exchange(sc, ce)
407 struct ch_softc *sc;
408 struct changer_exchange *ce;
409 {
410 struct scsi_exchange_medium cmd;
411 u_int16_t src, dst1, dst2;
412
413 /*
414 * Check arguments.
415 */
416 if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
417 (ce->ce_sdsttype > CHET_DT))
418 return (EINVAL);
419 if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
420 (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
421 (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
422 return (ENODEV);
423
424 /*
425 * Check the request against the changer's capabilities.
426 */
427 if (((sc->sc_exchangemask[ce->ce_srctype] &
428 (1 << ce->ce_fdsttype)) == 0) ||
429 ((sc->sc_exchangemask[ce->ce_fdsttype] &
430 (1 << ce->ce_sdsttype)) == 0))
431 return (EINVAL);
432
433 /*
434 * Calculate the source and destination elements.
435 */
436 src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
437 dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
438 dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
439
440 /*
441 * Build the SCSI command.
442 */
443 bzero(&cmd, sizeof(cmd));
444 cmd.opcode = EXCHANGE_MEDIUM;
445 scsi_uto2b(sc->sc_picker, cmd.tea);
446 scsi_uto2b(src, cmd.src);
447 scsi_uto2b(dst1, cmd.fdst);
448 scsi_uto2b(dst2, cmd.sdst);
449 if (ce->ce_flags & CE_INVERT1)
450 cmd.flags |= EXCHANGE_MEDIUM_INV1;
451 if (ce->ce_flags & CE_INVERT2)
452 cmd.flags |= EXCHANGE_MEDIUM_INV2;
453
454 /*
455 * Send command to changer.
456 */
457 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
458 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
459 }
460
461 static int
462 ch_position(sc, cp)
463 struct ch_softc *sc;
464 struct changer_position *cp;
465 {
466 struct scsi_position_to_element cmd;
467 u_int16_t dst;
468
469 /*
470 * Check arguments.
471 */
472 if (cp->cp_type > CHET_DT)
473 return (EINVAL);
474 if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
475 return (ENODEV);
476
477 /*
478 * Calculate the destination element.
479 */
480 dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
481
482 /*
483 * Build the SCSI command.
484 */
485 bzero(&cmd, sizeof(cmd));
486 cmd.opcode = POSITION_TO_ELEMENT;
487 scsi_uto2b(sc->sc_picker, cmd.tea);
488 scsi_uto2b(dst, cmd.dst);
489 if (cp->cp_flags & CP_INVERT)
490 cmd.flags |= POSITION_TO_ELEMENT_INVERT;
491
492 /*
493 * Send command to changer.
494 */
495 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
496 sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
497 }
498
499 /*
500 * Perform a READ ELEMENT STATUS on behalf of the user, and return to
501 * the user only the data the user is interested in (i.e. an array of
502 * flags bytes).
503 */
504 static int
505 ch_usergetelemstatus(sc, chet, uptr)
506 struct ch_softc *sc;
507 int chet;
508 u_int8_t *uptr;
509 {
510 struct read_element_status_header *st_hdr;
511 struct read_element_status_page_header *pg_hdr;
512 struct read_element_status_descriptor *desc;
513 caddr_t data = NULL;
514 size_t size, desclen;
515 int avail, i, error = 0;
516 u_int8_t *user_data = NULL;
517
518 /*
519 * If there are no elements of the requested type in the changer,
520 * the request is invalid.
521 */
522 if (sc->sc_counts[chet] == 0)
523 return (EINVAL);
524
525 /*
526 * Request one descriptor for the given element type. This
527 * is used to determine the size of the descriptor so that
528 * we can allocate enough storage for all of them. We assume
529 * that the first one can fit into 1k.
530 */
531 data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
532 if (error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024))
533 goto done;
534
535 st_hdr = (struct read_element_status_header *)data;
536 pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
537 sizeof(struct read_element_status_header));
538 desclen = scsi_2btou(pg_hdr->edl);
539
540 size = sizeof(struct read_element_status_header) +
541 sizeof(struct read_element_status_page_header) +
542 (desclen * sc->sc_counts[chet]);
543
544 /*
545 * Reallocate storage for descriptors and get them from the
546 * device.
547 */
548 free(data, M_DEVBUF);
549 data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
550 if (error = ch_getelemstatus(sc, sc->sc_firsts[chet],
551 sc->sc_counts[chet], data, size))
552 goto done;
553
554 /*
555 * Fill in the user status array.
556 */
557 st_hdr = (struct read_element_status_header *)data;
558 avail = scsi_2btou(st_hdr->count);
559 if (avail != sc->sc_counts[chet])
560 printf("%s: warning, READ ELEMENT STATUS avail != count\n",
561 sc->sc_dev.dv_xname);
562
563 user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK);
564
565 desc = (struct read_element_status_descriptor *)((u_long)data +
566 sizeof(struct read_element_status_header) +
567 sizeof(struct read_element_status_page_header));
568 for (i = 0; i < avail; ++i) {
569 user_data[i] = desc->flags1;
570 (u_long)desc += desclen;
571 }
572
573 /* Copy flags array out to userspace. */
574 error = copyout(user_data, uptr, avail);
575
576 done:
577 if (data != NULL)
578 free(data, M_DEVBUF);
579 if (user_data != NULL)
580 free(user_data, M_DEVBUF);
581 return (error);
582 }
583
584 static int
585 ch_getelemstatus(sc, first, count, data, datalen)
586 struct ch_softc *sc;
587 int first, count;
588 caddr_t data;
589 size_t datalen;
590 {
591 struct scsi_read_element_status cmd;
592
593 /*
594 * Build SCSI command.
595 */
596 bzero(&cmd, sizeof(cmd));
597 cmd.opcode = READ_ELEMENT_STATUS;
598 scsi_uto2b(first, cmd.sea);
599 scsi_uto2b(count, cmd.count);
600 scsi_uto3b(datalen, cmd.len);
601
602 /*
603 * Send command to changer.
604 */
605 return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
606 sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, SCSI_DATA_IN));
607 }
608
609
610 /*
611 * Ask the device about itself and fill in the parameters in our
612 * softc.
613 */
614 static int
615 ch_get_params(sc, scsiflags)
616 struct ch_softc *sc;
617 int scsiflags;
618 {
619 struct scsi_mode_sense cmd;
620 struct scsi_mode_sense_data {
621 struct scsi_mode_header header;
622 union {
623 struct page_element_address_assignment ea;
624 struct page_transport_geometry_parameters tg;
625 struct page_device_capabilities cap;
626 } pages;
627 } sense_data;
628 int error, from;
629 u_int8_t *moves, *exchanges;
630
631 /*
632 * Grab info from the element address assignment page.
633 */
634 bzero(&cmd, sizeof(cmd));
635 bzero(&sense_data, sizeof(sense_data));
636 cmd.op_code = MODE_SENSE;
637 cmd.byte2 |= 0x08; /* disable block descriptors */
638 cmd.page = 0x1d;
639 cmd.length = (sizeof(sense_data) & 0xff);
640 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
641 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES,
642 6000, NULL, scsiflags | SCSI_DATA_IN);
643 if (error) {
644 printf("%s: could not sense element address page\n",
645 sc->sc_dev.dv_xname);
646 return (error);
647 }
648
649 sc->sc_firsts[CHET_MT] = scsi_2btou(sense_data.pages.ea.mtea);
650 sc->sc_counts[CHET_MT] = scsi_2btou(sense_data.pages.ea.nmte);
651 sc->sc_firsts[CHET_ST] = scsi_2btou(sense_data.pages.ea.fsea);
652 sc->sc_counts[CHET_ST] = scsi_2btou(sense_data.pages.ea.nse);
653 sc->sc_firsts[CHET_IE] = scsi_2btou(sense_data.pages.ea.fieea);
654 sc->sc_counts[CHET_IE] = scsi_2btou(sense_data.pages.ea.niee);
655 sc->sc_firsts[CHET_DT] = scsi_2btou(sense_data.pages.ea.fdtea);
656 sc->sc_counts[CHET_DT] = scsi_2btou(sense_data.pages.ea.ndte);
657
658 /* XXX ask for page trasport geom */
659
660 /*
661 * Grab info from the capabilities page.
662 */
663 bzero(&cmd, sizeof(cmd));
664 bzero(&sense_data, sizeof(sense_data));
665 cmd.op_code = MODE_SENSE;
666 cmd.byte2 |= 0x08; /* disable block descriptors */
667 cmd.page = 0x1f;
668 cmd.length = (sizeof(sense_data) & 0xff);
669 error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
670 sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES,
671 6000, NULL, scsiflags | SCSI_DATA_IN);
672 if (error) {
673 printf("%s: could not sense capabilities page\n",
674 sc->sc_dev.dv_xname);
675 return (error);
676 }
677
678 bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
679 bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
680 moves = &sense_data.pages.cap.move_from_mt;
681 exchanges = &sense_data.pages.cap.exchange_with_mt;
682 for (from = CHET_MT; from <= CHET_DT; ++from) {
683 sc->sc_movemask[from] = moves[from];
684 sc->sc_exchangemask[from] = exchanges[from];
685 }
686
687 sc->sc_link->flags |= SDEV_MEDIA_LOADED;
688 return (0);
689 }
690
691 static int
692 chunit(dev)
693 dev_t dev;
694 {
695
696 return (CHUNIT(dev));
697 }
698
699 static dev_t
700 chsetunit(dev, unit)
701 dev_t dev;
702 int unit;
703 {
704
705 return (CHSETUNIT(dev, unit));
706 }
707
708
709 static ch_devsw_installed = 0;
710
711 static void
712 ch_drvinit(void *unused)
713 {
714 dev_t dev;
715
716 if( ! ch_devsw_installed ) {
717 dev = makedev(CDEV_MAJOR, 0);
718 cdevsw_add(&dev,&ch_cdevsw, NULL);
719 ch_devsw_installed = 1;
720 }
721 }
722
723 SYSINIT(chdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ch_drvinit,NULL)
Cache object: 616da992facc6e233bdfb7e63cc7d41a
|