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