* Copyright (c) 1986 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)ik.c 7.7 (Berkeley) 12/16/90
* PS300/IKON DR-11W Device Driver.
#include "../include/mtpr.h"
#include "../include/pte.h"
#include "../vba/vbavar.h"
#include "../vba/ikreg.h"
#include "../vba/psreg.h"
#include "../vba/psproto.h"
int ikprobe(), ikattach(), iktimer();
struct vba_device
*ikinfo
[NIK
];
struct vba_driver ikdriver
= { ikprobe
, 0, ikattach
, 0, ikstd
, "ik", ikinfo
};
* Devices are organized in pairs with the odd valued
* device being used for ``diagnostic'' purposes. That
* is diagnostic devices don't get auto-attach'd and
* detach'd on open-close.
#define IKUNIT(dev) (minor(dev) >> 1)
#define IKDIAG(dev) (minor(dev) & 01) /* is a diagnostic unit */
uid_t is_uid
; /* uid of open processes */
u_short is_timeout
; /* current timeout (seconds) */
u_short is_error
; /* internal error codes */
#define IKF_ATTACHED 0x1 /* unit is attached (not used yet) */
} is_nameaddr
; /* address of last symbol lookup */
caddr_t is_buf
[PS_MAXDMA
];/* i/o buffer XXX */
struct buf iktab
[NIK
]; /* unit command queue headers */
struct buf rikbuf
[NIK
]; /* buffers for read/write operations */
struct buf cikbuf
[NIK
]; /* buffers for control operations */
/* buf overlay definitions */
#define b_command b_resid
int ikdiotimo
= PS_DIOTIMO
; /* dio polling timeout */
int iktimeout
= PS_TIMEOUT
; /* attention/dma timeout (in hz) */
register int br
, cvec
; /* r12, r11 */
register struct ikdevice
*ik
;
br
= 0; cvec
= br
; br
= cvec
;
ik
= (struct ikdevice
*)reg
;
ik
->ik_vec
= --vi
->ui_hd
->vh_lastiv
;
* Use extended non-privileged address modifier
* to avoid address overlap with 24-bit devices.
ik
->ik_mod
= 0xf1; /* address modifier */
* Try and reset the PS300. Since this
* won't work if it's powered off, we
* can't use sucess/failure to decide
* if the device is present.
(void) psreset(ik
, IKCSR_IENA
);
br
= 0x18, cvec
= ik
->ik_vec
; /* XXX */
return (sizeof (struct ikdevice
));
* Perform a ``hard'' reset.
register struct ikdevice
*ik
;
ik
->ik_csr
= IKCSR_MCLR
|iena
;
ik
->ik_csr
= IKCSR_FNC3
|iena
;
return (dioread(ik
) == PS_RESET
);
ik_softc
[vi
->ui_unit
].is_uid
= -1;
* Open a PS300 and attach. We allow multiple
* processes with the same uid to share a unit.
register int unit
= IKUNIT(dev
);
register struct ik_softc
*sc
;
if (unit
>= NIK
|| (vi
= ikinfo
[unit
]) == 0 || vi
->ui_alive
== 0)
if (sc
->is_uid
!= (uid_t
)-1 && sc
->is_uid
!= u
.u_uid
)
if (sc
->is_uid
== (uid_t
)-1) {
timeout(iktimer
, (caddr_t
)unit
, hz
);
* Perform PS300 attach for first process.
if (ikcommand(dev
, PS_ATTACH
, 1)) {
* If attach fails, perform a hard
* reset once, then retry the command.
ik
= (struct ikdevice
*)ikinfo
[unit
]->ui_addr
;
if (!reset
++ && psreset(ik
, 0))
untimeout(iktimer
, (caddr_t
)unit
);
register struct ik_softc
*sc
= &ik_softc
[unit
];
(void) ikcommand(dev
, PS_DETACH
, 1); /* auto detach */
untimeout(iktimer
, (caddr_t
)unit
);
return (ikrw(dev
, uio
, B_READ
));
return (ikrw(dev
, uio
, B_WRITE
));
* Take read/write request and perform physical i/o
* transaction with PS300. This involves constructing
* a physical i/o request vector based on the uio
* vector, performing the dma, and, finally, moving
* the data to it's final destination (because of CCI
register struct uio
*uio
;
int error
, unit
= IKUNIT(dev
), s
, wrcmd
;
register struct iovec
*iov
;
register struct psalist
*ap
;
struct ik_softc
*sc
= &ik_softc
[unit
];
error
= 0, iov
= uio
->uio_iov
, wrcmd
= PS_WRPHY
;
for (; !error
&& uio
->uio_iovcnt
; iov
++, uio
->uio_iovcnt
--) {
* Hack way to set PS300 address w/o doing an lseek
* and specify write physical w/ refresh synchronization.
if ((int)iov
->iov_base
&PSIO_SYNC
)
uio
->uio_offset
= (int)iov
->iov_base
& ~PSIO_SYNC
;
if (iov
->iov_len
> PS_MAXDMA
) {
sc
->is_error
= PSERROR_INVALBC
, error
= EINVAL
;
if ((int)uio
->uio_offset
&01) {
sc
->is_error
= PSERROR_BADADDR
, error
= EINVAL
;
while (bp
->b_flags
&B_BUSY
) {
sleep((caddr_t
)bp
, PRIBIO
+1);
bp
->b_flags
= B_BUSY
| rw
;
* Construct address descriptor in buffer.
ap
= (struct psalist
*)sc
->is_buf
;
/* work-around dr300 word swapping */
ap
->addr
[0] = uio
->uio_offset
& 0xffff;
ap
->addr
[1] = uio
->uio_offset
>> 16;
ap
->wc
= (iov
->iov_len
+ 1) >> 1;
error
= copyin(iov
->iov_base
, (caddr_t
)&ap
[1],
error
= ikcommand(dev
, wrcmd
,
iov
->iov_len
+ sizeof (*ap
));
error
= ikcommand(dev
, PS_RDPHY
, sizeof (*ap
));
cp
= (caddr_t
)&ap
[1], len
= iov
->iov_len
;
for (; len
> 0; len
-= NBPG
, cp
+= NBPG
)
error
= copyout((caddr_t
)&ap
[1], iov
->iov_base
,
if (bp
->b_flags
&B_WANTED
)
uio
->uio_resid
-= iov
->iov_len
;
uio
->uio_offset
+= iov
->iov_len
;
bp
->b_flags
&= ~(B_BUSY
|B_WANTED
);
* Perform a PS300 command.
ikcommand(dev
, com
, count
)
bp
= &cikbuf
[IKUNIT(dev
)];
while (bp
->b_flags
&B_BUSY
) {
sleep((caddr_t
)bp
, PRIBIO
);
bp
->b_flags
= B_BUSY
|B_READ
;
if (bp
->b_flags
&B_WANTED
)
* Physio strategy routine
* Put request at end of controller queue.
dp
= &iktab
[IKUNIT(bp
->b_dev
)];
if (dp
->b_actf
!= NULL
) {
dp
->b_actl
->av_forw
= bp
;
dp
->b_actf
= dp
->b_actl
= bp
;
* Start the next command on the controller's queue.
register struct ikdevice
*ik
;
register struct ik_softc
*sc
;
* Pull a request off the controller queue
if ((bp
= dp
->b_actf
) == NULL
) {
* Mark controller busy and process this request.
unit
= IKUNIT(bp
->b_dev
);
ik
= (struct ikdevice
*)ikinfo
[unit
]->ui_addr
;
switch ((int)bp
->b_command
) {
case PS_ATTACH
: /* logical unit attach */
case PS_DETACH
: /* logical unit detach */
case PS_LOOKUP
: /* name lookup */
case PS_RDPHY
: /* physical i/o read */
case PS_WRPHY
: /* physical i/o write */
case PS_WRPHY_SYNC
: /* physical i/o write w/ sync */
* Handshake command and, optionally,
* byte count and byte swap flag.
if (sc
->is_error
= diowrite(ik
, (u_short
)bp
->b_command
))
if (bp
->b_command
< PS_DETACH
) {
if (sc
->is_error
= diowrite(ik
, (u_short
)bp
->b_bcount
))
if (sc
->is_error
= diowrite(ik
, (u_short
)0 /* !swab */))
* Set timeout and wait for an attention interrupt.
sc
->is_timeout
= iktimeout
;
case PS_DMAOUT
: /* dma data host->PS300 */
case PS_DMAIN
: /* dma data PS300->host */
csr
= IKCSR_CYCLE
|IKCSR_FNC1
;
log(LOG_ERR
, "ik%d: bad cmd %x\n", unit
, bp
->b_command
);
sc
->is_error
= PSERROR_BADCMD
;
/* initiate dma transfer */
addr
= vtoph((struct proc
*)0, (unsigned)sc
->is_buf
);
ik
->ik_bahi
= addr
>> 17;
ik
->ik_balo
= (addr
>> 1) & 0xffff;
ik
->ik_wc
= ((bc
+ 1) >> 1) - 1; /* round & convert */
ik
->ik_pulse
= IKPULSE_RATTF
|IKPULSE_RDMAF
;
sc
->is_timeout
= iktimeout
;
ik
->ik_csr
= IKCSR_IENA
|IKCSR_GO
|csr
;
dp
->b_actf
= bp
->av_forw
; /* remove from queue */
sc->is_error = PSERROR_NAMETIMO; \
sc->is_nameaddr.w[i] = v; \
* Process a device interrupt.
register struct ikdevice
*ik
;
register struct buf
*bp
, *dp
;
/* should go by controller, but for now... */
ik
= (struct ikdevice
*)ikinfo
[ikon
]->ui_addr
;
* Discard all non-attention interrupts. The
* interrupts we're throwing away should all be
* associated with DMA completion.
if ((ik
->ik_csr
&(IKCSR_ATTF
|IKCSR_STATC
)) != IKCSR_ATTF
) {
ik
->ik_pulse
= IKPULSE_RATTF
|IKPULSE_RDMAF
|IKPULSE_SIENA
;
* Fetch attention code immediately.
ik
->ik_csr
= IKCSR_RATTF
|IKCSR_RDMAF
|IKCSR_FNC1
;
ik
->ik_pulse
= IKPULSE_FNC2
;
* Get device and block structures, and a pointer
* to the vba_device for the device. We receive an
* unsolicited interrupt whenever the PS300 is power
* cycled (so ignore it in that case).
if ((bp
= dp
->b_actf
) == NULL
) {
if (PS_CODE(data
) != PS_RESET
) /* power failure */
log(LOG_WARNING
, "ik%d: spurious interrupt, code %x\n",
sc
= &ik_softc
[IKUNIT(bp
->b_dev
)];
sc
->is_timeout
= 0; /* disable timer */
case PS_LOOKUP
: /* name lookup */
if (data
== PS_LOOKUP
) { /* dma name */
bp
->b_command
= PS_DMAOUT
;
if (data
== PS_DMAOK(PS_LOOKUP
)) {
/* reenable interrupt and wait for address */
sc
->is_timeout
= iktimeout
;
* Address should be present, extract it one
* word at a time from the PS300 (yech).
if (data
!= PS_ADROK(PS_LOOKUP
))
case PS_WRPHY_SYNC
: /* physical i/o write w/ sync */
if (data
== PS_WRPHY_SYNC
) { /* start dma transfer */
bp
->b_command
= PS_DMAOUT
;
if (data
!= PS_DMAOK(PS_WRPHY_SYNC
))
case PS_WRPHY
: /* physical i/o write */
if (data
== PS_WRPHY
) { /* start dma transfer */
bp
->b_command
= PS_DMAOUT
;
if (data
!= PS_DMAOK(PS_WRPHY
))
case PS_ATTACH
: /* attach unit */
case PS_DETACH
: /* detach unit */
case PS_ABORT
: /* abort code from ps300 */
if (data
!= bp
->b_command
)
case PS_RDPHY
: /* physical i/o read */
if (data
== PS_RDPHY
) { /* dma address list */
bp
->b_command
= PS_DMAOUT
;
if (data
== PS_ADROK(PS_RDPHY
)) {
/* collect read byte count and start dma */
bp
->b_bcount
= dioread(ik
);
bp
->b_command
= PS_DMAIN
;
if (data
== PS_DMAOK(PS_RDPHY
))
dp
->b_actf
= bp
->av_forw
; /* remove from queue */
ik
->ik_pulse
= IKPULSE_SIENA
; /* explicitly reenable */
register struct ik_softc
*sc
= &ik_softc
[unit
];
if (sc
->is_timeout
&& --sc
->is_timeout
== 0) {
register struct buf
*dp
, *bp
;
log(LOG_ERR
, "ik%d: timeout\n", unit
);
/* should abort current command */
sc
->is_error
= PSERROR_CMDTIMO
;
dp
->b_actf
= bp
->av_forw
; /* remove from queue */
timeout(iktimer
, (caddr_t
)unit
, hz
);
* Handshake read from DR300.
register struct ikdevice
*ik
;
for (t
= ikdiotimo
; t
> 0; t
--)
if ((ik
->ik_csr
&(IKCSR_ATTF
|IKCSR_STATC
)) == IKCSR_ATTF
) {
ik
->ik_csr
= IKCSR_RATTF
|IKCSR_RDMAF
|IKCSR_FNC1
;
ik
->ik_pulse
= IKPULSE_FNC2
;
* Handshake write to DR300.
* Interrupts are enabled before completing the work
* so the caller should either be at splik or be
* prepared to take the interrupt immediately.
register struct ikdevice
*ik
;
* Deposit data and generate dr300 attention
ik
->ik_csr
= IKCSR_RDMAF
|IKCSR_RATTF
;
ik
->ik_pulse
= IKPULSE_FNC2
;
for (t
= ikdiotimo
; t
> 0; t
--) {
#define IKCSR_DONE (IKCSR_STATA|IKCSR_STATC)
if ((csr
&IKCSR_DONE
) == IKCSR_DONE
) {
* Done, complete handshake by notifying dr300.
ik
->ik_csr
= IKCSR_IENA
; /* ~IKCSR_FNC1 */
ik
->ik_pulse
= IKPULSE_FNC2
;
/* beware of potential deadlock with dioread */
if ((csr
&(IKCSR_ATTF
|IKCSR_STATC
)) == IKCSR_ATTF
)
return (PSERROR_DIOTIMO
);
ikioctl(dev
, cmd
, data
, flag
)
int error
= 0, unit
= IKUNIT(dev
), s
;
register struct ik_softc
*sc
= &ik_softc
[unit
];
case PSIOGETERROR
: /* get error code for last operation */
*(int *)data
= sc
->is_error
;
case PSIOLOOKUP
: { /* PS300 name lookup */
register struct pslookup
*lp
= (struct pslookup
*)data
;
if (lp
->pl_len
> PS_MAXNAMELEN
)
while (bp
->b_flags
&B_BUSY
) {
sleep((caddr_t
)bp
, PRIBIO
+1);
bp
->b_flags
= B_BUSY
| B_WRITE
;
error
= copyin(lp
->pl_name
, (caddr_t
)sc
->is_buf
,
sc
->is_buf
[lp
->pl_len
] = '\0';
error
= ikcommand(dev
, PS_LOOKUP
, lp
->pl_len
);
if (bp
->b_flags
&B_WANTED
)
bp
->b_flags
&= ~(B_BUSY
|B_WANTED
);
lp
->pl_addr
= sc
->is_nameaddr
.l
;