xref: /dragonfly/sys/dev/netif/lnc/lance.c (revision cf37dc20)
1 /*	$NetBSD: lance.c,v 1.34 2005/12/24 20:27:30 perry Exp $	*/
2 /*	$FreeBSD: src/sys/dev/le/lance.c,v 1.2 2006/05/16 21:04:01 marius Exp $	*/
3 
4 
5 /*-
6  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
11  * Simulation Facility, NASA Ames Research Center.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *	This product includes software developed by the NetBSD
24  *	Foundation, Inc. and its contributors.
25  * 4. Neither the name of The NetBSD Foundation nor the names of its
26  *    contributors may be used to endorse or promote products derived
27  *    from this software without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39  * POSSIBILITY OF SUCH DAMAGE.
40  */
41 
42 /*-
43  * Copyright (c) 1992, 1993
44  *	The Regents of the University of California.  All rights reserved.
45  *
46  * This code is derived from software contributed to Berkeley by
47  * Ralph Campbell and Rick Macklem.
48  *
49  * Redistribution and use in source and binary forms, with or without
50  * modification, are permitted provided that the following conditions
51  * are met:
52  * 1. Redistributions of source code must retain the above copyright
53  *    notice, this list of conditions and the following disclaimer.
54  * 2. Redistributions in binary form must reproduce the above copyright
55  *    notice, this list of conditions and the following disclaimer in the
56  *    documentation and/or other materials provided with the distribution.
57  * 3. Neither the name of the University nor the names of its contributors
58  *    may be used to endorse or promote products derived from this software
59  *    without specific prior written permission.
60  *
61  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
62  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
63  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
65  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
66  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
67  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
69  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
70  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
71  * SUCH DAMAGE.
72  *
73  *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
74  */
75 
76 #include <sys/param.h>
77 #include <sys/bus.h>
78 #include <sys/endian.h>
79 #include <sys/lock.h>
80 #include <sys/mbuf.h>
81 #include <sys/socket.h>
82 #include <sys/sockio.h>
83 
84 #include <net/ethernet.h>
85 #include <net/if.h>
86 #include <net/ifq_var.h>
87 #include <net/if_arp.h>
88 #include <net/if_dl.h>
89 #include <net/if_media.h>
90 #include <net/if_types.h>
91 #include <net/vlan/if_vlan_var.h>
92 
93 #include <dev/netif/lnc/lancereg.h>
94 #include <dev/netif/lnc/lancevar.h>
95 
96 devclass_t le_devclass;
97 
98 static void lance_start(struct ifnet *, struct ifaltq_subque *);
99 static void lance_init(void *);
100 static void lance_watchdog(struct ifnet *);
101 static int lance_mediachange(struct ifnet *);
102 static void lance_mediastatus(struct ifnet *, struct ifmediareq *);
103 static int lance_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
104 
105 void
106 lance_config(struct lance_softc *sc, const char* name, int unit)
107 {
108 	struct ifnet *ifp;
109 	int i, nbuf;
110 
111 	ifp = sc->ifp = &sc->sc_if;
112 
113 	/* Initialize ifnet structure. */
114 	ifp->if_softc = sc;
115 	if_initname(ifp, name, unit);
116 	ifp->if_start = lance_start;
117 	ifp->if_ioctl = lance_ioctl;
118 	ifp->if_watchdog = lance_watchdog;
119 	ifp->if_init = lance_init;
120 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
121 #ifdef LANCE_REVC_BUG
122 	ifp->if_flags &= ~IFF_MULTICAST;
123 #endif
124 	ifp->if_baudrate = IF_Mbps(10);
125 	ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
126 	ifq_set_ready(&ifp->if_snd);
127 
128 	/* Attach the interface. */
129 	ether_ifattach(ifp, sc->sc_enaddr, NULL);
130 
131 	/* Claim 802.1q capability. */
132 	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
133 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
134 	ifp->if_capenable |= IFCAP_VLAN_MTU;
135 
136 	/* Initialize ifmedia structures. */
137 	ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus);
138 	if (sc->sc_supmedia != NULL) {
139 		for (i = 0; i < sc->sc_nsupmedia; i++)
140 			ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], 0, NULL);
141 		ifmedia_set(&sc->sc_media, sc->sc_defaultmedia);
142 	} else {
143 		ifmedia_add(&sc->sc_media,
144 		    IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0), 0, NULL);
145 		ifmedia_set(&sc->sc_media,
146 		    IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0));
147 	}
148 
149 	switch (sc->sc_memsize) {
150 	case 8192:
151 		sc->sc_nrbuf = 4;
152 		sc->sc_ntbuf = 1;
153 		break;
154 	case 16384:
155 		sc->sc_nrbuf = 8;
156 		sc->sc_ntbuf = 2;
157 		break;
158 	case 32768:
159 		sc->sc_nrbuf = 16;
160 		sc->sc_ntbuf = 4;
161 		break;
162 	case 65536:
163 		sc->sc_nrbuf = 32;
164 		sc->sc_ntbuf = 8;
165 		break;
166 	case 131072:
167 		sc->sc_nrbuf = 64;
168 		sc->sc_ntbuf = 16;
169 		break;
170 	case 262144:
171 		sc->sc_nrbuf = 128;
172 		sc->sc_ntbuf = 32;
173 		break;
174 	default:
175 		/* weird memory size; cope with it */
176 		nbuf = sc->sc_memsize / LEBLEN;
177 		sc->sc_ntbuf = nbuf / 5;
178 		sc->sc_nrbuf = nbuf - sc->sc_ntbuf;
179 	}
180 
181 	if_printf(ifp, "%d receive buffers, %d transmit buffers\n",
182 	    sc->sc_nrbuf, sc->sc_ntbuf);
183 
184 	/* Make sure the chip is stopped. */
185 	lance_stop(sc);
186 }
187 
188 void
189 lance_suspend(struct lance_softc *sc)
190 {
191 
192 	lwkt_serialize_enter(sc->ifp->if_serializer);
193 	lance_stop(sc);
194 	lwkt_serialize_exit(sc->ifp->if_serializer);
195 }
196 
197 void
198 lance_resume(struct lance_softc *sc)
199 {
200 
201 	lwkt_serialize_enter(sc->ifp->if_serializer);
202 	if (sc->ifp->if_flags & IFF_UP)
203 		lance_init_locked(sc);
204 	lwkt_serialize_exit(sc->ifp->if_serializer);
205 }
206 
207 static void
208 lance_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
209 {
210 	struct lance_softc *sc = ifp->if_softc;
211 
212 	ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
213 	(*sc->sc_start_locked)(sc);
214 }
215 
216 void
217 lance_stop(struct lance_softc *sc)
218 {
219 	struct ifnet *ifp = sc->ifp;
220 
221 	/*
222 	 * Mark the interface down and cancel the watchdog timer.
223 	 */
224 	ifp->if_flags &= ~IFF_RUNNING;
225 	ifq_clr_oactive(&ifp->if_snd);
226 	ifp->if_timer = 0;
227 
228 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
229 }
230 
231 static void
232 lance_init(void *xsc)
233 {
234 	struct lance_softc *sc = (struct lance_softc *)xsc;
235 
236 	crit_enter();
237 	lance_init_locked(sc);
238 	crit_exit();
239 }
240 
241 /*
242  * Initialization of interface; set up initialization block
243  * and transmit/receive descriptor rings.
244  */
245 void
246 lance_init_locked(struct lance_softc *sc)
247 {
248 	struct ifnet *ifp = sc->ifp;
249 	u_long a;
250 	int timo;
251 
252 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
253 	DELAY(100);
254 
255 	/* Newer LANCE chips have a reset register. */
256 	if (sc->sc_hwreset)
257 		(*sc->sc_hwreset)(sc);
258 
259 	/* Set the correct byte swapping mode, etc. */
260 	(*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3);
261 
262 	/*
263 	 * Update our private copy of the Ethernet address.
264 	 * We NEED the copy so we can ensure its alignment!
265 	 */
266 	memcpy(sc->sc_enaddr, IF_LLADDR(ifp), ETHER_ADDR_LEN);
267 
268 	/* Set up LANCE init block. */
269 	(*sc->sc_meminit)(sc);
270 
271 	/* Give LANCE the physical address of its init block. */
272 	a = sc->sc_addr + LE_INITADDR(sc);
273 	(*sc->sc_wrcsr)(sc, LE_CSR1, a & 0xffff);
274 	(*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16);
275 
276 	/* Try to initialize the LANCE. */
277 	DELAY(100);
278 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT);
279 
280 	/* Wait for initialization to finish. */
281 	for (timo = 100000; timo; timo--)
282 		if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON)
283 			break;
284 
285 	/* Set the current media. */
286 	if (sc->sc_mediachange)
287 		(*sc->sc_mediachange)(sc);
288 
289 	if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) {
290 		/* Start the LANCE. */
291 		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT);
292 		ifp->if_flags |= IFF_RUNNING;
293 		ifq_clr_oactive(&ifp->if_snd);
294 		ifp->if_timer = 0;
295 		if_devstart(ifp);
296 	} else
297 		if_printf(ifp, "controller failed to initialize\n");
298 
299 	if (sc->sc_hwinit)
300 		(*sc->sc_hwinit)(sc);
301 }
302 
303 /*
304  * Routine to copy from mbuf chain to transmit buffer in
305  * network buffer memory.
306  */
307 int
308 lance_put(struct lance_softc *sc, int boff, struct mbuf *m)
309 {
310 	int tlen = 0;
311 
312 	for (; m; m = m_free(m)) {
313 		if (m->m_len == 0)
314 			continue;
315 		(*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, m->m_len);
316 		boff += m->m_len;
317 		tlen += m->m_len;
318 	}
319 	if (tlen < LEMINSIZE) {
320 		(*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen);
321 		tlen = LEMINSIZE;
322 	}
323 	return (tlen);
324 }
325 
326 /*
327  * Pull data off an interface.
328  * Len is length of data, with local net header stripped.
329  * We copy the data into mbufs.  When full cluster sized units are present
330  * we copy into clusters.
331  */
332 struct mbuf *
333 lance_get(struct lance_softc *sc, int boff, int totlen)
334 {
335 	struct ifnet *ifp = sc->ifp;
336 	struct mbuf *m0 = NULL, *newm;
337 	struct mbuf *m = NULL;
338 	int mlen;
339 
340 	if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) {
341 #ifdef LEDEBUG
342 		if_printf(ifp, "invalid packet size %d; dropping\n", totlen);
343 #endif
344 		return (NULL);
345 	}
346 
347 	do {
348 		newm = m_getl(totlen, M_NOWAIT, MT_DATA,
349 			      m0 == NULL ? M_PKTHDR : 0, &mlen);
350 		if (newm == NULL)
351 			goto bad;
352 
353 		if (m0 == NULL) {
354 			caddr_t newdata;
355 
356 			m0 = newm;
357 			m0->m_pkthdr.rcvif = ifp;
358 			m0->m_pkthdr.len = totlen;
359 			newdata = (caddr_t)
360 			    ALIGN(m0->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN;
361 			mlen -= newdata - m0->m_data;
362 			m0->m_data = newdata;
363 		} else {
364 			m->m_next = newm;
365 		}
366 		m = newm;
367 
368 		m->m_len = min(totlen, mlen);
369 		(*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, m->m_len);
370 		boff += m->m_len;
371 		totlen -= m->m_len;
372 	} while (totlen > 0);
373 
374 	return (m0);
375 
376 bad:
377 	m_freem(m0);
378 	return (NULL);
379 }
380 
381 static void
382 lance_watchdog(struct ifnet *ifp)
383 {
384 	struct lance_softc *sc = ifp->if_softc;
385 
386 	if_printf(ifp, "device timeout\n");
387 	IFNET_STAT_INC(ifp, oerrors, 1);
388 	lance_init_locked(sc);
389 }
390 
391 static int
392 lance_mediachange(struct ifnet *ifp)
393 {
394 	struct lance_softc *sc = ifp->if_softc;
395 	int error;
396 
397 	if (sc->sc_mediachange) {
398 		error = (*sc->sc_mediachange)(sc);
399 		return (error);
400 	}
401 	return (0);
402 }
403 
404 static void
405 lance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
406 {
407 	struct lance_softc *sc = ifp->if_softc;
408 
409 	if (!(ifp->if_flags & IFF_UP)) {
410 		return;
411 	}
412 
413 	ifmr->ifm_status = IFM_AVALID;
414 	if (sc->sc_flags & LE_CARRIER)
415 		ifmr->ifm_status |= IFM_ACTIVE;
416 
417 	if (sc->sc_mediastatus)
418 		(*sc->sc_mediastatus)(sc, ifmr);
419 }
420 
421 /*
422  * Process an ioctl request.
423  */
424 static int
425 lance_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
426 {
427 	struct lance_softc *sc = ifp->if_softc;
428 	struct ifreq *ifr = (struct ifreq *)data;
429 	int error = 0;
430 
431 	crit_enter();
432 
433 	switch (cmd) {
434 	case SIOCSIFFLAGS:
435 		if (ifp->if_flags & IFF_PROMISC) {
436 			if (!(sc->sc_flags & LE_PROMISC)) {
437 				sc->sc_flags |= LE_PROMISC;
438 				lance_init_locked(sc);
439 			}
440 		} else if (sc->sc_flags & LE_PROMISC) {
441 			sc->sc_flags &= ~LE_PROMISC;
442 			lance_init_locked(sc);
443 		}
444 
445 		if ((ifp->if_flags & IFF_ALLMULTI) &&
446 		    !(sc->sc_flags & LE_ALLMULTI)) {
447 			sc->sc_flags |= LE_ALLMULTI;
448 			lance_init_locked(sc);
449 		} else if (!(ifp->if_flags & IFF_ALLMULTI) &&
450 		    (sc->sc_flags & LE_ALLMULTI)) {
451 			sc->sc_flags &= ~LE_ALLMULTI;
452 			lance_init_locked(sc);
453 		}
454 
455 		if (!(ifp->if_flags & IFF_UP) &&
456 		    ifp->if_flags & IFF_RUNNING) {
457 			/*
458 			 * If interface is marked down and it is running, then
459 			 * stop it.
460 			 */
461 			lance_stop(sc);
462 		} else if (ifp->if_flags & IFF_UP &&
463 		    !(ifp->if_flags & IFF_RUNNING)) {
464 			/*
465 			 * If interface is marked up and it is stopped, then
466 			 * start it.
467 			 */
468 			lance_init_locked(sc);
469 		}
470 #ifdef LEDEBUG
471 		if (ifp->if_flags & IFF_DEBUG)
472 			sc->sc_flags |= LE_DEBUG;
473 		else
474 			sc->sc_flags &= ~LE_DEBUG;
475 #endif
476 		break;
477 
478 	case SIOCADDMULTI:
479 	case SIOCDELMULTI:
480 		/*
481 		 * Multicast list has changed; set the hardware filter
482 		 * accordingly.
483 		 */
484 		if (ifp->if_flags & IFF_RUNNING)
485 			lance_init_locked(sc);
486 		break;
487 
488 	case SIOCGIFMEDIA:
489 	case SIOCSIFMEDIA:
490 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
491 		break;
492 
493 	default:
494 		error = ether_ioctl(ifp, cmd, data);
495 		break;
496 	}
497 
498 	crit_exit();
499 
500 	return (error);
501 }
502 
503 /*
504  * Set up the logical address filter.
505  */
506 void
507 lance_setladrf(struct lance_softc *sc, uint16_t *af)
508 {
509 	struct ifnet *ifp = sc->ifp;
510 	struct ifmultiaddr *ifma;
511 	uint32_t crc;
512 
513 	/*
514 	 * Set up multicast address filter by passing all multicast addresses
515 	 * through a crc generator, and then using the high order 6 bits as an
516 	 * index into the 64 bit logical address filter.  The high order bit
517 	 * selects the word, while the rest of the bits select the bit within
518 	 * the word.
519 	 */
520 
521 	if (ifp->if_flags & IFF_PROMISC || sc->sc_flags & LE_ALLMULTI) {
522 		af[0] = af[1] = af[2] = af[3] = 0xffff;
523 		return;
524 	}
525 
526 	af[0] = af[1] = af[2] = af[3] = 0x0000;
527 
528 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
529 		if (ifma->ifma_addr->sa_family != AF_LINK)
530 			continue;
531 
532 		crc = ether_crc32_le(LLADDR((struct sockaddr_dl *)
533 		    ifma->ifma_addr), ETHER_ADDR_LEN);
534 
535 		/* Just want the 6 most significant bits. */
536 		crc >>= 26;
537 
538 		/* Set the corresponding bit in the filter. */
539 		af[crc >> 4] |= LE_HTOLE16(1 << (crc & 0xf));
540 	}
541 }
542 
543 /*
544  * Routines for accessing the transmit and receive buffers.
545  * The various CPU and adapter configurations supported by this
546  * driver require three different access methods for buffers
547  * and descriptors:
548  *	(1) contig (contiguous data; no padding),
549  *	(2) gap2 (two bytes of data followed by two bytes of padding),
550  *	(3) gap16 (16 bytes of data followed by 16 bytes of padding).
551  */
552 
553 /*
554  * contig: contiguous data with no padding.
555  *
556  * Buffers may have any alignment.
557  */
558 
559 void
560 lance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len)
561 {
562 	volatile caddr_t buf = sc->sc_mem;
563 
564 	/*
565 	 * Just call memcpy() to do the work.
566 	 */
567 	memcpy(buf + boff, from, len);
568 }
569 
570 void
571 lance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len)
572 {
573 	volatile caddr_t buf = sc->sc_mem;
574 
575 	/*
576 	 * Just call memcpy() to do the work.
577 	 */
578 	memcpy(to, buf + boff, len);
579 }
580 
581 void
582 lance_zerobuf_contig(struct lance_softc *sc, int boff, int len)
583 {
584 	volatile caddr_t buf = sc->sc_mem;
585 
586 	/*
587 	 * Just let memset() do the work
588 	 */
589 	memset(buf + boff, 0, len);
590 }
591 
592 #if 0
593 /*
594  * Examples only; duplicate these and tweak (if necessary) in
595  * machine-specific front-ends.
596  */
597 
598 /*
599  * gap2: two bytes of data followed by two bytes of pad.
600  *
601  * Buffers must be 4-byte aligned.  The code doesn't worry about
602  * doing an extra byte.
603  */
604 
605 static void
606 lance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len)
607 {
608 	volatile caddr_t buf = sc->sc_mem;
609 	caddr_t from = fromv;
610 	volatile uint16_t *bptr;
611 
612 	if (boff & 0x1) {
613 		/* Handle unaligned first byte. */
614 		bptr = ((volatile uint16_t *)buf) + (boff - 1);
615 		*bptr = (*from++ << 8) | (*bptr & 0xff);
616 		bptr += 2;
617 		len--;
618 	} else
619 		bptr = ((volatile uint16_t *)buf) + boff;
620 	while (len > 1) {
621 		*bptr = (from[1] << 8) | (from[0] & 0xff);
622 		bptr += 2;
623 		from += 2;
624 		len -= 2;
625 	}
626 	if (len == 1)
627 		*bptr = (uint16_t)*from;
628 }
629 
630 static void
631 lance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len)
632 {
633 	volatile caddr_t buf = sc->sc_mem;
634 	caddr_t to = tov;
635 	volatile uint16_t *bptr;
636 	uint16_t tmp;
637 
638 	if (boff & 0x1) {
639 		/* Handle unaligned first byte. */
640 		bptr = ((volatile uint16_t *)buf) + (boff - 1);
641 		*to++ = (*bptr >> 8) & 0xff;
642 		bptr += 2;
643 		len--;
644 	} else
645 		bptr = ((volatile uint16_t *)buf) + boff;
646 	while (len > 1) {
647 		tmp = *bptr;
648 		*to++ = tmp & 0xff;
649 		*to++ = (tmp >> 8) & 0xff;
650 		bptr += 2;
651 		len -= 2;
652 	}
653 	if (len == 1)
654 		*to = *bptr & 0xff;
655 }
656 
657 static void
658 lance_zerobuf_gap2(struct lance_softc *sc, int boff, int len)
659 {
660 	volatile caddr_t buf = sc->sc_mem;
661 	volatile uint16_t *bptr;
662 
663 	if ((unsigned)boff & 0x1) {
664 		bptr = ((volatile uint16_t *)buf) + (boff - 1);
665 		*bptr &= 0xff;
666 		bptr += 2;
667 		len--;
668 	} else
669 		bptr = ((volatile uint16_t *)buf) + boff;
670 	while (len > 0) {
671 		*bptr = 0;
672 		bptr += 2;
673 		len -= 2;
674 	}
675 }
676 
677 /*
678  * gap16: 16 bytes of data followed by 16 bytes of pad.
679  *
680  * Buffers must be 32-byte aligned.
681  */
682 
683 static void
684 lance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len)
685 {
686 	volatile caddr_t buf = sc->sc_mem;
687 	caddr_t bptr, from = fromv;
688 	int xfer;
689 
690 	bptr = buf + ((boff << 1) & ~0x1f);
691 	boff &= 0xf;
692 	xfer = min(len, 16 - boff);
693 	while (len > 0) {
694 		memcpy(bptr + boff, from, xfer);
695 		from += xfer;
696 		bptr += 32;
697 		boff = 0;
698 		len -= xfer;
699 		xfer = min(len, 16);
700 	}
701 }
702 
703 static void
704 lance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len)
705 {
706 	volatile caddr_t buf = sc->sc_mem;
707 	caddr_t bptr, to = tov;
708 	int xfer;
709 
710 	bptr = buf + ((boff << 1) & ~0x1f);
711 	boff &= 0xf;
712 	xfer = min(len, 16 - boff);
713 	while (len > 0) {
714 		memcpy(to, bptr + boff, xfer);
715 		to += xfer;
716 		bptr += 32;
717 		boff = 0;
718 		len -= xfer;
719 		xfer = min(len, 16);
720 	}
721 }
722 
723 static void
724 lance_zerobuf_gap16(struct lance_softc *sc, int boff, int len)
725 {
726 	volatile caddr_t buf = sc->sc_mem;
727 	caddr_t bptr;
728 	int xfer;
729 
730 	bptr = buf + ((boff << 1) & ~0x1f);
731 	boff &= 0xf;
732 	xfer = min(len, 16 - boff);
733 	while (len > 0) {
734 		memset(bptr + boff, 0, xfer);
735 		bptr += 32;
736 		boff = 0;
737 		len -= xfer;
738 		xfer = min(len, 16);
739 	}
740 }
741 #endif /* Example only */
742