xref: /original-bsd/sys/hp300/dev/ac.c (revision efc5bb34)
1 /*
2  * Copyright (c) 1991 University of Utah.
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Systems Programming Group of the University of Utah Computer
8  * Science Department.
9  *
10  * %sccs.include.redist.c%
11  *
12  * from: Utah $Hdr: ac.c 1.5 92/01/21$
13  *
14  *	@(#)ac.c	7.2 (Berkeley) 10/11/92
15  */
16 
17 /*
18  * SCSI driver for MO autochanger.
19  *
20  * Very crude.  Because of the lack of connect/disconnect support in the
21  * scsi driver, this driver can tie up the SCSI bus for a long time.  It
22  * also grabs a DMA channel and holds it for the duration even though it
23  * never uses it.
24  */
25 
26 #include "ac.h"
27 #if NAC > 0
28 
29 #include <sys/param.h>
30 #include <sys/buf.h>
31 #include <sys/errno.h>
32 #include <sys/user.h>
33 #include <sys/ioctl.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 
37 #include <hp/dev/device.h>
38 
39 #include <hp300/dev/scsireg.h>
40 #include <hp300/dev/acioctl.h>
41 #include <hp300/dev/acvar.h>
42 
43 extern int scsi_test_unit_rdy();
44 extern int scsi_request_sense();
45 extern int scsiustart();
46 extern int scsigo();
47 extern void scsifree();
48 extern void scsireset();
49 extern void scsi_delay();
50 
51 extern int scsi_immed_command();
52 
53 int	acinit(), acstart(), acgo(), acintr();
54 
55 struct	driver acdriver = {
56 	acinit, "ac", acstart, acgo, acintr,
57 };
58 
59 struct	ac_softc ac_softc[NAC];
60 static	struct buf acbuf[NAC];
61 static	struct scsi_fmt_cdb accmd[NAC];
62 
63 #ifdef DEBUG
64 int ac_debug = 0x0000;
65 #define ACD_FOLLOW	0x0001
66 #define ACD_OPEN	0x0002
67 #endif
68 
69 acinit(hd)
70 	register struct hp_device *hd;
71 {
72 	int unit = hd->hp_unit;
73 	register struct ac_softc *sc = &ac_softc[unit];
74 
75 	sc->sc_hd = hd;
76 	sc->sc_punit = hd->hp_flags & 7;
77 	if (acident(sc, hd) < 0)
78 		return(0);
79 	sc->sc_dq.dq_unit = unit;
80 	sc->sc_dq.dq_ctlr = hd->hp_ctlr;
81 	sc->sc_dq.dq_slave = hd->hp_slave;
82 	sc->sc_dq.dq_driver = &acdriver;
83 	sc->sc_bp = &acbuf[unit];
84 	sc->sc_cmd = &accmd[unit];
85 	sc->sc_flags = ACF_ALIVE;
86 	return(1);
87 }
88 
89 acident(sc, hd)
90 	register struct ac_softc *sc;
91 	register struct hp_device *hd;
92 {
93 	int unit;
94 	register int ctlr, slave;
95 	int i, stat;
96 	int tries = 5;
97 	char idstr[32];
98 	struct scsi_inquiry inqbuf;
99 	static struct scsi_fmt_cdb inq = {
100 		6,
101 		CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0
102 	};
103 
104 	ctlr = hd->hp_ctlr;
105 	slave = hd->hp_slave;
106 	unit = sc->sc_punit;
107 	scsi_delay(-1);
108 
109 	/*
110 	 * See if device is ready
111 	 */
112 	while ((i = scsi_test_unit_rdy(ctlr, slave, unit)) != 0) {
113 		if (i == -1 || --tries < 0)
114 			/* doesn't exist or not a CCS device */
115 			goto failed;
116 		if (i == STS_CHECKCOND) {
117 			u_char sensebuf[128];
118 			struct scsi_xsense *sp;
119 
120 			scsi_request_sense(ctlr, slave, unit,
121 					   sensebuf, sizeof(sensebuf));
122 			sp = (struct scsi_xsense *) sensebuf;
123 			if (sp->class == 7 && sp->key == 6)
124 				/* drive doing an RTZ -- give it a while */
125 				DELAY(1000000);
126 		}
127 		DELAY(1000);
128 	}
129 	/*
130 	 * Find out if it is an autochanger
131 	 */
132 	if (scsi_immed_command(ctlr, slave, unit, &inq,
133 			       (u_char *)&inqbuf, sizeof(inqbuf), B_READ))
134 		goto failed;
135 
136 	if (inqbuf.type != 8 || inqbuf.qual != 0x80 || inqbuf.version != 2)
137 		goto failed;
138 
139 	bcopy((caddr_t)&inqbuf.vendor_id, (caddr_t)idstr, 28);
140 	for (i = 27; i > 23; --i)
141 		if (idstr[i] != ' ')
142 			break;
143 	idstr[i+1] = 0;
144 	for (i = 23; i > 7; --i)
145 		if (idstr[i] != ' ')
146 			break;
147 	idstr[i+1] = 0;
148 	for (i = 7; i >= 0; --i)
149 		if (idstr[i] != ' ')
150 			break;
151 	idstr[i+1] = 0;
152 	printf("ac%d: %s %s rev %s\n", hd->hp_unit,
153 	       &idstr[0], &idstr[8], &idstr[24]);
154 
155 	scsi_delay(0);
156 	return(inqbuf.type);
157 failed:
158 	scsi_delay(0);
159 	return(-1);
160 }
161 
162 /*ARGSUSED*/
163 acopen(dev, flag, mode, p)
164 	dev_t dev;
165 	int flag, mode;
166 	struct proc *p;
167 {
168 	register int unit = minor(dev);
169 	register struct ac_softc *sc = &ac_softc[unit];
170 	int error = 0;
171 
172 	if (unit >= NAC || (sc->sc_flags & ACF_ALIVE) == 0)
173 		error = ENXIO;
174 	else if (sc->sc_flags & ACF_OPEN)
175 		error = EBUSY;
176 	else if (acgeteinfo(dev))
177 		error = EIO;
178 	else
179 		sc->sc_flags |= ACF_OPEN;
180 	return(error);
181 }
182 
183 /*ARGSUSED*/
184 acclose(dev, flag, mode, p)
185 	dev_t dev;
186 	int flag, mode;
187 	struct proc *p;
188 {
189 	struct ac_softc *sc = &ac_softc[minor(dev)];
190 
191 	sc->sc_flags &= ~ACF_OPEN;
192 }
193 
194 #define ACRESLEN(ep)	\
195 	(8 + (ep)->nmte*12 + (ep)->nse*12 + (ep)->niee*12 + (ep)->ndte*20)
196 
197 /*ARGSUSED*/
198 acioctl(dev, cmd, data, flag, p)
199 	dev_t dev;
200 	int cmd;
201 	caddr_t data;
202 	int flag;
203 	struct proc *p;
204 {
205 	register struct ac_softc *sc = &ac_softc[minor(dev)];
206 	char *dp;
207 	int dlen, error = 0;
208 
209 	switch (cmd) {
210 
211 	default:
212 		return (EINVAL);
213 
214 	/* perform an init element status and mode sense to reset state */
215 	case ACIOCINIT:
216 		error = accommand(dev, ACCMD_INITES, (caddr_t)0, 0);
217 		if (!error)
218 			error = acgeteinfo(dev);
219 		break;
220 
221 	/* copy internal element information */
222 	case ACIOCGINFO:
223 		*(struct acinfo *)data = sc->sc_einfo;
224 		break;
225 
226 	case ACIOCRAWES:
227 	{
228 		struct acbuffer *acbp = (struct acbuffer *)data;
229 
230 		dlen = ACRESLEN(&sc->sc_einfo);
231 		dp = (char *) malloc(dlen, M_DEVBUF, M_WAITOK);
232 		error = accommand(dev, ACCMD_READES, dp, dlen);
233 		if (!error) {
234 			dlen = *(int *)&dp[4] + 8;
235 			if (dlen > acbp->buflen)
236 				dlen = acbp->buflen;
237 			error = copyout(dp, acbp->bufptr, dlen);
238 		}
239 		break;
240 	}
241 
242 	case ACIOCGSTAT:
243 	{
244 		struct acbuffer *acbp = (struct acbuffer *)data;
245 
246 		dlen = ACRESLEN(&sc->sc_einfo);
247 		dp = (char *) malloc(dlen, M_DEVBUF, M_WAITOK);
248 		error = accommand(dev, ACCMD_READES, dp, dlen);
249 		if (!error) {
250 			int ne;
251 			char *tbuf;
252 
253 			ne = sc->sc_einfo.nmte + sc->sc_einfo.nse +
254 				sc->sc_einfo.niee + sc->sc_einfo.ndte;
255 			dlen = ne * sizeof(struct aceltstat);
256 			tbuf = (char *) malloc(dlen, M_DEVBUF, M_WAITOK);
257 			acconvert(dp, tbuf, ne);
258 			if (dlen > acbp->buflen)
259 				dlen = acbp->buflen;
260 			error = copyout(tbuf, acbp->bufptr, dlen);
261 			free(tbuf, M_DEVBUF);
262 		}
263 		free(dp, M_DEVBUF);
264 		break;
265 	}
266 
267 	case ACIOCMOVE:
268 		error = accommand(dev, ACCMD_MOVEM, data,
269 				  sizeof(struct acmove));
270 		break;
271 	}
272 	return(error);
273 }
274 
275 accommand(dev, command, bufp, buflen)
276 	dev_t dev;
277 	int command;
278 	char *bufp;
279 	int buflen;
280 {
281 	int unit = minor(dev);
282 	register struct ac_softc *sc = &ac_softc[unit];
283 	register struct buf *bp = sc->sc_bp;
284 	register struct scsi_fmt_cdb *cmd = sc->sc_cmd;
285 	int error;
286 
287 #ifdef DEBUG
288 	if (ac_debug & ACD_FOLLOW)
289 		printf("accommand(dev=%x, cmd=%x, buf=%x, buflen=%x)\n",
290 		       dev, command, bufp, buflen);
291 #endif
292 	if (sc->sc_flags & ACF_ACTIVE)
293 		panic("accommand: active!");
294 
295 	sc->sc_flags |= ACF_ACTIVE;
296 	bzero((caddr_t)cmd->cdb, sizeof(cmd->cdb));
297 	cmd->cdb[0] = command;
298 
299 	switch (command) {
300 	case ACCMD_INITES:
301 		cmd->len = 6;
302 		break;
303 	case ACCMD_READES:
304 		cmd->len = 12;
305 		*(short *)&cmd->cdb[2] = 0;
306 		*(short *)&cmd->cdb[4] =
307 			sc->sc_einfo.nmte + sc->sc_einfo.nse +
308 			sc->sc_einfo.niee + sc->sc_einfo.ndte;
309 		cmd->cdb[7] = buflen >> 16;
310 		cmd->cdb[8] = buflen >> 8;
311 		cmd->cdb[9] = buflen;
312 		break;
313 	case ACCMD_MODESENSE:
314 		cmd->len = 6;
315 		cmd->cdb[2] = 0x3F;	/* all pages */
316 		cmd->cdb[4] = buflen;
317 		break;
318 	case ACCMD_MOVEM:
319 		cmd->len = 12;
320 		*(short *)&cmd->cdb[2] = sc->sc_picker;
321 		*(short *)&cmd->cdb[4] = *(short *)&bufp[0];
322 		*(short *)&cmd->cdb[6] = *(short *)&bufp[2];
323 		if (*(short *)&bufp[4] & AC_INVERT)
324 			cmd->cdb[10] = 1;
325 		bufp = 0;
326 		buflen = 0;
327 		break;
328 	default:
329 		panic("accommand: bad command");
330 	}
331 	bp->b_flags = B_BUSY|B_READ;
332 	bp->b_dev = dev;
333 	bp->b_un.b_addr = bufp;
334 	bp->b_bcount = buflen;
335 	bp->b_resid = 0;
336 	bp->b_blkno = 0;
337 	bp->b_error = 0;
338 	if (scsireq(&sc->sc_dq))
339 		acstart(unit);
340 	error = biowait(bp);
341 	sc->sc_flags &= ~ACF_ACTIVE;
342 	return (error);
343 }
344 
345 acstart(unit)
346 	int unit;
347 {
348 #ifdef DEBUG
349 	if (ac_debug & ACD_FOLLOW)
350 		printf("acstart(unit=%x)\n", unit);
351 #endif
352 	if (scsiustart(ac_softc[unit].sc_hd->hp_ctlr))
353 		acgo(unit);
354 }
355 
356 acgo(unit)
357 	int unit;
358 {
359 	register struct ac_softc *sc = &ac_softc[unit];
360 	register struct buf *bp = sc->sc_bp;
361 	struct hp_device *hp = sc->sc_hd;
362 	int stat;
363 
364 #ifdef DEBUG
365 	if (ac_debug & ACD_FOLLOW)
366 		printf("acgo(unit=%x): ", unit);
367 #endif
368 	stat = scsigo(hp->hp_ctlr, hp->hp_slave, sc->sc_punit,
369 		      bp, sc->sc_cmd, 0);
370 #ifdef DEBUG
371 	if (ac_debug & ACD_FOLLOW)
372 		printf("scsigo returns %x\n", stat);
373 #endif
374 	if (stat) {
375 		bp->b_error = EIO;
376 		bp->b_flags |= B_ERROR;
377 		(void) biodone(bp);
378 		scsifree(&sc->sc_dq);
379 	}
380 }
381 
382 acintr(unit, stat)
383 	int unit, stat;
384 {
385 	register struct ac_softc *sc = &ac_softc[unit];
386 	register struct buf *bp = sc->sc_bp;
387 	u_char sensebuf[78];
388 	struct scsi_xsense *sp;
389 
390 #ifdef DEBUG
391 	if (ac_debug & ACD_FOLLOW)
392 		printf("acintr(unit=%x, stat=%x)\n", unit, stat);
393 #endif
394 	switch (stat) {
395 	case 0:
396 		bp->b_resid = 0;
397 		break;
398 	case STS_CHECKCOND:
399 		scsi_request_sense(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave,
400 				   sc->sc_punit, sensebuf, sizeof sensebuf);
401 		sp = (struct scsi_xsense *)sensebuf;
402 		printf("ac%d: acintr sense key=%x, ac=%x, acq=%x\n",
403 		       unit, sp->key, sp->info4, sp->len);
404 		bp->b_flags |= B_ERROR;
405 		bp->b_error = EIO;
406 		break;
407 	default:
408 		printf("ac%d: acintr unknown status 0x%x\n", unit, stat);
409 		break;
410 	}
411 	(void) biodone(sc->sc_bp);
412 	scsifree(&sc->sc_dq);
413 }
414 
415 acgeteinfo(dev)
416 	dev_t dev;
417 {
418 	register struct ac_softc *sc = &ac_softc[minor(dev)];
419 	register char *bp;
420 	char msbuf[48];
421 	int error;
422 
423 	bzero(msbuf, sizeof msbuf);
424 	error = accommand(dev, ACCMD_MODESENSE, msbuf, sizeof msbuf);
425 	if (error)
426 		return(error);
427 	bp = &msbuf[4];
428 	while (bp < &msbuf[48]) {
429 		switch (bp[0] & 0x3F) {
430 		case 0x1D:
431 			sc->sc_einfo = *(struct acinfo *)&bp[2];
432 			sc->sc_picker = sc->sc_einfo.fmte;	/* XXX */
433 			return(0);
434 		case 0x1E:
435 			bp += 4;
436 			break;
437 		case 0x1F:
438 			bp += 20;
439 			break;
440 		default:
441 			printf("acgeteinfo: bad page type %x\n", bp[0]);
442 			return(EIO);
443 		}
444 	}
445 	return(EIO);
446 }
447 
448 acconvert(sbuf, dbuf, ne)
449 	char *sbuf, *dbuf;
450 	int ne;
451 {
452 	register struct aceltstat *ep = (struct aceltstat *)dbuf;
453 	register struct ac_restatphdr *phdr;
454 	register struct ac_restatdb *dbp;
455 	struct ac_restathdr *hdr;
456 #ifdef DEBUG
457 	register int bcount;
458 #endif
459 
460 	hdr = (struct ac_restathdr *)&sbuf[0];
461 	sbuf += sizeof *hdr;
462 #ifdef DEBUG
463 	if (ac_debug & ACD_FOLLOW)
464 		printf("element status: first=%d, num=%d, len=%d\n",
465 		       hdr->ac_felt, hdr->ac_nelt, hdr->ac_bcount);
466 	if (hdr->ac_nelt != ne) {
467 		printf("acconvert: # of elements, %d != %d\n",
468 		       hdr->ac_nelt, ne);
469 		if (hdr->ac_nelt < ne)
470 			ne = hdr->ac_nelt;
471 	}
472 	bcount = hdr->ac_bcount;
473 #endif
474 	while (ne) {
475 		phdr = (struct ac_restatphdr *)sbuf;
476 		sbuf += sizeof *phdr;
477 #ifdef DEBUG
478 		bcount -= sizeof *phdr;
479 #endif
480 		dbp = (struct ac_restatdb *)sbuf;
481 		sbuf += phdr->ac_bcount;
482 #ifdef DEBUG
483 		bcount -= phdr->ac_bcount;
484 #endif
485 		while (dbp < (struct ac_restatdb *)sbuf) {
486 			ep->type = phdr->ac_type;
487 			ep->eaddr = dbp->ac_eaddr;
488 			ep->flags = 0;
489 			if (dbp->ac_full)
490 				ep->flags |= AC_FULL;
491 			if (dbp->ac_exc)
492 				ep->flags |= AC_ERROR;
493 			if (dbp->ac_acc)
494 				ep->flags |= AC_ACCESS;
495 			dbp = (struct ac_restatdb *)
496 				((char *)dbp + phdr->ac_dlen);
497 			ep++;
498 			ne--;
499 		}
500 #ifdef DEBUG
501 		if (ne < 0 || bcount < 0)
502 			panic("acconvert: inconsistant");
503 #endif
504 	}
505 }
506 #endif
507