* Copyright (c) 1988 Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)if_enp.c 7.8 (Berkeley) 12/16/90
* CMC ENP-20 Ethernet Controller.
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/ip_var.h"
#include "netinet/if_ether.h"
#include "../include/cpu.h"
#include "../include/pte.h"
#include "../include/mtpr.h"
#include "../vba/vbavar.h"
#include "../if/if_enpreg.h"
#define ENPSTART 0xf02000 /* standard enp start addr */
#define ENPUNIT(dev) (minor(dev)) /* for enp ram devices */
/* macros for dealing with longs in i/o space */
#define ENPGETLONG(a) ((((u_short *)(a))[0] << 16)|(((u_short *)(a))[1]))
#define ENPSETLONG(a,v) \
{ register u_short *wp = (u_short *)(a); \
wp[0] = ((u_short *)&(v))[0]; wp[1] = ((u_short *)&(v))[1];}
int enpprobe(), enpattach(), enpintr();
long enpstd
[] = { 0xfff41000, 0xfff61000, 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(), enpstart();
* 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_addr es_ac.ac_enaddr
short es_ivec
; /* interrupt vector */
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
;
ifp
->if_unit
= ui
->ui_unit
;
ifp
->if_ioctl
= enpioctl
;
ifp
->if_output
= ether_output
;
ifp
->if_start
= enpstart
;
ifp
->if_reset
= enpreset
;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
;
* 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
;
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) {
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
;
* 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
*)ENPGETLONG(&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.
m
= enpget((u_char
*)enp
, len
, off
, &es
->es_if
);
ether_input(&es
->es_if
, enp
, m
);
* Routine to copy from mbuf chain to transmitter buffer on the VERSAbus.
register struct enpdevice
*addr
;
register struct mbuf
*mp
;
int unit
= ifp
->if_unit
, ret
= 1;
addr
= (struct enpdevice
*)enpinfo
[unit
]->ui_addr
;
if (ringempty((RING
*)&addr
->enp_hostfree
)) {
/* ifp->if_flags |= IFF_OACTIVE; */
IF_DEQUEUE(&ifp
->if_snd
, m
);
ifp
->if_flags
&= ~IFF_OACTIVE
;
bcbp
= (BCB
*)ringget((RING
*)&addr
->enp_hostfree
);
bp
= (u_char
*)ENPGETLONG(&bcbp
->b_addr
);
for (mp
= m
; mp
; mp
= mp
->m_next
) {
enpcopy(mtod(mp
, u_char
*), bp
, len
);
bcbp
->b_len
= max(ETHERMIN
+sizeof (struct ether_header
), 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
, off
, ifp
)
struct mbuf
*top
= 0, **mp
= &top
;
rxbuf
+= sizeof (struct ether_header
);
packet_end
= cp
+ totlen
;
off
+= 2 * sizeof(u_short
);
totlen
-= 2 *sizeof(u_short
);
MGETHDR(m
, M_DONTWAIT
, MT_DATA
);
m
->m_pkthdr
.len
= totlen
;
MGET(m
, M_DONTWAIT
, MT_DATA
);
len
= min(totlen
, (packet_end
- cp
));
m
->m_len
= len
= min(len
, MCLBYTES
);
* Place initial small packet/header at end of mbuf.
if (top
== 0 && len
+ max_linkhdr
<= m
->m_len
)
m
->m_data
+= max_linkhdr
;
enpcopy(cp
, mtod(m
, u_char
*), (u_int
)len
);
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_addr
;
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
)
enpcopy(enaddr
, addr
->enp_addr
.e_baseaddr
.ea_addr
,
sizeof (struct ether_addr
));
struct enp_softc
*es
= &enp_softc
[unit
];
enpcopy(addr
->enp_addr
.e_baseaddr
.ea_addr
, es
->es_addr
,
sizeof (struct ether_addr
));
printf("enp%d: hardware address %s\n",
unit
, ether_sprintf(es
->es_addr
));
* 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
) {
ENPSETLONG(&rp
->r_slot
[rp
->r_wrtidx
], v
);
if ((idx
-= rp
->r_rdidx
) < 0)
return (idx
); /* num ring entries */
if (rp
->r_rdidx
!= rp
->r_wrtidx
) {
i
= ENPGETLONG(&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
;
if (useracc(iov
->iov_base
, (unsigned)iov
->iov_len
, 0) == 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
;
if (useracc(iov
->iov_base
, (unsigned)iov
->iov_len
, 1) == 0)
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
;
uio
->uio_offset
+= iov
->iov_len
;
enpr_ioctl(dev
, cmd
, data
)
register unit
= ENPUNIT(dev
);
addr
= (struct enpdevice
*)enpinfo
[unit
]->ui_addr
;
ENPSETLONG(&addr
->enp_base
, addr
);
addr
->enp_intrvec
= enp_softc
[unit
].es_ivec
;
* Fetch Ethernet address after link level
* is booted (firmware copies manufacturer's
* address from on-board ROM).
addr
->enp_state
= S_ENPRUN
;
addr
->enp_state
= S_ENPRESET
;