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