/* @(#)if_hdh.c 7.5 (Berkeley) %G% */
/************************************************************************\
________________________________________________________
| AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC |
| AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC |
| AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
| AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
| AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC |
| AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC |
\________________________________________________________/
Copyright (c) 1984 by Advanced Computer Communications
720 Santa Barbara Street, Santa Barbara, California 93101
This software may be duplicated and used on systems
which are licensed to run U.C. Berkeley versions of
the UNIX operating system. Any duplication of any
part of this software must include a copy of ACC's
Device specific driver for IF-11/HDH under 4.2BSD
Converted to 4.3, updated, UCB.
31-Aug-1984: V1.0 - First Implementation. A.B.
6-Nov-1984: V1.1 - Supress extra "LINE DOWN" msgs. A.B.
13-Jan-1984: V1.2 - Add conditionals for TWG. A.B.
\************************************************************************/
* ACC IF-11/HDH interface
#include "../machine/pte.h"
#include "../netimp/if_imp.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
int hdhprobe(), hdhattach(), hdhintr();
struct uba_device
*hdhinfo
[NHDH
];
u_short hdhstd
[] = { 0 };
struct uba_driver hdhdriver
=
{ hdhprobe
, 0, hdhattach
, 0, hdhstd
, "hdh", hdhinfo
};
#define HDHUNIT(x) minor(x)
int hdhinit(), hdhoutput(), hdhreset();
* "Lower half" of IMP interface driver.
* Each IMP interface is handled by a common module which handles
* the IMP-host protocol and a hardware driver which manages the
* hardware specific details of talking with the IMP.
* The hardware portion of the IMP driver handles DMA and related
* management of UNIBUS resources. The IMP protocol module interprets
* contents of these messages and "controls" the actions of the
* hardware module during IMP resets, but not, for instance, during
* The two modules are coupled at "attach time", and ever after,
* through the imp interface structure. Higher level protocols,
* e.g. IP, interact with the IMP driver, rather than the HDH.
#define NHDHCH 2 /* no. of FDX channels for HDH */
#define SUPR 0 /* supervisor channel */
#define DATA 1 /* data channel */
#define HDHSUPR 0 /* supervisor read */
#define HDHSUPW 1 /* supervisor write */
#define HDHDATR 2 /* data read */
#define HDHDATW 3 /* data write */
#define HDH_UP 2 /* HDH protocol is up */
#define HDH_STARTED 1 /* HDH has been initialized */
#define HCBUSY 1 /* HDH HDX channel busy flag */
/* The IF-11/HDH has four independent dath flow channels between the
/* front-end and the host. Two are used for reading and writing
/* control messages and two are used for data flow. Each IF-11/HDH
/* has a device dependent data structure (hdh_softc) which contains
/* an array of four channel dependent structures (hdh_chan) to maintain
/* the context of each channel. Channel structures can be linked into
/* a queue of I/O requests pending for the hardware interface.
/* UNIBUS mapping resources are allocated for each channel pair.
struct hdh_chan
{ /* HDH HDX channel structure */
struct hdh_chan
*hc_next
; /* link for Start I/O queuing */
char hc_chan
; /* HDX chan number */
char hc_adx
; /* extended UNIBUS address bits */
short hc_addr
; /* lower UNIBUS address bits */
short hc_cnt
; /* byte count */
char hc_func
; /* UMC I/O function */
char hc_sbfc
; /* UMC I/O subfunction */
short hc_flags
; /* status flags */
struct hdh_sioq
{ /* Start I/O queue head structure */
struct hdh_chan
*sioq_head
; /* pointer to queue head */
struct hdh_chan
*sioq_tail
; /* pointer to queue tail */
struct hdh_softc
{ /* HDH device dependent structure */
struct imp_softc
*hdh_imp
; /* pointer to IMP's imp_softc struct */
struct ifuba hdh_ifuba
[NHDHCH
]; /* UNIBUS resources */
struct hdh_chan hdh_chan
[2*NHDHCH
]; /* HDX HDH channels */
struct hdh_sioq hdh_sioq
; /* start i/o queue */
short hdh_flags
; /* various status conditions */
* Normally, code goes here to cause the device to interrupt to determine its
* interrupt vector. However, since the UMC must be told its vector in order
* to interrupt, we allocate and return an unused vector and initialize the
struct hdhregs
*addr
= (struct hdhregs
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
br
= 0x15; /* priority 21 (5 on UNIBUS) */
cvec
= 0270; /* use constant for now ... */
#ifdef VAXVMS /* if VMS */
cvec
= 0270; /* we can't allocate vectors */
cvec
= (uba_hd
[numuba
].uh_lastiv
-= 4); /* available vector */
addr
->ioini
= (char) 0; /* init UMC regs */
addr
->staack
= (char) 0; /* pass vector */
addr
->ionmi
= (char) 0; /* and kick UMC */
addr
->iochn
= (char) (cvec
>> 2);
addr
->csr
= (short) HDH_RST
;
addr
->csr
= (short) (HDH_IEN
|HDH_DMA
|HDH_WRT
); /* set enables */
DELAY(5000); /* give the UMC some time */
* Call the IMP module to allow it to set up its internal
* state, then tie the two modules together by setting up
* the back pointers to common data structures.
register struct uba_device
*ui
;
register struct hdh_softc
*sc
= &hdh_softc
[ui
->ui_unit
];
register struct impcb
*ip
;
if ((sc
->hdh_imp
= impattach(ui
->ui_driver
->ud_dname
, ui
->ui_unit
,
ip
= &sc
->hdh_imp
->imp_cb
;
ip
->ic_output
= hdhoutput
;
sc
->hdh_ifuba
[ui
->ui_unit
].ifu_flags
= UBA_CANTWAIT
;
* Reset interface after UNIBUS reset.
register struct uba_device
*ui
= hdhinfo
[unit
];
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
if ((unit
>= NHDH
) || (ui
== 0) || (ui
->ui_alive
== 0)
|| (ui
->ui_ubanum
!= uban
))
sc
->hdh_imp
->imp_if
.if_flags
&= ~IFF_RUNNING
;
sc
->hdh_imp
->imp_cb
.ic_oactive
= 0;
(*sc
->hdh_imp
->imp_if
.if_init
)(sc
->hdh_imp
->imp_if
.if_unit
);
* Initialize the imp interface.
HDHINIT
, /* SYSINIT opcode */
HDHRQUP
& 0xff, /* control code (LSB) */
(HDHRQUP
>>8) & 0xff, /* control code (MSB) */
10, /* command extension len */
0, /* loopback mode (off) */
3, /* our address (3=DTE) */
1, /* their address (1=DCE) */
3, /* frame ack t1 timeout */
3, /* poll ack timeout */
30, /* adm wait timeout */
3, /* rej wait timeout */
3, /* watchdog timeout */
0xaa /* baud rate (0xaa=38.4KB) */
/* (output on RS-232 pin 24, */
/* send/receive timing is always */
/* taken from pins 15/17) */
register struct hdh_softc
*sc
;
register struct uba_device
*ui
;
if (unit
>= NHDH
|| (ui
= hdhinfo
[unit
]) == NULL
printf("hdh%d: not alive\n", unit
);
if (sc
->hdh_flags
& HDH_STARTED
)
if ((sc
->hdh_imp
->imp_if
.if_flags
& IFF_RUNNING
) == 0)
if (if_ubainit(&sc
->hdh_ifuba
[i
], ui
->ui_ubanum
, 0,
(int)btoc(IMP_RCVBUF
)) == 0) {
printf("hdh%d: cannot get chan %d uba resources\n",
sc
->hdh_imp
->imp_if
.if_flags
|= IFF_RUNNING
;
sc
->hdh_flags
= HDH_STARTED
;
* hang a supervisor read (for line status)
hdh_iorq(unit
, HDHSUPR
, IMP_RCVBUF
, HDHRDB
);
hdh_iorq(unit
, HDHDATR
, IMP_RCVBUF
, HDHRDB
+HDHSTR
);
snd_supr(unit
, init_blk
, sizeof(init_blk
));
* Start an output operation on an mbuf.
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
* If output isn't active, attempt to
* start sending a new packet.
if (sc
->hdh_imp
->imp_cb
.ic_oactive
) {
printf("hdh%d: start on active unit\n", unit
);
if ((sc
->hdh_flags
& HDH_UP
) == 0) {
/* Link not up, can't xmit */
len
= if_wubaput(&sc
->hdh_ifuba
[DATA
], m
); /* copy data to mapped mem */
sc
->hdh_imp
->imp_cb
.ic_oactive
= 1;
hdh_iorq(unit
, HDHDATW
, len
, HDHWRT
+HDHEOS
);
* Start i/o operation on a UMC logical channel
hdh_iorq(unit
, lcn
, len
, func
)
int unit
, lcn
, len
, func
;
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
register struct hdh_chan
*hc
= &sc
->hdh_chan
[lcn
];
* If channel is busy (shouldn't be), drop.
if (hc
->hc_flags
& HCBUSY
) {
printf("hdh%d: channel busy lcn=%d\n", unit
, lcn
);
/* get appropriate UNIBUS mapping info */
if (lcn
& 1) /* read or write? */
info
= sc
->hdh_ifuba
[lcn
>>1].ifu_w
.ifrw_info
;
info
= sc
->hdh_ifuba
[lcn
>>1].ifu_r
.ifrw_info
;
hc
->hc_adx
= (char)((info
& 0x30000) >> 12);
hc
->hc_addr
= (unsigned short)(info
& 0xffff);
hc
->hc_func
= (char)func
;
* If UMC comm regs busy, queue start i/o for later.
if (sc
->hdh_sioq
.sioq_head
) {
(sc
->hdh_sioq
.sioq_tail
)->hc_next
= hc
;
sc
->hdh_sioq
.sioq_tail
= hc
;
/* start i/o on channel now */
sc
->hdh_sioq
.sioq_head
= hc
;
sc
->hdh_sioq
.sioq_tail
= hc
;
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
register struct hdh_chan
*hc
= sc
->hdh_sioq
.sioq_head
;
register struct hdhregs
*addr
= (struct hdhregs
*)hdhinfo
[unit
]->ui_addr
;
addr
->iochn
= hc
->hc_chan
;
addr
->ioadx
= hc
->hc_adx
;
addr
->ioadl
= hc
->hc_addr
;
addr
->iocnt
= hc
->hc_cnt
;
addr
->iofcn
= hc
->hc_func
;
addr
->iosbf
= hc
->hc_sbfc
;
/* signal UMC if necessary */
addr
->csr
= HDH_DMA
|HDH_WRT
|HDH_IEN
|HDH_NMI
;
* IF-11/HDH interrupt handler
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
register struct hdh_chan
*hc
;
register struct hdhregs
*addr
= (struct hdhregs
*)hdhinfo
[unit
]->ui_addr
;
* Check for hardware errors.
if (addr
->csr
& HDH_UER
) {
printf("hdh%d: hard error csr=%b\n", unit
, addr
->csr
, HDH_BITS
);
addr
->csr
= 0; /* disable i/f */
* Get logical channel info.
if ((lcn
= addr
->stachn
) >= (NHDHCH
*2)) {
printf("hdh%d: unknown channel lcn=%d\n", unit
, lcn
);
cnt
= hc
->hc_cnt
- addr
->stacnt
;
/* Figure out what kind of interrupt it was */
case HDHSACK
: /* start i/o accepted */
if (hc
!= sc
->hdh_sioq
.sioq_head
) {
printf("hdh%d: STARTIO error lcn=%d hc=%x sq=%x\n",
unit
, lcn
, hc
, sc
->hdh_sioq
.sioq_head
);
/* try to start any queued i/o request */
if (sc
->hdh_sioq
.sioq_head
= sc
->hdh_sioq
.sioq_head
->hc_next
) {
case HDHDONE
: /* i/o completion */
printf("hdh%d: I/O abort ", unit
);
printf("hdh%d: program error ", unit
);
printf("hdh%d: overrun error ", unit
);
printf("hdh%d: NXM timeout or UB parity error ", unit
);
printf("lcn=%d func=%x\n", lcn
, hc
->hc_func
);
if (hc
->hc_func
& HDHRDB
)
sc
->hdh_imp
->imp_if
.if_ierrors
++;
sc
->hdh_imp
->imp_if
.if_oerrors
++;
/* was it supervisor or data traffic? */
hdh_data(unit
, lcn
, cc
, cnt
);
addr
->csr
= HDH_DMA
|HDH_WRT
|HDH_IEN
|HDH_NMI
;
* data channel interrupt completion handler
hdh_data(unit
, lcn
, cc
, rcnt
)
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
register struct hdh_chan
*hc
= &sc
->hdh_chan
[lcn
];
/* was it read or write? */
if (hc
->hc_func
& HDHRDB
) {
* Queue good packet for input
sc
->hdh_imp
->imp_if
.if_ipackets
++;
m
= if_rubaget(&sc
->hdh_ifuba
[lcn
>>1], rcnt
, 0,
/* hang a new data read */
hdh_iorq(unit
, lcn
, IMP_RCVBUF
, HDHRDB
+HDHSTR
);
sc
->hdh_imp
->imp_if
.if_opackets
++;
sc
->hdh_imp
->imp_cb
.ic_oactive
= 0;
* supervisor channel interrupt completion handler
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
register struct hdh_chan
*hc
= &sc
->hdh_chan
[lcn
];
/* was it read or write? */
if (hc
->hc_func
& HDHRDB
) {
p
= (short *)(sc
->hdh_ifuba
[lcn
>>1].ifu_r
.ifrw_addr
);
/* figure out what kind of supervisor message */
printf("hdh%d: LINE UP\n", unit
);
if (sc
->hdh_flags
& HDH_UP
)
printf("hdh%d: LINE DOWN\n", unit
);
sc
->hdh_flags
&= ~HDH_UP
;
printf("hdh%d: HOST SEQUENCE ERROR\n", unit
);
printf("hdh%d: IMP SEQUENCE ERROR\n", unit
);
printf("hdh%d: HOST DATA ERROR\n", unit
);
printf("hdh%d: TIMEOUT\n", unit
);
printf("hdh%d: supervisor error, code=%x\n",
/* hang a new supr read */
hdh_iorq(unit
, HDHSUPR
, IMP_RCVBUF
, HDHRDB
+HDHSTR
);
register struct hdh_softc
*sc
= &hdh_softc
[unit
];
if ((m
= m_get(M_DONTWAIT
, MT_DATA
)) == NULL
) {
printf("hdh%d: cannot get supervisor cmnd buffer\n", unit
);
while(cnt
--) *p
++ = *msg
++;
cnt
= if_wubaput(&sc
->hdh_ifuba
[SUPR
], m
);
hdh_iorq(unit
, HDHSUPW
, cnt
, HDHWRT
+HDHEOS
);