/* if_vv.c 4.1 82/06/04 */
* Proteon 10 Meg Ring Driver.
* This device is called "vv" because its "real name",
* V2LNI won't work if shortened to the obvious "v2".
#include "../h/protosw.h"
#include "../net/in_systm.h"
#include "../net/if_vv.h"
#include "../net/if_uba.h"
#include "../net/ip_var.h"
#include "../net/route.h"
* N.B. - if WIRECENTER is defined wrong, it can well break
#define VV_CONF VV_HEN /* drive wire center relay */
#define VV_CONF VV_STE /* allow operation without wire center */
int vvprobe(), vvattach(), vvrint(), vvxint();
struct uba_device
*vvinfo
[NVV
];
struct uba_driver vvdriver
=
{ vvprobe
, 0, vvattach
, 0, vvstd
, "vv", vvinfo
};
#define VVUNIT(x) minor(x)
int vvinit(),vvoutput(),vvreset();
* Software status of each interface.
* Each interface is referenced by a network interface structure,
* vs_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
* We also have, for each interface, a UBA interface structure, which
* contains information about the UNIBUS resources held by the interface:
* map registers, buffered data paths, etc. Information is cached in this
* structure for use by the if_uba.c routines in running the interface
struct ifnet vs_if
; /* network-visible interface */
struct ifuba vs_ifuba
; /* UNIBUS resources */
short vs_oactive
; /* is output active? */
short vs_olen
; /* length of last output */
u_short vs_lastx
; /* last destination address */
short vs_tries
; /* current retry count */
short vs_init
; /* number of ring inits */
short vs_flush
; /* number of flushed packets */
short vs_nottaken
; /* number of packets refused */
register struct vvreg
*addr
= (struct vvreg
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
/* reset interface, enable, and wait till dust settles */
/* generate interrupt by doing 1 word DMA from 0 in uba space!! */
addr
->vvocsr
= VV_IEN
; /* enable interrupt */
addr
->vvoba
= 0; /* low 16 bits */
addr
->vvoea
= 0; /* extended bits */
addr
->vvowc
= -1; /* for 1 word */
addr
->vvocsr
|= VV_DEN
; /* start the DMA */
if (cvec
&& cvec
!= 0x200)
cvec
-= 4; /* backup so vector => recieve */
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct vv_softc
*vs
= &vv_softc
[ui
->ui_unit
];
register struct sockaddr_in
*sin
;
vs
->vs_if
.if_unit
= ui
->ui_unit
;
vs
->vs_if
.if_name
= "vv";
vs
->vs_if
.if_mtu
= VVMTU
;
vs
->vs_if
.if_net
= ui
->ui_flags
;
vs
->vs_if
.if_host
[0] = 0; /* this will be reset in vvinit() */
sin
= (struct sockaddr_in
*)&vs
->vs_if
.if_addr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(vs
->vs_if
.if_net
, vs
->vs_if
.if_host
[0]);
sin
= (struct sockaddr_in
*)&vs
->vs_if
.if_broadaddr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(vs
->vs_if
.if_net
, VV_BROADCAST
);
vs
->vs_if
.if_flags
= IFF_BROADCAST
;
vs
->vs_if
.if_init
= vvinit
;
vs
->vs_if
.if_output
= vvoutput
;
vs
->vs_if
.if_ubareset
= vvreset
;
vs
->vs_ifuba
.ifu_flags
= UBA_NEEDBDP
| UBA_NEED16
;
if (ui
->ui_flags
& ~0xff)
vvlhinit((ui
->ui_flags
&~ 0xff) | 0x0a);
* Reset of interface after UNIBUS reset.
* If interface is on specified uba, reset its state.
register struct uba_device
*ui
;
if (unit
>= NVV
|| (ui
= vvinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
register struct vv_softc
*vs
= &vv_softc
[unit
];
register struct uba_device
*ui
= vvinfo
[unit
];
register struct vvreg
*addr
;
int ubainfo
, retrying
, attempts
, waitcount
, s
;
if (if_ubainit(&vs
->vs_ifuba
, ui
->ui_ubanum
,
sizeof (struct vv_header
), (int)btoc(VVMTU
)) == 0) {
printf("vv%d: can't initialize\n", unit
);
addr
= (struct vvreg
*)ui
->ui_addr
;
* Build a multicast message to identify our address
attempts
= 0; /* total attempts, including bad msg type */
retrying
= 0; /* first time through */
panic("vvinit: can't get mbuf");
m
->m_len
= sizeof(struct vv_header
);
v
= mtod(m
, struct vv_header
*);
v
->vh_dhost
= 0; /* multicast destination address */
v
->vh_shost
= 0; /* will be overwritten with ours */
v
->vh_version
= RING_VERSION
;
v
->vh_type
= RING_WHOAMI
;
* Reset interface, establish Digital Loopback Mode, and
* send the multicast (to myself) with Input Copy enabled.
ubainfo
= vs
->vs_ifuba
.ifu_r
.ifrw_info
;
addr
->vviba
= (u_short
) ubainfo
;
addr
->vviea
= (u_short
) (ubainfo
>> 16);
addr
->vviwc
= -(sizeof (struct vv_header
) + VVMTU
) >> 1;
addr
->vvicsr
= VV_STE
| VV_DEN
| VV_ENB
| VV_LPB
;
/* map xmit message into uba if not already there */
vs
->vs_olen
= if_wubaput(&vs
->vs_ifuba
, m
);
if (vs
->vs_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(vs
->vs_ifuba
.ifu_uba
, vs
->vs_ifuba
.ifu_w
.ifrw_bdp
);
addr
->vvocsr
= VV_RST
| VV_CPB
; /* clear packet buffer */
ubainfo
= vs
->vs_ifuba
.ifu_w
.ifrw_info
;
addr
->vvoba
= (u_short
) ubainfo
;
addr
->vvoea
= (u_short
) (ubainfo
>> 16);
addr
->vvowc
= -((vs
->vs_olen
+ 1) >> 1);
addr
->vvocsr
= VV_CPB
| VV_DEN
| VV_INR
| VV_ENB
;
* Wait for receive side to finish.
* Extract source address (which will our own),
* and post to interface structure.
for (waitcount
= 0; ((addr
->vvicsr
) & VV_RDY
) == 0; waitcount
++) {
printf("vv%d: can't initialize\n", unit
);
printf("vvinit loopwait: icsr = %b\n",
0xffff&(addr
->vvicsr
),VV_IBITS
);
if (vs
->vs_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(vs
->vs_ifuba
.ifu_uba
, vs
->vs_ifuba
.ifu_w
.ifrw_bdp
);
if (vs
->vs_ifuba
.ifu_xtofree
)
m_freem(vs
->vs_ifuba
.ifu_xtofree
);
if (vs
->vs_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(vs
->vs_ifuba
.ifu_uba
, vs
->vs_ifuba
.ifu_r
.ifrw_bdp
);
m
= if_rubaget(&vs
->vs_ifuba
, sizeof(struct vv_header
), 0);
* check message type before we believe the source host address
v
= (struct vv_header
*)(vs
->vs_ifuba
.ifu_r
.ifrw_addr
);
if (v
->vh_type
== RING_WHOAMI
)
vs
->vs_if
.if_host
[0] = v
->vh_shost
;
vs
->vs_if
.if_host
[0] = 24;
printf("vv%d: host %d\n", unit
, vs
->vs_if
.if_host
[0]);
sin
= (struct sockaddr_in
*)&vs
->vs_if
.if_addr
;
sin
->sin_family
= AF_INET
;
if_makeaddr(vs
->vs_if
.if_net
, vs
->vs_if
.if_host
[0]);
* Reset the interface, and join the ring
addr
->vvocsr
= VV_RST
| VV_CPB
; /* clear packet buffer */
addr
->vvicsr
= VV_RST
| VV_CONF
; /* close logical relay */
sleep((caddr_t
)&lbolt
, PZERO
); /* let contacts settle */
* Hang a receive and start any
* pending writes by faking a transmit complete.
ubainfo
= vs
->vs_ifuba
.ifu_r
.ifrw_info
;
addr
->vviba
= (u_short
) ubainfo
;
addr
->vviea
= (u_short
) (ubainfo
>> 16);
addr
->vviwc
= -(sizeof (struct vv_header
) + VVMTU
) >> 1;
addr
->vvicsr
= VV_IEN
| VV_CONF
| VV_DEN
| VV_ENB
;
if_rtinit(&vs
->vs_if
, RTF_DIRECT
|RTF_UP
);
* Start or restart output on interface.
* If interface is not already active, get another datagram
* to send off of the interface queue, and map it to the interface
* before starting the output.
struct uba_device
*ui
= vvinfo
[unit
];
register struct vv_softc
*vs
= &vv_softc
[unit
];
register struct vvreg
*addr
;
* Not already active: dequeue another request
* and map it to the UNIBUS. If no more requests,
IF_DEQUEUE(&vs
->vs_if
.if_snd
, m
);
dest
= mtod(m
, struct vv_header
*)->vh_dhost
;
vs
->vs_olen
= if_wubaput(&vs
->vs_ifuba
, m
);
* Have request mapped to UNIBUS for transmission.
* Purge any stale data from this BDP, and start the otput.
if (vs
->vs_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(vs
->vs_ifuba
.ifu_uba
, vs
->vs_ifuba
.ifu_w
.ifrw_bdp
);
addr
= (struct vvreg
*)ui
->ui_addr
;
ubainfo
= vs
->vs_ifuba
.ifu_w
.ifrw_info
;
addr
->vvoba
= (u_short
) ubainfo
;
addr
->vvoea
= (u_short
) (ubainfo
>> 16);
addr
->vvowc
= -((vs
->vs_olen
+ 1) >> 1);
addr
->vvocsr
= VV_IEN
| VV_CPB
| VV_DEN
| VV_INR
| VV_ENB
;
* VVLNI transmit interrupt
* Start another output if more data to send.
register struct uba_device
*ui
= vvinfo
[unit
];
register struct vv_softc
*vs
= &vv_softc
[unit
];
register struct vvreg
*addr
;
addr
= (struct vvreg
*)ui
->ui_addr
;
oc
= 0xffff & (addr
->vvocsr
);
if (vs
->vs_oactive
== 0) {
printf("vv%d: stray interrupt vvocsr = %b\n", unit
,
if (oc
& (VV_OPT
| VV_RFS
)) {
if (++(vs
->vs_tries
) < VVRETRY
) {
addr
->vvocsr
= VV_IEN
| VV_ENB
| VV_INR
;
printf("vv%d: output timeout\n");
printf("vv%d: error vvocsr = %b\n", unit
, 0xffff & oc
,
if (vs
->vs_ifuba
.ifu_xtofree
) {
m_freem(vs
->vs_ifuba
.ifu_xtofree
);
vs
->vs_ifuba
.ifu_xtofree
= 0;
if (vs
->vs_if
.if_snd
.ifq_head
== 0) {
* V2lni interface receiver interrupt.
* If input error just drop packet.
* Otherwise purge input buffered data path and examine
* packet to determine type. If can't determine length
* from type, then have to drop packet. Othewise decapsulate
* packet based on type and pass to type specific higher-level
register struct vv_softc
*vs
= &vv_softc
[unit
];
struct vvreg
*addr
= (struct vvreg
*)vvinfo
[unit
]->ui_addr
;
register struct vv_header
*vv
;
register struct ifqueue
*inq
;
* Purge BDP; drop if input error indicated.
if (vs
->vs_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(vs
->vs_ifuba
.ifu_uba
, vs
->vs_ifuba
.ifu_r
.ifrw_bdp
);
if (addr
->vvicsr
& VVRERR
) {
printf("vv%d: error vvicsr = %b\n", unit
,
0xffff&(addr
->vvicsr
), VV_IBITS
);
vv
= (struct vv_header
*)(vs
->vs_ifuba
.ifu_r
.ifrw_addr
);
* Demultiplex on packet type and deal with oddities of
* trailer protocol format
len
= htons((u_short
)((struct ip
*) vv
)->ip_len
);
printf("vv%d: unknown pkt type 0x%x\n", unit
, vv
->vh_type
);
* Pull packet off interface. Off is nonzero if packet
* has trailing header; if_rubaget will then force this header
* information to be at the front, but we still have to drop
* the two-byte type which is at the front of any trailer data.
m
= if_rubaget(&vs
->vs_ifuba
, len
, off
);
ubainfo
= vs
->vs_ifuba
.ifu_r
.ifrw_info
;
addr
->vviba
= (u_short
) ubainfo
;
addr
->vviea
= (u_short
) (ubainfo
>> 16);
addr
->vviwc
= -(sizeof (struct vv_header
) + VVMTU
) >> 1;
addr
->vvicsr
= VV_RST
| VV_CONF
;
addr
->vvicsr
|= VV_IEN
| VV_DEN
| VV_ENB
;
* Encapsulate a packet of type family for the local net.
* Use trailer local net encapsulation if enough data in first
* packet leaves a multiple of 512 bytes of data in remainder.
register struct mbuf
*m
= m0
;
register struct vv_header
*vv
;
switch (dst
->sa_family
) {
register struct ip
*ip
= mtod(m0
, struct ip
*);
dest
= ip
->ip_dst
.s_addr
>> 24;
printf("vv%d: can't handle af%d\n", ifp
->if_unit
,
* Packet to be sent as trailer: move first packet
* (control information) to end of chain.
* Add local net header. If no space in first mbuf,
if (m
->m_off
> MMAXOFF
||
MMINOFF
+ sizeof (struct vv_header
) > m
->m_off
) {
m
->m_len
= sizeof (struct vv_header
);
m
->m_off
-= sizeof (struct vv_header
);
m
->m_len
+= sizeof (struct vv_header
);
vv
= mtod(m
, struct vv_header
*);
vv
->vh_shost
= ifp
->if_host
[0];
vv
->vh_version
= RING_VERSION
;
* Queue message on interface, and start output if interface
IF_ENQUEUE(&ifp
->if_snd
, m
);
if (vv_softc
[ifp
->if_unit
].vs_oactive
== 0)
* vvprt_hdr(s, v) print the local net header in "v"
register struct vv_header
*v
;
printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
0xff & (int)(v
->vh_dhost
), 0xff & (int)(v
->vh_shost
),
0xff & (int)(v
->vh_version
), 0xff & (int)(v
->vh_type
),
0xffff & (int)(v
->vh_info
));
* print "l" hex bytes starting at "s"
z
= 0xff & (int)(*(s
+ i
));
"0123456789abcdef"[(z
>> 4) & 0x0f],
"0123456789abcdef"[z
& 0x0f]
* Logical host interface driver.
* Allows host to appear as an ARPAnet
* logical host. Must also have routing
* table entry set up to forward packets
* to appropriate geteway on localnet.
* Called by localnet interface to allow logical
* host interface to "attach".
register struct ifnet
*ifp
= &vvlhif
;
register struct sockaddr_in
*sin
;
sin
= (struct sockaddr_in
*)&ifp
->if_addr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
.s_addr
= addr
;
ifp
->if_net
= netpart(sin
->sin_addr
);
ifp
->if_output
= looutput
;
rtinit(&ifp
->if_addr
, &ifp
->if_addr
, RTF_DIRECT
|RTF_UP
|RTF_HOST
);