* Copyright (c) 1985, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)dhu.c 7.1 (Berkeley) %G%
* based on dh.c 6.3 84/03/15
* and on dmf.c 6.2 84/02/16
* Dave Johnson, Brown University Computer Science
#include "../machine/pte.h"
* Definition of the driver for the auto-configuration program.
int dhuprobe(), dhuattach(), dhurint(), dhuxint();
struct uba_device
*dhuinfo
[NDHU
];
u_short dhustd
[] = { 160440, 160500, 0 }; /* some common addresses */
struct uba_driver dhudriver
=
{ dhuprobe
, 0, dhuattach
, 0, dhustd
, "dhu", dhuinfo
};
#define NDHULINE (NDHU*16)
#define UNIT(x) (minor(x))
#define IFLAGS (EVENP|ODDP|ECHO)
#define IFLAGS (EVENP|ODDP)
* default receive silo timeout value -- valid values are 2..255
* number of ms. to delay between first char received and receive interrupt
* A value of 20 gives same response as ABLE dh/dm with silo alarm = 0
* Other values for silo timeout register defined here but not used:
* receive interrupt only on modem control or silo alarm (3/4 full)
* receive interrupt immediately on receive character
* Local variables for the driver
* Baud rates: no 50, 200, or 38400 baud; all other rates are from "Group B".
{ 0, 0, 1, 2, 3, 4, 0, 5, 6, 7, 8, 10, 11, 13, 14, 9 };
struct tty dhu_tty
[NDHULINE
];
int dhuact
; /* mask of active dhu's */
int dhustart(), ttrstrt();
* The clist space is mapped by the driver onto each UNIBUS.
* The UBACVT macro converts a clist space address for unibus uban
* into an i/o space address for the DMA routine.
int dhu_ubinfo
[NUBA
]; /* info about allocated unibus map */
int cbase
[NUBA
]; /* base address in unibus map */
#define UBACVT(x, uban) (cbase[uban] + ((x)-(char *)cfree))
* Routine for configuration to force a dhu to interrupt.
register int br
, cvec
; /* these are ``value-result'' */
register struct dhudevice
*dhuaddr
= (struct dhudevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
* The basic idea here is:
* do a self-test by setting the Master-Reset bit
* if this fails, then return
* if successful, there will be 8 diagnostic codes in RX FIFO
* therefore ask for a Received-Data-Available interrupt
* reset the interrupt-enable bit and flush out the diag. codes
dhuaddr
->dhucsr
= DHU_CS_MCLR
;
for (i
= 0; i
< 1000; i
++) {
if ((dhuaddr
->dhucsr
&DHU_CS_MCLR
) == 0)
if (dhuaddr
->dhucsr
&DHU_CS_MCLR
)
if (dhuaddr
->dhucsr
&DHU_CS_DFAIL
)
dhuaddr
->dhucsr
= DHU_CS_RIE
;
while (dhuaddr
->dhurbuf
< 0)
return (sizeof(struct dhudevice
));
* Routine called to attach a dhu.
dhusoftCAR
[ui
->ui_unit
] = ui
->ui_flags
;
cbase
[ui
->ui_ubanum
] = -1;
* Open a DHU11 line, mapping the clist onto the uba if this
* is the first dhu on this uba. Turn on this dhu if this is
register struct dhudevice
*addr
;
register struct uba_device
*ui
;
if (unit
>= NDHULINE
|| (ui
= dhuinfo
[dhu
])== 0 || ui
->ui_alive
== 0)
if (tp
->t_state
& TS_XCLUDE
&& u
.u_uid
!= 0)
addr
= (struct dhudevice
*)ui
->ui_addr
;
tp
->t_addr
= (caddr_t
)addr
;
* While setting up state for this uba and this dhu,
* block uba resets which can clear the state.
if (cbase
[ui
->ui_ubanum
] == -1) {
dhu_ubinfo
[ui
->ui_ubanum
] =
uballoc(ui
->ui_ubanum
, (caddr_t
)cfree
,
nclist
*sizeof(struct cblock
), 0);
cbase
[ui
->ui_ubanum
] = UBAI_ADDR(dhu_ubinfo
[ui
->ui_ubanum
]);
if ((dhuact
&(1<<dhu
)) == 0) {
addr
->dhucsr
= DHU_SELECT(0) | DHU_IE
;
addr
->dhutimo
= DHU_DEF_TIMO
;
/* anything else to configure whole board */
* If this is first open, initialize tty state to default.
if ((tp
->t_state
&TS_ISOPEN
) == 0) {
tp
->t_state
|= TS_HUPCLS
;
* Wait for carrier, then process line discipline specific open.
if ((dhumctl(dev
, DHU_ON
, DMSET
) & DHU_CAR
) ||
(dhusoftCAR
[dhu
] & (1<<(unit
&0xf))))
tp
->t_state
|= TS_CARR_ON
;
while ((tp
->t_state
& TS_CARR_ON
) == 0) {
sleep((caddr_t
)&tp
->t_rawq
, TTIPRI
);
return ((*linesw
[tp
->t_line
].l_open
)(dev
, tp
));
* Close a DHU11 line, turning off the modem control.
(*linesw
[tp
->t_line
].l_close
)(tp
);
(void) dhumctl(unit
, DHU_BRK
, DMBIC
);
if ((tp
->t_state
&(TS_HUPCLS
|TS_WOPEN
)) || (tp
->t_state
&TS_ISOPEN
)==0)
(void) dhumctl(unit
, DHU_OFF
, DMSET
);
/* Hold DTR low for 0.5 seconds */
timeout(wakeup
, (caddr_t
) &tp
->t_dev
, hz
/2);
sleep((caddr_t
) &tp
->t_dev
, PZERO
);
(void) dhumctl(unit
, DHU_OFF
, DMSET
);
register struct tty
*tp
= &dhu_tty
[UNIT(dev
)];
return ((*linesw
[tp
->t_line
].l_read
)(tp
, uio
));
register struct tty
*tp
= &dhu_tty
[UNIT(dev
)];
return ((*linesw
[tp
->t_line
].l_write
)(tp
, uio
));
* DHU11 receiver interrupt.
register struct dhudevice
*addr
;
register struct tty
*tp0
;
register struct uba_device
*ui
;
if (ui
== 0 || ui
->ui_alive
== 0)
addr
= (struct dhudevice
*)ui
->ui_addr
;
* Loop fetching characters from the silo for this
* dhu until there are no more in the silo.
while ((c
= addr
->dhurbuf
) < 0) { /* (c & DHU_RB_VALID) == on */
if ((c
& DHU_RB_STAT
) == DHU_RB_STAT
) {
* modem changed or diag info
/* decode diagnostic messages */
(void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
else if ((dhusoftCAR
[dhu
] & (1<<line
)) == 0 &&
(*linesw
[tp
->t_line
].l_modem
)(tp
, 0) == 0)
(void) dhumctl((dhu
<<4)|line
, DHU_OFF
, DMSET
);
if ((tp
->t_state
&TS_ISOPEN
) == 0) {
wakeup((caddr_t
)&tp
->t_rawq
);
if ((tp
->t_state
&TS_WOPEN
) == 0)
if ((tp
->t_flags
&(EVENP
|ODDP
)) == EVENP
||
(tp
->t_flags
&(EVENP
|ODDP
)) == ODDP
)
if ((c
& DHU_RB_DO
) && overrun
== 0) {
log(LOG_WARNING
, "dhu%d: silo overflow\n", dhu
);
* At framing error (break) generate
* a null (in raw mode, for getty), or a
* interrupt (in cooked/cbreak mode).
if (tp
->t_line
== NETLDISC
) {
(*linesw
[tp
->t_line
].l_rint
)(c
, tp
);
dhuioctl(dev
, cmd
, data
, flag
)
register int unit
= UNIT(dev
);
error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, cmd
, data
, flag
);
error
= ttioctl(tp
, cmd
, data
, flag
);
if (cmd
== TIOCSETP
|| cmd
== TIOCSETN
|| cmd
== TIOCLSET
||
cmd
== TIOCLBIC
|| cmd
== TIOCLBIS
)
(void) dhumctl(unit
, DHU_BRK
, DMBIS
);
(void) dhumctl(unit
, DHU_BRK
, DMBIC
);
(void) dhumctl(unit
, DHU_DTR
|DHU_RTS
, DMBIS
);
(void) dhumctl(unit
, DHU_DTR
|DHU_RTS
, DMBIC
);
(void) dhumctl(dev
, dmtodhu(*(int *)data
), DMSET
);
(void) dhumctl(dev
, dmtodhu(*(int *)data
), DMBIS
);
(void) dhumctl(dev
, dmtodhu(*(int *)data
), DMBIC
);
*(int *)data
= dhutodm(dhumctl(dev
, 0, DMGET
));
if (bits
& DML_RTS
) b
|= DHU_RTS
;
if (bits
& DML_DTR
) b
|= DHU_DTR
;
if (bits
& DML_LE
) b
|= DHU_LE
;
if (bits
& DHU_DSR
) b
|= DML_DSR
;
if (bits
& DHU_RNG
) b
|= DML_RNG
;
if (bits
& DHU_CAR
) b
|= DML_CAR
;
if (bits
& DHU_CTS
) b
|= DML_CTS
;
if (bits
& DHU_RTS
) b
|= DML_RTS
;
if (bits
& DHU_DTR
) b
|= DML_DTR
;
if (bits
& DHU_LE
) b
|= DML_LE
;
* Set parameters from open or stty into the DHU hardware
register struct dhudevice
*addr
;
addr
= (struct dhudevice
*)tp
->t_addr
;
* Block interrupts so parameters will be set
* before the line interrupts.
if ((tp
->t_ispeed
) == 0) {
tp
->t_state
|= TS_HUPCLS
;
(void)dhumctl(unit
, DHU_OFF
, DMSET
);
lpar
= (dhu_speeds
[tp
->t_ospeed
]<<12) | (dhu_speeds
[tp
->t_ispeed
]<<8);
if ((tp
->t_ispeed
) == B134
)
lpar
|= DHU_LP_BITS6
|DHU_LP_PENABLE
;
else if (tp
->t_flags
& (RAW
|LITOUT
|PASS8
))
lpar
|= DHU_LP_BITS7
|DHU_LP_PENABLE
;
if ((tp
->t_ospeed
) == B110
)
addr
->dhucsr
= DHU_SELECT(unit
) | DHU_IE
;
* DHU11 transmitter interrupt.
* Restart each line which used to be active but has
* terminated transmission since the last interrupt.
register struct dhudevice
*addr
;
register struct tty
*tp0
;
register struct uba_device
*ui
;
addr
= (struct dhudevice
*)ui
->ui_addr
;
while ((t
= addr
->dhucsrh
) & DHU_CSH_TI
) {
printf("dhu(%d,%d): NXM fault\n", dhu
, line
);
/* SHOULD RESTART OR SOMETHING... */
if (tp
->t_state
&TS_FLUSH
)
tp
->t_state
&= ~TS_FLUSH
;
addr
->dhucsrl
= DHU_SELECT(line
) | DHU_IE
;
* Do arithmetic in a short to make up
UBACVT(tp
->t_outq
.c_cf
, ui
->ui_ubanum
);
ndflush(&tp
->t_outq
, (int)cntr
);
(*linesw
[tp
->t_line
].l_start
)(tp
);
* Start (restart) transmission on the given DHU11 line.
register struct dhudevice
*addr
;
register int car
, dhu
, unit
, nch
;
addr
= (struct dhudevice
*)tp
->t_addr
;
* Must hold interrupts in following code to prevent
* state of the tp from changing.
* If it's currently active, or delaying, no need to do anything.
if (tp
->t_state
&(TS_TIMEOUT
|TS_BUSY
|TS_TTSTOP
))
* If there are sleepers, and output has drained below low
* water mark, wake up the sleepers..
if (tp
->t_outq
.c_cc
<=TTLOWAT(tp
)) {
if (tp
->t_state
&TS_ASLEEP
) {
tp
->t_state
&= ~TS_ASLEEP
;
wakeup((caddr_t
)&tp
->t_outq
);
selwakeup(tp
->t_wsel
, tp
->t_state
& TS_WCOLL
);
tp
->t_state
&= ~TS_WCOLL
;
* Now restart transmission unless the output queue is
if (tp
->t_outq
.c_cc
== 0)
if (tp
->t_flags
& (RAW
|LITOUT
))
nch
= ndqb(&tp
->t_outq
, 0);
nch
= ndqb(&tp
->t_outq
, 0200);
* If first thing on queue is a delay process it.
timeout(ttrstrt
, (caddr_t
)tp
, (nch
&0x7f)+6);
tp
->t_state
|= TS_TIMEOUT
;
* If characters to transmit, restart transmission.
car
= UBACVT(tp
->t_outq
.c_cf
, dhuinfo
[dhu
]->ui_ubanum
);
addr
->dhucsrl
= DHU_SELECT(unit
) | DHU_IE
;
addr
->dhulcr
&= ~DHU_LC_TXABORT
;
addr
->dhubar2
= ((car
>> DHU_XBA_SHIFT
) & DHU_BA2_XBA
) |
* Stop output on a line, e.g. for ^S/^Q or output flush.
register struct dhudevice
*addr
;
addr
= (struct dhudevice
*)tp
->t_addr
;
* Block input/output interrupts while messing with state.
if (tp
->t_state
& TS_BUSY
) {
* Device is transmitting; stop output
* by selecting the line and setting the
* abort xmit bit. We will get an xmit interrupt,
* where we will figure out where to continue the
* next time the transmitter is enabled. If
* TS_FLUSH is set, the outq will be flushed.
* In either case, dhustart will clear the TXABORT bit.
addr
->dhucsrl
= DHU_SELECT(unit
) | DHU_IE
;
addr
->dhulcr
|= DHU_LC_TXABORT
;
if ((tp
->t_state
&TS_TTSTOP
)==0)
register struct dhudevice
*dhuaddr
;
register int unit
, mbits
;
dhuaddr
= (struct dhudevice
*)(dhu_tty
[unit
].t_addr
);
dhuaddr
->dhucsr
= DHU_SELECT(unit
) | DHU_IE
;
* combine byte from stat register (read only, bits 16..23)
* with lcr register (read write, bits 0..15).
mbits
= dhuaddr
->dhulcr
| (dhuaddr
->dhustat
<< 16);
mbits
= (mbits
& 0xff0000) | bits
;
dhuaddr
->dhulcr
= (mbits
& 0xffff) | DHU_LC_RXEN
;
dhuaddr
->dhulcr2
= DHU_LC2_TXEN
;
* Reset state of driver if UBA reset was necessary.
* Reset the line and modem control registers.
register struct uba_device
*ui
;
register struct dhudevice
*addr
;
for (dhu
= 0; dhu
< NDHU
; dhu
++) {
if (ui
== 0 || ui
->ui_alive
== 0 || ui
->ui_ubanum
!= uban
)
dhu_ubinfo
[uban
] = uballoc(uban
, (caddr_t
)cfree
,
nclist
*sizeof (struct cblock
), 0);
cbase
[uban
] = UBAI_ADDR(dhu_ubinfo
[uban
]);
addr
= (struct dhudevice
*)ui
->ui_addr
;
addr
->dhucsr
= DHU_SELECT(0) | DHU_IE
;
addr
->dhutimo
= DHU_DEF_TIMO
;
for (i
= 0; i
< 16; i
++) {
if (tp
->t_state
& (TS_ISOPEN
|TS_WOPEN
)) {
(void)dhumctl(unit
, DHU_ON
, DMSET
);