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