ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.17
Committed: 2005-05-13T17:43:38Z (19 years, 6 months ago) by gbeauche
Branch: MAIN
Changes since 1.16: +24 -4 lines
Log Message:
Use a custom poll_fd() function implemented as select() on platforms that
don't support poll() natively, e.g. MacOS X and some older BSDs.

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD)
3     *
4 gbeauche 1.13 * Basilisk II (C) 1997-2005 Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "sysdeps.h"
22    
23 gbeauche 1.17 #ifdef HAVE_SYS_POLL_H
24     #include <sys/poll.h>
25     #endif
26 cebix 1.1 #include <sys/ioctl.h>
27 cebix 1.3 #include <sys/socket.h>
28 gbeauche 1.9 #include <sys/wait.h>
29 cebix 1.3 #include <netinet/in.h>
30 cebix 1.1 #include <pthread.h>
31     #include <semaphore.h>
32     #include <errno.h>
33     #include <stdio.h>
34 cebix 1.3 #include <map>
35 cebix 1.2
36 gbeauche 1.17 #if defined(__FreeBSD__) || defined(sgi) || (defined(__APPLE__) && defined(__MACH__))
37 cebix 1.1 #include <net/if.h>
38     #endif
39    
40 gbeauche 1.9 #if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H)
41     #include <linux/if.h>
42     #include <linux/if_tun.h>
43     #endif
44    
45     #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_IF_TUN_H)
46     #include <net/if.h>
47     #include <net/if_tun.h>
48     #endif
49    
50 gbeauche 1.16 // XXX: slirp works on 64-bit platforms, sometimes
51 gbeauche 1.15 #define HAVE_SLIRP 1
52 gbeauche 1.16 #include "libslirp.h"
53 gbeauche 1.15
54 cebix 1.1 #include "cpu_emulation.h"
55     #include "main.h"
56     #include "macos_util.h"
57     #include "prefs.h"
58     #include "user_strings.h"
59     #include "ether.h"
60     #include "ether_defs.h"
61    
62 cebix 1.3 #ifndef NO_STD_NAMESPACE
63     using std::map;
64     #endif
65    
66 cebix 1.1 #define DEBUG 0
67     #include "debug.h"
68    
69     #define MONITOR 0
70    
71    
72 gbeauche 1.9 // Ethernet device types
73     enum {
74     NET_IF_SHEEPNET,
75     NET_IF_ETHERTAP,
76     NET_IF_TUNTAP,
77 gbeauche 1.15 NET_IF_SLIRP
78 gbeauche 1.9 };
79    
80     // Constants
81     static const char ETHERCONFIG_FILE_NAME[] = DATADIR "/tunconfig";
82    
83 cebix 1.1 // Global variables
84     static int fd = -1; // fd of sheep_net device
85     static pthread_t ether_thread; // Packet reception thread
86     static pthread_attr_t ether_thread_attr; // Packet reception thread attributes
87     static bool thread_active = false; // Flag: Packet reception thread installed
88     static sem_t int_ack; // Interrupt acknowledge semaphore
89 cebix 1.2 static bool udp_tunnel; // Flag: UDP tunnelling active, fd is the socket descriptor
90 gbeauche 1.9 static int net_if_type = -1; // Ethernet device type
91 gbeauche 1.10 static char *net_if_name = NULL; // TUN/TAP device name
92 gbeauche 1.9 static const char *net_if_script = NULL; // Network config script
93 gbeauche 1.15 static pthread_t slirp_thread; // Slirp reception thread
94     static bool slirp_thread_active = false; // Flag: Slirp reception threadinstalled
95     static int slirp_output_fd = -1; // fd of slirp output pipe
96 cebix 1.1
97 cebix 1.3 // Attached network protocols, maps protocol type to MacOS handler address
98     static map<uint16, uint32> net_protocols;
99    
100 cebix 1.1 // Prototypes
101     static void *receive_func(void *arg);
102 gbeauche 1.15 static void *slirp_receive_func(void *arg);
103 gbeauche 1.17 static int poll_fd(int fd);
104 cebix 1.1
105    
106     /*
107 cebix 1.2 * Start packet reception thread
108     */
109    
110     static bool start_thread(void)
111     {
112     if (sem_init(&int_ack, 0, 0) < 0) {
113     printf("WARNING: Cannot init semaphore");
114     return false;
115     }
116    
117 cebix 1.7 Set_pthread_attr(&ether_thread_attr, 1);
118 cebix 1.2 thread_active = (pthread_create(&ether_thread, &ether_thread_attr, receive_func, NULL) == 0);
119     if (!thread_active) {
120     printf("WARNING: Cannot start Ethernet thread");
121     return false;
122     }
123    
124 gbeauche 1.15 #ifdef HAVE_SLIRP
125     if (net_if_type == NET_IF_SLIRP) {
126     slirp_thread_active = (pthread_create(&slirp_thread, NULL, slirp_receive_func, NULL) == 0);
127     if (!slirp_thread_active) {
128     printf("WARNING: Cannot start slirp reception thread\n");
129     return false;
130     }
131     }
132     #endif
133    
134 cebix 1.2 return true;
135     }
136    
137    
138     /*
139     * Stop packet reception thread
140     */
141    
142     static void stop_thread(void)
143     {
144 gbeauche 1.15 #ifdef HAVE_SLIRP
145     if (slirp_thread_active) {
146     pthread_cancel(slirp_thread);
147     pthread_join(slirp_thread, NULL);
148     slirp_thread_active = false;
149     }
150     #endif
151    
152 cebix 1.2 if (thread_active) {
153     pthread_cancel(ether_thread);
154     pthread_join(ether_thread, NULL);
155     sem_destroy(&int_ack);
156     thread_active = false;
157     }
158     }
159    
160    
161     /*
162 gbeauche 1.9 * Execute network script up|down
163     */
164    
165     static bool execute_network_script(const char *action)
166     {
167     if (net_if_script == NULL || net_if_name == NULL)
168     return false;
169    
170     int pid = fork();
171     if (pid >= 0) {
172     if (pid == 0) {
173     char *args[4];
174     args[0] = (char *)net_if_script;
175 gbeauche 1.10 args[1] = net_if_name;
176 gbeauche 1.9 args[2] = (char *)action;
177     args[3] = NULL;
178     execv(net_if_script, args);
179     exit(1);
180     }
181     int status;
182     while (waitpid(pid, &status, 0) != pid);
183     return WIFEXITED(status) && WEXITSTATUS(status) == 0;
184     }
185    
186     return false;
187     }
188    
189    
190     /*
191 cebix 1.1 * Initialization
192     */
193    
194 cebix 1.2 bool ether_init(void)
195 cebix 1.1 {
196     int nonblock = 1;
197     char str[256];
198    
199     // Do nothing if no Ethernet device specified
200     const char *name = PrefsFindString("ether");
201     if (name == NULL)
202 cebix 1.2 return false;
203 cebix 1.1
204 gbeauche 1.9 // Determine Ethernet device type
205     net_if_type = -1;
206     if (strncmp(name, "tap", 3) == 0)
207     net_if_type = NET_IF_ETHERTAP;
208     #if ENABLE_TUNTAP
209     else if (strcmp(name, "tun") == 0)
210     net_if_type = NET_IF_TUNTAP;
211     #endif
212 gbeauche 1.15 #ifdef HAVE_SLIRP
213     else if (strcmp(name, "slirp") == 0)
214     net_if_type = NET_IF_SLIRP;
215     #endif
216 gbeauche 1.9 else
217     net_if_type = NET_IF_SHEEPNET;
218 cebix 1.1
219 gbeauche 1.15 #ifdef HAVE_SLIRP
220     // Initialize slirp library
221     if (net_if_type == NET_IF_SLIRP) {
222     slirp_init();
223    
224     // Open slirp output pipe
225     int fds[2];
226     if (pipe(fds) < 0)
227     return false;
228     fd = fds[0];
229     slirp_output_fd = fds[1];
230     }
231     #endif
232    
233 gbeauche 1.11 // Open sheep_net or ethertap or TUN/TAP device
234 cebix 1.1 char dev_name[16];
235 gbeauche 1.9 switch (net_if_type) {
236     case NET_IF_ETHERTAP:
237 cebix 1.1 sprintf(dev_name, "/dev/%s", name);
238 gbeauche 1.9 break;
239     case NET_IF_TUNTAP:
240 gbeauche 1.11 strcpy(dev_name, "/dev/net/tun");
241 gbeauche 1.9 break;
242     case NET_IF_SHEEPNET:
243 cebix 1.1 strcpy(dev_name, "/dev/sheep_net");
244 gbeauche 1.9 break;
245     }
246 gbeauche 1.15 if (net_if_type != NET_IF_SLIRP) {
247     fd = open(dev_name, O_RDWR);
248     if (fd < 0) {
249     sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
250     WarningAlert(str);
251     goto open_error;
252     }
253 cebix 1.1 }
254    
255 gbeauche 1.9 #if ENABLE_TUNTAP
256     // Open TUN/TAP interface
257     if (net_if_type == NET_IF_TUNTAP) {
258     struct ifreq ifr;
259     memset(&ifr, 0, sizeof(ifr));
260     ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
261     strcpy(ifr.ifr_name, "tun%d");
262     if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) {
263     sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
264     WarningAlert(str);
265     goto open_error;
266     }
267    
268     // Get network config script file path
269     net_if_script = PrefsFindString("etherconfig");
270     if (net_if_script == NULL)
271     net_if_script = ETHERCONFIG_FILE_NAME;
272    
273     // Start network script up
274     if (net_if_script == NULL) {
275     sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script not found");
276     WarningAlert(str);
277     goto open_error;
278     }
279 gbeauche 1.10 net_if_name = strdup(ifr.ifr_name);
280 gbeauche 1.9 if (!execute_network_script("up")) {
281     sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script execute error");
282     WarningAlert(str);
283     goto open_error;
284     }
285     D(bug("Connected to host network interface: %s\n", net_if_name));
286     }
287     #endif
288    
289 cebix 1.1 #if defined(__linux__)
290     // Attach sheep_net to selected Ethernet card
291 gbeauche 1.9 if (net_if_type == NET_IF_SHEEPNET && ioctl(fd, SIOCSIFLINK, name) < 0) {
292 cebix 1.1 sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
293     WarningAlert(str);
294     goto open_error;
295     }
296     #endif
297    
298     // Set nonblocking I/O
299     ioctl(fd, FIONBIO, &nonblock);
300    
301     // Get Ethernet address
302 gbeauche 1.9 if (net_if_type == NET_IF_ETHERTAP) {
303 cebix 1.1 pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID
304     ether_addr[0] = 0xfe;
305     ether_addr[1] = 0xfd;
306     ether_addr[2] = p >> 24;
307     ether_addr[3] = p >> 16;
308     ether_addr[4] = p >> 8;
309     ether_addr[5] = p;
310 gbeauche 1.15 #ifdef HAVE_SLIRP
311     } else if (net_if_type == NET_IF_SLIRP) {
312     ether_addr[0] = 0x52;
313     ether_addr[1] = 0x54;
314     ether_addr[2] = 0x00;
315     ether_addr[3] = 0x12;
316     ether_addr[4] = 0x34;
317     ether_addr[5] = 0x56;
318     #endif
319 cebix 1.1 } else
320     ioctl(fd, SIOCGIFADDR, ether_addr);
321     D(bug("Ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5]));
322    
323     // Start packet reception thread
324 cebix 1.2 if (!start_thread())
325 cebix 1.1 goto open_error;
326    
327     // Everything OK
328 cebix 1.2 return true;
329 cebix 1.1
330     open_error:
331 cebix 1.2 stop_thread();
332    
333 cebix 1.1 if (fd > 0) {
334     close(fd);
335     fd = -1;
336     }
337 gbeauche 1.15 if (slirp_output_fd >= 0) {
338     close(slirp_output_fd);
339     slirp_output_fd = -1;
340     }
341 cebix 1.2 return false;
342 cebix 1.1 }
343    
344    
345     /*
346     * Deinitialization
347     */
348    
349 cebix 1.2 void ether_exit(void)
350 cebix 1.1 {
351     // Stop reception thread
352     if (thread_active) {
353     pthread_cancel(ether_thread);
354     pthread_join(ether_thread, NULL);
355     sem_destroy(&int_ack);
356     thread_active = false;
357     }
358    
359 gbeauche 1.10 // Shut down TUN/TAP interface
360     if (net_if_type == NET_IF_TUNTAP)
361     execute_network_script("down");
362    
363     // Free TUN/TAP device name
364     if (net_if_name)
365     free(net_if_name);
366    
367 cebix 1.1 // Close sheep_net device
368     if (fd > 0)
369     close(fd);
370 gbeauche 1.15
371     // Close slirp output buffer
372     if (slirp_output_fd > 0)
373     close(slirp_output_fd);
374 cebix 1.1 }
375    
376    
377     /*
378     * Reset
379     */
380    
381 cebix 1.3 void ether_reset(void)
382 cebix 1.1 {
383 cebix 1.3 net_protocols.clear();
384 cebix 1.1 }
385    
386    
387     /*
388     * Add multicast address
389     */
390    
391     int16 ether_add_multicast(uint32 pb)
392     {
393 gbeauche 1.15 switch (net_if_type) {
394     case NET_IF_ETHERTAP:
395     case NET_IF_SHEEPNET:
396     if (ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
397     D(bug("WARNING: Couldn't enable multicast address\n"));
398     if (net_if_type == NET_IF_ETHERTAP)
399     return noErr;
400     else
401     return eMultiErr;
402     }
403     default:
404 cebix 1.1 return noErr;
405 gbeauche 1.15 }
406 cebix 1.1 }
407    
408    
409     /*
410     * Delete multicast address
411     */
412    
413     int16 ether_del_multicast(uint32 pb)
414     {
415 gbeauche 1.15 switch (net_if_type) {
416     case NET_IF_ETHERTAP:
417     case NET_IF_SHEEPNET:
418     if (ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
419     D(bug("WARNING: Couldn't disable multicast address\n"));
420     return eMultiErr;
421     }
422     default:
423 cebix 1.1 return noErr;
424 gbeauche 1.15 }
425 cebix 1.1 }
426    
427    
428     /*
429     * Attach protocol handler
430     */
431    
432     int16 ether_attach_ph(uint16 type, uint32 handler)
433     {
434 cebix 1.3 if (net_protocols.find(type) != net_protocols.end())
435 cebix 1.1 return lapProtErr;
436 cebix 1.3 net_protocols[type] = handler;
437     return noErr;
438 cebix 1.1 }
439    
440    
441     /*
442     * Detach protocol handler
443     */
444    
445     int16 ether_detach_ph(uint16 type)
446     {
447 cebix 1.3 if (net_protocols.erase(type) == 0)
448 cebix 1.1 return lapProtErr;
449 cebix 1.3 return noErr;
450 cebix 1.1 }
451    
452    
453     /*
454     * Transmit raw ethernet packet
455     */
456    
457     int16 ether_write(uint32 wds)
458     {
459     // Copy packet to buffer
460     uint8 packet[1516], *p = packet;
461     int len = 0;
462     #if defined(__linux__)
463 gbeauche 1.9 if (net_if_type == NET_IF_ETHERTAP) {
464 cebix 1.1 *p++ = 0; // Linux ethertap discards the first 2 bytes
465     *p++ = 0;
466     len += 2;
467     }
468     #endif
469 cebix 1.2 len += ether_wds_to_buffer(wds, p);
470 cebix 1.1
471     #if MONITOR
472     bug("Sending Ethernet packet:\n");
473     for (int i=0; i<len; i++) {
474     bug("%02x ", packet[i]);
475     }
476     bug("\n");
477     #endif
478    
479     // Transmit packet
480 gbeauche 1.15 #ifdef HAVE_SLIRP
481     if (net_if_type == NET_IF_SLIRP) {
482     slirp_input(packet, len);
483     return noErr;
484     } else
485     #endif
486 cebix 1.1 if (write(fd, packet, len) < 0) {
487     D(bug("WARNING: Couldn't transmit packet\n"));
488     return excessCollsns;
489     } else
490     return noErr;
491     }
492    
493    
494     /*
495 cebix 1.2 * Start UDP packet reception thread
496     */
497    
498     bool ether_start_udp_thread(int socket_fd)
499     {
500     fd = socket_fd;
501     udp_tunnel = true;
502     return start_thread();
503     }
504    
505    
506     /*
507     * Stop UDP packet reception thread
508     */
509    
510     void ether_stop_udp_thread(void)
511     {
512     stop_thread();
513     fd = -1;
514     }
515    
516    
517     /*
518 gbeauche 1.15 * SLIRP output buffer glue
519     */
520    
521     #ifdef HAVE_SLIRP
522     int slirp_can_output(void)
523     {
524     return 1;
525     }
526    
527     void slirp_output(const uint8 *packet, int len)
528     {
529     write(slirp_output_fd, packet, len);
530     }
531    
532     void *slirp_receive_func(void *arg)
533     {
534     for (;;) {
535     // Wait for packets to arrive
536     fd_set rfds, wfds, xfds;
537     int nfds;
538     struct timeval tv;
539    
540     nfds = -1;
541     FD_ZERO(&rfds);
542     FD_ZERO(&wfds);
543     FD_ZERO(&xfds);
544     slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
545     tv.tv_sec = 0;
546     tv.tv_usec = 16667;
547     if (select(nfds + 1, &rfds, &wfds, &xfds, &tv) >= 0)
548     slirp_select_poll(&rfds, &wfds, &xfds);
549     }
550     return NULL;
551     }
552     #else
553     int slirp_can_output(void)
554     {
555     return 0;
556     }
557    
558     void slirp_output(const uint8 *packet, int len)
559     {
560     }
561     #endif
562    
563    
564     /*
565 gbeauche 1.17 * Wait for data to arrive
566     */
567    
568     static inline int poll_fd(int fd)
569     {
570     #ifdef HAVE_POLL
571     struct pollfd pf = {fd, POLLIN, 0};
572     return poll(&pf, 1, -1);
573     #else
574     fd_set rfds;
575     FD_ZERO(&rfds);
576     FD_SET(fd, &rfds);
577     return select(1, &rfds, NULL, NULL, NULL);
578     #endif
579     }
580    
581    
582     /*
583 cebix 1.1 * Packet reception thread
584     */
585    
586     static void *receive_func(void *arg)
587     {
588     for (;;) {
589    
590     // Wait for packets to arrive
591 gbeauche 1.17 int res = poll_fd(fd);
592 cebix 1.1 if (res <= 0)
593     break;
594    
595     // Trigger Ethernet interrupt
596     D(bug(" packet received, triggering Ethernet interrupt\n"));
597     SetInterruptFlag(INTFLAG_ETHER);
598     TriggerInterrupt();
599    
600     // Wait for interrupt acknowledge by EtherInterrupt()
601     sem_wait(&int_ack);
602     }
603     return NULL;
604     }
605    
606    
607     /*
608     * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers
609     */
610    
611     void EtherInterrupt(void)
612     {
613     D(bug("EtherIRQ\n"));
614    
615     // Call protocol handler for received packets
616 gbeauche 1.14 EthernetPacket ether_packet;
617     uint32 packet = ether_packet.addr();
618 cebix 1.2 ssize_t length;
619 cebix 1.1 for (;;) {
620    
621 cebix 1.2 if (udp_tunnel) {
622    
623     // Read packet from socket
624     struct sockaddr_in from;
625     socklen_t from_len = sizeof(from);
626 gbeauche 1.14 length = recvfrom(fd, Mac2HostAddr(packet), 1514, 0, (struct sockaddr *)&from, &from_len);
627 cebix 1.2 if (length < 14)
628     break;
629     ether_udp_read(packet, length, &from);
630    
631     } else {
632    
633     // Read packet from sheep_net device
634 cebix 1.1 #if defined(__linux__)
635 gbeauche 1.14 length = read(fd, Mac2HostAddr(packet), net_if_type == NET_IF_ETHERTAP ? 1516 : 1514);
636 cebix 1.1 #else
637 gbeauche 1.14 length = read(fd, Mac2HostAddr(packet), 1514);
638 cebix 1.1 #endif
639 cebix 1.2 if (length < 14)
640     break;
641 cebix 1.1
642     #if MONITOR
643 cebix 1.2 bug("Receiving Ethernet packet:\n");
644     for (int i=0; i<length; i++) {
645 gbeauche 1.14 bug("%02x ", ReadMacInt8(packet + i));
646 cebix 1.2 }
647     bug("\n");
648 cebix 1.1 #endif
649    
650 cebix 1.2 // Pointer to packet data (Ethernet header)
651 gbeauche 1.14 uint32 p = packet;
652 cebix 1.1 #if defined(__linux__)
653 gbeauche 1.9 if (net_if_type == NET_IF_ETHERTAP) {
654 cebix 1.2 p += 2; // Linux ethertap has two random bytes before the packet
655     length -= 2;
656     }
657 cebix 1.1 #endif
658    
659 cebix 1.2 // Get packet type
660 gbeauche 1.14 uint16 type = ReadMacInt16(p + 12);
661 cebix 1.1
662 cebix 1.2 // Look for protocol
663 cebix 1.3 uint16 search_type = (type <= 1500 ? 0 : type);
664     if (net_protocols.find(search_type) == net_protocols.end())
665 cebix 1.2 continue;
666 cebix 1.3 uint32 handler = net_protocols[search_type];
667 cebix 1.2
668     // No default handler
669 cebix 1.3 if (handler == 0)
670 cebix 1.2 continue;
671    
672     // Copy header to RHA
673 gbeauche 1.14 Mac2Mac_memcpy(ether_data + ed_RHA, p, 14);
674 cebix 1.2 D(bug(" header %08x%04x %08x%04x %04x\n", ReadMacInt32(ether_data + ed_RHA), ReadMacInt16(ether_data + ed_RHA + 4), ReadMacInt32(ether_data + ed_RHA + 6), ReadMacInt16(ether_data + ed_RHA + 10), ReadMacInt16(ether_data + ed_RHA + 12)));
675    
676     // Call protocol handler
677     M68kRegisters r;
678     r.d[0] = type; // Packet type
679     r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket)
680 gbeauche 1.14 r.a[0] = p + 14; // Pointer to packet (Mac address, for ReadPacket)
681 cebix 1.2 r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA
682     r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines
683 cebix 1.3 D(bug(" calling protocol handler %08x, type %08x, length %08x, data %08x, rha %08x, read_packet %08x\n", handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4]));
684     Execute68k(handler, &r);
685 cebix 1.2 }
686 cebix 1.1 }
687    
688     // Acknowledge interrupt to reception thread
689     D(bug(" EtherIRQ done\n"));
690     sem_post(&int_ack);
691     }