Files
Arquivotheca.SunOS-4.1.4/sys/sundev/vpc.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

436 lines
10 KiB
C

#ifndef lint
static char sccsid[] = "@(#)vpc.c 1.1 94/10/31 Copyr 1986 Sun Micro";
#endif
/*
* Copyright (c) 1986 by Sun Microsystems, Inc.
*/
/*
* DMA interface driver for Systech vpc-2200 Versatec/Centronics interface
* uses Intel 8237 DMA controller chip
* uses 16 bit I/O transfers and 68000 byte order
* the driver tends to be rather ugly because there are two channels on one
* device, and we need to deal with each channel as an independent device
* to allow for separate DMA on each channel
* the board does not allow DMA transfers to begin on an odd byte boundary
* for 16 bit I/O so we copy the odd byte to an even address location and
* break the DMA into 2 separate operations
*/
#include "vpc.h"
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/map.h>
#include <sys/ioctl.h>
#include <sys/vcmd.h>
#include <sys/uio.h>
#include <machine/psl.h>
#include <machine/mmu.h>
#include <sundev/vpcreg.h>
#include <sundev/mbvar.h>
#define VPCPRI (PZERO-1)
/*
* NNVPC is the real number of channels on the VPCs, there are NCHANNELS
* minor devices per board, one minor device for each channel.
*/
#define NCHANNELS 2
#define NNVPC (NVPC * NCHANNELS)
/* NNVPC is (NVPC=2) * 2 = 4 */
/*
* channel numbers for the centronics and the versatec interface
* VERSATEC IS THE FIRST CHANNELL
*/
#define VERSATEC 0
#define PRINTER 1
/*
* software state structure, one for each channel
*/
struct vpc_softc {
int sc_state;
int sc_mbinfo;
int sc_addr;
int sc_length;
} vpc_softc[NNVPC];
/*
* sc_state bits for internal state
*/
#define VPC_BUSY 0100000
#define VPC_OPEN 0200000
#define VPC_ODD_ADDR 0400000 /* DMA starts on an odd address */
/*
* sc_state bits - passed in VGETSTATE and VSETSTATE ioctls
*/
#define VPC_MODE 0000700
#define VPC_SPP 0000400
#define VPC_PLOT 0000200
#define VPC_PRINT 0000100
#define VPC_CMNDS 0000076
/*
* macros for determining board, channel and minor device
*/
#define VPC_BOARD(dev) (minor(dev) / 2)
#define VPC_CHANNEL(dev) (minor(dev) & 0x01)
#define VPC_UNIT(dev) (minor(dev))
/*
* one buf for each channel for multi-threaded DMA
*/
struct buf rvpcbuf[NNVPC];
/*
* one scratch word (2 bytes) for each channel to support writing
* buffers that begin at odd addresses
*/
char *odd_addr_words;
int vpcprobe(), vpcintr();
struct mb_device *vpcdinfo[NVPC];
struct mb_driver vpcdriver = {
vpcprobe, 0, 0, 0, 0, vpcintr,
sizeof (struct vpcdevice), "vpc", vpcdinfo, 0, 0, MDR_DMA,
};
vpcprobe(reg)
caddr_t reg;
{
register struct vpcdevice *vpcaddr = (struct vpcdevice *)reg;
if (peekc((char *)&vpcaddr->control[VERSATEC].csr) == -1)
return (0);
if (peekc((char *)&vpcaddr->control[PRINTER].csr) == -1)
return (0);
if (pokec((char *)&vpcaddr->vpc_clear, VPC_ANY))
return (0);
if (pokec((char *)&vpcaddr->control[PRINTER].misc, VPC_ANY))
return (0);
vpcaddr->vpc_mode = VPC_MVPCMODE | 0x00; /* channel 0 */
vpcaddr->vpc_mode = VPC_MVPCMODE | 0x01; /* channel 1 */
/*
* bit to enabling interrupts for the board is hidden in
* the printer csr
*/
vpcaddr->control[PRINTER].csr = VPC_BC_EN_INT;
/*
* allocate space that we can DMA from for writing single bytes
* from transfers beginning on odd boundaries, two bytes for each
* channel, allign the space an even boundary. Also, allocate
* an even number of bytes to prevent possible alignment problems
* for other device drivers.
*/
odd_addr_words = (char *)rmalloc(iopbmap, (long)((NNVPC * 2) + 4));
if ((int)odd_addr_words & 0x01) {
odd_addr_words += 1;
}
return (sizeof(struct vpcdevice));
}
vpcopen(dev)
dev_t dev;
{
register struct vpc_softc *sc;
register struct mb_device *md;
register struct vpcdevice *vpcaddr;
register int s;
if (VPC_UNIT(dev) >= NNVPC ||
((sc = &vpc_softc[VPC_UNIT(dev)])->sc_state & VPC_OPEN) ||
(md = vpcdinfo[VPC_BOARD(dev)]) == 0 || md->md_alive == 0) {
return (ENXIO);
}
vpcaddr = (struct vpcdevice *)md->md_addr;
s = splx(pritospl(md->md_intpri));
if ((vpcaddr->control[VPC_CHANNEL(dev)].csr & VPC_PS_OK)!=VPC_PS_OK) {
(void) splx(s);
return (EIO);
}
vpcaddr->control[VPC_CHANNEL(dev)].interrupt = VPC_IC_EN_ATN;
if (VPC_CHANNEL(dev) == PRINTER) {
sc->sc_state = VPC_OPEN;
} else {
sc->sc_state = VPC_OPEN | VPC_PRINT | VPC_CLRCOM | VPC_RESET;
while (sc->sc_state & VPC_CMNDS) {
if (vpcwait(dev)) {
vpcclose(dev);
(void) splx(s);
return (EIO);
}
vpccmd(dev);
}
}
(void) splx(s);
return (0);
}
vpcclose(dev)
dev_t dev;
{
register int s;
register struct vpc_softc *sc = &vpc_softc[VPC_UNIT(dev)];
register struct mb_device *md = vpcdinfo[VPC_BOARD(dev)];
register struct vpcdevice *vpcaddr = (struct vpcdevice *)md->md_addr;
s = splx(pritospl(md->md_intpri));
sc->sc_state = 0;
vpcaddr->control[VPC_CHANNEL(dev)].interrupt = VPC_IC_DIS_ATN;
(void) splx(s);
}
vpcstrategy(bp)
register struct buf *bp;
{
register struct vpc_softc *sc = &vpc_softc[VPC_UNIT(bp->b_dev)];
register struct mb_device *md = vpcdinfo[VPC_BOARD(bp->b_dev)];
register int s;
register int addr;
register int length;
register int channel_number;
char *odd_addr_byte;
int tmp_addr;
s = splx(pritospl(md->md_intpri));
sc->sc_mbinfo = mbsetup(md->md_hd, bp, 0);
addr = MBI_ADDR(sc->sc_mbinfo);
length = bp->b_bcount;
bp->b_resid = 0;
channel_number = VPC_CHANNEL(bp->b_dev);
sc->sc_state |= VPC_BUSY;
if (addr & 0x01) {
odd_addr_byte = (char *)((int)odd_addr_words +
(VPC_UNIT(bp->b_dev) * 2));
*odd_addr_byte = DVMA[addr];
tmp_addr = odd_addr_byte - DVMA;
if (length > 1) {
sc->sc_state |= VPC_ODD_ADDR;
sc->sc_addr = addr + 1;
sc->sc_length = length - 1;
}
vpc_io(md, channel_number, tmp_addr, 1);
} else {
vpc_io(md, channel_number, addr, length);
}
(void) splx(s);
}
vpc_io(md, channel_number, addr, length)
register struct mb_device *md;
register int channel_number;
register int addr;
register int length;
{
register int s;
register struct vpcdevice *vpcaddr = (struct vpcdevice *)md->md_addr;
s = splx(pritospl(md->md_intpri));
if (length & 0x01) {
vpcaddr->control[channel_number].interrupt = VPC_IC_ODD_XFER;
length++;
}
length >>= 1;
length--; /* 8237 quirk */
vpcaddr->vpc_clearff = VPC_ANY;
vpcaddr->channel[channel_number].count = length & 0xff;
vpcaddr->channel[channel_number].count = (length >> 8) & 0xff;
addr >>= 1;
vpcaddr->channel[channel_number].addr = addr & 0xff;
vpcaddr->channel[channel_number].addr = (addr >> 8) & 0xff;
vpcaddr->control[channel_number].hiaddr = (addr >> 15) & 0x0f;
vpcaddr->control[channel_number].interrupt = VPC_IC_EN_XDONE;
vpcaddr->vpc_smb = channel_number;
(void) splx(s);
}
/*ARGSUSED*/
vpcwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
return (physio(vpcstrategy, &rvpcbuf[VPC_UNIT(dev)], dev, B_WRITE,
minphys, uio));
}
/*ARGSUSED*/
vpcintr()
{
register u_char status;
register struct vpc_softc *sc;
register int device;
register int board;
register int channel;
register int found;
struct vpcdevice *vpcaddr;
struct mb_device *md;
found = 0;
for (board = 0; board < NVPC; board++) {
if ((md = vpcdinfo[board]) == NULL)
continue;
vpcaddr = (struct vpcdevice *) md->md_addr;
for (channel = 0; channel < NCHANNELS; channel++) {
status = vpcaddr->control[channel].interrupt;
device = (board * NCHANNELS) + channel;
sc = &vpc_softc[device];
/*
* we should probably check for the specific reason
* for the attention interrupt, but because every
* printer seems to handle paper out / printer on-line
* conditions differently all we do is clear it
*/
if (status & VPC_IS_ATN) {
found = 1;
vpcaddr->control[channel].interrupt =
VPC_IC_CL_ATN;
wakeup((caddr_t)sc);
}
if (status & VPC_IS_DONE) {
found = 1;
vpcaddr->control[channel].interrupt =
VPC_IC_CL_DONE;
if (status & VPC_IS_ERR) {
printf("vpc%d: bus address error channel %d.\n",
board, channel);
/*
* reset the printer logic (but not
* the 8237 DMA chip)
*/
vpcaddr->control[PRINTER].misc =
VPC_ANY;
vpcaddr->control[PRINTER].csr =
VPC_BC_EN_INT;
}
if (sc->sc_state & VPC_ODD_ADDR) {
sc->sc_state &= ~VPC_ODD_ADDR;
vpc_io(md, channel,
sc->sc_addr, sc->sc_length);
return (found);
}
if (sc->sc_state & VPC_BUSY) {
sc->sc_state &= ~VPC_BUSY;
mbrelse(md->md_hd, &sc->sc_mbinfo);
iodone(&rvpcbuf[device]);
}
wakeup((caddr_t)sc);
}
}
}
return (found);
}
vpcwait(dev)
dev_t dev;
{
register struct vpc_softc *sc = &vpc_softc[VPC_UNIT(dev)];
register struct vpcdevice *vpcaddr =
(struct vpcdevice *)vpcdinfo[VPC_BOARD(dev)]->md_addr;
for (;;) {
if ((sc->sc_state & VPC_BUSY) == 0 &&
vpcaddr->control[VERSATEC].csr & VPC_PS_OK) {
break;
}
(void) sleep((caddr_t)sc, VPCPRI);
}
return (0); /* NO ERRORS YET */
}
struct pair {
char soft; /* software bit */
char hard; /* hardware bit */
} vpcbits[] = {
VPC_RESET, VPC_PC_V_RESET,
VPC_CLRCOM, VPC_PC_V_CLEAR,
VPC_EOTCOM, VPC_PC_V_EOT,
VPC_FFCOM, VPC_PC_V_FFEED,
VPC_TERMCOM, VPC_PC_V_LTER,
0, 0,
};
vpccmd(dev)
dev_t dev;
{
register struct vpc_softc *sc = &vpc_softc[VPC_UNIT(dev)];
register struct vpcdevice *vpcaddr =
(struct vpcdevice *)vpcdinfo[VPC_BOARD(dev)]->md_addr;
register struct pair *bit;
for (bit = vpcbits; bit->soft != 0; bit++) {
if (sc->sc_state & bit->soft) {
vpcaddr->control[VERSATEC].csr = bit->hard;
sc->sc_state &= ~bit->soft;
return;
}
}
}
/*ARGSUSED*/
vpcioctl(dev, cmd, data, flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
register int m;
register struct vpc_softc *sc = &vpc_softc[VPC_UNIT(dev)];
register struct mb_device *md = vpcdinfo[VPC_BOARD(dev)];
register struct vpcdevice *vpcaddr = (struct vpcdevice *)md->md_addr;
register int s;
if (VPC_CHANNEL(dev) != VERSATEC)
return (ENOTTY);
switch (cmd) {
case VGETSTATE:
*(int *)data = sc->sc_state;
break;
case VSETSTATE:
m = *(int *)data;
sc->sc_state =
(sc->sc_state & ~VPC_MODE) | (m & (VPC_MODE | VPC_CMNDS));
break;
default:
return (ENOTTY);
}
s = splx(pritospl(md->md_intpri));
(void) vpcwait(dev);
if (sc->sc_state & VPC_SPP) {
vpcaddr->control[VERSATEC].csr = VPC_PC_V_SPP;
} else if (sc->sc_state & VPC_PLOT) {
vpcaddr->control[VERSATEC].csr = VPC_PC_V_PLOT;
} else {
vpcaddr->control[VERSATEC].csr = 0;
}
while (sc->sc_state & VPC_CMNDS) {
(void) vpcwait(dev);
vpcaddr->control[VERSATEC].interrupt = VPC_IC_EN_RDONE;
vpccmd(dev);
(void) vpcwait(dev);
}
(void) splx(s);
return (0);
}