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