/* if_enp.c 1.3 86/12/15 */
* Modified 3Com Ethernet Controller interface
* enp modifications added S. F. Holmgren
#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/ip_var.h"
#include "../netinet/if_ether.h"
#include "../netns/ns_if.h"
#include "../tahoe/cpu.h"
#include "../tahoe/pte.h"
#include "../tahoe/mtpr.h"
#include "../tahoevba/vbavar.h"
#include "../tahoeif/if_enpreg.h"
#define ENPSTART 0xf02000 /* standard enp start addr */
#define ENPUNIT(dev) (minor(dev)) /* for enp ram devices */
int enpprobe(), enpattach(), enpintr();
long enpstd
[] = { 0xf41000, 0xf61000, 0 };
struct vba_device
*enpinfo
[NENP
];
struct vba_driver enpdriver
=
{ enpprobe
, 0, enpattach
, 0, enpstd
, "enp", enpinfo
, "enp-20", 0 };
int enpinit(), enpioctl(), enpreset(), enpoutput();
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* es_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
struct arpcom es_ac
; /* common ethernet structures */
#define es_if es_ac.ac_if
#define es_enaddr es_ac.ac_enaddr
short es_flags
; /* flags for devices */
short es_ivec
; /* interrupt vector */
struct pte
*es_map
; /* map for dual ported memory */
caddr_t es_ram
; /* virtual address of mapped memory */
extern struct ifnet loif
;
register br
, cvec
; /* must be r12, r11 */
register struct enpdevice
*addr
= (struct enpdevice
*)reg
;
struct enp_softc
*es
= &enp_softc
[vi
->ui_unit
];
br
= 0; cvec
= br
; br
= cvec
;
if (badaddr((caddr_t
)addr
, 2) || badaddr((caddr_t
)&addr
->enp_ram
[0], 2))
es
->es_ivec
= --vi
->ui_hd
->vh_lastiv
;
addr
->enp_state
= S_ENPRESET
; /* reset by VERSAbus reset */
br
= 0x14, cvec
= es
->es_ivec
; /* XXX */
return (sizeof (struct enpdevice
));
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct vba_device
*ui
;
struct enp_softc
*es
= &enp_softc
[ui
->ui_unit
];
register struct ifnet
*ifp
= &es
->es_if
;
register struct enpdevice
*addr
= (struct enpdevice
*)ui
->ui_addr
;
ifp
->if_unit
= ui
->ui_unit
;
* Get station's addresses.
enpcopy((u_char
*)&addr
->enp_addr
.e_baseaddr
, es
->es_enaddr
,
printf("enp%d: hardware address %s\n", ui
->ui_unit
,
ether_sprintf(es
->es_enaddr
));
vbmemalloc(128, ((caddr_t
)addr
)+0x1000, &es
->es_map
, &es
->es_ram
);
ifp
->if_ioctl
= enpioctl
;
ifp
->if_output
= enpoutput
;
ifp
->if_reset
= enpreset
;
ifp
->if_flags
= IFF_BROADCAST
;
* Reset of interface after "system" reset.
register struct vba_device
*ui
;
if (unit
>= NENP
|| (ui
= enpinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
* Initialization of interface; clear recorded pending operations.
struct enp_softc
*es
= &enp_softc
[unit
];
register struct vba_device
*ui
= enpinfo
[unit
];
register struct ifnet
*ifp
= &es
->es_if
;
if (ifp
->if_addrlist
== (struct ifaddr
*)0)
if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
addr
= (struct enpdevice
*)ui
->ui_addr
;
addr
->enp_intrvec
= es
->es_ivec
;
es
->es_if
.if_flags
|= IFF_RUNNING
;
* Ethernet interface interrupt.
register struct enpdevice
*addr
;
addr
= (struct enpdevice
*)enpinfo
[unit
]->ui_addr
;
while ((bcbp
= (BCB
*)ringget((RING
*)&addr
->enp_tohost
)) != 0) {
(void) enpread(&enp_softc
[unit
], bcbp
);
(void) ringput((RING
*)&addr
->enp_enpfree
, bcbp
);
* Read input packet, examine its packet type, and enqueue it.
register struct ether_header
*enp
;
register struct ifqueue
*inq
;
* Get pointer to ethernet header (in input buffer).
* 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.
len
= bcbp
->b_msglen
- sizeof (struct ether_header
);
enp
= (struct ether_header
*)bcbp
->b_addr
;
#define enpdataaddr(enp, off, type) \
((type)(((caddr_t)(((char *)enp)+sizeof (struct ether_header))+(off))))
enp
->ether_type
= ntohs((u_short
)enp
->ether_type
);
if (enp
->ether_type
>= ETHERTYPE_TRAIL
&&
enp
->ether_type
< ETHERTYPE_TRAIL
+ETHERTYPE_NTRAILER
) {
off
= (enp
->ether_type
- ETHERTYPE_TRAIL
) * 512;
enp
->ether_type
= ntohs(*enpdataaddr(enp
, off
, u_short
*));
resid
= ntohs(*(enpdataaddr(enp
, off
+2, u_short
*)));
* Pull packet off interface. Off is nonzero if packet
* has trailing header; enpget 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
= enpget(bcbp
->b_addr
, len
, off
, &es
->es_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
;
switch (enp
->ether_type
) {
* Ethernet output routine. (called by user)
* 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.
* If destination is this address or broadcast, send packet to
* loop device to kludge around the fact that 3com interfaces can't
register struct enp_softc
*es
= &enp_softc
[ifp
->if_unit
];
register struct mbuf
*m
= m0
;
register struct ether_header
*enp
;
struct mbuf
*mcopy
= (struct mbuf
*)0;
int type
, s
, error
, usetrailers
;
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(&es
->es_ac
, m
, &idst
, edst
, &usetrailers
))
return (0); /* if not yet resolved */
if (!bcmp((caddr_t
)edst
, (caddr_t
)etherbroadcastaddr
,
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
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
*) = ETHERTYPE_IP
;
*(mtod(m
, u_short
*) + 1) = m
->m_len
;
bcopy((caddr_t
)&(((struct sockaddr_ns
*)dst
)->sns_addr
.x_host
),
(caddr_t
)edst
, sizeof (edst
));
if (!bcmp((caddr_t
)edst
, (caddr_t
)&ns_broadhost
, sizeof (edst
)))
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
else if (!bcmp((caddr_t
)edst
, (caddr_t
)&ns_thishost
,
return (looutput(&loif
, m
, dst
));
enp
= (struct ether_header
*)dst
->sa_data
;
bcopy((caddr_t
)enp
->ether_dhost
, (caddr_t
)edst
, sizeof (edst
));
log(LOG_ERR
, "enp%d: can't handle af%d\n",
ifp
->if_unit
, dst
->sa_family
);
* 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
);
enp
= mtod(m
, struct ether_header
*);
bcopy((caddr_t
)edst
, (caddr_t
)enp
->ether_dhost
, sizeof (edst
));
bcopy((caddr_t
)es
->es_enaddr
, (caddr_t
)enp
->ether_shost
,
enp
->ether_type
= htons((u_short
)type
);
* Queue message on interface if possible
if (enpput(ifp
->if_unit
, m
)) {
return (mcopy
? looutput(&loif
, mcopy
, dst
) : 0);
* Routine to copy from mbuf chain to transmitter buffer on the VERSAbus.
register struct enpdevice
*addr
;
register struct mbuf
*mp
;
addr
= (struct enpdevice
*)enpinfo
[unit
]->ui_addr
;
if (ringempty((RING
*)&addr
->enp_hostfree
))
bcbp
= (BCB
*)ringget((RING
*)&addr
->enp_hostfree
);
bp
= (u_char
*)bcbp
->b_addr
;
for (mp
= m
; mp
; mp
= mp
->m_next
) {
mcp
= mtod(mp
, u_char
*);
bcbp
->b_len
= MAX(ETHERMIN
, bcbp
->b_len
);
if (ringput((RING
*)&addr
->enp_toenp
, bcbp
) == 1)
* Routine to copy from VERSAbus memory into mbufs.
* Warning: This makes the fairly safe assumption that
* mbufs have even lengths.
enpget(rxbuf
, totlen
, off0
, ifp
)
register u_char
*cp
, *mcp
;
struct mbuf
*top
= 0, **mp
= &top
;
cp
= rxbuf
+ sizeof (struct ether_header
);
MGET(m
, M_DONTWAIT
, MT_DATA
);
cp
= rxbuf
+ sizeof (struct ether_header
) + off
;
m
->m_len
= len
= MIN(len
, CLBYTES
);
m
->m_len
= len
= MIN(MLEN
, len
);
m
->m_len
= len
= MIN(MLEN
, len
);
* Prepend interface pointer to first mbuf.
*(mtod(m
, struct ifnet
**)) = ifp
;
enpcopy(cp
, mcp
, (u_int
)len
);
cp
= rxbuf
+ sizeof (struct ether_header
);
register u_char
*from
, *to
;
if (((int)from
&01) && ((int)to
&01)) {
/* source & dest at odd addresses */
if (cnt
> 1 && (((int)to
&01) == 0) && (((int)from
&01) == 0)) {
for (c
= cnt
>>1; c
; --c
) /* even address copy */
while ((int)cnt
-- > 0) /* one of the address(es) must be odd */
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
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
);
struct ns_addr
*ina
= &IA_SNS(ifa
)->sns_addr
;
struct enp_softc
*es
= &enp_softc
[ifp
->if_unit
];
if (!ns_nullhost(*ina
)) {
ifp
->if_flags
&= ~IFF_RUNNING
;
addr
= (struct enpdevice
*)
enpinfo
[ifp
->if_unit
]->ui_addr
;
enpsetaddr(ifp
->if_unit
, addr
,
ina
->x_host
= *(union ns_host
*)es
->es_enaddr
;
if ((ifp
->if_flags
&IFF_UP
) == 0 && ifp
->if_flags
&IFF_RUNNING
) {
enpinit(ifp
->if_unit
); /* reset board */
ifp
->if_flags
&= ~IFF_RUNNING
;
} else if (ifp
->if_flags
&IFF_UP
&&
(ifp
->if_flags
&IFF_RUNNING
) == 0)
enpsetaddr(unit
, addr
, enaddr
)
cp
= &addr
->enp_addr
.e_baseaddr
.ea_addr
[0];
enpcopy((u_char
*)&addr
->enp_addr
.e_listsize
, (u_char
*)&code
,
enpcopy((u_char
*)&code
, (u_char
*)&addr
->enp_addr
.e_listsize
,
* Routines to synchronize enp and host.
rp
->r_rdidx
= rp
->r_wrtidx
= 0;
idx
= (rp
->r_wrtidx
+ 1) & (rp
->r_size
-1);
return (idx
== rp
->r_rdidx
);
return (rp
->r_rdidx
!= rp
->r_wrtidx
? rp
->r_slot
[rp
->r_rdidx
] : 0);
return (rp
->r_rdidx
== rp
->r_wrtidx
);
idx
= (rp
->r_wrtidx
+ 1) & (rp
->r_size
-1);
if (idx
!= rp
->r_rdidx
) {
rp
->r_slot
[rp
->r_wrtidx
] = (int)v
;
if ((idx
-= rp
->r_rdidx
) < 0)
return (idx
); /* num ring entries */
if (rp
->r_rdidx
!= rp
->r_wrtidx
) {
i
= rp
->r_slot
[rp
->r_rdidx
];
rp
->r_rdidx
= (++rp
->r_rdidx
) & (rp
->r_size
-1);
register int unit
= ENPUNIT(dev
);
if (unit
>= NENP
|| (ui
= enpinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
(addr
= (struct enpdevice
*)ui
->ui_addr
) == 0)
if (addr
->enp_state
!= S_ENPRESET
)
return (EACCES
); /* enp is not in reset state, don't open */
register struct uio
*uio
;
register struct iovec
*iov
;
if (uio
->uio_offset
> RAM_SIZE
)
if (uio
->uio_offset
+ iov
->iov_len
> RAM_SIZE
)
iov
->iov_len
= RAM_SIZE
- uio
->uio_offset
;
addr
= (struct enpdevice
*)enpinfo
[ENPUNIT(dev
)]->ui_addr
;
error
= useracc(iov
->iov_base
, (unsigned)iov
->iov_len
, 0);
enpcopy((u_char
*)&addr
->enp_ram
[uio
->uio_offset
],
(u_char
*)iov
->iov_base
, (u_int
)iov
->iov_len
);
uio
->uio_resid
-= iov
->iov_len
;
register struct uio
*uio
;
register struct enpdevice
*addr
;
register struct iovec
*iov
;
addr
= (struct enpdevice
*)enpinfo
[ENPUNIT(dev
)]->ui_addr
;
if (uio
->uio_offset
> RAM_SIZE
)
if (uio
->uio_offset
+ iov
->iov_len
> RAM_SIZE
)
iov
->iov_len
= RAM_SIZE
- uio
->uio_offset
;
error
= useracc(iov
->iov_base
, (unsigned)iov
->iov_len
, 1);
enpcopy((u_char
*)iov
->iov_base
,
(u_char
*)&addr
->enp_ram
[uio
->uio_offset
], (u_int
)iov
->iov_len
);
uio
->uio_resid
-= iov
->iov_len
;
enpr_ioctl(dev
, cmd
, data
)
register struct enpdevice
*addr
;
register unit
= ENPUNIT(dev
);
addr
= (struct enpdevice
*)enpinfo
[unit
]->ui_addr
;
/* not needed if prom based version */
addr
->enp_base
= (int)addr
;
addr
->enp_intrvec
= enp_softc
[unit
].es_ivec
;
addr
->enp_state
= S_ENPRUN
; /* it is running now */
addr
->enp_state
= S_ENPRESET
; /* it is reset now */