* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)if_il.c 7.2 (Berkeley) 8/7/86
* Interlan Ethernet Communications Controller interface
#include "../machine/pte.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "../netinet/if_ether.h"
#include "../netns/ns_if.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
int ilprobe(), ilattach(), ilrint(), ilcint();
struct uba_device
*ilinfo
[NIL
];
struct uba_driver ildriver
=
{ ilprobe
, 0, ilattach
, 0, ilstd
, "il", ilinfo
};
#define ILUNIT(x) minor(x)
int ilinit(),iloutput(),ilioctl(),ilreset(),ilwatch();
* 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 arpcom is_ac
; /* Ethernet common part */
#define is_if is_ac.ac_if /* network-visible interface */
#define is_addr is_ac.ac_enaddr /* hardware Ethernet address */
struct ifuba is_ifuba
; /* UNIBUS resources */
#define ILF_OACTIVE 0x1 /* output is active */
#define ILF_RCVPENDING 0x2 /* start rcv in ilcint */
#define ILF_STATPENDING 0x4 /* stat cmd pending */
#define ILF_RUNNING 0x8 /* board is running */
#define ILF_SETADDR 0x10 /* physical address is changed */
short is_lastcmd
; /* can't read csr, so must save it */
short is_scaninterval
; /* interval of stat collection */
#define ILWATCHINTERVAL 60 /* once every 60 seconds */
struct il_stats is_stats
; /* holds on-board statistics */
struct il_stats is_sum
; /* summation over time */
int is_ubaddr
; /* mapping registers of is_stats */
register int br
, cvec
; /* r11, r10 value-result */
register struct ildevice
*addr
= (struct ildevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
i
= 0; ilrint(i
); ilcint(i
); ilwatch(i
);
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 ifnet
*ifp
= &is
->is_if
;
register struct ildevice
*addr
= (struct ildevice
*)ui
->ui_addr
;
ifp
->if_unit
= ui
->ui_unit
;
ifp
->if_flags
= IFF_BROADCAST
;
* Reset the board and map the statistics
* buffer onto the Unibus.
addr
->il_csr
= ILC_RESET
;
(void)ilwait(ui
, "reset");
is
->is_ubaddr
= uballoc(ui
->ui_ubanum
, (caddr_t
)&is
->is_stats
,
sizeof (struct il_stats
), 0);
addr
->il_bar
= is
->is_ubaddr
& 0xffff;
addr
->il_bcr
= sizeof (struct il_stats
);
addr
->il_csr
= ((is
->is_ubaddr
>> 2) & IL_EUA
)|ILC_STAT
;
(void)ilwait(ui
, "status");
ubarelse(ui
->ui_ubanum
, &is
->is_ubaddr
);
printf("il%d: module=%s firmware=%s\n", ui
->ui_unit
,
is
->is_stats
.ils_module
, is
->is_stats
.ils_firmware
);
bcopy((caddr_t
)is
->is_stats
.ils_addr
, (caddr_t
)is
->is_addr
,
printf("il%d: hardware address %s\n", ui
->ui_unit
,
ether_sprintf(is
->is_addr
));
ifp
->if_output
= iloutput
;
is
->is_ifuba
.ifu_flags
= UBA_CANTWAIT
;
register struct ildevice
*addr
= (struct ildevice
*)ui
->ui_addr
;
while ((addr
->il_csr
&IL_CDONE
) == 0)
if (addr
->il_csr
&IL_STATUS
) {
printf("il%d: %s failed, csr=%b\n", ui
->ui_unit
, op
,
* 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 ||
il_softc
[unit
].is_if
.if_flags
&= ~IFF_RUNNING
;
il_softc
[unit
].is_flags
&= ~ILF_RUNNING
;
* 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
;
register struct ifnet
*ifp
= &is
->is_if
;
/* not yet, if address still unknown */
if (ifp
->if_addrlist
== (struct ifaddr
*)0)
if (is
->is_flags
& ILF_RUNNING
)
if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
if (if_ubainit(&is
->is_ifuba
, ui
->ui_ubanum
,
sizeof (struct il_rheader
), (int)btoc(ETHERMTU
)) == 0) {
printf("il%d: can't initialize\n", unit
);
is
->is_if
.if_flags
&= ~IFF_UP
;
is
->is_ubaddr
= uballoc(ui
->ui_ubanum
, (caddr_t
)&is
->is_stats
,
sizeof (struct il_stats
), 0);
ifp
->if_watchdog
= ilwatch
;
is
->is_scaninterval
= ILWATCHINTERVAL
;
ifp
->if_timer
= is
->is_scaninterval
;
addr
= (struct ildevice
*)ui
->ui_addr
;
* Turn off source address insertion (it's faster this way),
* and set board online. Former doesn't work if board is
* already online (happens on ubareset), so we put it offline
addr
->il_csr
= ILC_RESET
;
if (ilwait(ui
, "hardware diag")) {
is
->is_if
.if_flags
&= ~IFF_UP
;
while ((addr
->il_csr
& IL_CDONE
) == 0)
* If we must reprogram this board's physical ethernet
* address (as for secondary XNS interfaces), we do so
* before putting it on line, and starting receive requests.
* If you try this on an older 1010 board, it will total
if (is
->is_flags
& ILF_SETADDR
) {
bcopy((caddr_t
)is
->is_addr
, (caddr_t
)&is
->is_stats
,
addr
->il_bar
= is
->is_ubaddr
& 0xffff;
addr
->il_bcr
= sizeof is
->is_addr
;
addr
->il_csr
= ((is
->is_ubaddr
>> 2) & IL_EUA
)|ILC_LDPA
;
if (ilwait(ui
, "setaddr"))
addr
->il_bar
= is
->is_ubaddr
& 0xffff;
addr
->il_bcr
= sizeof (struct il_stats
);
addr
->il_csr
= ((is
->is_ubaddr
>> 2) & IL_EUA
)|ILC_STAT
;
if (ilwait(ui
, "verifying setaddr"))
if (bcmp((caddr_t
)is
->is_stats
.ils_addr
, (caddr_t
)is
->is_addr
,
sizeof (is
->is_addr
)) != 0) {
printf("il%d: setaddr didn't work\n", ui
->ui_unit
);
* Hang receive buffer and start any pending
* writes by faking a transmit complete.
* Receive bcr is not a multiple of 8 so buffer
addr
->il_csr
= ILC_ONLINE
;
while ((addr
->il_csr
& IL_CDONE
) == 0)
addr
->il_bar
= is
->is_ifuba
.ifu_r
.ifrw_info
& 0xffff;
addr
->il_bcr
= sizeof(struct il_rheader
) + ETHERMTU
+ 6;
((is
->is_ifuba
.ifu_r
.ifrw_info
>> 2) & IL_EUA
)|ILC_RCV
|IL_RIE
;
while ((addr
->il_csr
& IL_CDONE
) == 0)
is
->is_flags
= ILF_OACTIVE
;
is
->is_if
.if_flags
|= IFF_RUNNING
;
is
->is_flags
|= ILF_RUNNING
;
* Start output on interface.
* Get another datagram to send off of the interface queue,
* and map it to the interface before starting the output.
int unit
= ILUNIT(dev
), len
;
struct uba_device
*ui
= ilinfo
[unit
];
register struct il_softc
*is
= &il_softc
[unit
];
register struct ildevice
*addr
;
IF_DEQUEUE(&is
->is_if
.if_snd
, m
);
addr
= (struct ildevice
*)ui
->ui_addr
;
if ((is
->is_flags
& ILF_STATPENDING
) == 0)
addr
->il_bar
= is
->is_ubaddr
& 0xffff;
addr
->il_bcr
= sizeof (struct il_stats
);
csr
= ((is
->is_ubaddr
>> 2) & IL_EUA
)|ILC_STAT
|IL_RIE
|IL_CIE
;
is
->is_flags
&= ~ILF_STATPENDING
;
len
= if_wubaput(&is
->is_ifuba
, m
);
* Ensure minimum packet length.
* This makes the safe assumtion that there are no virtual holes
* For security, it might be wise to zero out the added bytes,
* but we're mainly interested in speed at the moment.
if (len
- sizeof(struct ether_header
) < ETHERMIN
)
len
= ETHERMIN
+ sizeof(struct ether_header
);
if (is
->is_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(is
->is_ifuba
.ifu_uba
, is
->is_ifuba
.ifu_w
.ifrw_bdp
);
addr
->il_bar
= is
->is_ifuba
.ifu_w
.ifrw_info
& 0xffff;
((is
->is_ifuba
.ifu_w
.ifrw_info
>> 2) & IL_EUA
)|ILC_XMIT
|IL_CIE
|IL_RIE
;
is
->is_lastcmd
= csr
& IL_CMD
;
is
->is_flags
|= ILF_OACTIVE
;
* Command done interrupt.
register struct il_softc
*is
= &il_softc
[unit
];
struct uba_device
*ui
= ilinfo
[unit
];
register struct ildevice
*addr
= (struct ildevice
*)ui
->ui_addr
;
if ((is
->is_flags
& ILF_OACTIVE
) == 0) {
printf("il%d: stray xmit interrupt, csr=%b\n", unit
,
* Hang receive buffer if it couldn't
* be done earlier (in ilrint).
if (is
->is_flags
& ILF_RCVPENDING
) {
addr
->il_bar
= is
->is_ifuba
.ifu_r
.ifrw_info
& 0xffff;
addr
->il_bcr
= sizeof(struct il_rheader
) + ETHERMTU
+ 6;
((is
->is_ifuba
.ifu_r
.ifrw_info
>> 2) & IL_EUA
)|ILC_RCV
|IL_RIE
;
while ((addr
->il_csr
& IL_CDONE
) == 0)
is
->is_flags
&= ~ILF_RCVPENDING
;
is
->is_flags
&= ~ILF_OACTIVE
;
switch (is
->is_lastcmd
) {
if (csr
== ILERR_SUCCESS
)
if (is
->is_ifuba
.ifu_xtofree
) {
m_freem(is
->is_ifuba
.ifu_xtofree
);
is
->is_ifuba
.ifu_xtofree
= 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
&(ILFSTAT_A
|ILFSTAT_C
)) || len
< 46 ||
if (is
->is_if
.if_ierrors
% 100 == 0)
printf("il%d: += 100 input errors\n", unit
);
* Deal with trailer protocol: if type is trailer type
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
il
->ilr_type
= ntohs((u_short
)il
->ilr_type
);
#define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off))))
if (il
->ilr_type
>= ETHERTYPE_TRAIL
&&
il
->ilr_type
< ETHERTYPE_TRAIL
+ETHERTYPE_NTRAILER
) {
off
= (il
->ilr_type
- ETHERTYPE_TRAIL
) * 512;
il
->ilr_type
= ntohs(*ildataaddr(il
, off
, u_short
*));
resid
= ntohs(*(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
, &is
->is_if
);
ifp
= *(mtod(m
, struct ifnet
**));
m
->m_off
+= 2 * sizeof (u_short
);
m
->m_len
-= 2 * sizeof (u_short
);
*(mtod(m
, struct ifnet
**)) = ifp
;
* Reset for next packet if possible.
* If waiting for transmit command completion, set flag
* and wait until command completes.
if (is
->is_flags
& ILF_OACTIVE
) {
is
->is_flags
|= ILF_RCVPENDING
;
addr
->il_bar
= is
->is_ifuba
.ifu_r
.ifrw_info
& 0xffff;
addr
->il_bcr
= sizeof(struct il_rheader
) + ETHERMTU
+ 6;
((is
->is_ifuba
.ifu_r
.ifrw_info
>> 2) & IL_EUA
)|ILC_RCV
|IL_RIE
;
while ((addr
->il_csr
& IL_CDONE
) == 0)
* 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.
register struct il_softc
*is
= &il_softc
[ifp
->if_unit
];
register struct mbuf
*m
= m0
;
register struct ether_header
*il
;
if ((ifp
->if_flags
& (IFF_UP
|IFF_RUNNING
)) != (IFF_UP
|IFF_RUNNING
)) {
switch (dst
->sa_family
) {
idst
= ((struct sockaddr_in
*)dst
)->sin_addr
;
if (!arpresolve(&is
->is_ac
, m
, &idst
, edst
, &usetrailers
))
return (0); /* if not yet resolved */
off
= ntohs((u_short
)mtod(m
, struct ip
*)->ip_len
) - m
->m_len
;
if (usetrailers
&& off
> 0 && (off
& 0x1ff) == 0 &&
m
->m_off
>= MMINOFF
+ 2 * sizeof (u_short
)) {
type
= ETHERTYPE_TRAIL
+ (off
>>9);
m
->m_off
-= 2 * sizeof (u_short
);
m
->m_len
+= 2 * sizeof (u_short
);
*mtod(m
, u_short
*) = htons((u_short
)ETHERTYPE_IP
);
*(mtod(m
, u_short
*) + 1) = htons((u_short
)m
->m_len
);
bcopy((caddr_t
)&(((struct sockaddr_ns
*)dst
)->sns_addr
.x_host
),
(caddr_t
)edst
, sizeof (edst
));
il
= (struct ether_header
*)dst
->sa_data
;
bcopy((caddr_t
)il
->ether_dhost
, (caddr_t
)edst
, sizeof (edst
));
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 ether_header
) > m
->m_off
) {
m
= m_get(M_DONTWAIT
, MT_HEADER
);
m
->m_len
= sizeof (struct ether_header
);
m
->m_off
-= sizeof (struct ether_header
);
m
->m_len
+= sizeof (struct ether_header
);
il
= mtod(m
, struct ether_header
*);
il
->ether_type
= htons((u_short
)type
);
bcopy((caddr_t
)edst
, (caddr_t
)il
->ether_dhost
, sizeof (edst
));
bcopy((caddr_t
)is
->is_addr
, (caddr_t
)il
->ether_shost
,
sizeof(il
->ether_shost
));
* Queue message on interface, and start output if interface
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
if ((is
->is_flags
& ILF_OACTIVE
) == 0)
* Watchdog routine, request statistics from board.
register struct il_softc
*is
= &il_softc
[unit
];
register struct ifnet
*ifp
= &is
->is_if
;
if (is
->is_flags
& ILF_STATPENDING
) {
ifp
->if_timer
= is
->is_scaninterval
;
is
->is_flags
|= ILF_STATPENDING
;
if ((is
->is_flags
& ILF_OACTIVE
) == 0)
ifp
->if_timer
= is
->is_scaninterval
;
* Total up the on-board statistics.
register struct il_softc
*is
;
register u_short
*interval
, *sum
, *end
;
interval
= &is
->is_stats
.ils_frames
;
sum
= &is
->is_sum
.ils_frames
;
end
= is
->is_sum
.ils_fill2
;
is
->is_if
.if_collisions
= is
->is_sum
.ils_collis
;
if ((is
->is_flags
& ILF_SETADDR
) &&
(bcmp((caddr_t
)is
->is_stats
.ils_addr
, (caddr_t
)is
->is_addr
,
sizeof (is
->is_addr
)) != 0)) {
log(LOG_ERR
, "il%d: physaddr reverted\n", is
->is_if
.if_unit
);
is
->is_flags
&= ~ILF_RUNNING
;
ilinit(is
->is_if
.if_unit
);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
register struct il_softc
*is
= &il_softc
[ifp
->if_unit
];
int s
= splimp(), error
= 0;
switch (ifa
->ifa_addr
.sa_family
) {
((struct arpcom
*)ifp
)->ac_ipaddr
=
arpwhohas((struct arpcom
*)ifp
, &IA_SIN(ifa
)->sin_addr
);
register struct ns_addr
*ina
= &(IA_SNS(ifa
)->sns_addr
);
ina
->x_host
= * (union ns_host
*)
(il_softc
[ifp
->if_unit
].is_addr
);
il_setaddr(ina
->x_host
.c_host
, ifp
->if_unit
);
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
is
->is_flags
& ILF_RUNNING
) {
(ilinfo
[ifp
->if_unit
]->ui_addr
))->il_csr
= ILC_RESET
;
is
->is_flags
&= ~ILF_RUNNING
;
} else if (ifp
->if_flags
& IFF_UP
&&
(is
->is_flags
& ILF_RUNNING
) == 0)
* set ethernet address for unit
il_setaddr(physaddr
, unit
)
register struct il_softc
*is
= &il_softc
[unit
];
if (! (is
->is_flags
& ILF_RUNNING
))
bcopy((caddr_t
)physaddr
, (caddr_t
)is
->is_addr
, sizeof is
->is_addr
);
is
->is_flags
&= ~ILF_RUNNING
;
is
->is_flags
|= ILF_SETADDR
;