Line data Source code
1 : /* $OpenBSD: ch.c,v 1.52 2016/03/12 15:16:04 krw Exp $ */
2 : /* $NetBSD: ch.c,v 1.26 1997/02/21 22:06:52 thorpej Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
6 : * All rights reserved.
7 : *
8 : * Partially based on an autochanger driver written by Stefan Grefen
9 : * and on an autochanger driver written by the Systems Programming Group
10 : * at the University of Utah Computer Science Department.
11 : *
12 : * Redistribution and use in source and binary forms, with or without
13 : * modification, are permitted provided that the following conditions
14 : * are met:
15 : * 1. Redistributions of source code must retain the above copyright
16 : * notice, this list of conditions and the following disclaimer.
17 : * 2. Redistributions in binary form must reproduce the above copyright
18 : * notice, this list of conditions and the following disclaimer in the
19 : * documentation and/or other materials provided with the distribution.
20 : * 3. All advertising materials mentioning features or use of this software
21 : * must display the following acknowledgements:
22 : * This product includes software developed by Jason R. Thorpe
23 : * for And Communications, http://www.and.com/
24 : * 4. The name of the author may not be used to endorse or promote products
25 : * derived from this software without specific prior written permission.
26 : *
27 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32 : * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33 : * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
34 : * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35 : * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 : * SUCH DAMAGE.
38 : */
39 :
40 : #include <sys/param.h>
41 : #include <sys/systm.h>
42 : #include <sys/errno.h>
43 : #include <sys/ioctl.h>
44 : #include <sys/chio.h>
45 : #include <sys/device.h>
46 : #include <sys/malloc.h>
47 : #include <sys/pool.h>
48 : #include <sys/conf.h>
49 : #include <sys/fcntl.h>
50 :
51 : #include <scsi/scsi_all.h>
52 : #include <scsi/scsi_changer.h>
53 : #include <scsi/scsiconf.h>
54 :
55 : #define CHRETRIES 2
56 : #define CHUNIT(x) (minor((x)))
57 :
58 : struct ch_softc {
59 : struct device sc_dev; /* generic device info */
60 : struct scsi_link *sc_link; /* link in the SCSI bus */
61 :
62 : int sc_picker; /* current picker */
63 :
64 : /*
65 : * The following information is obtained from the
66 : * element address assignment page.
67 : */
68 : int sc_firsts[4]; /* firsts, indexed by CHET_* */
69 : int sc_counts[4]; /* counts, indexed by CHET_* */
70 :
71 : /*
72 : * The following mask defines the legal combinations
73 : * of elements for the MOVE MEDIUM command.
74 : */
75 : u_int8_t sc_movemask[4];
76 :
77 : /*
78 : * As above, but for EXCHANGE MEDIUM.
79 : */
80 : u_int8_t sc_exchangemask[4];
81 :
82 : int flags; /* misc. info */
83 :
84 : /*
85 : * Quirks; see below.
86 : */
87 : int sc_settledelay; /* delay for settle */
88 :
89 : };
90 :
91 : /* sc_flags */
92 : #define CHF_ROTATE 0x01 /* picker can rotate */
93 :
94 : /* Autoconfiguration glue */
95 : int chmatch(struct device *, void *, void *);
96 : void chattach(struct device *, struct device *, void *);
97 :
98 : struct cfattach ch_ca = {
99 : sizeof(struct ch_softc), chmatch, chattach
100 : };
101 :
102 : struct cfdriver ch_cd = {
103 : NULL, "ch", DV_DULL
104 : };
105 :
106 : const struct scsi_inquiry_pattern ch_patterns[] = {
107 : {T_CHANGER, T_REMOV,
108 : "", "", ""},
109 : };
110 :
111 : int ch_move(struct ch_softc *, struct changer_move *);
112 : int ch_exchange(struct ch_softc *, struct changer_exchange *);
113 : int ch_position(struct ch_softc *, struct changer_position *);
114 : int ch_usergetelemstatus(struct ch_softc *,
115 : struct changer_element_status_request *);
116 : int ch_getelemstatus(struct ch_softc *, int, int, caddr_t, size_t, int);
117 : int ch_get_params(struct ch_softc *, int);
118 : int ch_interpret_sense(struct scsi_xfer *xs);
119 : void ch_get_quirks(struct ch_softc *, struct scsi_inquiry_data *);
120 :
121 : /*
122 : * SCSI changer quirks.
123 : */
124 : struct chquirk {
125 : struct scsi_inquiry_pattern cq_match; /* device id pattern */
126 : int cq_settledelay; /* settle delay, in seconds */
127 : };
128 :
129 : struct chquirk chquirks[] = {
130 : {{T_CHANGER, T_REMOV,
131 : "SPECTRA", "9000", "0200"},
132 : 75},
133 : };
134 :
135 : int
136 0 : chmatch(struct device *parent, void *match, void *aux)
137 : {
138 0 : struct scsi_attach_args *sa = aux;
139 0 : int priority;
140 :
141 0 : (void)scsi_inqmatch(sa->sa_inqbuf,
142 : ch_patterns, nitems(ch_patterns),
143 : sizeof(ch_patterns[0]), &priority);
144 :
145 0 : return (priority);
146 0 : }
147 :
148 : void
149 0 : chattach(struct device *parent, struct device *self, void *aux)
150 : {
151 0 : struct ch_softc *sc = (struct ch_softc *)self;
152 0 : struct scsi_attach_args *sa = aux;
153 0 : struct scsi_link *link = sa->sa_sc_link;
154 :
155 : /* Glue into the SCSI bus */
156 0 : sc->sc_link = link;
157 0 : link->interpret_sense = ch_interpret_sense;
158 0 : link->device_softc = sc;
159 0 : link->openings = 1;
160 :
161 0 : printf("\n");
162 :
163 : /*
164 : * Store our our device's quirks.
165 : */
166 0 : ch_get_quirks(sc, sa->sa_inqbuf);
167 :
168 0 : }
169 :
170 : int
171 0 : chopen(dev_t dev, int flags, int fmt, struct proc *p)
172 : {
173 : struct ch_softc *sc;
174 0 : int oldcounts[4];
175 : int i, unit, error = 0;
176 :
177 0 : unit = CHUNIT(dev);
178 0 : if ((unit >= ch_cd.cd_ndevs) ||
179 0 : ((sc = ch_cd.cd_devs[unit]) == NULL))
180 0 : return (ENXIO);
181 :
182 : /*
183 : * Only allow one open at a time.
184 : */
185 0 : if (sc->sc_link->flags & SDEV_OPEN)
186 0 : return (EBUSY);
187 :
188 0 : sc->sc_link->flags |= SDEV_OPEN;
189 :
190 : /*
191 : * Absorb any unit attention errors. We must notice
192 : * "Not ready" errors as a changer will report "In the
193 : * process of getting ready" any time it must rescan
194 : * itself to determine the state of the changer.
195 : */
196 0 : error = scsi_test_unit_ready(sc->sc_link, TEST_READY_RETRIES,
197 : SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
198 0 : if (error)
199 : goto bad;
200 :
201 : /*
202 : * Get information about the device. Save old information
203 : * so we can decide whether to be verbose about new parameters.
204 : */
205 0 : for (i = 0; i < 4; i++) {
206 0 : oldcounts[i] = sc->sc_counts[i];
207 : }
208 0 : error = ch_get_params(sc, scsi_autoconf);
209 0 : if (error)
210 : goto bad;
211 :
212 0 : for (i = 0; i < 4; i++) {
213 0 : if (oldcounts[i] != sc->sc_counts[i]) {
214 : break;
215 : }
216 : }
217 : if (i < 4) {
218 : #ifdef CHANGER_DEBUG
219 : #define PLURAL(c) (c) == 1 ? "" : "s"
220 : printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
221 : sc->sc_dev.dv_xname,
222 : sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
223 : sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
224 : sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
225 : sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
226 : #undef PLURAL
227 : printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
228 : sc->sc_dev.dv_xname,
229 : sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
230 : sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
231 : printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
232 : sc->sc_dev.dv_xname,
233 : sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
234 : sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
235 : #endif /* CHANGER_DEBUG */
236 : }
237 :
238 : /* Default the current picker. */
239 0 : sc->sc_picker = sc->sc_firsts[CHET_MT];
240 :
241 0 : return (0);
242 :
243 : bad:
244 0 : sc->sc_link->flags &= ~SDEV_OPEN;
245 0 : return (error);
246 0 : }
247 :
248 : int
249 0 : chclose(dev_t dev, int flags, int fmt, struct proc *p)
250 : {
251 0 : struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
252 :
253 0 : sc->sc_link->flags &= ~SDEV_OPEN;
254 0 : return (0);
255 : }
256 :
257 : int
258 0 : chioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
259 : {
260 0 : struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
261 : int error = 0;
262 :
263 : /*
264 : * If this command can change the device's state, we must
265 : * have the device open for writing.
266 : */
267 0 : switch (cmd) {
268 : case CHIOGPICKER:
269 : case CHIOGPARAMS:
270 : case CHIOGSTATUS:
271 : break;
272 :
273 : default:
274 0 : if ((flags & FWRITE) == 0)
275 0 : return (EBADF);
276 : }
277 :
278 0 : switch (cmd) {
279 : case CHIOMOVE:
280 0 : error = ch_move(sc, (struct changer_move *)data);
281 0 : break;
282 :
283 : case CHIOEXCHANGE:
284 0 : error = ch_exchange(sc, (struct changer_exchange *)data);
285 0 : break;
286 :
287 : case CHIOPOSITION:
288 0 : error = ch_position(sc, (struct changer_position *)data);
289 0 : break;
290 :
291 : case CHIOGPICKER:
292 0 : *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT];
293 0 : break;
294 :
295 : case CHIOSPICKER: {
296 0 : int new_picker = *(int *)data;
297 :
298 0 : if (new_picker > (sc->sc_counts[CHET_MT] - 1))
299 0 : return (EINVAL);
300 0 : sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker;
301 0 : break; }
302 :
303 : case CHIOGPARAMS: {
304 0 : struct changer_params *cp = (struct changer_params *)data;
305 :
306 0 : cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT];
307 0 : cp->cp_npickers = sc->sc_counts[CHET_MT];
308 0 : cp->cp_nslots = sc->sc_counts[CHET_ST];
309 0 : cp->cp_nportals = sc->sc_counts[CHET_IE];
310 0 : cp->cp_ndrives = sc->sc_counts[CHET_DT];
311 : break; }
312 :
313 : case CHIOGSTATUS: {
314 : struct changer_element_status_request *cesr =
315 0 : (struct changer_element_status_request *)data;
316 :
317 0 : error = ch_usergetelemstatus(sc, cesr);
318 : break; }
319 :
320 : /* Implement prevent/allow? */
321 :
322 : default:
323 0 : error = scsi_do_ioctl(sc->sc_link, cmd, data, flags);
324 0 : break;
325 : }
326 :
327 0 : return (error);
328 0 : }
329 :
330 : int
331 0 : ch_move(struct ch_softc *sc, struct changer_move *cm)
332 : {
333 : struct scsi_move_medium *cmd;
334 : struct scsi_xfer *xs;
335 : int error;
336 : u_int16_t fromelem, toelem;
337 :
338 : /*
339 : * Check arguments.
340 : */
341 0 : if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
342 0 : return (EINVAL);
343 0 : if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
344 0 : (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
345 0 : return (ENODEV);
346 :
347 : /*
348 : * Check the request against the changer's capabilities.
349 : */
350 0 : if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
351 0 : return (EINVAL);
352 :
353 : /*
354 : * Calculate the source and destination elements.
355 : */
356 0 : fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
357 0 : toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
358 :
359 : /*
360 : * Build the SCSI command.
361 : */
362 0 : xs = scsi_xs_get(sc->sc_link, 0);
363 0 : if (xs == NULL)
364 0 : return (ENOMEM);
365 0 : xs->cmdlen = sizeof(*cmd);
366 0 : xs->retries = CHRETRIES;
367 0 : xs->timeout = 100000;
368 :
369 0 : cmd = (struct scsi_move_medium *)xs->cmd;
370 0 : cmd->opcode = MOVE_MEDIUM;
371 0 : _lto2b(sc->sc_picker, cmd->tea);
372 0 : _lto2b(fromelem, cmd->src);
373 0 : _lto2b(toelem, cmd->dst);
374 0 : if (cm->cm_flags & CM_INVERT)
375 0 : cmd->flags |= MOVE_MEDIUM_INVERT;
376 :
377 0 : error = scsi_xs_sync(xs);
378 0 : scsi_xs_put(xs);
379 :
380 0 : return (error);
381 0 : }
382 :
383 : int
384 0 : ch_exchange(struct ch_softc *sc, struct changer_exchange *ce)
385 : {
386 : struct scsi_exchange_medium *cmd;
387 : struct scsi_xfer *xs;
388 : int error;
389 : u_int16_t src, dst1, dst2;
390 :
391 : /*
392 : * Check arguments.
393 : */
394 0 : if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
395 0 : (ce->ce_sdsttype > CHET_DT))
396 0 : return (EINVAL);
397 0 : if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
398 0 : (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
399 0 : (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
400 0 : return (ENODEV);
401 :
402 : /*
403 : * Check the request against the changer's capabilities.
404 : */
405 0 : if (((sc->sc_exchangemask[ce->ce_srctype] &
406 0 : (1 << ce->ce_fdsttype)) == 0) ||
407 0 : ((sc->sc_exchangemask[ce->ce_fdsttype] &
408 0 : (1 << ce->ce_sdsttype)) == 0))
409 0 : return (EINVAL);
410 :
411 : /*
412 : * Calculate the source and destination elements.
413 : */
414 0 : src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
415 0 : dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
416 0 : dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
417 :
418 : /*
419 : * Build the SCSI command.
420 : */
421 0 : xs = scsi_xs_get(sc->sc_link, 0);
422 0 : if (xs == NULL)
423 0 : return (ENOMEM);
424 0 : xs->cmdlen = sizeof(*cmd);
425 0 : xs->retries = CHRETRIES;
426 0 : xs->timeout = 100000;
427 :
428 0 : cmd = (struct scsi_exchange_medium *)xs->cmd;
429 0 : cmd->opcode = EXCHANGE_MEDIUM;
430 0 : _lto2b(sc->sc_picker, cmd->tea);
431 0 : _lto2b(src, cmd->src);
432 0 : _lto2b(dst1, cmd->fdst);
433 0 : _lto2b(dst2, cmd->sdst);
434 0 : if (ce->ce_flags & CE_INVERT1)
435 0 : cmd->flags |= EXCHANGE_MEDIUM_INV1;
436 0 : if (ce->ce_flags & CE_INVERT2)
437 0 : cmd->flags |= EXCHANGE_MEDIUM_INV2;
438 :
439 0 : error = scsi_xs_sync(xs);
440 0 : scsi_xs_put(xs);
441 :
442 0 : return (error);
443 0 : }
444 :
445 : int
446 0 : ch_position(struct ch_softc *sc, struct changer_position *cp)
447 : {
448 : struct scsi_position_to_element *cmd;
449 : struct scsi_xfer *xs;
450 : int error;
451 : u_int16_t dst;
452 :
453 : /*
454 : * Check arguments.
455 : */
456 0 : if (cp->cp_type > CHET_DT)
457 0 : return (EINVAL);
458 0 : if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
459 0 : return (ENODEV);
460 :
461 : /*
462 : * Calculate the destination element.
463 : */
464 0 : dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
465 :
466 : /*
467 : * Build the SCSI command.
468 : */
469 0 : xs = scsi_xs_get(sc->sc_link, 0);
470 0 : if (xs == NULL)
471 0 : return (ENOMEM);
472 0 : xs->cmdlen = sizeof(*cmd);
473 0 : xs->retries = CHRETRIES;
474 0 : xs->timeout = 100000;
475 :
476 0 : cmd = (struct scsi_position_to_element *)xs->cmd;
477 0 : cmd->opcode = POSITION_TO_ELEMENT;
478 0 : _lto2b(sc->sc_picker, cmd->tea);
479 0 : _lto2b(dst, cmd->dst);
480 0 : if (cp->cp_flags & CP_INVERT)
481 0 : cmd->flags |= POSITION_TO_ELEMENT_INVERT;
482 :
483 0 : error = scsi_xs_sync(xs);
484 0 : scsi_xs_put(xs);
485 :
486 0 : return (error);
487 0 : }
488 :
489 : /*
490 : * Copy a volume tag to a volume_tag struct, converting SCSI byte order
491 : * to host native byte order in the volume serial number. The volume
492 : * label as returned by the changer is transferred to user mode as
493 : * nul-terminated string. Volume labels are truncated at the first
494 : * space, as suggested by SCSI-2.
495 : */
496 : static void
497 0 : copy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
498 : {
499 : int i;
500 :
501 0 : for (i=0; i<CH_VOLTAG_MAXLEN; i++) {
502 0 : char c = voltag->vif[i];
503 0 : if (c && c != ' ')
504 0 : uvoltag->cv_volid[i] = c;
505 : else
506 0 : break;
507 0 : }
508 0 : uvoltag->cv_volid[i] = '\0';
509 0 : uvoltag->cv_serial = _2btol(voltag->vsn);
510 0 : }
511 :
512 : /*
513 : * Copy an an element status descriptor to a user-mode
514 : * changer_element_status structure.
515 : */
516 : static void
517 0 : copy_element_status(int flags, struct read_element_status_descriptor *desc,
518 : struct changer_element_status *ces)
519 : {
520 0 : ces->ces_flags = desc->flags1;
521 :
522 0 : if (flags & READ_ELEMENT_STATUS_PVOLTAG)
523 0 : copy_voltag(&ces->ces_pvoltag, &desc->pvoltag);
524 0 : if (flags & READ_ELEMENT_STATUS_AVOLTAG)
525 0 : copy_voltag(&ces->ces_avoltag, &desc->avoltag);
526 0 : }
527 :
528 : /*
529 : * Perform a READ ELEMENT STATUS on behalf of the user, and return to
530 : * the user only the data the user is interested in (i.e. an array of
531 : * changer_element_status structures)
532 : */
533 : int
534 0 : ch_usergetelemstatus(struct ch_softc *sc,
535 : struct changer_element_status_request *cesr)
536 : {
537 : struct changer_element_status *user_data = NULL;
538 : struct read_element_status_header *st_hdr;
539 : struct read_element_status_page_header *pg_hdr;
540 : caddr_t desc;
541 : caddr_t data = NULL;
542 : size_t size, desclen, udsize;
543 0 : int chet = cesr->cesr_type;
544 : int avail, i, error = 0;
545 0 : int want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;
546 :
547 : /*
548 : * If there are no elements of the requested type in the changer,
549 : * the request is invalid.
550 : */
551 0 : if (sc->sc_counts[chet] == 0)
552 0 : return (EINVAL);
553 :
554 : /*
555 : * Request one descriptor for the given element type. This
556 : * is used to determine the size of the descriptor so that
557 : * we can allocate enough storage for all of them. We assume
558 : * that the first one can fit into 1k.
559 : */
560 : size = 1024;
561 0 : data = dma_alloc(size, PR_WAITOK);
562 0 : error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, size,
563 : want_voltags);
564 0 : if (error)
565 : goto done;
566 :
567 0 : st_hdr = (struct read_element_status_header *)data;
568 0 : pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);
569 0 : desclen = _2btol(pg_hdr->edl);
570 :
571 0 : dma_free(data, size);
572 :
573 : /*
574 : * Reallocate storage for descriptors and get them from the
575 : * device.
576 : */
577 : size = sizeof(struct read_element_status_header) +
578 0 : sizeof(struct read_element_status_page_header) +
579 0 : (desclen * sc->sc_counts[chet]);
580 0 : data = dma_alloc(size, PR_WAITOK);
581 0 : error = ch_getelemstatus(sc, sc->sc_firsts[chet],
582 0 : sc->sc_counts[chet], data, size, want_voltags);
583 0 : if (error)
584 : goto done;
585 :
586 : /*
587 : * Fill in the user status array.
588 : */
589 0 : st_hdr = (struct read_element_status_header *)data;
590 0 : pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);
591 :
592 0 : avail = _2btol(st_hdr->count);
593 0 : if (avail != sc->sc_counts[chet]) {
594 : error = EINVAL;
595 0 : goto done;
596 : }
597 :
598 0 : user_data = mallocarray(avail, sizeof(struct changer_element_status),
599 : M_DEVBUF, M_WAITOK | M_ZERO);
600 0 : udsize = avail * sizeof(struct changer_element_status);
601 :
602 0 : desc = (caddr_t)(pg_hdr + 1);
603 0 : for (i = 0; i < avail; ++i) {
604 0 : struct changer_element_status *ces = &(user_data[i]);
605 0 : copy_element_status(pg_hdr->flags,
606 0 : (struct read_element_status_descriptor *)desc, ces);
607 0 : desc += desclen;
608 : }
609 :
610 : /* Copy array out to userspace. */
611 0 : error = copyout(user_data, cesr->cesr_data, udsize);
612 :
613 : done:
614 0 : if (data != NULL)
615 0 : dma_free(data, size);
616 0 : if (user_data != NULL)
617 0 : free(user_data, M_DEVBUF, udsize);
618 0 : return (error);
619 0 : }
620 :
621 : int
622 0 : ch_getelemstatus(struct ch_softc *sc, int first, int count, caddr_t data,
623 : size_t datalen, int voltag)
624 : {
625 : struct scsi_read_element_status *cmd;
626 : struct scsi_xfer *xs;
627 : int error;
628 :
629 : /*
630 : * Build SCSI command.
631 : */
632 0 : xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
633 0 : if (xs == NULL)
634 0 : return (ENOMEM);
635 0 : xs->cmdlen = sizeof(*cmd);
636 0 : xs->data = data;
637 0 : xs->datalen = datalen;
638 0 : xs->retries = CHRETRIES;
639 0 : xs->timeout = 100000;
640 :
641 0 : cmd = (struct scsi_read_element_status *)xs->cmd;
642 0 : cmd->opcode = READ_ELEMENT_STATUS;
643 0 : _lto2b(first, cmd->sea);
644 0 : _lto2b(count, cmd->count);
645 0 : _lto3b(datalen, cmd->len);
646 0 : if (voltag)
647 0 : cmd->byte2 |= READ_ELEMENT_STATUS_VOLTAG;
648 :
649 0 : error = scsi_xs_sync(xs);
650 0 : scsi_xs_put(xs);
651 :
652 0 : return (error);
653 0 : }
654 :
655 : /*
656 : * Ask the device about itself and fill in the parameters in our
657 : * softc.
658 : */
659 : int
660 0 : ch_get_params(struct ch_softc *sc, int flags)
661 : {
662 : union scsi_mode_sense_buf *data;
663 0 : struct page_element_address_assignment *ea;
664 0 : struct page_device_capabilities *cap;
665 : int error, from;
666 : u_int8_t *moves, *exchanges;
667 :
668 0 : data = dma_alloc(sizeof(*data), PR_NOWAIT);
669 0 : if (data == NULL)
670 0 : return (ENOMEM);
671 :
672 : /*
673 : * Grab info from the element address assignment page (0x1d).
674 : */
675 0 : error = scsi_do_mode_sense(sc->sc_link, 0x1d, data,
676 0 : (void **)&ea, NULL, NULL, NULL, sizeof(*ea), flags, NULL);
677 0 : if (error == 0 && ea == NULL)
678 : error = EIO;
679 0 : if (error != 0) {
680 : #ifdef CHANGER_DEBUG
681 : printf("%s: could not sense element address page\n",
682 : sc->sc_dev.dv_xname);
683 : #endif
684 0 : dma_free(data, sizeof(*data));
685 0 : return (error);
686 : }
687 :
688 0 : sc->sc_firsts[CHET_MT] = _2btol(ea->mtea);
689 0 : sc->sc_counts[CHET_MT] = _2btol(ea->nmte);
690 0 : sc->sc_firsts[CHET_ST] = _2btol(ea->fsea);
691 0 : sc->sc_counts[CHET_ST] = _2btol(ea->nse);
692 0 : sc->sc_firsts[CHET_IE] = _2btol(ea->fieea);
693 0 : sc->sc_counts[CHET_IE] = _2btol(ea->niee);
694 0 : sc->sc_firsts[CHET_DT] = _2btol(ea->fdtea);
695 0 : sc->sc_counts[CHET_DT] = _2btol(ea->ndte);
696 :
697 : /* XXX Ask for transport geometry page. */
698 :
699 : /*
700 : * Grab info from the capabilities page (0x1f).
701 : */
702 0 : error = scsi_do_mode_sense(sc->sc_link, 0x1f, data,
703 0 : (void **)&cap, NULL, NULL, NULL, sizeof(*cap), flags, NULL);
704 0 : if (cap == NULL)
705 : error = EIO;
706 0 : if (error != 0) {
707 : #ifdef CHANGER_DEBUG
708 : printf("%s: could not sense capabilities page\n",
709 : sc->sc_dev.dv_xname);
710 : #endif
711 0 : dma_free(data, sizeof(*data));
712 0 : return (error);
713 : }
714 :
715 0 : bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
716 0 : bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
717 0 : moves = &cap->move_from_mt;
718 0 : exchanges = &cap->exchange_with_mt;
719 0 : for (from = CHET_MT; from <= CHET_DT; ++from) {
720 0 : sc->sc_movemask[from] = moves[from];
721 0 : sc->sc_exchangemask[from] = exchanges[from];
722 : }
723 :
724 0 : sc->sc_link->flags |= SDEV_MEDIA_LOADED;
725 0 : dma_free(data, sizeof(*data));
726 0 : return (0);
727 0 : }
728 :
729 : void
730 0 : ch_get_quirks(struct ch_softc *sc, struct scsi_inquiry_data *inqbuf)
731 : {
732 : const struct chquirk *match;
733 0 : int priority;
734 :
735 0 : sc->sc_settledelay = 0;
736 :
737 0 : match = (const struct chquirk *)scsi_inqmatch(inqbuf,
738 : (caddr_t)chquirks,
739 : sizeof(chquirks) / sizeof(chquirks[0]),
740 : sizeof(chquirks[0]), &priority);
741 0 : if (priority != 0) {
742 0 : sc->sc_settledelay = match->cq_settledelay;
743 0 : }
744 0 : }
745 :
746 : /*
747 : * Look at the returned sense and act on the error and detirmine
748 : * The unix error number to pass back... (0 = report no error)
749 : * (-1 = continue processing)
750 : */
751 : int
752 0 : ch_interpret_sense(struct scsi_xfer *xs)
753 : {
754 0 : struct scsi_sense_data *sense = &xs->sense;
755 0 : struct scsi_link *link = xs->sc_link;
756 0 : u_int8_t serr = sense->error_code & SSD_ERRCODE;
757 0 : u_int8_t skey = sense->flags & SSD_KEY;
758 :
759 0 : if (((link->flags & SDEV_OPEN) == 0) ||
760 0 : (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
761 0 : return (scsi_interpret_sense(xs));
762 :
763 0 : switch (skey) {
764 :
765 : /*
766 : * We do custom processing in ch for the unit becoming ready case.
767 : * in this case we do not allow xs->retries to be decremented
768 : * only on the "Unit Becoming Ready" case. This is because tape
769 : * changers report "Unit Becoming Ready" when they rescan their
770 : * state (i.e. when the door got opened) and can take a long time
771 : * for large units. Rather than having a massive timeout for
772 : * all operations (which would cause other problems) we allow
773 : * changers to wait (but be interruptable with Ctrl-C) forever
774 : * as long as they are reporting that they are becoming ready.
775 : * all other cases are handled as per the default.
776 : */
777 : case SKEY_NOT_READY:
778 0 : if ((xs->flags & SCSI_IGNORE_NOT_READY) != 0)
779 0 : return (0);
780 0 : switch (ASC_ASCQ(sense)) {
781 : case SENSE_NOT_READY_BECOMING_READY:
782 : SC_DEBUG(link, SDEV_DB1, ("not ready: busy (%#x)\n",
783 : sense->add_sense_code_qual));
784 : /* don't count this as a retry */
785 0 : xs->retries++;
786 0 : return (scsi_delay(xs, 1));
787 : default:
788 0 : return (scsi_interpret_sense(xs));
789 : }
790 : default:
791 0 : return (scsi_interpret_sense(xs));
792 : }
793 0 : }
|