* Copyright (c) 1991 University of Utah.
* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* %sccs.include.redist.c%
* from: Utah $Hdr: ac.c 1.5 92/01/21$
* @(#)ac.c 7.6 (Berkeley) %G%
* SCSI driver for MO autochanger.
* Very crude. Because of the lack of connect/disconnect support in the
* scsi driver, this driver can tie up the SCSI bus for a long time. It
* also grabs a DMA channel and holds it for the duration even though it
#include <hp/dev/device.h>
#include <hp300/dev/scsireg.h>
#include <hp300/dev/acioctl.h>
#include <hp300/dev/acvar.h>
extern int scsi_test_unit_rdy();
extern int scsi_request_sense();
extern void scsi_delay();
extern int scsi_immed_command();
int acinit(), acstart(), acgo(), acintr();
struct driver acdriver
= {
acinit
, "ac", acstart
, acgo
, acintr
,
struct ac_softc ac_softc
[NAC
];
static struct buf acbuf
[NAC
];
static struct scsi_fmt_cdb accmd
[NAC
];
#define ACD_FOLLOW 0x0001
register struct hp_device
*hd
;
register struct ac_softc
*sc
= &ac_softc
[unit
];
sc
->sc_punit
= hd
->hp_flags
& 7;
sc
->sc_dq
.dq_unit
= unit
;
sc
->sc_dq
.dq_ctlr
= hd
->hp_ctlr
;
sc
->sc_dq
.dq_slave
= hd
->hp_slave
;
sc
->sc_dq
.dq_driver
= &acdriver
;
sc
->sc_bp
= &acbuf
[unit
];
sc
->sc_cmd
= &accmd
[unit
];
sc
->sc_flags
= ACF_ALIVE
;
register struct ac_softc
*sc
;
register struct hp_device
*hd
;
register int ctlr
, slave
;
struct scsi_inquiry inqbuf
;
static struct scsi_fmt_cdb inq
= {
CMD_INQUIRY
, 0, 0, 0, sizeof(inqbuf
), 0
while ((i
= scsi_test_unit_rdy(ctlr
, slave
, unit
)) != 0) {
if (i
== -1 || --tries
< 0)
/* doesn't exist or not a CCS device */
if (i
== STS_CHECKCOND
) {
scsi_request_sense(ctlr
, slave
, unit
,
sensebuf
, sizeof(sensebuf
));
sp
= (struct scsi_xsense
*) sensebuf
;
if (sp
->class == 7 && sp
->key
== 6)
/* drive doing an RTZ -- give it a while */
* Find out if it is an autochanger
if (scsi_immed_command(ctlr
, slave
, unit
, &inq
,
(u_char
*)&inqbuf
, sizeof(inqbuf
), B_READ
))
if (inqbuf
.type
!= 8 || inqbuf
.qual
!= 0x80 || inqbuf
.version
!= 2)
bcopy((caddr_t
)&inqbuf
.vendor_id
, (caddr_t
)idstr
, 28);
for (i
= 27; i
> 23; --i
)
printf("ac%d: %s %s rev %s\n", hd
->hp_unit
,
&idstr
[0], &idstr
[8], &idstr
[24]);
acopen(dev
, flag
, mode
, p
)
register int unit
= minor(dev
);
register struct ac_softc
*sc
= &ac_softc
[unit
];
if (unit
>= NAC
|| (sc
->sc_flags
& ACF_ALIVE
) == 0)
if (sc
->sc_flags
& ACF_OPEN
)
* Since acgeteinfo can block we mark the changer open now.
sc
->sc_flags
|= ACF_OPEN
;
sc
->sc_flags
&= ~ACF_OPEN
;
acclose(dev
, flag
, mode
, p
)
struct ac_softc
*sc
= &ac_softc
[minor(dev
)];
sc
->sc_flags
&= ~ACF_OPEN
;
(8 + (ep)->nmte*12 + (ep)->nse*12 + (ep)->niee*12 + (ep)->ndte*20)
acioctl(dev
, cmd
, data
, flag
, p
)
register struct ac_softc
*sc
= &ac_softc
[minor(dev
)];
/* perform an init element status and mode sense to reset state */
error
= accommand(dev
, ACCMD_INITES
, (caddr_t
)0, 0);
/* copy internal element information */
*(struct acinfo
*)data
= sc
->sc_einfo
;
struct acbuffer
*acbp
= (struct acbuffer
*)data
;
dlen
= ACRESLEN(&sc
->sc_einfo
);
dp
= (char *) malloc(dlen
, M_DEVBUF
, M_WAITOK
);
error
= accommand(dev
, ACCMD_READES
, dp
, dlen
);
dlen
= *(int *)&dp
[4] + 8;
error
= copyout(dp
, acbp
->bufptr
, dlen
);
struct acbuffer
*acbp
= (struct acbuffer
*)data
;
dlen
= ACRESLEN(&sc
->sc_einfo
);
dp
= (char *) malloc(dlen
, M_DEVBUF
, M_WAITOK
);
error
= accommand(dev
, ACCMD_READES
, dp
, dlen
);
ne
= sc
->sc_einfo
.nmte
+ sc
->sc_einfo
.nse
+
sc
->sc_einfo
.niee
+ sc
->sc_einfo
.ndte
;
dlen
= ne
* sizeof(struct aceltstat
);
tbuf
= (char *) malloc(dlen
, M_DEVBUF
, M_WAITOK
);
error
= copyout(tbuf
, acbp
->bufptr
, dlen
);
error
= accommand(dev
, ACCMD_MOVEM
, data
,
accommand(dev
, command
, bufp
, buflen
)
register struct ac_softc
*sc
= &ac_softc
[unit
];
register struct buf
*bp
= sc
->sc_bp
;
register struct scsi_fmt_cdb
*cmd
= sc
->sc_cmd
;
if (ac_debug
& ACD_FOLLOW
)
printf("accommand(dev=%x, cmd=%x, buf=%x, buflen=%x)\n",
dev
, command
, bufp
, buflen
);
if (sc
->sc_flags
& ACF_ACTIVE
)
panic("accommand: active!");
sc
->sc_flags
|= ACF_ACTIVE
;
bzero((caddr_t
)cmd
->cdb
, sizeof(cmd
->cdb
));
*(short *)&cmd
->cdb
[2] = 0;
sc
->sc_einfo
.nmte
+ sc
->sc_einfo
.nse
+
sc
->sc_einfo
.niee
+ sc
->sc_einfo
.ndte
;
cmd
->cdb
[7] = buflen
>> 16;
cmd
->cdb
[8] = buflen
>> 8;
cmd
->cdb
[2] = 0x3F; /* all pages */
*(short *)&cmd
->cdb
[2] = sc
->sc_picker
;
*(short *)&cmd
->cdb
[4] = *(short *)&bufp
[0];
*(short *)&cmd
->cdb
[6] = *(short *)&bufp
[2];
if (*(short *)&bufp
[4] & AC_INVERT
)
panic("accommand: bad command");
bp
->b_flags
= B_BUSY
|B_READ
;
sc
->sc_flags
&= ~ACF_ACTIVE
;
if (ac_debug
& ACD_FOLLOW
)
printf("acstart(unit=%x)\n", unit
);
if (scsiustart(ac_softc
[unit
].sc_hd
->hp_ctlr
))
register struct ac_softc
*sc
= &ac_softc
[unit
];
register struct buf
*bp
= sc
->sc_bp
;
struct hp_device
*hp
= sc
->sc_hd
;
if (ac_debug
& ACD_FOLLOW
)
printf("acgo(unit=%x): ", unit
);
stat
= scsigo(hp
->hp_ctlr
, hp
->hp_slave
, sc
->sc_punit
,
if (ac_debug
& ACD_FOLLOW
)
printf("scsigo returns %x\n", stat
);
register struct ac_softc
*sc
= &ac_softc
[unit
];
register struct buf
*bp
= sc
->sc_bp
;
if (ac_debug
& ACD_FOLLOW
)
printf("acintr(unit=%x, stat=%x)\n", unit
, stat
);
scsi_request_sense(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
,
sc
->sc_punit
, sensebuf
, sizeof sensebuf
);
sp
= (struct scsi_xsense
*)sensebuf
;
printf("ac%d: acintr sense key=%x, ac=%x, acq=%x\n",
unit
, sp
->key
, sp
->info4
, sp
->len
);
printf("ac%d: acintr unknown status 0x%x\n", unit
, stat
);
(void) biodone(sc
->sc_bp
);
register struct ac_softc
*sc
= &ac_softc
[minor(dev
)];
bzero(msbuf
, sizeof msbuf
);
error
= accommand(dev
, ACCMD_MODESENSE
, msbuf
, sizeof msbuf
);
while (bp
< &msbuf
[48]) {
sc
->sc_einfo
= *(struct acinfo
*)&bp
[2];
sc
->sc_picker
= sc
->sc_einfo
.fmte
; /* XXX */
printf("acgeteinfo: bad page type %x\n", bp
[0]);
acconvert(sbuf
, dbuf
, ne
)
register struct aceltstat
*ep
= (struct aceltstat
*)dbuf
;
register struct ac_restatphdr
*phdr
;
register struct ac_restatdb
*dbp
;
struct ac_restathdr
*hdr
;
hdr
= (struct ac_restathdr
*)&sbuf
[0];
if (ac_debug
& ACD_FOLLOW
)
printf("element status: first=%d, num=%d, len=%d\n",
hdr
->ac_felt
, hdr
->ac_nelt
, hdr
->ac_bcount
);
if (hdr
->ac_nelt
!= ne
) {
printf("acconvert: # of elements, %d != %d\n",
phdr
= (struct ac_restatphdr
*)sbuf
;
dbp
= (struct ac_restatdb
*)sbuf
;
bcount
-= phdr
->ac_bcount
;
while (dbp
< (struct ac_restatdb
*)sbuf
) {
ep
->type
= phdr
->ac_type
;
ep
->eaddr
= dbp
->ac_eaddr
;
dbp
= (struct ac_restatdb
*)
((char *)dbp
+ phdr
->ac_dlen
);
if (ne
< 0 || bcount
< 0)
panic("acconvert: inconsistant");