No empty .Rs/.Re
[netbsd-mini2440.git] / sys / net / if_ecosubr.c
blob0b552e96b4485bb6825fce0138604b81f6fc2936
1 /* $NetBSD: if_ecosubr.c,v 1.32 2009/03/18 16:00:22 cegger Exp $ */
3 /*-
4 * Copyright (c) 2001 Ben Harris
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * Copyright (c) 1982, 1989, 1993
31 * The Regents of the University of California. All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
57 * @(#)if_ethersubr.c 8.2 (Berkeley) 4/4/96
60 #include <sys/cdefs.h>
61 __KERNEL_RCSID(0, "$NetBSD: if_ecosubr.c,v 1.32 2009/03/18 16:00:22 cegger Exp $");
63 #include "bpfilter.h"
64 #include "opt_inet.h"
65 #include "opt_pfil_hooks.h"
67 #include <sys/param.h>
68 #include <sys/errno.h>
69 #include <sys/kernel.h>
70 #include <sys/socket.h>
71 #include <sys/sockio.h>
72 #include <sys/syslog.h>
73 #include <sys/systm.h>
75 #include <net/if.h>
76 #include <net/if_dl.h>
77 #include <net/if_eco.h>
78 #include <net/if_types.h>
79 #include <net/netisr.h>
80 #include <net/route.h>
82 #if NBPFILTER > 0
83 #include <net/bpf.h>
84 #endif
86 #ifdef INET
87 #include <net/ethertypes.h>
88 #include <net/if_arp.h>
89 #include <netinet/in.h>
90 #include <netinet/in_var.h>
91 #endif
93 struct eco_retryparms {
94 int erp_delay;
95 int erp_count;
98 /* Default broadcast address */
99 static const uint8_t eco_broadcastaddr[] = { 0xff, 0xff };
101 static int eco_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
102 struct rtentry *);
103 static void eco_input(struct ifnet *, struct mbuf *);
104 static void eco_start(struct ifnet *);
105 static int eco_ioctl(struct ifnet *, u_long, void *);
107 static int eco_interestingp(struct ifnet *ifp, struct mbuf *m);
108 static struct mbuf *eco_immediate(struct ifnet *ifp, struct mbuf *m);
109 static struct mbuf *eco_ack(struct ifnet *ifp, struct mbuf *m);
111 static void eco_defer(struct ifnet *, struct mbuf *, int);
112 static void eco_retry_free(struct eco_retry *er);
113 static void eco_retry(void *);
115 void
116 eco_ifattach(struct ifnet *ifp, const uint8_t *lla)
118 struct ecocom *ec = (void *)ifp;
120 ifp->if_type = IFT_ECONET;
121 ifp->if_addrlen = ECO_ADDR_LEN;
122 ifp->if_hdrlen = ECO_HDR_LEN;
123 ifp->if_dlt = DLT_ECONET;
124 ifp->if_mtu = ECO_MTU;
126 ifp->if_output = eco_output;
127 ifp->if_input = eco_input;
128 ifp->if_start = eco_start;
129 ifp->if_ioctl = eco_ioctl;
131 /* ifp->if_baudrate...; */
132 if_set_sadl(ifp, lla, ECO_ADDR_LEN, FALSE);
134 ifp->if_broadcastaddr = eco_broadcastaddr;
136 LIST_INIT(&ec->ec_retries);
138 #if NBPFILTER > 0
139 bpfattach(ifp, ifp->if_dlt, ECO_HDR_LEN);
140 #endif
143 #define senderr(e) do { \
144 error = (e); \
145 goto bad; \
146 } while (/*CONSTCOND*/0)
149 eco_init(struct ifnet *ifp)
151 struct ecocom *ec = (struct ecocom *)ifp;
153 if ((ifp->if_flags & IFF_RUNNING) == 0)
154 ec->ec_state = ECO_UNKNOWN;
155 return 0;
158 void
159 eco_stop(struct ifnet *ifp, int disable)
161 struct ecocom *ec = (struct ecocom *)ifp;
163 while (!LIST_EMPTY(&ec->ec_retries))
164 eco_retry_free(LIST_FIRST(&ec->ec_retries));
167 static int
168 eco_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst,
169 struct rtentry *rt0)
171 struct eco_header ehdr, *eh;
172 int error;
173 struct mbuf *m = m0, *mcopy = NULL;
174 struct rtentry *rt;
175 int hdrcmplt;
176 int retry_delay, retry_count;
177 struct m_tag *mtag;
178 struct eco_retryparms *erp;
179 #ifdef INET
180 struct mbuf *m1;
181 struct arphdr *ah;
182 void *tha;
183 struct eco_arp *ecah;
184 #endif
185 ALTQ_DECL(struct altq_pktattr pktattr;)
187 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
188 senderr(ENETDOWN);
189 if ((rt = rt0) != NULL) {
190 if ((rt->rt_flags & RTF_UP) == 0) {
191 if ((rt0 = rt = rtalloc1(dst, 1)) != NULL) {
192 rt->rt_refcnt--;
193 if (rt->rt_ifp != ifp)
194 return (*rt->rt_ifp->if_output)
195 (ifp, m0, dst, rt);
196 } else
197 senderr(EHOSTUNREACH);
199 if ((rt->rt_flags & RTF_GATEWAY) && dst->sa_family != AF_NS) {
200 if (rt->rt_gwroute == 0)
201 goto lookup;
202 if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
203 rtfree(rt); rt = rt0;
204 lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
205 if ((rt = rt->rt_gwroute) == 0)
206 senderr(EHOSTUNREACH);
207 /* the "G" test below also prevents rt == rt0 */
208 if ((rt->rt_flags & RTF_GATEWAY) ||
209 (rt->rt_ifp != ifp)) {
210 rt->rt_refcnt--;
211 rt0->rt_gwroute = 0;
212 senderr(EHOSTUNREACH);
216 if (rt->rt_flags & RTF_REJECT)
217 if (rt->rt_rmx.rmx_expire == 0 ||
218 time_second < rt->rt_rmx.rmx_expire)
219 senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
222 * If the queueing discipline needs packet classification,
223 * do it before prepending link headers.
225 IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr);
227 hdrcmplt = 0;
228 retry_delay = hz / 16;
229 retry_count = 16;
230 switch (dst->sa_family) {
231 #ifdef INET
232 case AF_INET:
233 if (m->m_flags & M_BCAST)
234 memcpy(ehdr.eco_dhost, eco_broadcastaddr,
235 ECO_ADDR_LEN);
237 else if (!arpresolve(ifp, rt, m, dst, ehdr.eco_dhost))
238 return (0); /* if not yet resolved */
239 /* If broadcasting on a simplex interface, loopback a copy */
240 if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
241 mcopy = m_copy(m, 0, (int)M_COPYALL);
242 ehdr.eco_port = ECO_PORT_IP;
243 ehdr.eco_control = ECO_CTL_IP;
244 break;
246 case AF_ARP:
247 ah = mtod(m, struct arphdr *);
249 if (ntohs(ah->ar_pro) != ETHERTYPE_IP)
250 return EAFNOSUPPORT;
251 ehdr.eco_port = ECO_PORT_IP;
252 switch (ntohs(ah->ar_op)) {
253 case ARPOP_REQUEST:
254 ehdr.eco_control = ECO_CTL_ARP_REQUEST;
255 break;
256 case ARPOP_REPLY:
257 ehdr.eco_control = ECO_CTL_ARP_REPLY;
258 break;
259 default:
260 return EOPNOTSUPP;
263 if (m->m_flags & M_BCAST)
264 memcpy(ehdr.eco_dhost, eco_broadcastaddr,
265 ECO_ADDR_LEN);
266 else {
267 tha = ar_tha(ah);
268 if (tha == NULL)
269 return 0;
270 memcpy(ehdr.eco_dhost, tha, ECO_ADDR_LEN);
273 MGETHDR(m1, M_DONTWAIT, MT_DATA);
274 if (m1 == NULL)
275 senderr(ENOBUFS);
276 M_MOVE_PKTHDR(m1, m);
277 m1->m_len = sizeof(*ecah);
278 m1->m_pkthdr.len = m1->m_len;
279 MH_ALIGN(m1, m1->m_len);
280 ecah = mtod(m1, struct eco_arp *);
281 memset(ecah, 0, m1->m_len);
282 memcpy(ecah->ecar_spa, ar_spa(ah), ah->ar_pln);
283 memcpy(ecah->ecar_tpa, ar_tpa(ah), ah->ar_pln);
284 m_freem(m);
285 m = m1;
286 break;
287 #endif
288 case pseudo_AF_HDRCMPLT:
289 hdrcmplt = 1;
290 /* FALLTHROUGH */
291 case AF_UNSPEC:
292 ehdr = *(struct eco_header const *)dst->sa_data;
293 break;
294 default:
295 log(LOG_ERR, "%s: can't handle af%d\n", ifp->if_xname,
296 dst->sa_family);
297 senderr(EAFNOSUPPORT);
300 if (mcopy)
301 (void) looutput(ifp, mcopy, dst, rt);
304 * Add local net header. If no space in first mbuf,
305 * allocate another.
307 M_PREPEND(m, sizeof (struct eco_header), M_DONTWAIT);
308 if (m == 0)
309 senderr(ENOBUFS);
310 eh = mtod(m, struct eco_header *);
311 *eh = ehdr;
312 if (!hdrcmplt)
313 memcpy(eh->eco_shost, CLLADDR(ifp->if_sadl),
314 ECO_ADDR_LEN);
316 if ((m->m_flags & M_BCAST) == 0) {
317 /* Attach retry info to packet. */
318 mtag = m_tag_get(PACKET_TAG_ECO_RETRYPARMS,
319 sizeof(struct eco_retryparms), M_NOWAIT);
320 if (mtag == NULL)
321 senderr(ENOBUFS);
322 erp = (struct eco_retryparms *)(mtag + 1);
323 erp->erp_delay = retry_delay;
324 erp->erp_count = retry_count;
327 #ifdef PFIL_HOOKS
328 if ((error = pfil_run_hooks(&ifp->if_pfil, &m, ifp, PFIL_OUT)) != 0)
329 return (error);
330 if (m == NULL)
331 return (0);
332 #endif
334 return ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(&pktattr));
336 bad:
337 if (m)
338 m_freem(m);
339 return error;
343 * Given a scout, decide if we want the rest of the packet.
345 static int
346 eco_interestingp(struct ifnet *ifp, struct mbuf *m)
348 struct eco_header *eh;
350 eh = mtod(m, struct eco_header *);
351 switch (eh->eco_port) {
352 #ifdef INET
353 case ECO_PORT_IP:
354 return 1;
355 #endif
357 return 0;
360 static void
361 eco_input(struct ifnet *ifp, struct mbuf *m)
363 struct ifqueue *inq;
364 struct eco_header ehdr, *eh;
365 int s, i;
366 #ifdef INET
367 struct arphdr *ah;
368 struct eco_arp *ecah;
369 struct mbuf *m1;
370 void *tha;
371 #endif
373 #ifdef PFIL_HOOKS
374 if (pfil_run_hooks(&ifp->if_pfil, &m, ifp, PFIL_IN) != 0)
375 return;
376 if (m == NULL)
377 return;
378 #endif
380 /* Copy the mbuf header and trim it off. */
381 /* XXX use m_split? */
382 eh = &ehdr;
383 m_copydata(m, 0, ECO_HDR_LEN, (void *)eh);
384 m_adj(m, ECO_HDR_LEN);
386 switch (eh->eco_port) {
387 #ifdef INET
388 case ECO_PORT_IP:
389 switch (eh->eco_control) {
390 case ECO_CTL_IP:
391 schednetisr(NETISR_IP);
392 inq = &ipintrq;
393 break;
394 case ECO_CTL_ARP_REQUEST:
395 case ECO_CTL_ARP_REPLY:
397 * ARP over Econet is strange, because Econet only
398 * supports 8 bytes of data in a broadcast packet.
399 * To cope with this, only the source and destination
400 * IP addresses are actually contained in the packet
401 * and we have to infer the rest and build a fake ARP
402 * packet to pass upwards.
404 if (m->m_pkthdr.len != sizeof(struct eco_arp))
405 goto drop;
406 if (m->m_len < sizeof(struct eco_arp)) {
407 m = m_pullup(m, sizeof(struct eco_arp));
408 if (m == NULL) goto drop;
410 ecah = mtod(m, struct eco_arp *);
411 /* This code derived from arprequest() */
412 MGETHDR(m1, M_DONTWAIT, MT_DATA);
413 if (m1 == NULL)
414 goto drop;
415 M_MOVE_PKTHDR(m1, m);
416 m1->m_len = sizeof(*ah) + 2*sizeof(struct in_addr) +
417 2*ifp->if_data.ifi_addrlen;
418 m1->m_pkthdr.len = m1->m_len;
419 MH_ALIGN(m1, m1->m_len);
420 ah = mtod(m1, struct arphdr *);
421 memset((void *)ah, 0, m1->m_len);
422 ah->ar_pro = htons(ETHERTYPE_IP);
423 ah->ar_hln = ifp->if_data.ifi_addrlen;
424 ah->ar_pln = sizeof(struct in_addr);
425 if (eh->eco_control == ECO_CTL_ARP_REQUEST)
426 ah->ar_op = htons(ARPOP_REQUEST);
427 else
428 ah->ar_op = htons(ARPOP_REPLY);
429 tha = ar_tha(ah);
430 KASSERT(tha != NULL);
431 memcpy(ar_sha(ah), eh->eco_shost, ah->ar_hln);
432 memcpy(tha, eh->eco_dhost, ah->ar_hln);
433 memcpy(ar_spa(ah), ecah->ecar_spa, ah->ar_pln);
434 memcpy(ar_tpa(ah), ecah->ecar_tpa, ah->ar_pln);
435 m_freem(m);
436 m = m1;
437 schednetisr(NETISR_ARP);
438 inq = &arpintrq;
439 break;
440 case ECO_CTL_IPBCAST_REQUEST:
442 struct sockaddr_storage dst_store;
443 struct sockaddr *dst = (struct sockaddr *)&dst_store;
445 /* Queue? */
446 memcpy(eh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
447 eh->eco_control = ECO_CTL_IPBCAST_REPLY;
448 /* dst->sa_len??? */
449 dst->sa_family = AF_UNSPEC;
450 memcpy(dst->sa_data, eh, ECO_HDR_LEN);
451 ifp->if_output(ifp, m, dst, NULL);
452 return;
454 default:
455 printf("%s: unknown IP stn %s ctl 0x%02x len %d:",
456 ifp->if_xname, eco_sprintf(eh->eco_shost),
457 eh->eco_control, m->m_pkthdr.len);
458 if (m->m_len == 0) {
459 m = m_pullup(m, 1);
460 if (m == 0) {
461 printf("\n");
462 goto drop;
465 for (i = 0; i < m->m_len; i++)
466 printf(" %02x", mtod(m, uint8_t *)[i]);
467 printf("\n");
468 goto drop;
470 break;
471 #endif
472 default:
473 printf("%s: unknown port stn %s port 0x%02x ctl 0x%02x\n",
474 ifp->if_xname, eco_sprintf(eh->eco_shost),
475 eh->eco_port, eh->eco_control);
476 drop:
477 m_freem(m);
478 return;
481 s = splnet();
482 if (IF_QFULL(inq)) {
483 IF_DROP(inq);
484 m_freem(m);
485 } else
486 IF_ENQUEUE(inq, m);
487 splx(s);
490 static void
491 eco_start(struct ifnet *ifp)
493 struct ecocom *ec = (void *)ifp;
494 struct mbuf *m;
495 struct eco_header *eh;
497 if (ec->ec_state != ECO_IDLE) return;
498 IFQ_DEQUEUE(&ifp->if_snd, m);
499 if (m == NULL) return;
500 if (ec->ec_claimwire(ifp) == 0) {
501 eh = mtod(m, struct eco_header *);
502 if (eh->eco_port == ECO_PORT_IMMEDIATE) {
503 ec->ec_txframe(ifp, m);
504 ec->ec_state = ECO_IMMED_SENT;
505 } else if (eh->eco_dhost[0] == 255) {
506 ec->ec_txframe(ifp, m);
507 ec->ec_state = ECO_DONE;
508 } else {
509 ec->ec_packet = m;
510 m = m_copym(m, 0, ECO_HDR_LEN, M_DONTWAIT);
511 if (m == NULL) {
512 m_freem(ec->ec_packet);
513 ec->ec_packet = NULL;
514 return;
516 ec->ec_txframe(ifp, m);
517 ec->ec_state = ECO_SCOUT_SENT;
519 ifp->if_flags |= IFF_OACTIVE;
520 } else {
521 log(LOG_ERR, "%s: line jammed\n", ifp->if_xname);
522 m_freem(m);
526 static int
527 eco_ioctl(struct ifnet *ifp, u_long cmd, void *data)
529 struct ifaddr *ifa = (struct ifaddr *)data;
530 int error;
532 switch (cmd) {
533 case SIOCINITIFADDR:
534 ifp->if_flags |= IFF_UP;
535 if ((ifp->if_flags & IFF_RUNNING) == 0 &&
536 (error = (*ifp->if_init)(ifp)) != 0)
537 return error;
538 switch (ifa->ifa_addr->sa_family) {
539 #ifdef INET
540 case AF_INET:
541 arp_ifinit(ifp, ifa);
542 break;
543 #endif
544 default:
545 break;
547 return 0;
548 case SIOCSIFMTU:
549 if ((error = ifioctl_common(ifp, cmd, data)) != ENETRESET)
550 return error;
551 else if (ifp->if_flags & IFF_UP)
552 return (*ifp->if_init)(ifp);
553 else
554 return 0;
555 break;
556 case SIOCSIFFLAGS:
557 if ((error = ifioctl_common(ifp, cmd, data)) != 0)
558 return error;
559 switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) {
560 case IFF_RUNNING:
562 * If interface is marked down and it is running,
563 * then stop and disable it.
565 (*ifp->if_stop)(ifp, 1);
566 return 0;
567 case IFF_UP:
569 * If interface is marked up and it is stopped, then
570 * start it.
572 return (*ifp->if_init)(ifp);
573 case IFF_UP|IFF_RUNNING:
575 * Reset the interface to pick up changes in any other
576 * flags that affect the hardware state.
578 return (*ifp->if_init)(ifp);
579 case 0:
580 return 0;
582 break;
583 default:
584 return ifioctl_common(ifp, cmd, data);
587 return 0;
591 * Handle a raw Econet frame off the interface. The interface may be
592 * flag-filling for a response.
594 * May be called from IPL_NET or IPL_SOFTNET.
597 struct mbuf *
598 eco_inputframe(struct ifnet *ifp, struct mbuf *m)
600 struct ecocom *ec = (void *)ifp;
601 struct eco_header *eh, *eh0;
602 struct mbuf *m0;
603 struct mbuf *reply;
604 int len;
606 eh = mtod(m, struct eco_header *);
607 switch (ec->ec_state) {
608 case ECO_IDLE: /* Start of a packet (bcast, immed, scout) */
609 if (m->m_pkthdr.len < ECO_HDR_LEN) {
610 log(LOG_NOTICE, "%s: undersize scout\n",
611 ifp->if_xname);
612 goto drop;
614 if (memcmp(eh->eco_dhost, eco_broadcastaddr,
615 ECO_ADDR_LEN) == 0) {
616 /* Broadcast */
617 eco_input(ifp, m);
618 } else if (memcmp(eh->eco_dhost, CLLADDR(ifp->if_sadl),
619 ECO_ADDR_LEN) == 0) {
620 /* Unicast for us */
621 if (eh->eco_port == ECO_PORT_IMMEDIATE)
622 return eco_immediate(ifp, m);
623 else {
624 if (eco_interestingp(ifp, m)) {
625 reply = eco_ack(ifp, m);
626 if (reply == NULL) {
627 m_freem(m);
628 return NULL;
630 ec->ec_state = ECO_SCOUT_RCVD;
631 ec->ec_scout = m;
632 return reply;
633 } else {
634 m_freem(m);
635 return NULL;
638 } else
639 /* Not for us. Throw it away. */
640 m_freem(m);
641 break;
642 case ECO_SCOUT_RCVD: /* Packet data */
643 KASSERT(ec->ec_scout != NULL);
644 m0 = ec->ec_scout;
645 eh0 = mtod(m0, struct eco_header *);
646 if (m->m_pkthdr.len < ECO_SHDR_LEN ||
647 memcmp(eh->eco_shost, eh0->eco_shost, ECO_ADDR_LEN) != 0 ||
648 memcmp(eh->eco_dhost, eh0->eco_dhost, ECO_ADDR_LEN) != 0) {
649 log(LOG_NOTICE, "%s: garbled data packet header\n",
650 ifp->if_xname);
651 goto drop;
653 reply = eco_ack(ifp, m);
655 * Chop off the small header from this frame, and put
656 * the scout (which holds the control byte and port)
657 * in its place.
659 ec->ec_scout = NULL;
660 m_adj(m, ECO_SHDR_LEN);
661 len = m0->m_pkthdr.len + m->m_pkthdr.len;
662 m_cat(m0, m);
663 m0->m_pkthdr.len = len;
664 ec->ec_state = ECO_DONE;
665 eco_input(ifp, m0);
666 return reply;
667 case ECO_SCOUT_SENT: /* Scout ack */
668 KASSERT(ec->ec_packet != NULL);
669 m0 = ec->ec_packet;
670 eh0 = mtod(m0, struct eco_header *);
671 if (m->m_pkthdr.len != ECO_SHDR_LEN ||
672 memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
673 memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
674 log(LOG_NOTICE, "%s: garbled scout ack\n",
675 ifp->if_xname);
676 goto drop;
678 m_freem(m);
679 /* Chop out the control and port bytes. */
680 m0 = m_copym(ec->ec_packet, 0, ECO_SHDR_LEN, M_DONTWAIT);
681 if (m0 == NULL) {
682 m_freem(ec->ec_packet);
683 return NULL;
685 m = ec->ec_packet;
686 ec->ec_packet = m_copypacket(m, M_DONTWAIT);
687 if (ec->ec_packet == NULL) {
688 m_freem(m0);
689 m_freem(m);
690 return NULL;
692 m_adj(m, ECO_HDR_LEN);
693 len = m0->m_pkthdr.len + m->m_pkthdr.len;
694 m_cat(m0, m); /* Doesn't update packet header */
695 m0->m_pkthdr.len = len;
696 ec->ec_state = ECO_DATA_SENT;
697 return m0;
698 case ECO_DATA_SENT: /* Data ack */
699 KASSERT(ec->ec_packet != NULL);
700 m0 = ec->ec_packet;
701 eh0 = mtod(m0, struct eco_header *);
702 if (m->m_pkthdr.len != ECO_SHDR_LEN ||
703 memcmp(eh->eco_shost, eh0->eco_dhost, ECO_ADDR_LEN) != 0 ||
704 memcmp(eh->eco_dhost, eh0->eco_shost, ECO_ADDR_LEN) != 0) {
705 log(LOG_NOTICE, "%s: garbled data ack\n",
706 ifp->if_xname);
707 goto drop;
709 m_freem(m);
710 m_freem(ec->ec_packet);
711 ec->ec_packet = NULL;
712 ec->ec_state = ECO_DONE;
713 return NULL;
714 default:
715 drop:
716 m_freem(m);
717 break;
719 return NULL;
723 * Handle an immediate operation, and return the reply, or NULL not to reply.
724 * Frees the incoming mbuf.
727 static struct mbuf *
728 eco_immediate(struct ifnet *ifp, struct mbuf *m)
730 struct eco_header *eh, *reh;
731 struct mbuf *n;
732 static const uint8_t machinepeek_data[] = { 42, 0, 0, 1 };
734 eh = mtod(m, struct eco_header *);
735 switch (eh->eco_control) {
736 case ECO_CTL_MACHINEPEEK:
737 MGETHDR(n, M_DONTWAIT, MT_DATA);
738 if (n == NULL)
739 goto bad;
740 n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN + 4;
741 reh = mtod(n, struct eco_header *);
742 memcpy(reh->eco_dhost, eh->eco_shost,
743 ECO_ADDR_LEN);
744 memcpy(reh->eco_shost, CLLADDR(ifp->if_sadl),
745 ECO_ADDR_LEN);
746 memcpy(mtod(n, char *) + ECO_SHDR_LEN, machinepeek_data,
747 sizeof(machinepeek_data));
748 m_freem(m);
749 return n;
750 default:
751 bad:
752 m_freem(m);
753 return NULL;
758 * Generate (and return) an acknowledgement for a frame. Doesn't free the
759 * original frame, since it's probably needed elsewhere.
761 static struct mbuf *
762 eco_ack(struct ifnet *ifp, struct mbuf *m)
764 struct eco_header *eh, *reh;
765 struct mbuf *n;
767 eh = mtod(m, struct eco_header *);
768 MGETHDR(n, M_DONTWAIT, MT_DATA);
769 if (n == NULL)
770 return NULL;
771 n->m_len = n->m_pkthdr.len = ECO_SHDR_LEN;
772 reh = mtod(n, struct eco_header *);
773 memcpy(reh->eco_dhost, eh->eco_shost, ECO_ADDR_LEN);
774 memcpy(reh->eco_shost, CLLADDR(ifp->if_sadl), ECO_ADDR_LEN);
775 return n;
778 void
779 eco_inputidle(struct ifnet *ifp)
781 struct ecocom *ec = (void *)ifp;
782 struct mbuf *m;
783 struct m_tag *mtag;
784 struct eco_retryparms *erp;
786 switch (ec->ec_state) {
787 case ECO_SCOUT_SENT:
788 case ECO_DATA_SENT:
789 case ECO_IMMED_SENT:
790 /* Outgoing packet failed. Check if we should retry. */
791 m = ec->ec_packet;
792 ec->ec_packet = NULL;
793 mtag = m_tag_find(m, PACKET_TAG_ECO_RETRYPARMS, NULL);
794 if (mtag == NULL)
795 m_freem(m);
796 else {
797 erp = (struct eco_retryparms *)(mtag + 1);
798 if (--erp->erp_count > 0)
799 eco_defer(ifp, m, erp->erp_delay);
800 else
801 printf("%s: pkt failed\n", ifp->if_xname);
803 break;
804 case ECO_SCOUT_RCVD:
805 m_freem(ec->ec_scout);
806 ec->ec_scout = NULL;
807 break;
808 default:
809 break;
811 ec->ec_state = ECO_IDLE;
812 ifp->if_start(ifp);
816 * Convert Econet address to printable (loggable) representation.
818 char *
819 eco_sprintf(const uint8_t *ea)
821 static char buf[8];
823 if (ea[1] == 0)
824 snprintf(buf, sizeof(buf), "%d", ea[0]);
825 else
826 snprintf(buf, sizeof(buf), "%d.%d", ea[1], ea[0]);
827 return buf;
831 * Econet retry handling.
833 static void
834 eco_defer(struct ifnet *ifp, struct mbuf *m, int retry_delay)
836 struct ecocom *ec = (struct ecocom *)ifp;
837 struct eco_retry *er;
838 int s;
840 er = malloc(sizeof(*er), M_TEMP, M_NOWAIT);
841 if (er == NULL) {
842 m_freem(m);
843 return;
845 callout_init(&er->er_callout, 0);
846 er->er_packet = m;
847 er->er_ifp = ifp;
848 s = splnet();
849 LIST_INSERT_HEAD(&ec->ec_retries, er, er_link);
850 splx(s);
851 callout_reset(&er->er_callout, retry_delay, eco_retry, er);
854 static void
855 eco_retry_free(struct eco_retry *er)
857 int s;
859 callout_stop(&er->er_callout);
860 m_freem(er->er_packet);
861 s = splnet();
862 LIST_REMOVE(er, er_link);
863 splx(s);
864 callout_destroy(&er->er_callout);
865 free(er, M_TEMP);
868 static void
869 eco_retry(void *arg)
871 struct eco_retry *er = arg;
872 struct mbuf *m;
873 struct ifnet *ifp;
875 ifp = er->er_ifp;
876 m = er->er_packet;
877 LIST_REMOVE(er, er_link);
878 (void)ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(NULL));
879 free(er, M_TEMP);