/* if_il.c 4.3 82/05/25 */
* Interlan Ethernet Communications Controller interface
#include "../h/protosw.h"
#include "../net/in_systm.h"
#include "../net/if_il.h"
#include "../net/if_uba.h"
#include "../net/ip_var.h"
#include "../net/route.h"
int ilprobe(), ilattach(), ilrint(), ilcint();
struct uba_device
*ilinfo
[NIL
];
struct uba_driver ildriver
=
{ ilprobe
, 0, ilattach
, 0, ilstd
, "il", ilinfo
};
u_char il_ectop
[3] = { 0x02, 0x60, 0x8c };
#define ILUNIT(x) minor(x)
int ilinit(),iloutput(),ilreset();
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* is_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 is_if
; /* network-visible interface */
struct ifuba is_ifuba
; /* UNIBUS resources */
short is_oactive
; /* is output active? */
short is_startrcv
; /* hang receive next chance */
u_char is_enaddr
[6]; /* board's ethernet address */
* Do an OFFLINE command. This will cause an interrupt for the
register int br
, cvec
; /* r11, r10 value-result */
register struct ildevice
*addr
= (struct ildevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
addr
->il_csr
= ILC_OFFLINE
|IL_CIE
;
i
= addr
->il_csr
; /* Clear CDONE */
if (cvec
> 0 && cvec
!= 0x200)
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets. A STATUS command is done to get the ethernet
* address and other interesting data.
register struct il_softc
*is
= &il_softc
[ui
->ui_unit
];
register struct sockaddr_in
*sin
;
register struct ildevice
*addr
= (struct ildevice
*)ui
->ui_addr
;
is
->is_if
.if_unit
= ui
->ui_unit
;
is
->is_if
.if_name
= "il";
is
->is_if
.if_mtu
= ILMTU
;
is
->is_if
.if_net
= ui
->ui_flags
& 0xff;
addr
->il_csr
= ((ubaddr
>>2)&0xc000)|ILC_RESET
;
while (!(addr
->il_csr
& IL_CDONE
))
if (addr
->il_csr
& IL_STATUS
)
printf("il%d: %s\n", ui
->ui_unit
,
ildiag
[addr
->il_csr
& IL_STATUS
]);
* Map the status buffer to the Unibus, do the status command,
ubaddr
= uballoc(ui
->ui_ubanum
, &ilbuf
, sizeof(ilbuf
), 0);
addr
->il_bar
= ubaddr
& 0xffff;
addr
->il_bcr
= sizeof(ilbuf
);
addr
->il_csr
= ((ubaddr
>>2)&0xc000)|ILC_STAT
;
while (!(addr
->il_csr
& IL_CDONE
))
if (addr
->il_csr
& IL_STATUS
)
printf("il%d: %s\n", ui
->ui_unit
,
ilerrs
[addr
->il_csr
& IL_STATUS
]);
ubarelse(ui
->ui_ubanum
, &ubaddr
);
* Fill in the Ethernet address from the status buffer
is
->is_enaddr
[i
] = ilbuf
.ils_addr
[i
];
printf("il%d: addr=%x:%x:%x:%x:%x:%x module=%s firmware=%s\n",
is
->is_enaddr
[0]&0xff, is
->is_enaddr
[1]&0xff,
is
->is_enaddr
[2]&0xff, is
->is_enaddr
[3]&0xff,
is
->is_enaddr
[4]&0xff, is
->is_enaddr
[5]&0xff,
ilbuf
.ils_module
, ilbuf
.ils_firmware
);
is
->is_if
.if_host
[0] = ((is
->is_enaddr
[3]&0xff)<<16) | 0x800000 |
((is
->is_enaddr
[4]&0xff)<<8) | (is
->is_enaddr
[5]&0xff);
sin
= (struct sockaddr_in
*)&is
->is_if
.if_addr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(is
->is_if
.if_net
, is
->is_if
.if_host
[0]);
sin
= (struct sockaddr_in
*)&is
->is_if
.if_broadaddr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(is
->is_if
.if_net
, 0);
is
->is_if
.if_flags
= IFF_BROADCAST
;
is
->is_if
.if_init
= ilinit
;
is
->is_if
.if_output
= iloutput
;
is
->is_if
.if_ubareset
= ilreset
;
is
->is_ifuba
.ifu_flags
= UBA_CANTWAIT
;
if (ui
->ui_flags
&~ 0xff)
illhinit(&is
->is_if
, (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
>= NIL
|| (ui
= ilinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
register struct il_softc
*is
= &il_softc
[unit
];
register struct uba_device
*ui
= ilinfo
[unit
];
register struct ildevice
*addr
;
if (if_ubainit(&is
->is_ifuba
, ui
->ui_ubanum
,
sizeof (struct il_rheader
), (int)btoc(ILMTU
)) == 0) {
printf("il%d: can't initialize\n", unit
);
is
->is_if
.if_flags
&= ~IFF_UP
;
addr
= (struct ildevice
*)ui
->ui_addr
;
* Hang receive buffer and start any pending
* writes by faking a transmit complete.
* Receive bcr is not a muliple of 4 so buffer
addr
->il_csr
= ILC_ONLINE
;
while (!(addr
->il_csr
& IL_CDONE
))
addr
->il_bar
= is
->is_ifuba
.ifu_r
.ifrw_info
& 0xffff;
addr
->il_bcr
= sizeof(struct il_rheader
) + ILMTU
+ 6;
addr
->il_csr
= ((is
->is_ifuba
.ifu_r
.ifrw_info
>>2)&0xc000)|
while (!(addr
->il_csr
& IL_CDONE
))
is
->is_if
.if_flags
|= IFF_UP
;
if_rtinit(&is
->is_if
, RTF_DIRECT
|RTF_UP
);
* Start output on interface.
* Get another datagram to send off of the interface queue,
* and map it to the interface before starting the output.
struct uba_device
*ui
= ilinfo
[unit
];
register struct il_softc
*is
= &il_softc
[unit
];
register struct ildevice
*addr
;
* Dequeue another request and copy it into the buffer.
* If no more requests, just return.
IF_DEQUEUE(&is
->is_if
.if_snd
, m
);
len
= if_wubaput(&is
->is_ifuba
, m
);
* Flush BDP, then start the output.
if (is
->is_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(is
->is_ifuba
.ifu_uba
, is
->is_ifuba
.ifu_w
.ifrw_bdp
);
addr
= (struct ildevice
*)ui
->ui_addr
;
addr
->il_bar
= is
->is_ifuba
.ifu_w
.ifrw_info
& 0xffff;
addr
->il_csr
= ((is
->is_ifuba
.ifu_w
.ifrw_info
>>2)&0xc000)|
* Command done interrupt.
* This should only happen after a transmit command,
* so it is equivalent to a transmit interrupt.
* Start another output if more data to send.
register struct uba_device
*ui
= ilinfo
[unit
];
register struct il_softc
*is
= &il_softc
[unit
];
register struct ildevice
*addr
= (struct ildevice
*)ui
->ui_addr
;
if (is
->is_oactive
== 0) {
printf("il%d: strange xmit interrupt!\n", unit
);
if (err
= (addr
->il_csr
& IL_STATUS
)){
printf("il%d: output error %d\n", unit
, err
);
* Hang receive buffer if it couldn't be done earlier (in ilrint).
addr
->il_bar
= is
->is_ifuba
.ifu_r
.ifrw_info
& 0xffff;
addr
->il_bcr
= sizeof(struct il_rheader
) + ILMTU
+ 6;
addr
->il_csr
= ((is
->is_ifuba
.ifu_r
.ifrw_info
>>2)&0xc000)|
while (!(addr
->il_csr
& IL_CDONE
))
if (is
->is_ifuba
.ifu_xtofree
) {
m_freem(is
->is_ifuba
.ifu_xtofree
);
is
->is_ifuba
.ifu_xtofree
= 0;
if (is
->is_if
.if_snd
.ifq_head
== 0) {
* Ethernet 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 il_softc
*is
= &il_softc
[unit
];
struct ildevice
*addr
= (struct ildevice
*)ilinfo
[unit
]->ui_addr
;
register struct il_rheader
*il
;
register struct ifqueue
*inq
;
if (is
->is_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(is
->is_ifuba
.ifu_uba
, is
->is_ifuba
.ifu_r
.ifrw_bdp
);
il
= (struct il_rheader
*)(is
->is_ifuba
.ifu_r
.ifrw_addr
);
len
= il
->ilr_length
- sizeof(struct il_rheader
);
if (il
->ilr_status
&0x3 || len
< 46 || len
> ILMTU
) {
if (is
->is_if
.if_ierrors
% 100 == 0)
printf("il%d: += 100 input errors\n", unit
);
printf("il%d: input error (status=%x, len=%d)\n", unit
,
* Deal with trailer protocol: if type is PUP trailer
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
#define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off))))
if (il
->ilr_type
>= ILPUP_TRAIL
&&
il
->ilr_type
< ILPUP_TRAIL
+ILPUP_NTRAILER
) {
off
= (il
->ilr_type
- ILPUP_TRAIL
) * 512;
il
->ilr_type
= *ildataaddr(il
, off
, u_short
*);
resid
= *(ildataaddr(il
, off
+2, u_short
*));
* Pull packet off interface. Off is nonzero if packet
* has trailing header; ilget will then force this header
* information to be at the front, but we still have to drop
* the type and length which are at the front of any trailer data.
m
= if_rubaget(&is
->is_ifuba
, len
, off
);
m
->m_off
+= 2 * sizeof (u_short
);
m
->m_len
-= 2 * sizeof (u_short
);
* Reset for next packet if possible.
* If waiting for transmit command completion, set flag
* and wait until command completes.
addr
->il_bar
= is
->is_ifuba
.ifu_r
.ifrw_info
& 0xffff;
addr
->il_bcr
= sizeof(struct il_rheader
) + ILMTU
+ 6;
addr
->il_csr
= ((is
->is_ifuba
.ifu_r
.ifrw_info
>>2)&0xc000)|
while (!(addr
->il_csr
& IL_CDONE
))
* Ethernet output routine.
* 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.
int type
, dest
, s
, error
;
register struct il_softc
*is
= &il_softc
[ifp
->if_unit
];
register struct mbuf
*m
= m0
;
register struct il_xheader
*il
;
switch (dst
->sa_family
) {
dest
= ((struct sockaddr_in
*)dst
)->sin_addr
.s_addr
;
off
= ntohs((u_short
)mtod(m
, struct ip
*)->ip_len
) - m
->m_len
;
if (off
> 0 && (off
& 0x1ff) == 0 &&
m
->m_off
>= MMINOFF
+ 2 * sizeof (u_short
)) {
type
= ILPUP_TRAIL
+ (off
>>9);
m
->m_off
-= 2 * sizeof (u_short
);
m
->m_len
+= 2 * sizeof (u_short
);
*mtod(m
, u_short
*) = ILPUP_IPTYPE
;
*(mtod(m
, u_short
*) + 1) = m
->m_len
;
printf("il%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 il_xheader
) > m
->m_off
) {
m
->m_len
= sizeof (struct il_xheader
);
m
->m_off
-= sizeof (struct il_xheader
);
m
->m_len
+= sizeof (struct il_xheader
);
il
= mtod(m
, struct il_xheader
*);
il
->ilx_dhost
[0] = is
->is_enaddr
[0];
il
->ilx_dhost
[1] = is
->is_enaddr
[1];
il
->ilx_dhost
[2] = is
->is_enaddr
[2];
il
->ilx_dhost
[0] = il_ectop
[0];
il
->ilx_dhost
[1] = il_ectop
[1];
il
->ilx_dhost
[2] = il_ectop
[2];
il
->ilx_dhost
[3] = (dest
>>8) & 0x7f;
il
->ilx_dhost
[4] = (dest
>>16) & 0xff;
il
->ilx_dhost
[5] = (dest
>>24) & 0xff;
* Queue message on interface, and start output if interface
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
* 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 gateway on localnet.
* Called by localnet interface to allow logical
* host interface to "attach". Nothing should ever
* be sent locally to this interface, it's purpose
* is simply to establish the host's arpanet address.
register struct ifnet
*ifp
= &illhif
;
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
= sin
->sin_addr
.s_net
;
ifp
->if_dstaddr
= ilifp
->if_addr
;
ifp
->if_flags
= IFF_UP
|IFF_POINTOPOINT
;
ifp
->if_output
= looutput
;
rtinit(&ifp
->if_addr
, &ifp
->if_addr
, RTF_UP
|RTF_DIRECT
|RTF_HOST
);