xref: /netbsd-src/sys/dev/ic/lance.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1 /*	$NetBSD: lance.c,v 1.60 2020/01/29 06:17:07 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
9  * Simulation Facility, NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*-
34  * Copyright (c) 1992, 1993
35  *	The Regents of the University of California.  All rights reserved.
36  *
37  * This code is derived from software contributed to Berkeley by
38  * Ralph Campbell and Rick Macklem.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
65  */
66 
67 #include <sys/cdefs.h>
68 __KERNEL_RCSID(0, "$NetBSD: lance.c,v 1.60 2020/01/29 06:17:07 thorpej Exp $");
69 
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/mbuf.h>
73 #include <sys/syslog.h>
74 #include <sys/socket.h>
75 #include <sys/device.h>
76 #include <sys/malloc.h>
77 #include <sys/ioctl.h>
78 #include <sys/errno.h>
79 #include <sys/rndsource.h>
80 
81 #include <net/if.h>
82 #include <net/if_dl.h>
83 #include <net/if_ether.h>
84 #include <net/if_media.h>
85 #include <net/bpf.h>
86 
87 #include <dev/ic/lancereg.h>
88 #include <dev/ic/lancevar.h>
89 
90 #if defined(_KERNEL_OPT)
91 #include "opt_ddb.h"
92 #endif
93 
94 #ifdef DDB
95 #define	integrate
96 #define hide
97 #else
98 #define	integrate	static inline
99 #define hide		static
100 #endif
101 
102 integrate struct mbuf *lance_get(struct lance_softc *, int, int);
103 
104 hide bool lance_shutdown(device_t, int);
105 
106 int lance_mediachange(struct ifnet *);
107 void lance_mediastatus(struct ifnet *, struct ifmediareq *);
108 
109 static inline uint16_t ether_cmp(void *, void *);
110 
111 void lance_stop(struct ifnet *, int);
112 int lance_ioctl(struct ifnet *, u_long, void *);
113 void lance_watchdog(struct ifnet *);
114 
115 /*
116  * Compare two Ether/802 addresses for equality, inlined and
117  * unrolled for speed.  Use this like memcmp().
118  *
119  * XXX: Add <machine/inlines.h> for stuff like this?
120  * XXX: or maybe add it to libkern.h instead?
121  *
122  * "I'd love to have an inline assembler version of this."
123  * XXX: Who wanted that? mycroft?  I wrote one, but this
124  * version in C is as good as hand-coded assembly. -gwr
125  *
126  * Please do NOT tweak this without looking at the actual
127  * assembly code generated before and after your tweaks!
128  */
129 static inline uint16_t
130 ether_cmp(void *one, void *two)
131 {
132 	uint16_t *a = (uint16_t *)one;
133 	uint16_t *b = (uint16_t *)two;
134 	uint16_t diff;
135 
136 #ifdef	m68k
137 	/*
138 	 * The post-increment-pointer form produces the best
139 	 * machine code for m68k.  This was carefully tuned
140 	 * so it compiles to just 8 short (2-byte) op-codes!
141 	 */
142 	diff  = *a++ - *b++;
143 	diff |= *a++ - *b++;
144 	diff |= *a++ - *b++;
145 #else
146 	/*
147 	 * Most modern CPUs do better with a single expression.
148 	 * Note that short-cut evaluation is NOT helpful here,
149 	 * because it just makes the code longer, not faster!
150 	 */
151 	diff = (a[0] - b[0]) | (a[1] - b[1]) | (a[2] - b[2]);
152 #endif
153 
154 	return (diff);
155 }
156 
157 #define ETHER_CMP	ether_cmp
158 
159 #ifdef LANCE_REVC_BUG
160 /* Make sure this is short-aligned, for ether_cmp(). */
161 static uint16_t bcast_enaddr[3] = { ~0, ~0, ~0 };
162 #endif
163 
164 void
165 lance_config(struct lance_softc *sc)
166 {
167 	int i, nbuf;
168 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
169 
170 	/* Initialize ifnet structure. */
171 	strcpy(ifp->if_xname, device_xname(sc->sc_dev));
172 	ifp->if_softc = sc;
173 	ifp->if_start = sc->sc_start;
174 	ifp->if_ioctl = lance_ioctl;
175 	ifp->if_watchdog = lance_watchdog;
176 	ifp->if_init = lance_init;
177 	ifp->if_stop = lance_stop;
178 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
179 #ifdef LANCE_REVC_BUG
180 	ifp->if_flags &= ~IFF_MULTICAST;
181 #endif
182 	IFQ_SET_READY(&ifp->if_snd);
183 
184 	/* Initialize ifmedia structures. */
185 	sc->sc_ethercom.ec_ifmedia = &sc->sc_media;
186 	ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus);
187 	if (sc->sc_supmedia != NULL) {
188 		for (i = 0; i < sc->sc_nsupmedia; i++)
189 			ifmedia_add(&sc->sc_media, sc->sc_supmedia[i],
190 			   0, NULL);
191 		ifmedia_set(&sc->sc_media, sc->sc_defaultmedia);
192 	} else {
193 		ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_MANUAL, 0, NULL);
194 		ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_MANUAL);
195 	}
196 
197 	switch (sc->sc_memsize) {
198 	case 8192:
199 		sc->sc_nrbuf = 4;
200 		sc->sc_ntbuf = 1;
201 		break;
202 	case 16384:
203 		sc->sc_nrbuf = 8;
204 		sc->sc_ntbuf = 2;
205 		break;
206 	case 32768:
207 		sc->sc_nrbuf = 16;
208 		sc->sc_ntbuf = 4;
209 		break;
210 	case 65536:
211 		sc->sc_nrbuf = 32;
212 		sc->sc_ntbuf = 8;
213 		break;
214 	case 131072:
215 		sc->sc_nrbuf = 64;
216 		sc->sc_ntbuf = 16;
217 		break;
218 	case 262144:
219 		sc->sc_nrbuf = 128;
220 		sc->sc_ntbuf = 32;
221 		break;
222 	default:
223 		/* weird memory size; cope with it */
224 		nbuf = sc->sc_memsize / LEBLEN;
225 		sc->sc_ntbuf = nbuf / 5;
226 		sc->sc_nrbuf = nbuf - sc->sc_ntbuf;
227 	}
228 
229 	aprint_normal(": address %s\n", ether_sprintf(sc->sc_enaddr));
230 	aprint_normal_dev(sc->sc_dev,
231 	    "%d receive buffers, %d transmit buffers\n",
232 	    sc->sc_nrbuf, sc->sc_ntbuf);
233 
234 	/* Make sure the chip is stopped. */
235 	lance_stop(ifp, 0);
236 
237 	/* claim 802.1q capability */
238 	sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
239 	/* Attach the interface. */
240 	if_attach(ifp);
241 	ether_ifattach(ifp, sc->sc_enaddr);
242 
243 	if (pmf_device_register1(sc->sc_dev, NULL, NULL, lance_shutdown))
244 		pmf_class_network_register(sc->sc_dev, ifp);
245 	else
246 		aprint_error_dev(sc->sc_dev,
247 		    "couldn't establish power handler\n");
248 
249 	sc->sc_rbufaddr = malloc(sc->sc_nrbuf * sizeof(int), M_DEVBUF,
250 					M_WAITOK);
251 	sc->sc_tbufaddr = malloc(sc->sc_ntbuf * sizeof(int), M_DEVBUF,
252 					M_WAITOK);
253 
254 	rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
255 			  RND_TYPE_NET, RND_FLAG_DEFAULT);
256 }
257 
258 void
259 lance_reset(struct lance_softc *sc)
260 {
261 	int s;
262 
263 	s = splnet();
264 	lance_init(&sc->sc_ethercom.ec_if);
265 	splx(s);
266 }
267 
268 void
269 lance_stop(struct ifnet *ifp, int disable)
270 {
271 	struct lance_softc *sc = ifp->if_softc;
272 
273 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
274 }
275 
276 /*
277  * Initialization of interface; set up initialization block
278  * and transmit/receive descriptor rings.
279  */
280 int
281 lance_init(struct ifnet *ifp)
282 {
283 	struct lance_softc *sc = ifp->if_softc;
284 	int timo;
285 	u_long a;
286 
287 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
288 	DELAY(100);
289 
290 	/* Newer LANCE chips have a reset register */
291 	if (sc->sc_hwreset)
292 		(*sc->sc_hwreset)(sc);
293 
294 	/* Set the correct byte swapping mode, etc. */
295 	(*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3);
296 
297 	/* Set up LANCE init block. */
298 	(*sc->sc_meminit)(sc);
299 
300 	/* Give LANCE the physical address of its init block. */
301 	a = sc->sc_addr + LE_INITADDR(sc);
302 	(*sc->sc_wrcsr)(sc, LE_CSR1, a);
303 	(*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16);
304 
305 	/* Try to initialize the LANCE. */
306 	DELAY(100);
307 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT);
308 
309 	/* Wait for initialization to finish. */
310 	for (timo = 100000; timo; timo--)
311 		if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON)
312 			break;
313 
314 	if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) {
315 		/* Start the LANCE. */
316 		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT);
317 		ifp->if_flags |= IFF_RUNNING;
318 		ifp->if_flags &= ~IFF_OACTIVE;
319 		ifp->if_timer = 0;
320 		(*sc->sc_start)(ifp);
321 	} else
322 		printf("%s: controller failed to initialize\n",
323 			device_xname(sc->sc_dev));
324 	if (sc->sc_hwinit)
325 		(*sc->sc_hwinit)(sc);
326 
327 	return (0);
328 }
329 
330 /*
331  * Routine to copy from mbuf chain to transmit buffer in
332  * network buffer memory.
333  */
334 int
335 lance_put(struct lance_softc *sc, int boff, struct mbuf *m)
336 {
337 	struct mbuf *n;
338 	int len, tlen = 0;
339 
340 	for (; m; m = n) {
341 		len = m->m_len;
342 		if (len == 0) {
343 			n = m_free(m);
344 			continue;
345 		}
346 		(*sc->sc_copytobuf)(sc, mtod(m, void *), boff, len);
347 		boff += len;
348 		tlen += len;
349 		n = m_free(m);
350 	}
351 	if (tlen < LEMINSIZE) {
352 		(*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen);
353 		tlen = LEMINSIZE;
354 	}
355 	return (tlen);
356 }
357 
358 /*
359  * Pull data off an interface.
360  * Len is length of data, with local net header stripped.
361  * We copy the data into mbufs.  When full cluster sized units are present
362  * we copy into clusters.
363  */
364 integrate struct mbuf *
365 lance_get(struct lance_softc *sc, int boff, int totlen)
366 {
367 	struct mbuf *m, *m0, *newm;
368 	int len;
369 
370 	MGETHDR(m0, M_DONTWAIT, MT_DATA);
371 	if (m0 == 0)
372 		return (0);
373 	m_set_rcvif(m0, &sc->sc_ethercom.ec_if);
374 	m0->m_pkthdr.len = totlen;
375 	len = MHLEN;
376 	m = m0;
377 
378 	while (totlen > 0) {
379 		if (totlen >= MINCLSIZE) {
380 			MCLGET(m, M_DONTWAIT);
381 			if ((m->m_flags & M_EXT) == 0)
382 				goto bad;
383 			len = MCLBYTES;
384 		}
385 
386 		if (m == m0) {
387 			char *newdata = (char *)
388 			    ALIGN(m->m_data + sizeof(struct ether_header)) -
389 			    sizeof(struct ether_header);
390 			len -= newdata - m->m_data;
391 			m->m_data = newdata;
392 		}
393 
394 		m->m_len = len = uimin(totlen, len);
395 		(*sc->sc_copyfrombuf)(sc, mtod(m, void *), boff, len);
396 		boff += len;
397 
398 		totlen -= len;
399 		if (totlen > 0) {
400 			MGET(newm, M_DONTWAIT, MT_DATA);
401 			if (newm == 0)
402 				goto bad;
403 			len = MLEN;
404 			m = m->m_next = newm;
405 		}
406 	}
407 
408 	return (m0);
409 
410 bad:
411 	m_freem(m0);
412 	return (0);
413 }
414 
415 /*
416  * Pass a packet to the higher levels.
417  */
418 void
419 lance_read(struct lance_softc *sc, int boff, int len)
420 {
421 	struct mbuf *m;
422 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
423 	struct ether_header *eh;
424 
425 	if (len <= sizeof(struct ether_header) ||
426 	    len > ((sc->sc_ethercom.ec_capenable & ETHERCAP_VLAN_MTU) ?
427 		ETHER_VLAN_ENCAP_LEN + ETHERMTU + sizeof(struct ether_header) :
428 		ETHERMTU + sizeof(struct ether_header))) {
429 #ifdef LEDEBUG
430 		printf("%s: invalid packet size %d; dropping\n",
431 		    device_xname(sc->sc_dev), len);
432 #endif
433 		if_statinc(ifp, if_ierrors);
434 		return;
435 	}
436 
437 	/* Pull packet off interface. */
438 	m = lance_get(sc, boff, len);
439 	if (m == 0) {
440 		if_statinc(ifp, if_ierrors);
441 		return;
442 	}
443 
444 	eh = mtod(m, struct ether_header *);
445 
446 #ifdef LANCE_REVC_BUG
447 	/*
448 	 * The old LANCE (Rev. C) chips have a bug which causes
449 	 * garbage to be inserted in front of the received packet.
450 	 * The work-around is to ignore packets with an invalid
451 	 * destination address (garbage will usually not match).
452 	 * Of course, this precludes multicast support...
453 	 */
454 	if (ETHER_CMP(eh->ether_dhost, sc->sc_enaddr) &&
455 	    ETHER_CMP(eh->ether_dhost, bcast_enaddr)) {
456 		m_freem(m);
457 		return;
458 	}
459 #endif
460 
461 	/*
462 	 * Some lance device does not present IFF_SIMPLEX behavior on multicast
463 	 * packets.  Make sure to drop it if it is from ourselves.
464 	 */
465 	if (!ETHER_CMP(eh->ether_shost, sc->sc_enaddr)) {
466 		m_freem(m);
467 		return;
468 	}
469 
470 	/* Pass the packet up. */
471 	if_percpuq_enqueue(ifp->if_percpuq, m);
472 }
473 
474 #undef	ifp
475 
476 void
477 lance_watchdog(struct ifnet *ifp)
478 {
479 	struct lance_softc *sc = ifp->if_softc;
480 
481 	log(LOG_ERR, "%s: device timeout\n", device_xname(sc->sc_dev));
482 	if_statinc(ifp, if_oerrors);
483 
484 	lance_reset(sc);
485 }
486 
487 int
488 lance_mediachange(struct ifnet *ifp)
489 {
490 	struct lance_softc *sc = ifp->if_softc;
491 
492 	if (sc->sc_mediachange)
493 		return ((*sc->sc_mediachange)(sc));
494 	return (0);
495 }
496 
497 void
498 lance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
499 {
500 	struct lance_softc *sc = ifp->if_softc;
501 
502 	if ((ifp->if_flags & IFF_UP) == 0)
503 		return;
504 
505 	ifmr->ifm_status = IFM_AVALID;
506 	if (sc->sc_havecarrier)
507 		ifmr->ifm_status |= IFM_ACTIVE;
508 
509 	if (sc->sc_mediastatus)
510 		(*sc->sc_mediastatus)(sc, ifmr);
511 }
512 
513 /*
514  * Process an ioctl request.
515  */
516 int
517 lance_ioctl(struct ifnet *ifp, u_long cmd, void *data)
518 {
519 	struct lance_softc *sc = ifp->if_softc;
520 	int s, error = 0;
521 
522 	s = splnet();
523 
524 	switch (cmd) {
525 	default:
526 		if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET)
527 			break;
528 		error = 0;
529 		if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
530 			break;
531 		if (ifp->if_flags & IFF_RUNNING) {
532 			/*
533 			 * Multicast list has changed; set the hardware filter
534 			 * accordingly.
535 			 */
536 			lance_reset(sc);
537 		}
538 		break;
539 
540 	}
541 
542 	splx(s);
543 	return (error);
544 }
545 
546 hide bool
547 lance_shutdown(device_t self, int howto)
548 {
549 	struct lance_softc *sc = device_private(self);
550 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
551 
552 	lance_stop(ifp, 0);
553 
554 	return true;
555 }
556 
557 /*
558  * Set up the logical address filter.
559  */
560 void
561 lance_setladrf(struct ethercom *ec, uint16_t *af)
562 {
563 	struct ifnet *ifp = &ec->ec_if;
564 	struct ether_multi *enm;
565 	uint32_t crc;
566 	struct ether_multistep step;
567 
568 	/*
569 	 * Set up multicast address filter by passing all multicast addresses
570 	 * through a crc generator, and then using the high order 6 bits as an
571 	 * index into the 64 bit logical address filter.  The high order bit
572 	 * selects the word, while the rest of the bits select the bit within
573 	 * the word.
574 	 */
575 
576 	if (ifp->if_flags & IFF_PROMISC)
577 		goto allmulti;
578 
579 	af[0] = af[1] = af[2] = af[3] = 0x0000;
580 
581 	ETHER_LOCK(ec);
582 	ETHER_FIRST_MULTI(step, ec, enm);
583 	while (enm != NULL) {
584 		if (ETHER_CMP(enm->enm_addrlo, enm->enm_addrhi)) {
585 			/*
586 			 * We must listen to a range of multicast addresses.
587 			 * For now, just accept all multicasts, rather than
588 			 * trying to set only those filter bits needed to match
589 			 * the range.  (At this time, the only use of address
590 			 * ranges is for IP multicast routing, for which the
591 			 * range is big enough to require all bits set.)
592 			 */
593 			ETHER_UNLOCK(ec);
594 			goto allmulti;
595 		}
596 
597 		crc = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);
598 
599 		/* Just want the 6 most significant bits. */
600 		crc >>= 26;
601 
602 		/* Set the corresponding bit in the filter. */
603 		af[crc >> 4] |= 1 << (crc & 0xf);
604 
605 		ETHER_NEXT_MULTI(step, enm);
606 	}
607 	ETHER_UNLOCK(ec);
608 	ifp->if_flags &= ~IFF_ALLMULTI;
609 	return;
610 
611 allmulti:
612 	ifp->if_flags |= IFF_ALLMULTI;
613 	af[0] = af[1] = af[2] = af[3] = 0xffff;
614 }
615 
616 /*
617  * Routines for accessing the transmit and receive buffers.
618  * The various CPU and adapter configurations supported by this
619  * driver require three different access methods for buffers
620  * and descriptors:
621  *	(1) contig (contiguous data; no padding),
622  *	(2) gap2 (two bytes of data followed by two bytes of padding),
623  *	(3) gap16 (16 bytes of data followed by 16 bytes of padding).
624  */
625 
626 /*
627  * contig: contiguous data with no padding.
628  *
629  * Buffers may have any alignment.
630  */
631 
632 void
633 lance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len)
634 {
635 	uint8_t *buf = sc->sc_mem;
636 
637 	/*
638 	 * Just call memcpy() to do the work.
639 	 */
640 	memcpy(buf + boff, from, len);
641 }
642 
643 void
644 lance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len)
645 {
646 	uint8_t *buf = sc->sc_mem;
647 
648 	/*
649 	 * Just call memcpy() to do the work.
650 	 */
651 	memcpy(to, buf + boff, len);
652 }
653 
654 void
655 lance_zerobuf_contig(struct lance_softc *sc, int boff, int len)
656 {
657 	uint8_t *buf = sc->sc_mem;
658 
659 	/*
660 	 * Just let memset() do the work
661 	 */
662 	memset(buf + boff, 0, len);
663 }
664 
665 #if 0
666 /*
667  * Examples only; duplicate these and tweak (if necessary) in
668  * machine-specific front-ends.
669  */
670 
671 /*
672  * gap2: two bytes of data followed by two bytes of pad.
673  *
674  * Buffers must be 4-byte aligned.  The code doesn't worry about
675  * doing an extra byte.
676  */
677 
678 void
679 lance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len)
680 {
681 	volatile void *buf = sc->sc_mem;
682 	void *from = fromv;
683 	volatile uint16_t *bptr;
684 
685 	if (boff & 0x1) {
686 		/* handle unaligned first byte */
687 		bptr = ((volatile uint16_t *)buf) + (boff - 1);
688 		*bptr = (*from++ << 8) | (*bptr & 0xff);
689 		bptr += 2;
690 		len--;
691 	} else
692 		bptr = ((volatile uint16_t *)buf) + boff;
693 	while (len > 1) {
694 		*bptr = (from[1] << 8) | (from[0] & 0xff);
695 		bptr += 2;
696 		from += 2;
697 		len -= 2;
698 	}
699 	if (len == 1)
700 		*bptr = (uint16_t)*from;
701 }
702 
703 void
704 lance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len)
705 {
706 	volatile void *buf = sc->sc_mem;
707 	void *to = tov;
708 	volatile uint16_t *bptr;
709 	uint16_t tmp;
710 
711 	if (boff & 0x1) {
712 		/* handle unaligned first byte */
713 		bptr = ((volatile uint16_t *)buf) + (boff - 1);
714 		*to++ = (*bptr >> 8) & 0xff;
715 		bptr += 2;
716 		len--;
717 	} else
718 		bptr = ((volatile uint16_t *)buf) + boff;
719 	while (len > 1) {
720 		tmp = *bptr;
721 		*to++ = tmp & 0xff;
722 		*to++ = (tmp >> 8) & 0xff;
723 		bptr += 2;
724 		len -= 2;
725 	}
726 	if (len == 1)
727 		*to = *bptr & 0xff;
728 }
729 
730 void
731 lance_zerobuf_gap2(struct lance_softc *sc, int boff, int len)
732 {
733 	volatile void *buf = sc->sc_mem;
734 	volatile uint16_t *bptr;
735 
736 	if ((unsigned int)boff & 0x1) {
737 		bptr = ((volatile uint16_t *)buf) + (boff - 1);
738 		*bptr &= 0xff;
739 		bptr += 2;
740 		len--;
741 	} else
742 		bptr = ((volatile uint16_t *)buf) + boff;
743 	while (len > 0) {
744 		*bptr = 0;
745 		bptr += 2;
746 		len -= 2;
747 	}
748 }
749 
750 /*
751  * gap16: 16 bytes of data followed by 16 bytes of pad.
752  *
753  * Buffers must be 32-byte aligned.
754  */
755 
756 void
757 lance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len)
758 {
759 	volatile uint8_t *buf = sc->sc_mem;
760 	void *from = fromv;
761 	uint8_t *bptr;
762 	int xfer;
763 
764 	bptr = buf + ((boff << 1) & ~0x1f);
765 	boff &= 0xf;
766 	xfer = uimin(len, 16 - boff);
767 	while (len > 0) {
768 		memcpy(bptr + boff, from, xfer);
769 		from += xfer;
770 		bptr += 32;
771 		boff = 0;
772 		len -= xfer;
773 		xfer = uimin(len, 16);
774 	}
775 }
776 
777 void
778 lance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len)
779 {
780 	volatile uint8_t *buf = sc->sc_mem;
781 	void *to = tov;
782 	uint8_t *bptr;
783 	int xfer;
784 
785 	bptr = buf + ((boff << 1) & ~0x1f);
786 	boff &= 0xf;
787 	xfer = uimin(len, 16 - boff);
788 	while (len > 0) {
789 		memcpy(to, bptr + boff, xfer);
790 		to += xfer;
791 		bptr += 32;
792 		boff = 0;
793 		len -= xfer;
794 		xfer = uimin(len, 16);
795 	}
796 }
797 
798 void
799 lance_zerobuf_gap16(struct lance_softc *sc, int boff, int len)
800 {
801 	volatile uint8_t *buf = sc->sc_mem;
802 	uint8_t *bptr;
803 	int xfer;
804 
805 	bptr = buf + ((boff << 1) & ~0x1f);
806 	boff &= 0xf;
807 	xfer = uimin(len, 16 - boff);
808 	while (len > 0) {
809 		memset(bptr + boff, 0, xfer);
810 		bptr += 32;
811 		boff = 0;
812 		len -= xfer;
813 		xfer = uimin(len, 16);
814 	}
815 }
816 #endif /* Example only */
817