1 | /* $NetBSD: ch.c,v 1.91 2016/11/20 15:37:19 mlelstv Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1996, 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
9 | * NASA Ames Research Center. |
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 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: ch.c,v 1.91 2016/11/20 15:37:19 mlelstv Exp $" ); |
35 | |
36 | #include <sys/param.h> |
37 | #include <sys/systm.h> |
38 | #include <sys/kernel.h> |
39 | #include <sys/errno.h> |
40 | #include <sys/ioctl.h> |
41 | #include <sys/buf.h> |
42 | #include <sys/proc.h> |
43 | #include <sys/chio.h> |
44 | #include <sys/device.h> |
45 | #include <sys/malloc.h> |
46 | #include <sys/conf.h> |
47 | #include <sys/fcntl.h> |
48 | #include <sys/vnode.h> |
49 | #include <sys/time.h> |
50 | #include <sys/select.h> |
51 | #include <sys/poll.h> |
52 | |
53 | #include <dev/scsipi/scsipi_all.h> |
54 | #include <dev/scsipi/scsi_all.h> |
55 | #include <dev/scsipi/scsi_changer.h> |
56 | #include <dev/scsipi/scsiconf.h> |
57 | |
58 | #define CHRETRIES 2 |
59 | #define CHTIMEOUT (5 * 60 * 1000) |
60 | |
61 | #define CHUNIT(x) (minor((x))) |
62 | |
63 | struct ch_softc { |
64 | device_t sc_dev; /* generic device info */ |
65 | struct scsipi_periph *sc_periph;/* our periph data */ |
66 | |
67 | u_int sc_events; /* event bitmask */ |
68 | struct selinfo sc_selq; /* select/poll queue for events */ |
69 | |
70 | int sc_flags; /* misc. info */ |
71 | |
72 | int sc_picker; /* current picker */ |
73 | |
74 | /* |
75 | * The following information is obtained from the |
76 | * element address assignment page. |
77 | */ |
78 | int sc_firsts[4]; /* firsts, indexed by CHET_* */ |
79 | int sc_counts[4]; /* counts, indexed by CHET_* */ |
80 | |
81 | /* |
82 | * The following mask defines the legal combinations |
83 | * of elements for the MOVE MEDIUM command. |
84 | */ |
85 | u_int8_t sc_movemask[4]; |
86 | |
87 | /* |
88 | * As above, but for EXCHANGE MEDIUM. |
89 | */ |
90 | u_int8_t sc_exchangemask[4]; |
91 | |
92 | /* |
93 | * Quirks; see below. |
94 | */ |
95 | int sc_settledelay; /* delay for settle */ |
96 | |
97 | }; |
98 | |
99 | /* sc_flags */ |
100 | #define CHF_ROTATE 0x01 /* picker can rotate */ |
101 | |
102 | /* Autoconfiguration glue */ |
103 | static int chmatch(device_t, cfdata_t, void *); |
104 | static void chattach(device_t, device_t, void *); |
105 | |
106 | CFATTACH_DECL_NEW(ch, sizeof(struct ch_softc), |
107 | chmatch, chattach, NULL, NULL); |
108 | |
109 | extern struct cfdriver ch_cd; |
110 | |
111 | static struct scsipi_inquiry_pattern ch_patterns[] = { |
112 | {T_CHANGER, T_REMOV, |
113 | "" , "" , "" }, |
114 | }; |
115 | |
116 | static dev_type_open(chopen); |
117 | static dev_type_close(chclose); |
118 | static dev_type_read(chread); |
119 | static dev_type_ioctl(chioctl); |
120 | static dev_type_poll(chpoll); |
121 | static dev_type_kqfilter(chkqfilter); |
122 | |
123 | const struct cdevsw ch_cdevsw = { |
124 | .d_open = chopen, |
125 | .d_close = chclose, |
126 | .d_read = chread, |
127 | .d_write = nowrite, |
128 | .d_ioctl = chioctl, |
129 | .d_stop = nostop, |
130 | .d_tty = notty, |
131 | .d_poll = chpoll, |
132 | .d_mmap = nommap, |
133 | .d_kqfilter = chkqfilter, |
134 | .d_discard = nodiscard, |
135 | .d_flag = D_OTHER | D_MPSAFE |
136 | }; |
137 | |
138 | /* SCSI glue */ |
139 | static int ch_interpret_sense(struct scsipi_xfer *); |
140 | |
141 | static const struct scsipi_periphsw ch_switch = { |
142 | ch_interpret_sense, /* check our error handler first */ |
143 | NULL, /* no queue; our commands are synchronous */ |
144 | NULL, /* have no async handler */ |
145 | NULL, /* nothing to be done when xfer is done */ |
146 | }; |
147 | |
148 | static int ch_move(struct ch_softc *, struct changer_move_request *); |
149 | static int ch_exchange(struct ch_softc *, |
150 | struct changer_exchange_request *); |
151 | static int ch_position(struct ch_softc *, |
152 | struct changer_position_request *); |
153 | static int ch_ielem(struct ch_softc *); |
154 | static int ch_ousergetelemstatus(struct ch_softc *, int, u_int8_t *); |
155 | static int ch_usergetelemstatus(struct ch_softc *, |
156 | struct changer_element_status_request *); |
157 | static int ch_getelemstatus(struct ch_softc *, int, int, void *, |
158 | size_t, int, int); |
159 | static int ch_setvoltag(struct ch_softc *, |
160 | struct changer_set_voltag_request *); |
161 | static int ch_get_params(struct ch_softc *, int); |
162 | static void ch_get_quirks(struct ch_softc *, |
163 | struct scsipi_inquiry_pattern *); |
164 | static void ch_event(struct ch_softc *, u_int); |
165 | static int ch_map_element(struct ch_softc *, u_int16_t, int *, int *); |
166 | |
167 | static void ch_voltag_convert_in(const struct changer_volume_tag *, |
168 | struct changer_voltag *); |
169 | static int ch_voltag_convert_out(const struct changer_voltag *, |
170 | struct changer_volume_tag *); |
171 | |
172 | /* |
173 | * SCSI changer quirks. |
174 | */ |
175 | struct chquirk { |
176 | struct scsipi_inquiry_pattern cq_match; /* device id pattern */ |
177 | int cq_settledelay; /* settle delay, in seconds */ |
178 | }; |
179 | |
180 | static const struct chquirk chquirks[] = { |
181 | {{T_CHANGER, T_REMOV, |
182 | "SPECTRA" , "9000" , "0200" }, |
183 | 75}, |
184 | }; |
185 | |
186 | static int |
187 | chmatch(device_t parent, cfdata_t match, |
188 | void *aux) |
189 | { |
190 | struct scsipibus_attach_args *sa = aux; |
191 | int priority; |
192 | |
193 | (void)scsipi_inqmatch(&sa->sa_inqbuf, |
194 | (void *)ch_patterns, sizeof(ch_patterns) / sizeof(ch_patterns[0]), |
195 | sizeof(ch_patterns[0]), &priority); |
196 | |
197 | return (priority); |
198 | } |
199 | |
200 | static void |
201 | chattach(device_t parent, device_t self, void *aux) |
202 | { |
203 | struct ch_softc *sc = device_private(self); |
204 | struct scsipibus_attach_args *sa = aux; |
205 | struct scsipi_periph *periph = sa->sa_periph; |
206 | |
207 | sc->sc_dev = self; |
208 | selinit(&sc->sc_selq); |
209 | |
210 | /* Glue into the SCSI bus */ |
211 | sc->sc_periph = periph; |
212 | periph->periph_dev = sc->sc_dev; |
213 | periph->periph_switch = &ch_switch; |
214 | |
215 | printf("\n" ); |
216 | |
217 | /* |
218 | * Find out our device's quirks. |
219 | */ |
220 | ch_get_quirks(sc, &sa->sa_inqbuf); |
221 | |
222 | /* |
223 | * Some changers require a long time to settle out, to do |
224 | * tape inventory, for instance. |
225 | */ |
226 | if (sc->sc_settledelay) { |
227 | printf("%s: waiting %d seconds for changer to settle...\n" , |
228 | device_xname(sc->sc_dev), sc->sc_settledelay); |
229 | delay(1000000 * sc->sc_settledelay); |
230 | } |
231 | |
232 | /* |
233 | * Get information about the device. Note we can't use |
234 | * interrupts yet. |
235 | */ |
236 | if (ch_get_params(sc, XS_CTL_DISCOVERY|XS_CTL_IGNORE_MEDIA_CHANGE)) |
237 | printf("%s: offline\n" , device_xname(sc->sc_dev)); |
238 | else { |
239 | #define PLURAL(c) (c) == 1 ? "" : "s" |
240 | printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n" , |
241 | device_xname(sc->sc_dev), |
242 | sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]), |
243 | sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]), |
244 | sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]), |
245 | sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE])); |
246 | #undef PLURAL |
247 | #ifdef CHANGER_DEBUG |
248 | printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n" , |
249 | device_xname(sc->sc_dev), |
250 | sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], |
251 | sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); |
252 | printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n" , |
253 | device_xname(sc->sc_dev), |
254 | sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], |
255 | sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); |
256 | #endif /* CHANGER_DEBUG */ |
257 | } |
258 | |
259 | /* Default the current picker. */ |
260 | sc->sc_picker = sc->sc_firsts[CHET_MT]; |
261 | } |
262 | |
263 | static int |
264 | chopen(dev_t dev, int flags, int fmt, struct lwp *l) |
265 | { |
266 | struct ch_softc *sc; |
267 | struct scsipi_periph *periph; |
268 | struct scsipi_adapter *adapt; |
269 | int unit, error; |
270 | |
271 | unit = CHUNIT(dev); |
272 | sc = device_lookup_private(&ch_cd, unit); |
273 | if (sc == NULL) |
274 | return (ENXIO); |
275 | |
276 | periph = sc->sc_periph; |
277 | adapt = periph->periph_channel->chan_adapter; |
278 | |
279 | /* |
280 | * Only allow one open at a time. |
281 | */ |
282 | if (periph->periph_flags & PERIPH_OPEN) |
283 | return (EBUSY); |
284 | |
285 | if ((error = scsipi_adapter_addref(adapt)) != 0) |
286 | return (error); |
287 | |
288 | /* |
289 | * Make sure the unit is on-line. If a UNIT ATTENTION |
290 | * occurs, we will mark that an Init-Element-Status is |
291 | * needed in ch_get_params(). |
292 | * |
293 | * We ignore NOT READY in case e.g a magazine isn't actually |
294 | * loaded into the changer or a tape isn't in the drive. |
295 | */ |
296 | error = scsipi_test_unit_ready(periph, XS_CTL_IGNORE_NOT_READY); |
297 | if (error) |
298 | goto bad; |
299 | |
300 | periph->periph_flags |= PERIPH_OPEN; |
301 | |
302 | /* |
303 | * Make sure our parameters are up to date. |
304 | */ |
305 | if ((error = ch_get_params(sc, 0)) != 0) |
306 | goto bad; |
307 | |
308 | return (0); |
309 | |
310 | bad: |
311 | scsipi_adapter_delref(adapt); |
312 | periph->periph_flags &= ~PERIPH_OPEN; |
313 | return (error); |
314 | } |
315 | |
316 | static int |
317 | chclose(dev_t dev, int flags, int fmt, struct lwp *l) |
318 | { |
319 | struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); |
320 | struct scsipi_periph *periph = sc->sc_periph; |
321 | struct scsipi_adapter *adapt = periph->periph_channel->chan_adapter; |
322 | |
323 | scsipi_wait_drain(periph); |
324 | |
325 | scsipi_adapter_delref(adapt); |
326 | |
327 | sc->sc_events = 0; |
328 | |
329 | periph->periph_flags &= ~PERIPH_OPEN; |
330 | return (0); |
331 | } |
332 | |
333 | static int |
334 | chread(dev_t dev, struct uio *uio, int flags) |
335 | { |
336 | struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); |
337 | int error; |
338 | |
339 | if (uio->uio_resid != CHANGER_EVENT_SIZE) |
340 | return (EINVAL); |
341 | |
342 | /* |
343 | * Read never blocks; if there are no events pending, we just |
344 | * return an all-clear bitmask. |
345 | */ |
346 | error = uiomove(&sc->sc_events, CHANGER_EVENT_SIZE, uio); |
347 | if (error == 0) |
348 | sc->sc_events = 0; |
349 | return (error); |
350 | } |
351 | |
352 | static int |
353 | chioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) |
354 | { |
355 | struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); |
356 | int error = 0; |
357 | |
358 | /* |
359 | * If this command can change the device's state, we must |
360 | * have the device open for writing. |
361 | */ |
362 | switch (cmd) { |
363 | case CHIOGPICKER: |
364 | case CHIOGPARAMS: |
365 | case OCHIOGSTATUS: |
366 | break; |
367 | |
368 | default: |
369 | if ((flags & FWRITE) == 0) |
370 | return (EBADF); |
371 | } |
372 | |
373 | switch (cmd) { |
374 | case CHIOMOVE: |
375 | error = ch_move(sc, (struct changer_move_request *)data); |
376 | break; |
377 | |
378 | case CHIOEXCHANGE: |
379 | error = ch_exchange(sc, |
380 | (struct changer_exchange_request *)data); |
381 | break; |
382 | |
383 | case CHIOPOSITION: |
384 | error = ch_position(sc, |
385 | (struct changer_position_request *)data); |
386 | break; |
387 | |
388 | case CHIOGPICKER: |
389 | *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; |
390 | break; |
391 | |
392 | case CHIOSPICKER: |
393 | { |
394 | int new_picker = *(int *)data; |
395 | |
396 | if (new_picker > (sc->sc_counts[CHET_MT] - 1)) |
397 | return (EINVAL); |
398 | sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; |
399 | break; |
400 | } |
401 | |
402 | case CHIOGPARAMS: |
403 | { |
404 | struct changer_params *cp = (struct changer_params *)data; |
405 | |
406 | cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; |
407 | cp->cp_npickers = sc->sc_counts[CHET_MT]; |
408 | cp->cp_nslots = sc->sc_counts[CHET_ST]; |
409 | cp->cp_nportals = sc->sc_counts[CHET_IE]; |
410 | cp->cp_ndrives = sc->sc_counts[CHET_DT]; |
411 | break; |
412 | } |
413 | |
414 | case CHIOIELEM: |
415 | error = ch_ielem(sc); |
416 | if (error == 0) { |
417 | sc->sc_periph->periph_flags |= PERIPH_MEDIA_LOADED; |
418 | } |
419 | break; |
420 | |
421 | case OCHIOGSTATUS: |
422 | { |
423 | struct ochanger_element_status_request *cesr = |
424 | (struct ochanger_element_status_request *)data; |
425 | |
426 | error = ch_ousergetelemstatus(sc, cesr->cesr_type, |
427 | cesr->cesr_data); |
428 | break; |
429 | } |
430 | |
431 | case CHIOGSTATUS: |
432 | error = ch_usergetelemstatus(sc, |
433 | (struct changer_element_status_request *)data); |
434 | break; |
435 | |
436 | case CHIOSVOLTAG: |
437 | error = ch_setvoltag(sc, |
438 | (struct changer_set_voltag_request *)data); |
439 | break; |
440 | |
441 | /* Implement prevent/allow? */ |
442 | |
443 | default: |
444 | error = scsipi_do_ioctl(sc->sc_periph, dev, cmd, data, |
445 | flags, l); |
446 | break; |
447 | } |
448 | |
449 | return (error); |
450 | } |
451 | |
452 | static int |
453 | chpoll(dev_t dev, int events, struct lwp *l) |
454 | { |
455 | struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); |
456 | int revents; |
457 | |
458 | revents = events & (POLLOUT | POLLWRNORM); |
459 | |
460 | if ((events & (POLLIN | POLLRDNORM)) == 0) |
461 | return (revents); |
462 | |
463 | if (sc->sc_events == 0) |
464 | revents |= events & (POLLIN | POLLRDNORM); |
465 | else |
466 | selrecord(l, &sc->sc_selq); |
467 | |
468 | return (revents); |
469 | } |
470 | |
471 | static void |
472 | filt_chdetach(struct knote *kn) |
473 | { |
474 | struct ch_softc *sc = kn->kn_hook; |
475 | |
476 | SLIST_REMOVE(&sc->sc_selq.sel_klist, kn, knote, kn_selnext); |
477 | } |
478 | |
479 | static int |
480 | filt_chread(struct knote *kn, long hint) |
481 | { |
482 | struct ch_softc *sc = kn->kn_hook; |
483 | |
484 | if (sc->sc_events == 0) |
485 | return (0); |
486 | kn->kn_data = CHANGER_EVENT_SIZE; |
487 | return (1); |
488 | } |
489 | |
490 | static const struct filterops chread_filtops = |
491 | { 1, NULL, filt_chdetach, filt_chread }; |
492 | |
493 | static const struct filterops chwrite_filtops = |
494 | { 1, NULL, filt_chdetach, filt_seltrue }; |
495 | |
496 | static int |
497 | chkqfilter(dev_t dev, struct knote *kn) |
498 | { |
499 | struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); |
500 | struct klist *klist; |
501 | |
502 | switch (kn->kn_filter) { |
503 | case EVFILT_READ: |
504 | klist = &sc->sc_selq.sel_klist; |
505 | kn->kn_fop = &chread_filtops; |
506 | break; |
507 | |
508 | case EVFILT_WRITE: |
509 | klist = &sc->sc_selq.sel_klist; |
510 | kn->kn_fop = &chwrite_filtops; |
511 | break; |
512 | |
513 | default: |
514 | return (EINVAL); |
515 | } |
516 | |
517 | kn->kn_hook = sc; |
518 | |
519 | SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
520 | |
521 | return (0); |
522 | } |
523 | |
524 | static int |
525 | ch_interpret_sense(struct scsipi_xfer *xs) |
526 | { |
527 | struct scsipi_periph *periph = xs->xs_periph; |
528 | struct scsi_sense_data *sense = &xs->sense.scsi_sense; |
529 | struct ch_softc *sc = device_private(periph->periph_dev); |
530 | u_int16_t asc_ascq; |
531 | |
532 | /* |
533 | * If the periph is already recovering, just do the |
534 | * normal error recovering. |
535 | */ |
536 | if (periph->periph_flags & PERIPH_RECOVERING) |
537 | return (EJUSTRETURN); |
538 | |
539 | /* |
540 | * If it isn't an extended or extended/deferred error, let |
541 | * the generic code handle it. |
542 | */ |
543 | if (SSD_RCODE(sense->response_code) != SSD_RCODE_CURRENT && |
544 | SSD_RCODE(sense->response_code) != SSD_RCODE_DEFERRED) |
545 | return (EJUSTRETURN); |
546 | |
547 | /* |
548 | * We're only interested in condtions that |
549 | * indicate potential inventory violation. |
550 | * |
551 | * We use ASC/ASCQ codes for this. |
552 | */ |
553 | |
554 | asc_ascq = (((u_int16_t) sense->asc) << 8) | |
555 | sense->ascq; |
556 | |
557 | switch (asc_ascq) { |
558 | case 0x2800: |
559 | /* "Not Ready To Ready Transition (Medium May Have Changed)" */ |
560 | case 0x2900: |
561 | /* "Power On, Reset, or Bus Device Reset Occurred" */ |
562 | sc->sc_periph->periph_flags &= ~PERIPH_MEDIA_LOADED; |
563 | /* |
564 | * Enqueue an Element-Status-Changed event, and wake up |
565 | * any processes waiting for them. |
566 | */ |
567 | if ((xs->xs_control & XS_CTL_IGNORE_MEDIA_CHANGE) == 0) |
568 | ch_event(sc, CHEV_ELEMENT_STATUS_CHANGED); |
569 | break; |
570 | default: |
571 | break; |
572 | } |
573 | |
574 | return (EJUSTRETURN); |
575 | } |
576 | |
577 | static void |
578 | ch_event(struct ch_softc *sc, u_int event) |
579 | { |
580 | |
581 | sc->sc_events |= event; |
582 | selnotify(&sc->sc_selq, 0, 0); |
583 | } |
584 | |
585 | static int |
586 | ch_move(struct ch_softc *sc, struct changer_move_request *cm) |
587 | { |
588 | struct scsi_move_medium cmd; |
589 | u_int16_t fromelem, toelem; |
590 | |
591 | /* |
592 | * Check arguments. |
593 | */ |
594 | if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) |
595 | return (EINVAL); |
596 | if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || |
597 | (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) |
598 | return (ENODEV); |
599 | |
600 | /* |
601 | * Check the request against the changer's capabilities. |
602 | */ |
603 | if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) |
604 | return (ENODEV); |
605 | |
606 | /* |
607 | * Calculate the source and destination elements. |
608 | */ |
609 | fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; |
610 | toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; |
611 | |
612 | /* |
613 | * Build the SCSI command. |
614 | */ |
615 | memset(&cmd, 0, sizeof(cmd)); |
616 | cmd.opcode = MOVE_MEDIUM; |
617 | _lto2b(sc->sc_picker, cmd.tea); |
618 | _lto2b(fromelem, cmd.src); |
619 | _lto2b(toelem, cmd.dst); |
620 | if (cm->cm_flags & CM_INVERT) |
621 | cmd.flags |= MOVE_MEDIUM_INVERT; |
622 | |
623 | /* |
624 | * Send command to changer. |
625 | */ |
626 | return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, |
627 | CHRETRIES, CHTIMEOUT, NULL, 0)); |
628 | } |
629 | |
630 | static int |
631 | ch_exchange(struct ch_softc *sc, struct changer_exchange_request *ce) |
632 | { |
633 | struct scsi_exchange_medium cmd; |
634 | u_int16_t src, dst1, dst2; |
635 | |
636 | /* |
637 | * Check arguments. |
638 | */ |
639 | if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || |
640 | (ce->ce_sdsttype > CHET_DT)) |
641 | return (EINVAL); |
642 | if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || |
643 | (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || |
644 | (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) |
645 | return (ENODEV); |
646 | |
647 | /* |
648 | * Check the request against the changer's capabilities. |
649 | */ |
650 | if (((sc->sc_exchangemask[ce->ce_srctype] & |
651 | (1 << ce->ce_fdsttype)) == 0) || |
652 | ((sc->sc_exchangemask[ce->ce_fdsttype] & |
653 | (1 << ce->ce_sdsttype)) == 0)) |
654 | return (ENODEV); |
655 | |
656 | /* |
657 | * Calculate the source and destination elements. |
658 | */ |
659 | src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; |
660 | dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; |
661 | dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; |
662 | |
663 | /* |
664 | * Build the SCSI command. |
665 | */ |
666 | memset(&cmd, 0, sizeof(cmd)); |
667 | cmd.opcode = EXCHANGE_MEDIUM; |
668 | _lto2b(sc->sc_picker, cmd.tea); |
669 | _lto2b(src, cmd.src); |
670 | _lto2b(dst1, cmd.fdst); |
671 | _lto2b(dst2, cmd.sdst); |
672 | if (ce->ce_flags & CE_INVERT1) |
673 | cmd.flags |= EXCHANGE_MEDIUM_INV1; |
674 | if (ce->ce_flags & CE_INVERT2) |
675 | cmd.flags |= EXCHANGE_MEDIUM_INV2; |
676 | |
677 | /* |
678 | * Send command to changer. |
679 | */ |
680 | return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, |
681 | CHRETRIES, CHTIMEOUT, NULL, 0)); |
682 | } |
683 | |
684 | static int |
685 | ch_position(struct ch_softc *sc, struct changer_position_request *cp) |
686 | { |
687 | struct scsi_position_to_element cmd; |
688 | u_int16_t dst; |
689 | |
690 | /* |
691 | * Check arguments. |
692 | */ |
693 | if (cp->cp_type > CHET_DT) |
694 | return (EINVAL); |
695 | if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) |
696 | return (ENODEV); |
697 | |
698 | /* |
699 | * Calculate the destination element. |
700 | */ |
701 | dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; |
702 | |
703 | /* |
704 | * Build the SCSI command. |
705 | */ |
706 | memset(&cmd, 0, sizeof(cmd)); |
707 | cmd.opcode = POSITION_TO_ELEMENT; |
708 | _lto2b(sc->sc_picker, cmd.tea); |
709 | _lto2b(dst, cmd.dst); |
710 | if (cp->cp_flags & CP_INVERT) |
711 | cmd.flags |= POSITION_TO_ELEMENT_INVERT; |
712 | |
713 | /* |
714 | * Send command to changer. |
715 | */ |
716 | return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, |
717 | CHRETRIES, CHTIMEOUT, NULL, 0)); |
718 | } |
719 | |
720 | /* |
721 | * Perform a READ ELEMENT STATUS on behalf of the user, and return to |
722 | * the user only the data the user is interested in. This returns the |
723 | * old data format. |
724 | */ |
725 | static int |
726 | ch_ousergetelemstatus(struct ch_softc *sc, int chet, u_int8_t *uptr) |
727 | { |
728 | struct read_element_status_header *st_hdrp, st_hdr; |
729 | struct read_element_status_page_header *pg_hdrp; |
730 | struct read_element_status_descriptor *desc; |
731 | size_t size, desclen; |
732 | void *data; |
733 | int avail, i, error = 0; |
734 | u_int8_t user_data; |
735 | |
736 | /* |
737 | * If there are no elements of the requested type in the changer, |
738 | * the request is invalid. |
739 | */ |
740 | if (sc->sc_counts[chet] == 0) |
741 | return (EINVAL); |
742 | |
743 | /* |
744 | * Do the request the user wants, but only read the status header. |
745 | * This will tell us the amount of storage we must allocate in |
746 | * order to read all data. |
747 | */ |
748 | error = ch_getelemstatus(sc, sc->sc_firsts[chet], |
749 | sc->sc_counts[chet], &st_hdr, sizeof(st_hdr), 0, 0); |
750 | if (error) |
751 | return (error); |
752 | |
753 | size = sizeof(struct read_element_status_header) + |
754 | _3btol(st_hdr.nbytes); |
755 | |
756 | /* |
757 | * We must have at least room for the status header and |
758 | * one page header (since we only ask for one element type |
759 | * at a time). |
760 | */ |
761 | if (size < (sizeof(struct read_element_status_header) + |
762 | sizeof(struct read_element_status_page_header))) |
763 | return (EIO); |
764 | |
765 | /* |
766 | * Allocate the storage and do the request again. |
767 | */ |
768 | data = malloc(size, M_DEVBUF, M_WAITOK); |
769 | error = ch_getelemstatus(sc, sc->sc_firsts[chet], |
770 | sc->sc_counts[chet], data, size, 0, 0); |
771 | if (error) |
772 | goto done; |
773 | |
774 | st_hdrp = (struct read_element_status_header *)data; |
775 | pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp + |
776 | sizeof(struct read_element_status_header)); |
777 | desclen = _2btol(pg_hdrp->edl); |
778 | |
779 | /* |
780 | * Fill in the user status array. |
781 | */ |
782 | avail = _2btol(st_hdrp->count); |
783 | |
784 | if (avail != sc->sc_counts[chet]) |
785 | printf("%s: warning, READ ELEMENT STATUS avail != count\n" , |
786 | device_xname(sc->sc_dev)); |
787 | |
788 | desc = (struct read_element_status_descriptor *)((u_long)data + |
789 | sizeof(struct read_element_status_header) + |
790 | sizeof(struct read_element_status_page_header)); |
791 | for (i = 0; i < avail; ++i) { |
792 | user_data = desc->flags1; |
793 | error = copyout(&user_data, &uptr[i], avail); |
794 | if (error) |
795 | break; |
796 | desc = (struct read_element_status_descriptor *)((u_long)desc |
797 | + desclen); |
798 | } |
799 | |
800 | done: |
801 | if (data != NULL) |
802 | free(data, M_DEVBUF); |
803 | return (error); |
804 | } |
805 | |
806 | /* |
807 | * Perform a READ ELEMENT STATUS on behalf of the user. This returns |
808 | * the new (more complete) data format. |
809 | */ |
810 | static int |
811 | ch_usergetelemstatus(struct ch_softc *sc, |
812 | struct changer_element_status_request *cesr) |
813 | { |
814 | struct scsipi_channel *chan = sc->sc_periph->periph_channel; |
815 | struct scsipi_periph *dtperiph; |
816 | struct read_element_status_header *st_hdrp, st_hdr; |
817 | struct read_element_status_page_header *pg_hdrp; |
818 | struct read_element_status_descriptor *desc; |
819 | struct changer_volume_tag *avol, *pvol; |
820 | size_t size, desclen, stddesclen, offset; |
821 | int first, avail, i, error = 0; |
822 | void *data; |
823 | void *uvendptr; |
824 | struct changer_element_status ces; |
825 | |
826 | /* |
827 | * Check arguments. |
828 | */ |
829 | if (cesr->cesr_type > CHET_DT) |
830 | return (EINVAL); |
831 | if (sc->sc_counts[cesr->cesr_type] == 0) |
832 | return (ENODEV); |
833 | if (cesr->cesr_unit > (sc->sc_counts[cesr->cesr_type] - 1)) |
834 | return (ENODEV); |
835 | if (cesr->cesr_count > |
836 | (sc->sc_counts[cesr->cesr_type] + cesr->cesr_unit)) |
837 | return (EINVAL); |
838 | |
839 | /* |
840 | * Do the request the user wants, but only read the status header. |
841 | * This will tell us the amount of storage we must allocate |
842 | * in order to read all the data. |
843 | */ |
844 | error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + |
845 | cesr->cesr_unit, cesr->cesr_count, &st_hdr, sizeof(st_hdr), 0, |
846 | cesr->cesr_flags); |
847 | if (error) |
848 | return (error); |
849 | |
850 | size = sizeof(struct read_element_status_header) + |
851 | _3btol(st_hdr.nbytes); |
852 | |
853 | /* |
854 | * We must have at least room for the status header and |
855 | * one page header (since we only ask for oen element type |
856 | * at a time). |
857 | */ |
858 | if (size < (sizeof(struct read_element_status_header) + |
859 | sizeof(struct read_element_status_page_header))) |
860 | return (EIO); |
861 | |
862 | /* |
863 | * Allocate the storage and do the request again. |
864 | */ |
865 | data = malloc(size, M_DEVBUF, M_WAITOK); |
866 | error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + |
867 | cesr->cesr_unit, cesr->cesr_count, data, size, 0, |
868 | cesr->cesr_flags); |
869 | if (error) |
870 | goto done; |
871 | |
872 | st_hdrp = (struct read_element_status_header *)data; |
873 | pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp + |
874 | sizeof(struct read_element_status_header)); |
875 | desclen = _2btol(pg_hdrp->edl); |
876 | |
877 | /* |
878 | * Fill in the user status array. |
879 | */ |
880 | first = _2btol(st_hdrp->fear); |
881 | if (first < (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit) || |
882 | first >= (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit + |
883 | cesr->cesr_count)) { |
884 | error = EIO; |
885 | goto done; |
886 | } |
887 | first -= sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit; |
888 | |
889 | avail = _2btol(st_hdrp->count); |
890 | if (avail <= 0 || avail > cesr->cesr_count) { |
891 | error = EIO; |
892 | goto done; |
893 | } |
894 | |
895 | offset = sizeof(struct read_element_status_header) + |
896 | sizeof(struct read_element_status_page_header); |
897 | |
898 | for (i = 0; i < cesr->cesr_count; i++) { |
899 | memset(&ces, 0, sizeof(ces)); |
900 | if (i < first || i >= (first + avail)) { |
901 | error = copyout(&ces, &cesr->cesr_data[i], |
902 | sizeof(ces)); |
903 | if (error) |
904 | goto done; |
905 | } |
906 | |
907 | desc = (struct read_element_status_descriptor *) |
908 | ((char *)data + offset); |
909 | stddesclen = sizeof(struct read_element_status_descriptor); |
910 | offset += desclen; |
911 | |
912 | ces.ces_flags = CESTATUS_STATUS_VALID; |
913 | |
914 | /* |
915 | * The SCSI flags conveniently map directly to the |
916 | * chio API flags. |
917 | */ |
918 | ces.ces_flags |= (desc->flags1 & 0x3f); |
919 | |
920 | ces.ces_asc = desc->sense_code; |
921 | ces.ces_ascq = desc->sense_qual; |
922 | |
923 | /* |
924 | * For Data Transport elemenets, get the SCSI ID and LUN, |
925 | * and attempt to map them to a device name if they're |
926 | * on the same SCSI bus. |
927 | */ |
928 | if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) { |
929 | ces.ces_target = desc->dt_scsi_addr; |
930 | ces.ces_flags |= CESTATUS_TARGET_VALID; |
931 | } |
932 | if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUVALID) { |
933 | ces.ces_lun = desc->dt_scsi_flags & |
934 | READ_ELEMENT_STATUS_DT_LUNMASK; |
935 | ces.ces_flags |= CESTATUS_LUN_VALID; |
936 | } |
937 | if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_NOTBUS) |
938 | ces.ces_flags |= CESTATUS_NOTBUS; |
939 | else if ((ces.ces_flags & |
940 | (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) == |
941 | (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) { |
942 | if (ces.ces_target < chan->chan_ntargets && |
943 | ces.ces_lun < chan->chan_nluns && |
944 | (dtperiph = scsipi_lookup_periph(chan, |
945 | ces.ces_target, ces.ces_lun)) != NULL && |
946 | dtperiph->periph_dev != NULL) { |
947 | strlcpy(ces.ces_xname, |
948 | device_xname(dtperiph->periph_dev), |
949 | sizeof(ces.ces_xname)); |
950 | ces.ces_flags |= CESTATUS_XNAME_VALID; |
951 | } |
952 | } |
953 | |
954 | if (desc->flags2 & READ_ELEMENT_STATUS_INVERT) |
955 | ces.ces_flags |= CESTATUS_INVERTED; |
956 | |
957 | if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) { |
958 | if (ch_map_element(sc, _2btol(desc->ssea), |
959 | &ces.ces_from_type, &ces.ces_from_unit)) |
960 | ces.ces_flags |= CESTATUS_FROM_VALID; |
961 | } |
962 | |
963 | /* |
964 | * Extract volume tag information. |
965 | */ |
966 | switch (pg_hdrp->flags & |
967 | (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG)) { |
968 | case (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG): |
969 | pvol = (struct changer_volume_tag *)(desc + 1); |
970 | avol = pvol + 1; |
971 | break; |
972 | |
973 | case READ_ELEMENT_STATUS_PVOLTAG: |
974 | pvol = (struct changer_volume_tag *)(desc + 1); |
975 | avol = NULL; |
976 | break; |
977 | |
978 | case READ_ELEMENT_STATUS_AVOLTAG: |
979 | pvol = NULL; |
980 | avol = (struct changer_volume_tag *)(desc + 1); |
981 | break; |
982 | |
983 | default: |
984 | avol = pvol = NULL; |
985 | break; |
986 | } |
987 | |
988 | if (pvol != NULL) { |
989 | ch_voltag_convert_in(pvol, &ces.ces_pvoltag); |
990 | ces.ces_flags |= CESTATUS_PVOL_VALID; |
991 | stddesclen += sizeof(struct changer_volume_tag); |
992 | } |
993 | if (avol != NULL) { |
994 | ch_voltag_convert_in(avol, &ces.ces_avoltag); |
995 | ces.ces_flags |= CESTATUS_AVOL_VALID; |
996 | stddesclen += sizeof(struct changer_volume_tag); |
997 | } |
998 | |
999 | /* |
1000 | * Compute vendor-specific length. Note the 4 reserved |
1001 | * bytes between the volume tags and the vendor-specific |
1002 | * data. Copy it out of the user wants it. |
1003 | */ |
1004 | stddesclen += 4; |
1005 | if (desclen > stddesclen) |
1006 | ces.ces_vendor_len = desclen - stddesclen; |
1007 | |
1008 | if (ces.ces_vendor_len != 0 && cesr->cesr_vendor_data != NULL) { |
1009 | error = copyin(&cesr->cesr_vendor_data[i], &uvendptr, |
1010 | sizeof(uvendptr)); |
1011 | if (error) |
1012 | goto done; |
1013 | error = copyout((void *)((u_long)desc + stddesclen), |
1014 | uvendptr, ces.ces_vendor_len); |
1015 | if (error) |
1016 | goto done; |
1017 | } |
1018 | |
1019 | /* |
1020 | * Now copy out the status descriptor we've constructed. |
1021 | */ |
1022 | error = copyout(&ces, &cesr->cesr_data[i], sizeof(ces)); |
1023 | if (error) |
1024 | goto done; |
1025 | } |
1026 | |
1027 | done: |
1028 | if (data != NULL) |
1029 | free(data, M_DEVBUF); |
1030 | return (error); |
1031 | } |
1032 | |
1033 | static int |
1034 | ch_getelemstatus(struct ch_softc *sc, int first, int count, void *data, |
1035 | size_t datalen, int scsiflags, int flags) |
1036 | { |
1037 | struct scsi_read_element_status cmd; |
1038 | |
1039 | /* |
1040 | * Build SCSI command. |
1041 | */ |
1042 | memset(&cmd, 0, sizeof(cmd)); |
1043 | cmd.opcode = READ_ELEMENT_STATUS; |
1044 | cmd.byte2 = ELEMENT_TYPE_ALL; |
1045 | if (flags & CESR_VOLTAGS) |
1046 | cmd.byte2 |= READ_ELEMENT_STATUS_VOLTAG; |
1047 | _lto2b(first, cmd.sea); |
1048 | _lto2b(count, cmd.count); |
1049 | _lto3b(datalen, cmd.len); |
1050 | |
1051 | /* |
1052 | * Send command to changer. |
1053 | */ |
1054 | return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), |
1055 | (void *)data, datalen, |
1056 | CHRETRIES, CHTIMEOUT, NULL, scsiflags | XS_CTL_DATA_IN)); |
1057 | } |
1058 | |
1059 | static int |
1060 | ch_setvoltag(struct ch_softc *sc, struct changer_set_voltag_request *csvr) |
1061 | { |
1062 | struct scsi_send_volume_tag cmd; |
1063 | struct changer_volume_tag voltag; |
1064 | void *data = NULL; |
1065 | size_t datalen = 0; |
1066 | int error; |
1067 | u_int16_t dst; |
1068 | |
1069 | /* |
1070 | * Check arguments. |
1071 | */ |
1072 | if (csvr->csvr_type > CHET_DT) |
1073 | return (EINVAL); |
1074 | if (csvr->csvr_unit > (sc->sc_counts[csvr->csvr_type] - 1)) |
1075 | return (ENODEV); |
1076 | |
1077 | dst = sc->sc_firsts[csvr->csvr_type] + csvr->csvr_unit; |
1078 | |
1079 | /* |
1080 | * Build the SCSI command. |
1081 | */ |
1082 | memset(&cmd, 0, sizeof(cmd)); |
1083 | cmd.opcode = SEND_VOLUME_TAG; |
1084 | _lto2b(dst, cmd.eaddr); |
1085 | |
1086 | #define ALTERNATE (csvr->csvr_flags & CSVR_ALTERNATE) |
1087 | |
1088 | switch (csvr->csvr_flags & CSVR_MODE_MASK) { |
1089 | case CSVR_MODE_SET: |
1090 | cmd.sac = ALTERNATE ? SAC_ASSERT_ALT : SAC_ASSERT_PRIMARY; |
1091 | break; |
1092 | |
1093 | case CSVR_MODE_REPLACE: |
1094 | cmd.sac = ALTERNATE ? SAC_REPLACE_ALT : SAC_REPLACE_PRIMARY; |
1095 | break; |
1096 | |
1097 | case CSVR_MODE_CLEAR: |
1098 | cmd.sac = ALTERNATE ? SAC_UNDEFINED_ALT : SAC_UNDEFINED_PRIMARY; |
1099 | break; |
1100 | |
1101 | default: |
1102 | return (EINVAL); |
1103 | } |
1104 | |
1105 | #undef ALTERNATE |
1106 | |
1107 | if (cmd.sac < SAC_UNDEFINED_PRIMARY) { |
1108 | error = ch_voltag_convert_out(&csvr->csvr_voltag, &voltag); |
1109 | if (error) |
1110 | return (error); |
1111 | data = &voltag; |
1112 | datalen = sizeof(voltag); |
1113 | _lto2b(datalen, cmd.length); |
1114 | } |
1115 | |
1116 | /* |
1117 | * Send command to changer. |
1118 | */ |
1119 | return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), |
1120 | (void *)data, datalen, CHRETRIES, CHTIMEOUT, NULL, |
1121 | datalen ? XS_CTL_DATA_OUT : 0)); |
1122 | } |
1123 | |
1124 | static int |
1125 | ch_ielem(struct ch_softc *sc) |
1126 | { |
1127 | int tmo; |
1128 | struct scsi_initialize_element_status cmd; |
1129 | |
1130 | /* |
1131 | * Build SCSI command. |
1132 | */ |
1133 | memset(&cmd, 0, sizeof(cmd)); |
1134 | cmd.opcode = INITIALIZE_ELEMENT_STATUS; |
1135 | |
1136 | /* |
1137 | * Send command to changer. |
1138 | * |
1139 | * The problem is, how long to allow for the command? |
1140 | * It can take a *really* long time, and also depends |
1141 | * on unknowable factors such as whether there are |
1142 | * *almost* readable labels on tapes that a barcode |
1143 | * reader is trying to decipher. |
1144 | * |
1145 | * I'm going to make this long enough to allow 5 minutes |
1146 | * per element plus an initial 10 minute wait. |
1147 | */ |
1148 | tmo = sc->sc_counts[CHET_MT] + |
1149 | sc->sc_counts[CHET_ST] + |
1150 | sc->sc_counts[CHET_IE] + |
1151 | sc->sc_counts[CHET_DT]; |
1152 | tmo *= 5 * 60 * 1000; |
1153 | tmo += (10 * 60 * 1000); |
1154 | |
1155 | return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, |
1156 | CHRETRIES, tmo, NULL, XS_CTL_IGNORE_ILLEGAL_REQUEST)); |
1157 | } |
1158 | |
1159 | /* |
1160 | * Ask the device about itself and fill in the parameters in our |
1161 | * softc. |
1162 | */ |
1163 | static int |
1164 | ch_get_params(struct ch_softc *sc, int scsiflags) |
1165 | { |
1166 | struct scsi_mode_sense_data { |
1167 | struct scsi_mode_parameter_header_6 ; |
1168 | union { |
1169 | struct page_element_address_assignment ea; |
1170 | struct page_transport_geometry_parameters tg; |
1171 | struct page_device_capabilities cap; |
1172 | } pages; |
1173 | } sense_data; |
1174 | int error, from; |
1175 | u_int8_t *moves, *exchanges; |
1176 | |
1177 | /* |
1178 | * Grab info from the element address assignment page. |
1179 | */ |
1180 | memset(&sense_data, 0, sizeof(sense_data)); |
1181 | error = scsipi_mode_sense(sc->sc_periph, SMS_DBD, 0x1d, |
1182 | &sense_data.header, sizeof(sense_data), |
1183 | scsiflags, CHRETRIES, 6000); |
1184 | if (error) { |
1185 | aprint_error_dev(sc->sc_dev, "could not sense element address page\n" ); |
1186 | return (error); |
1187 | } |
1188 | |
1189 | sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); |
1190 | sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); |
1191 | sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); |
1192 | sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); |
1193 | sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); |
1194 | sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); |
1195 | sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); |
1196 | sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); |
1197 | |
1198 | /* XXX ask for transport geometry page XXX */ |
1199 | |
1200 | /* |
1201 | * Grab info from the capabilities page. |
1202 | */ |
1203 | memset(&sense_data, 0, sizeof(sense_data)); |
1204 | /* |
1205 | * XXX: Note: not all changers can deal with disabled block descriptors |
1206 | */ |
1207 | error = scsipi_mode_sense(sc->sc_periph, SMS_DBD, 0x1f, |
1208 | &sense_data.header, sizeof(sense_data), |
1209 | scsiflags, CHRETRIES, 6000); |
1210 | if (error) { |
1211 | aprint_error_dev(sc->sc_dev, "could not sense capabilities page\n" ); |
1212 | return (error); |
1213 | } |
1214 | |
1215 | memset(sc->sc_movemask, 0, sizeof(sc->sc_movemask)); |
1216 | memset(sc->sc_exchangemask, 0, sizeof(sc->sc_exchangemask)); |
1217 | moves = &sense_data.pages.cap.move_from_mt; |
1218 | exchanges = &sense_data.pages.cap.exchange_with_mt; |
1219 | for (from = CHET_MT; from <= CHET_DT; ++from) { |
1220 | sc->sc_movemask[from] = moves[from]; |
1221 | sc->sc_exchangemask[from] = exchanges[from]; |
1222 | } |
1223 | |
1224 | #ifdef CH_AUTOMATIC_IELEM_POLICY |
1225 | /* |
1226 | * If we need to do an Init-Element-Status, |
1227 | * do that now that we know what's in the changer. |
1228 | */ |
1229 | if ((scsiflags & XS_CTL_IGNORE_MEDIA_CHANGE) == 0) { |
1230 | if ((sc->sc_periph->periph_flags & PERIPH_MEDIA_LOADED) == 0) |
1231 | error = ch_ielem(sc); |
1232 | if (error == 0) |
1233 | sc->sc_periph->periph_flags |= PERIPH_MEDIA_LOADED; |
1234 | else |
1235 | sc->sc_periph->periph_flags &= ~PERIPH_MEDIA_LOADED; |
1236 | } |
1237 | #endif |
1238 | return (error); |
1239 | } |
1240 | |
1241 | static void |
1242 | ch_get_quirks(struct ch_softc *sc, struct scsipi_inquiry_pattern *inqbuf) |
1243 | { |
1244 | const struct chquirk *match; |
1245 | int priority; |
1246 | |
1247 | sc->sc_settledelay = 0; |
1248 | |
1249 | match = scsipi_inqmatch(inqbuf, chquirks, |
1250 | sizeof(chquirks) / sizeof(chquirks[0]), |
1251 | sizeof(chquirks[0]), &priority); |
1252 | if (priority != 0) |
1253 | sc->sc_settledelay = match->cq_settledelay; |
1254 | } |
1255 | |
1256 | static int |
1257 | ch_map_element(struct ch_softc *sc, u_int16_t elem, int *typep, int *unitp) |
1258 | { |
1259 | int chet; |
1260 | |
1261 | for (chet = CHET_MT; chet <= CHET_DT; chet++) { |
1262 | if (elem >= sc->sc_firsts[chet] && |
1263 | elem < (sc->sc_firsts[chet] + sc->sc_counts[chet])) { |
1264 | *typep = chet; |
1265 | *unitp = elem - sc->sc_firsts[chet]; |
1266 | return (1); |
1267 | } |
1268 | } |
1269 | return (0); |
1270 | } |
1271 | |
1272 | static void |
1273 | ch_voltag_convert_in(const struct changer_volume_tag *sv, |
1274 | struct changer_voltag *cv) |
1275 | { |
1276 | int i; |
1277 | |
1278 | memset(cv, 0, sizeof(struct changer_voltag)); |
1279 | |
1280 | /* |
1281 | * Copy the volume tag string from the SCSI representation. |
1282 | * Per the SCSI-2 spec, we stop at the first blank character. |
1283 | */ |
1284 | for (i = 0; i < sizeof(sv->volid); i++) { |
1285 | if (sv->volid[i] == ' ') |
1286 | break; |
1287 | cv->cv_tag[i] = sv->volid[i]; |
1288 | } |
1289 | cv->cv_tag[i] = '\0'; |
1290 | |
1291 | cv->cv_serial = _2btol(sv->volseq); |
1292 | } |
1293 | |
1294 | static int |
1295 | ch_voltag_convert_out(const struct changer_voltag *cv, |
1296 | struct changer_volume_tag *sv) |
1297 | { |
1298 | int i; |
1299 | |
1300 | memset(sv, ' ', sizeof(struct changer_volume_tag)); |
1301 | |
1302 | for (i = 0; i < sizeof(sv->volid); i++) { |
1303 | if (cv->cv_tag[i] == '\0') |
1304 | break; |
1305 | /* |
1306 | * Limit the character set to what is suggested in |
1307 | * the SCSI-2 spec. |
1308 | */ |
1309 | if ((cv->cv_tag[i] < '0' || cv->cv_tag[i] > '9') && |
1310 | (cv->cv_tag[i] < 'A' || cv->cv_tag[i] > 'Z') && |
1311 | (cv->cv_tag[i] != '_')) |
1312 | return (EINVAL); |
1313 | sv->volid[i] = cv->cv_tag[i]; |
1314 | } |
1315 | |
1316 | _lto2b(cv->cv_serial, sv->volseq); |
1317 | |
1318 | return (0); |
1319 | } |
1320 | |