NetBSD/sys/arch/i386/isa/if_we.c

809 lines
21 KiB
C

/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tim L. Tucker.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 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
* SUCH DAMAGE.
*
* @(#)if_we.c 7.3 (Berkeley) 5/21/91
*/
/*
* Modification history
*
* 8/28/89 - Initial version(if_wd.c), Tim L Tucker
*/
#include "we.h"
#if NWE > 0
/*
* Western Digital 8003 ethernet/starlan adapter
*
* Supports the following interface cards:
* WD8003E, WD8003EBT, WD8003S, WD8003SBT, WD8013EBT
*
* The Western Digital card is one of many AT/MCA ethernet interfaces
* based on the National DS8390 Network Interface chip set.
*/
#include "param.h"
#include "mbuf.h"
#include "socket.h"
#include "ioctl.h"
#include "errno.h"
#include "syslog.h"
#include "net/if.h"
#include "net/netisr.h"
#ifdef INET
#include "netinet/in.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/ip.h"
#include "netinet/if_ether.h"
#endif
#ifdef NS
#include "netns/ns.h"
#include "netns/ns_if.h"
#endif
#include "i386/isa/isa.h"
#include "i386/isa/if_wereg.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/icu.h"
/*
* This constant should really be 60 because the we adds 4 bytes of crc.
* However when set to 60 our packets are ignored by deunas , 3coms are
* okay ??????????????????????????????????????????
*/
#define ETHER_MIN_LEN 64
#define ETHER_ADDR_LEN 6
#define ETHER_HDR_SIZE 14
/*
* Ethernet software status per interface.
*
* Each interface is referenced by a network interface structure,
* qe_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
*/
struct we_softc {
struct arpcom we_ac; /* Ethernet common part */
#define we_if we_ac.ac_if /* network-visible interface */
#define we_addr we_ac.ac_enaddr /* hardware Ethernet address */
u_char we_flags; /* software state */
#define WDF_RUNNING 0x01
#define WDF_TXBUSY 0x02
u_char we_type; /* interface type code */
u_short we_vector; /* interrupt vector */
short we_io_ctl_addr; /* i/o bus address, control */
short we_io_nic_addr; /* i/o bus address, DS8390 */
caddr_t we_vmem_addr; /* card RAM virtual memory base */
u_long we_vmem_size; /* card RAM bytes */
caddr_t we_vmem_ring; /* receive ring RAM vaddress */
caddr_t we_vmem_end; /* receive ring RAM end */
} we_softc[NWE];
int weprobe(), weattach(), weintr(), westart();
int weinit(), ether_output(), weioctl(), wereset(), wewatchdog();
struct isa_driver wedriver = {
weprobe, weattach, "we",
};
static unsigned short wemask[] =
{ IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15, IRQ4 };
/*
* Probe the WD8003 to see if its there
*/
weprobe(is)
struct isa_device *is;
{
register int i;
register struct we_softc *sc = &we_softc[is->id_unit];
union we_mem_sel wem;
u_char sum;
/* reset card to force it into a known state. */
outb(is->id_iobase, 0x80);
DELAY(100);
outb(is->id_iobase, 0x00);
/* wait in the case this card is reading it's EEROM */
DELAY(5000);
/*
* Here we check the card ROM, if the checksum passes, and the
* type code and ethernet address check out, then we know we have
* a wd8003 card.
*
* Autoconfiguration: No warning message is printed on error.
*/
for (sum = 0, i = 0; i < 8; ++i)
sum += inb(is->id_iobase + WD_ROM_OFFSET + i);
if (sum != WD_CHECKSUM)
return (0);
sc->we_type = inb(is->id_iobase + WD_ROM_OFFSET + 6);
#ifdef nope
if ((sc->we_type & WD_REVMASK) != 2 /* WD8003E or WD8003S */
&& (sc->we_type & WD_REVMASK) != 4 /* WD8003EBT */
&& (sc->we_type & WD_REVMASK) != 6) /* WD8003ELB? */
return (0);
#endif
/*printf("type %x ", sc->we_type);*/
if (sc->we_type & WD_SOFTCONFIG) {
int iv = inb(is->id_iobase + 1) & 4 |
((inb(is->id_iobase+4) & 0x60) >> 5);
/*printf("iv %d ", iv);*/
if (wemask[iv] != is->id_irq)
return(0);
outb(is->id_iobase+4, inb(is->id_iobase+4) | 0x80);
}
/*
* Setup card RAM area and i/o addresses
* Kernel Virtual to segment C0000-DFFFF?????
*/
sc->we_io_ctl_addr = is->id_iobase;
sc->we_io_nic_addr = sc->we_io_ctl_addr + WD_NIC_OFFSET;
sc->we_vector = is->id_irq;
sc->we_vmem_addr = (caddr_t)is->id_maddr;
sc->we_vmem_size = is->id_msize;
sc->we_vmem_ring = sc->we_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE);
sc->we_vmem_end = sc->we_vmem_addr + is->id_msize;
/*
* Save board ROM station address
*/
for (i = 0; i < ETHER_ADDR_LEN; ++i)
sc->we_addr[i] = inb(sc->we_io_ctl_addr + WD_ROM_OFFSET + i);
/*
* Mapin interface memory, setup memory select register
*/
wem.ms_addr = kvtop(sc->we_vmem_addr) >> 13;
wem.ms_enable = 1;
wem.ms_reset = 0;
outb(sc->we_io_ctl_addr, wem.ms_byte);
/*
* clear interface memory, then sum to make sure its valid
*/
for (i = 0; i < sc->we_vmem_size; ++i)
sc->we_vmem_addr[i] = 0x0;
for (sum = 0, i = 0; i < sc->we_vmem_size; ++i)
sum += sc->we_vmem_addr[i];
if (sum != 0x0) {
printf("we%d: wd8003 dual port RAM address error\n", is->id_unit);
return (0);
}
return (WD_IO_PORTS);
}
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
weattach(is)
struct isa_device *is;
{
register struct we_softc *sc = &we_softc[is->id_unit];
register struct ifnet *ifp = &sc->we_if;
union we_command wecmd;
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_stp = 1;
wecmd.cs_sta = 0;
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
/*
* Initialize ifnet structure
*/
ifp->if_unit = is->id_unit;
ifp->if_name = "we" ;
ifp->if_mtu = ETHERMTU;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS ;
ifp->if_init = weinit;
ifp->if_output = ether_output;
ifp->if_start = westart;
ifp->if_ioctl = weioctl;
ifp->if_reset = wereset;
ifp->if_watchdog = wewatchdog;
if_attach(ifp);
/*
* Banner...
*/
printf(" %s address %s",
(sc->we_type & WD_ETHERNET) ? "ethernet" : "starlan",
ether_sprintf(sc->we_addr));
}
/*
* Reset of interface.
*/
wereset(unit, uban)
int unit, uban;
{
if (unit >= NWE)
return;
printf("we%d: reset\n", unit);
/* we_softc[unit].we_flags &= ~WDF_RUNNING; */
weinit(unit);
}
/*
* Take interface offline.
*/
westop(unit)
int unit;
{
register struct we_softc *sc = &we_softc[unit];
union we_command wecmd;
int s;
/*
* Shutdown DS8390
*/
s = splimp();
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_stp = 1;
wecmd.cs_sta = 0;
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
(void) splx(s);
}
wewatchdog(unit) {
log(LOG_WARNING,"we%d: soft reset\n", unit);
westop(unit);
weinit(unit);
}
static Bdry;
/*
* Initialization of interface (really just DS8390).
*/
weinit(unit)
int unit;
{
register struct we_softc *sc = &we_softc[unit];
register struct ifnet *ifp = &sc->we_if;
union we_command wecmd;
int i, s;
/* address not known */
if (ifp->if_addrlist == (struct ifaddr *)0)
return;
/* already running */
/*if (ifp->if_flags & IFF_RUNNING) return; */
/*
* Initialize DS8390 in order given in NSC NIC manual.
* this is stock code...please see the National manual for details.
*/
s = splhigh();
Bdry = 0;
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_stp = 1;
wecmd.cs_sta = 0;
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG);
outb(sc->we_io_nic_addr + WD_P0_RBCR0, 0);
outb(sc->we_io_nic_addr + WD_P0_RBCR1, 0);
outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_MON);
outb(sc->we_io_nic_addr + WD_P0_TCR, WD_T_CONFIG);
outb(sc->we_io_nic_addr + WD_P0_TPSR, 0);
outb(sc->we_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE);
outb(sc->we_io_nic_addr + WD_P0_PSTOP,
sc->we_vmem_size / WD_PAGE_SIZE);
outb(sc->we_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE);
outb(sc->we_io_nic_addr + WD_P0_ISR, 0xff);
outb(sc->we_io_nic_addr + WD_P0_IMR, WD_I_CONFIG);
wecmd.cs_ps = 1;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
for (i = 0; i < ETHER_ADDR_LEN; ++i)
outb(sc->we_io_nic_addr + WD_P1_PAR0 + i, sc->we_addr[i]);
for (i = 0; i < ETHER_ADDR_LEN; ++i) /* == broadcast addr */
outb(sc->we_io_nic_addr + WD_P1_MAR0 + i, 0xff);
outb(sc->we_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE);
wecmd.cs_ps = 0;
wecmd.cs_stp = 0;
wecmd.cs_sta = 1;
wecmd.cs_rd = 0x4;
outb(sc->we_io_nic_addr + WD_P1_COMMAND, wecmd.cs_byte);
outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG);
/*
* Take the interface out of reset, program the vector,
* enable interrupts, and tell the world we are up.
*/
ifp->if_flags |= IFF_RUNNING;
sc->we_flags &= ~WDF_TXBUSY;
(void) splx(s);
westart(ifp);
}
/*
* Start output on interface.
*/
westart(ifp)
struct ifnet *ifp;
{
register struct we_softc *sc = &we_softc[ifp->if_unit];
struct mbuf *m0, *m;
register caddr_t buffer;
int len, s;
union we_command wecmd;
/*
* The DS8390 has only one transmit buffer, if it is busy we
* must wait until the transmit interrupt completes.
*/
s = splhigh();
if (sc->we_flags & WDF_TXBUSY) {
(void) splx(s);
return;
}
IF_DEQUEUE(&sc->we_if.if_snd, m);
if (m == 0) {
(void) splx(s);
return;
}
sc->we_flags |= WDF_TXBUSY;
(void) splx(s);
/*
* Copy the mbuf chain into the transmit buffer
*/
buffer = sc->we_vmem_addr;
len = 0;
for (m0 = m; m != 0; m = m->m_next) {
bcopy(mtod(m, caddr_t), buffer, m->m_len);
buffer += m->m_len;
len += m->m_len;
}
m_freem(m0);
/*
* Init transmit length registers, and set transmit start flag.
*/
s = splhigh();
len = MAX(len, ETHER_MIN_LEN);
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
outb(sc->we_io_nic_addr + WD_P0_TBCR0, len & 0xff);
outb(sc->we_io_nic_addr + WD_P0_TBCR1, len >> 8);
wecmd.cs_txp = 1;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
(void) splx(s);
}
/*
* Ethernet interface interrupt processor
*/
weintr(unit)
int unit;
{
register struct we_softc *sc = &we_softc[unit];
union we_command wecmd;
union we_interrupt weisr;
unit =0;
/* disable onboard interrupts, then get interrupt status */
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
loop:
outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte);
/* transmit error */
if (weisr.is_txe) {
/* need to read these registers to clear status */
sc->we_if.if_collisions +=
inb(sc->we_io_nic_addr + WD_P0_TBCR0);
++sc->we_if.if_oerrors;
}
/* receiver error */
if (weisr.is_rxe) {
/* need to read these registers to clear status */
(void) inb(sc->we_io_nic_addr + 0xD);
(void) inb(sc->we_io_nic_addr + 0xE);
(void) inb(sc->we_io_nic_addr + 0xF);
++sc->we_if.if_ierrors;
}
/* normal transmit complete */
if (weisr.is_ptx || weisr.is_txe)
wetint (unit);
/* normal receive notification */
if (weisr.is_prx || weisr.is_rxe)
werint (unit);
/* try to start transmit */
westart(&sc->we_if);
/* re-enable onboard interrupts */
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/);
weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
if (weisr.is_byte) goto loop;
}
/*
* Ethernet interface transmit interrupt.
*/
wetint(unit)
int unit;
{
register struct we_softc *sc = &we_softc[unit];
/*
* Do some statistics (assume page zero of NIC mapped in)
*/
sc->we_flags &= ~WDF_TXBUSY;
sc->we_if.if_timer = 0;
++sc->we_if.if_opackets;
sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0);
}
/*
* Ethernet interface receiver interrupt.
*/
werint(unit)
int unit;
{
register struct we_softc *sc = &we_softc[unit];
u_char bnry, curr;
long len;
union we_command wecmd;
struct we_ring *wer;
/*
* Traverse the receive ring looking for packets to pass back.
* The search is complete when we find a descriptor not in use.
*/
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY);
wecmd.cs_ps = 1;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
if(Bdry)
bnry =Bdry;
while (bnry != curr)
{
/* get pointer to this buffer header structure */
wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8));
/* count includes CRC */
len = wer->we_count - 4;
if (len > 30 && len <= ETHERMTU+100
/*&& (*(char *)wer == 1 || *(char *) wer == 0x21)*/)
weread(sc, (caddr_t)(wer + 1), len);
else printf("reject %d", len);
outofbufs:
wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
wecmd.cs_ps = 0;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
/* advance on chip Boundry register */
if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) {
bnry = WD_TXBUF_SIZE;
outb(sc->we_io_nic_addr + WD_P0_BNRY,
sc->we_vmem_size / WD_PAGE_SIZE-1);
} else {
if (len > 30 && len <= ETHERMTU+100)
bnry = wer->we_next_packet;
else bnry = curr;
/* watch out for NIC overflow, reset Boundry if invalid */
if ((bnry - 1) < WD_TXBUF_SIZE) {
outb(sc->we_io_nic_addr + WD_P0_BNRY,
(sc->we_vmem_size / WD_PAGE_SIZE) - 1);
bnry = WD_TXBUF_SIZE;
} else
outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1);
}
/* refresh our copy of CURR */
wecmd.cs_ps = 1;
outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
}
Bdry = bnry;
}
/*
* Process an ioctl request.
*/
weioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
register struct ifaddr *ifa = (struct ifaddr *)data;
struct we_softc *sc = &we_softc[ifp->if_unit];
struct ifreq *ifr = (struct ifreq *)data;
int s = splimp(), error = 0;
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
weinit(ifp->if_unit); /* before arpwhohas */
((struct arpcom *)ifp)->ac_ipaddr =
IA_SIN(ifa)->sin_addr;
arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
break;
#endif
#ifdef NS
case AF_NS:
{
register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
if (ns_nullhost(*ina))
ina->x_host = *(union ns_host *)(sc->ns_addr);
else {
/*
* The manual says we cant change the address
* while the receiver is armed,
* so reset everything
*/
ifp->if_flags &= ~IFF_RUNNING;
bcopy((caddr_t)ina->x_host.c_host,
(caddr_t)sc->ns_addr, sizeof(sc->ns_addr));
}
weinit(ifp->if_unit); /* does ne_setaddr() */
break;
}
#endif
default:
weinit(ifp->if_unit);
break;
}
break;
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP) == 0 &&
ifp->if_flags & IFF_RUNNING) {
ifp->if_flags &= ~IFF_RUNNING;
westop(ifp->if_unit);
} else if (ifp->if_flags & IFF_UP &&
(ifp->if_flags & IFF_RUNNING) == 0)
weinit(ifp->if_unit);
break;
#ifdef notdef
case SIOCGHWADDR:
bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data,
sizeof(sc->sc_addr));
break;
#endif
default:
error = EINVAL;
}
splx(s);
return (error);
}
/*
* set ethernet address for unit
*/
wesetaddr(physaddr, unit)
u_char *physaddr;
int unit;
{
register struct we_softc *sc = &we_softc[unit];
register int i;
/*
* Rewrite ethernet address, and then force restart of NIC
*/
for (i = 0; i < ETHER_ADDR_LEN; i++)
sc->we_addr[i] = physaddr[i];
sc->we_flags &= ~WDF_RUNNING;
weinit(unit);
}
#define wedataaddr(sc, eh, off, type) \
((type) ((caddr_t)((eh)+1)+(off) >= (sc)->we_vmem_end) ? \
(((caddr_t)((eh)+1)+(off))) - (sc)->we_vmem_end \
+ (sc)->we_vmem_ring: \
((caddr_t)((eh)+1)+(off)))
/*
* Pass a packet to the higher levels.
* We deal with the trailer protocol here.
*/
weread(sc, buf, len)
register struct we_softc *sc;
char *buf;
int len;
{
register struct ether_header *eh;
struct mbuf *m, *weget();
int off, resid;
/*
* 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.
*/
eh = (struct ether_header *)buf;
eh->ether_type = ntohs((u_short)eh->ether_type);
if (eh->ether_type >= ETHERTYPE_TRAIL &&
eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
if (off >= ETHERMTU) return; /* sanity */
eh->ether_type = ntohs(*wedataaddr(sc, eh, off, u_short *));
resid = ntohs(*(wedataaddr(sc, eh, off+2, u_short *)));
if (off + resid > len) return; /* sanity */
len = off + resid;
} else off = 0;
len -= sizeof(struct ether_header);
if (len <= 0) return;
/*
* Pull packet off interface. Off is nonzero if packet
* has trailing header; neget 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 = weget(buf, len, off, &sc->we_if, sc);
if (m == 0) return;
ether_input(&sc->we_if, eh, m);
}
/*
* Supporting routines
*/
/*
* Pull read data off a interface.
* Len is length of data, with local net header stripped.
* Off is non-zero if a trailer protocol was used, and
* gives the offset of the trailer information.
* We copy the trailer information and then all the normal
* data into mbufs. When full cluster sized units are present
* we copy into clusters.
*/
struct mbuf *
weget(buf, totlen, off0, ifp, sc)
caddr_t buf;
int totlen, off0;
struct ifnet *ifp;
struct we_softc *sc;
{
struct mbuf *top, **mp, *m, *p;
int off = off0, len;
register caddr_t cp = buf;
char *epkt;
int tc =totlen;
buf += sizeof(struct ether_header);
cp = buf;
epkt = cp + totlen;
if (off) {
cp += off + 2 * sizeof(u_short);
totlen -= 2 * sizeof(u_short);
}
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0)
return (0);
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = totlen;
m->m_len = MHLEN;
top = 0;
mp = &top;
while (totlen > 0) {
if (top) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0) {
m_freem(top);
return (0);
}
m->m_len = MLEN;
}
len = min(totlen, epkt - cp);
if (len >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (m->m_flags & M_EXT)
m->m_len = len = min(len, MCLBYTES);
else
len = m->m_len;
} else {
/*
* Place initial small packet/header at end of mbuf.
*/
if (len < m->m_len) {
if (top == 0 && len + max_linkhdr <= m->m_len)
m->m_data += max_linkhdr;
m->m_len = len;
} else
len = m->m_len;
}
totlen -= len;
/* only do up to end of buffer */
if (cp+len > sc->we_vmem_end) {
unsigned toend = sc->we_vmem_end - cp;
bcopy(cp, mtod(m, caddr_t), toend);
cp = sc->we_vmem_ring;
bcopy(cp, mtod(m, caddr_t)+toend, len - toend);
cp += len - toend;
epkt = cp + totlen;
} else {
bcopy(cp, mtod(m, caddr_t), (unsigned)len);
cp += len;
}
*mp = m;
mp = &m->m_next;
if (cp == epkt) {
cp = buf;
epkt = cp + tc;
}
}
return (top);
}
#endif