/* if_enp.c 1.1 86/07/20 */
* Modified 3 Com 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/ip.h"
#include "../netinet/ip_var.h"
#include "../netinet/if_ether.h"
#include "../tahoevba/vbavar.h"
#include "../tahoeif/if_enp.h"
#include "../machine/mtpr.h"
#include "../tahoeif/if_debug.h"
#define ENP0_PHYSADDR 0xf40000 /* board # 0 physical base addr */
#define ENP1_PHYSADDR 0xf60000 /* board # 1 physical base addr */
#define ENPSTART 0xf02000 /* standard enp start addr */
int enpprobe(), enpattach(), enpintr();
struct vba_device
*enpinfo
[ NENP
];
/* Maximun 2 controllers per system supported */
long enpstd
[] = { ENP0_PHYSADDR
+0x1000,ENP1_PHYSADDR
+0x1000, 0 };
extern char enp0utl
[], enp1utl
[]; /* enp accessible ram map */
char *enpmap
[]= { enp0utl
, enp1utl
};
extern long ENP0map
[], ENP1map
[];
long *ENPmap
[] = {ENP0map
, ENP1map
};
long ENPmapa
[] = {0xfff41000, 0xfff61000};
unsigned short intvec
[4] =
{ 0xc1, 0xc2, 0xc3, 0xc4 }; /* intrvec of upto 4 enps */
struct vba_driver enpdriver
=
/* use of prom based version
enpprobe, 0, enpattach, 0, 0, enpintr,
enpstd
, "enp", enpinfo
, "ENP 20", 0
extern struct ifnet loif
;
* 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 enp_softc enp_softc
[NENP
];
long stat_addr
[NENP
]; /* enp statistic addr (for nstat use) */
long ring_addr
[NENP
]; /* enp dev ring addresses (for nstat use) */
int enp_intr
= 0, /* no. of enp_to_host interrupts */
host_intr
= 0; /* no. of host_to_enp interrupts */
short enpram
[NENP
]; /* open/close flags for enp devices */
/* Debugging tools, used to trace input packets */
extern int printerror
; /* error print flag, from if_ace.c */
#define ENPTRACE(X) if (save_enp_inpkt) X;
struct inp_err enperr
[NENP
];
register ENPDEVICE
*addr
= (ENPDEVICE
*)reg
;
if( (badaddr( addr
, 2 ) ) || (badaddr( &addr
->enp_ram
[0], 2 ) ) )
addr
->enp_state
= S_ENPRESET
; /* controller is reset by vbus reset */
/* save address of statistic area for nstat uses */
stat_addr
[unit
] = (long) &(addr
->enp_stat
);
ring_addr
[unit
++] = (long) &(addr
->enp_toenp
);
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct vba_device
*md
;
struct enp_softc
*es
= &enp_softc
[md
->ui_unit
];
register struct ifnet
*ifp
= &es
->es_if
;
register ENPDEVICE
*addr
= (ENPDEVICE
*)md
->ui_addr
;
enpgetaddr( md
->ui_unit
);
ifp
->if_unit
= md
->ui_unit
;
/* bcopy(&es->es_boardaddr, es->es_enaddr, sizeof(es->es_enaddr)); */
sin
= (struct sockaddr_in
*)&es
->es_if
.if_addr
;
sin
->sin_family
= AF_INET
;
ifp
->if_ioctl
= enpioctl
;
ifp
->if_output
= enpoutput
;
ifp
->if_reset
= enpreset
;
* Reset of interface after UNIBUS reset.
register struct vba_device
*md
;
if (unit
>= NENP
|| (md
= enpinfo
[unit
]) == 0 || md
->ui_alive
== 0)
* Initialization of interface; clear recorded pending
struct enp_softc
*es
= &enp_softc
[unit
];
register struct ifnet
*ifp
= &es
->es_if
;
register struct sockaddr_in
*sin
, *sinb
;
sin
= (struct sockaddr_in
*)&ifp
->if_addr
;
if ( !enpismapped
[unit
] ) {
ioaccess(ENPmap
[unit
],ENPmapa
[unit
],ENPBPTE
);
if ((addr
= (ENPDEVICE
*)enpinfo
[unit
]->ui_addr
) == (ENPDEVICE
*)0)
/* only needed if not downloading ( ie, ROM-resident ENP code) */
addr
->enp_intrvec
= intvec
[unit
];
/* end of ROM-resident */
es
->es_if
.if_flags
|= IFF_UP
|IFF_RUNNING
; /* open for business*/
if_rtinit( &es
->es_if
,RTF_UP
);
arpwhohas(&es
->es_ac
, &sin
->sin_addr
);
* Ethernet interface interrupt.
register ENPDEVICE
*addr
;
register struct vba_device
*md
;
if (unit
>= NENP
|| (md
= enpinfo
[unit
]) == 0)
addr
= (ENPDEVICE
*)md
->ui_addr
;
if( IS_ENP_INTR(addr
) == 0 )
while( (bcbp
= (BCB
*)ringget( &addr
->enp_tohost
)) != 0 )
enpread( &enp_softc
[ unit
],bcbp
, unit
);
ringput( &addr
->enp_enpfree
,bcbp
);
int maxl_tosave
= 200; /* save only the first 200 bytes */
saverrpkt(errbuf
, errtype
, len
)
remain
= MAXBLEN
- bufptr
;
if (remain
< 50) /* if too small */
return; /* no space avail */
len
= (len
> maxl_tosave
|| len
<= 0) ? maxl_tosave
: len
;
len
= len
> remain
? (remain
- 2*sizeof(len
)): len
;
newptr
= bufptr
+ len
+ 2*sizeof(len
);
enpcopy((char *)&len
, &errpkt
[bufptr
], sizeof(len
));
enpcopy((char *)&errtype
, &errpkt
[bufptr
+sizeof(len
)],
enpcopy(errbuf
, &errpkt
[bufptr
+(2*sizeof(len
))], len
);
* Read input packet, examine its packet type, and enqueue it.
enpread( es
, bcbp
, unit
)
register struct ether_header
*enp
;
register short *vp
= (short *)&v
,
int len
, off
, resid
, enptype
;
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_ETHEADER
;
sp
= (short *)&bcbp
->b_addr
;
*vp
= *sp
; vp
[1] = sp
[1];
enp
= (struct ether_header
*) v
;
enp
= (struct ether_header
*)bcbp
->b_addr
;
#define enpdataaddr(enp, off, type) ((type)(((caddr_t)(((char *)enp)+SIZEOF_ETHEADER)+(off))))
enptype
= enp
->ether_type
;
if (enptype
>= ETHERPUP_TRAIL
&& enptype
< ETHERPUP_TRAIL
+ETHERPUP_NTRAILER
)
off
= (enptype
- ETHERPUP_TRAIL
) * 512;
enperr
[unit
].bad_offset
++;
ENPTRACE(saverrpkt((char *)enp
, B_OFFSET
, bcbp
->b_msglen
));
enptype
= *enpdataaddr(enp
, off
, u_short
*);
resid
= *(enpdataaddr(enp
, off
+2, u_short
*));
enperr
[unit
].bad_length
++;
ENPTRACE(saverrpkt((char *)enp
, B_LENGTH
, bcbp
->b_msglen
));
enperr
[unit
].bad_length
++;
ENPTRACE(saverrpkt((char *)enp
, B_LENGTH
, bcbp
->b_msglen
));
* 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
, len
, off
);
enperr
[unit
].h_nobuffer
++; /* host runs out of buf */
m
->m_off
+= 2 * sizeof (u_short
);
m
->m_len
-= 2 * sizeof (u_short
);
default: /* unrecognized ethernet header */
enperr
[unit
].bad_packetype
++;
printf("\nenp%d: Undefined packet type 0x%x ", unit
,
printf("from host: %x.%x.%x.%x.%x.%x\n",
enp
->ether_shost
[0], enp
->ether_shost
[1],
enp
->ether_shost
[2], enp
->ether_shost
[3],
enp
->ether_shost
[4], enp
->ether_shost
[5]);
} /* end debugging aid */
ENPTRACE(saverrpkt((char *)enp
, B_PACKETYPE
, bcbp
->b_msglen
));
* 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; /* Null */
idst
= ((struct sockaddr_in
*)dst
)->sin_addr
;
/* translate internet to ethernet address */
switch(arpresolve(&es
->es_ac
, m
, &idst
, &edst
)) {
case ARPRESOLVE_WILLSEND
:
return (0); /* if not yet resolved */
case ARPRESOLVE_BROADCAST
:
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
looutput(&loif
, mcopy
, dst
);
off
= ((u_short
)mtod(m
, struct ip
*)->ip_len
) - m
->m_len
;
if ((ifp
->if_flags
& IFF_NOTRAILERS
) == 0)
if (off
> 0 && (off
& 0x1ff) == 0 &&
m
->m_off
>= MMINOFF
+ 2 * sizeof (u_short
))
type
= ETHERPUP_TRAIL
+ (off
>>9);
m
->m_off
-= 2 * sizeof (u_short
);
m
->m_len
+= 2 * sizeof (u_short
);
*mtod(m
, u_short
*) = ETHERPUP_IPTYPE
;
*(mtod(m
, u_short
*) + 1) = m
->m_len
;
enp
= mtod(m
, struct ether_header
*);
if (m
->m_len
< sizeof *enp
)
enp
= (struct ether_header
*)dst
->sa_data
;
bcopy( enp
->ether_dhost
, &edst
, sizeof(edst
));
printf("enp%d: can't handle af%d\n", 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_ETHEADER
> m
->m_off
)
m
= m_get(M_DONTWAIT
, MT_HEADER
);
enperr
[unit
].h_nobuffer
++; /* host runs out of buf */
m
->m_len
= SIZEOF_ETHEADER
;
m
->m_off
-= SIZEOF_ETHEADER
;
m
->m_len
+= SIZEOF_ETHEADER
;
enp
= mtod(m
, struct ether_header
*);
bcopy( &edst
, enp
->ether_dhost
, sizeof(enp
->ether_dhost
) );
bcopy( es
->es_enaddr
, enp
->ether_shost
, sizeof(enp
->ether_shost
));
* Queue message on interface if possible
enperr
[unit
].c_nobuffer
++; /* controller runs out of buf */
* Routine to copy from mbuf chain to transmitter
* buffer in Multibus memory.
register ENPDEVICE
*addr
;
register struct mbuf
*mp
;
register short *vp
= (short *)&v
,
addr
= (ENPDEVICE
*)enpinfo
[ unit
]->ui_addr
;
if ( ringempty( &addr
->enp_hostfree
) )
bcbp
= (BCB
*)ringget( &addr
->enp_hostfree
);
sp
= (short *)&bcbp
->b_addr
;
*vp
= *sp
; vp
[1] = sp
[1];
bp
= (u_char
*)bcbp
->b_addr
;
for (mp
= m
; mp
; mp
= mp
->m_next
)
mcp
= mtod( mp
,u_char
* );
bcbp
->b_len
= max( MINPKTSIZE
,bcbp
->b_len
);
if ( ringput( &addr
->enp_toenp
,bcbp
) == 1 ) {
* Routine to copy from Multibus memory into mbufs.
* Warning: This makes the fairly safe assumption that
* mbufs have even lengths.
enpget( bcbp
, totlen
, off0
)
register unsigned char *cp
;
register short *vp
= (short *)&v
,
sp
= (short *)&bcbp
->b_addr
;
*vp
= *sp
; vp
[1] = sp
[1];
cp
= (unsigned char *)v
+ SIZEOF_ETHEADER
;
cp
= (unsigned char *)bcbp
->b_addr
+ SIZEOF_ETHEADER
;
MGET(m
, M_DONTWAIT
, MT_DATA
);
sp
= (short *)&bcbp
->b_addr
;
*vp
= *sp
; vp
[1] = sp
[1];
cp
= (unsigned char *)v
+ SIZEOF_ETHEADER
cp
= (unsigned char *)bcbp
->b_addr
+
m
->m_len
= len
= CLBYTES
;
m
->m_off
= (int)p
- (int)m
;
m
->m_len
= len
= MIN(MLEN
, len
);
m
->m_len
= len
= MIN(MLEN
, len
);
sp
= (short *)&bcbp
->b_addr
;
*vp
= *sp
; vp
[1] = sp
[1];
cp
= (unsigned char *)v
+ SIZEOF_ETHEADER
;
cp
= (unsigned char *)bcbp
->b_addr
+ SIZEOF_ETHEADER
;
* Process an ioctl request.
* this can be called via the "socket" route for SIOCSIFADDR or
* by the cdev/inode route for SIOCSIFCCFWR/RD
register struct ifnet
*ifp
;
register int unit
= ifp
->if_unit
;
register struct vba_device
*md
;
struct enp_softc
*es
= &enp_softc
[ifp
->if_unit
];
struct ifreq
*ifr
= (struct ifreq
*)data
;
struct sockaddr_in
*et_addr
;
if (unit
>= NENP
|| (md
= enpinfo
[unit
]) == 0 || md
->ui_alive
== 0)
sa
= (struct sockaddr
*)&ifr
->ifr_addr
;
if (sa
->sa_family
== AF_UNSPEC
) {
if (sa
->sa_data
[0] & 1){ /*broad or multi-cast*/
bcopy(sa
->sa_data
,es
->es_enaddr
,sizeof(es
->es_enaddr
));
sin
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
if (sin
->sin_family
!= AF_INET
){
if (ifp
->if_flags
& IFF_RUNNING
)
if_rtinit(ifp
, -1); /* delete previous route */
enpgetaddr( ifp
->if_unit
);
case SIOCSETETADDR
: /* Set Ethernet station address */
ifp
->if_flags
&= (~IFF_RUNNING
| IFF_UP
);
et_addr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
addr
= (ENPDEVICE
*)enpinfo
[ifp
->if_unit
]->ui_addr
;
/* Set station address and reset controller board */
u_char
*to
= &addr
->enp_addr
.e_baseaddr
.ea_addr
[0];
char *from
= &et_addr
->sin_zero
[2];
for (i
= 0 ; i
< ETHADDR_SIZE
; i
++)
*to
++ = (u_char
) (~(*from
++ & 0xff));
enpcopy(&addr
->enp_addr
.e_listsize
, &code
, sizeof(code
));
enpcopy(&code
, &addr
->enp_addr
.e_listsize
, sizeof(code
));
enpreset(ifp
->if_unit
); /* Re-initialize */
enpgetaddr(ifp
->if_unit
);
case SIOCGETETADDR
: /* Get Foreign Hosts' Ethernet addresses */
arpwhohas(&es
->es_ac
, (struct in_addr
*)ifr
->ifr_data
);
register struct ifnet
*ifp
;
register struct sockaddr_in
*sin
;
ifp
->if_addr
= *(struct sockaddr
*)sin
;
ifp
->if_net
= in_netof(sin
->sin_addr
);
ifp
->if_host
[0] = in_lnaof(sin
->sin_addr
);
sin
= (struct sockaddr_in
*)&ifp
->if_broadaddr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(ifp
->if_net
, INADDR_ANY
);
ifp
->if_flags
|= IFF_BROADCAST
;
* Get the ethernet addr, store it and print it
* Read the ethernet address off the board, one byte at a time.
register struct enp_softc
*es
= &enp_softc
[unit
];
register ENPDEVICE
*addr
=(ENPDEVICE
*)enpinfo
[unit
]->ui_addr
;
enpcopy(&addr
->enp_addr
.e_baseaddr
, &es
->es_boardaddr
, sizeof(es
->es_boardaddr
));
es
->es_boardaddr
= addr
->enp_addr
.e_baseaddr
;
bcopy(&es
->es_boardaddr
, es
->es_enaddr
, ETHADDR_SIZE
);
register int unit
= minor(dev
);
register struct vba_device
*md
;
register ENPDEVICE
*addr
;
if (unit
>= NENP
|| (md
= enpinfo
[unit
]) == 0 || md
->ui_alive
== 0 ||
(addr
= (ENPDEVICE
*)md
->ui_addr
) == (ENPDEVICE
*)0)
if (addr
->enp_state
!= S_ENPRESET
)
return(EACCES
); /* enp is not in reset state, don't open */
if ( !enpismapped
[unit
] ) {
ioaccess(ENPmap
[unit
],ENPmapa
[unit
],ENPBPTE
);
enpram
[minor(dev
)] = ENP_CLOSE
;
register struct uio
*uio
;
register ENPDEVICE
*addr
;
register struct iovec
*iov
;
if (enpram
[minor(dev
)] != ENP_OPEN
)
if ( uio
->uio_offset
> RAM_SIZE
)
if ( uio
->uio_offset
+ iov
->iov_len
> RAM_SIZE
)
iov
->iov_len
= RAM_SIZE
- uio
->uio_offset
;
addr
= (ENPDEVICE
*)enpinfo
[ minor( dev
) ]->ui_addr
;
if( r
= enpcopyout( &addr
->enp_ram
[ uio
->uio_offset
], iov
->iov_base
,
uio
->uio_resid
-= iov
->iov_len
;
register struct uio
*uio
;
register ENPDEVICE
*addr
;
register struct iovec
*iov
;
if (enpram
[minor(dev
)] != ENP_OPEN
)
addr
= (ENPDEVICE
*)enpinfo
[ minor( 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
;
if( r
= enpcopyin( iov
->iov_base
, &addr
->enp_ram
[ uio
->uio_offset
],
uio
->uio_resid
-= iov
->iov_len
;
enpr_ioctl( dev
,cmd
,arg
,fflag
)
register ENPDEVICE
*addr
;
register short *vp
= (short *)&v
, *sp
;
register unit
= minor(dev
);
register struct vba_device
*md
;
if (unit
>= NENP
|| (md
= enpinfo
[unit
]) == 0 || md
->ui_alive
== 0 ||
(addr
= (ENPDEVICE
*)md
->ui_addr
) == (ENPDEVICE
*)0)
/* not needed if prom based version */
sp
= (short *)&addr
->enp_base
;
*sp
= *vp
; sp
[1] = vp
[1];
addr
->enp_base
= (int)addr
;
addr
->enp_intrvec
= intvec
[ unit
];
ENP_GO( addr
, ENPSTART
);
enpattach( enpinfo
[ unit
] );
addr
->enp_state
= S_ENPRUN
; /* it is running now */
addr
->enp_state
= S_ENPRESET
; /* it is reset now */
* routines to synchronize enp and host
rp
->r_rdidx
= rp
->r_wrtidx
= 0;
return( rp
->r_rdidx
== rp
->r_wrtidx
);
idx
= (rp
->r_wrtidx
+ 1) & (rp
->r_size
-1);
return( idx
== rp
->r_rdidx
);
register short *vp
= (short *)&v
,
idx
= (rp
->r_wrtidx
+ 1) & (rp
->r_size
-1);
sp
= (short *)&rp
->r_slot
[ rp
->r_wrtidx
];
*sp
= *vp
; sp
[1] = vp
[1];
rp
->r_slot
[ rp
->r_wrtidx
] = v
;
if( (idx
-= rp
->r_rdidx
) < 0 )
return( idx
); /* num ring entries */
register short *vp
= (short *)&v
,
if( rp
->r_rdidx
!= rp
->r_wrtidx
)
sp
= (short *)&rp
->r_slot
[ rp
->r_rdidx
];
*vp
= *sp
; vp
[1] = sp
[1];
i
= rp
->r_slot
[ rp
->r_rdidx
];
rp
->r_rdidx
= (++rp
->r_rdidx
) & (rp
->r_size
-1);
register short *vp
= (short *)&v
,
if( rp
->r_rdidx
!= rp
->r_wrtidx
)
sp
= (short *)&rp
->r_slot
[ rp
->r_rdidx
];
*vp
= *sp
; vp
[1] = sp
[1];
/* *sp = 0xffff; sp[1] = 0xffff; */
v
= rp
->r_slot
[ rp
->r_rdidx
];
rp
->r_rdidx
= (++rp
->r_rdidx
) & (rp
->r_size
-1);
return( (struct mbuf
*)v
);
register short *vp
= (short *)&v
,
if( rp
->r_rdidx
!= rp
->r_wrtidx
)
sp
= (short *)&rp
->r_slot
[ rp
->r_rdidx
];
*vp
= *sp
; vp
[1] = sp
[1];
return( rp
->r_slot
[ rp
->r_rdidx
] );
for( i
= 0; i
< 12; i
++ )
printf("%X ",*addr
&0377);
register 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 */
if ( cnt
) { /* odd len */
while (cnt
-- > 0) /* one of the address(es) must be odd */
enpcopyin(userv
, kernv
, cnt
)
if (useracc(userv
, cnt
, 1)) {
enpcopy( userv
, kernv
, cnt
);
enpcopyout(kernv
, userv
, cnt
)
if (useracc(userv
, cnt
, 0)) {
enpcopy( kernv
, userv
, cnt
);