Commit | Line | Data |
---|---|---|
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 | |
41 | extern int scsi_test_unit_rdy(); | |
42 | extern int scsi_request_sense(); | |
43 | extern int scsiustart(); | |
44 | extern int scsigo(); | |
45 | extern void scsifree(); | |
46 | extern void scsireset(); | |
47 | extern void scsi_delay(); | |
48 | ||
49 | extern int scsi_immed_command(); | |
50 | ||
51 | int acinit(), acstart(), acgo(), acintr(); | |
52 | ||
53 | struct driver acdriver = { | |
54 | acinit, "ac", acstart, acgo, acintr, | |
55 | }; | |
56 | ||
57 | struct ac_softc ac_softc[NAC]; | |
58 | static struct buf acbuf[NAC]; | |
59 | static struct scsi_fmt_cdb accmd[NAC]; | |
60 | ||
61 | #ifdef DEBUG | |
62 | int ac_debug = 0x0000; | |
63 | #define ACD_FOLLOW 0x0001 | |
64 | #define ACD_OPEN 0x0002 | |
65 | #endif | |
66 | ||
67 | acinit(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 | ||
87 | acident(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); | |
155 | failed: | |
156 | scsi_delay(0); | |
157 | return(-1); | |
158 | } | |
159 | ||
160 | /*ARGSUSED*/ | |
161 | acopen(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*/ | |
186 | acclose(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*/ | |
200 | acioctl(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 | ||
277 | accommand(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 | ||
347 | acstart(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 | ||
358 | acgo(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 | ||
384 | acintr(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 | ||
417 | acgeteinfo(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 | ||
450 | acconvert(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 |