ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.1
Committed: 2001-03-29T14:20:53Z (23 years, 3 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-29052001, release-0_9-1
Log Message:
- FreeBSD configure script cleanups [Michael Alyn Miller]
- ether_linux.cpp moved and renamed to ether_unix.cpp, now also works with
  the tap driver under FreeBSD [Michael Alyn Miller]
- fpu_x86_asm.h: fixed problem in with newer GCC pre-processors

File Contents

# Content
1 /*
2 * ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD)
3 *
4 * Basilisk II (C) 1997-2001 Christian Bauer
5 *
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 #include <sys/ioctl.h>
24 #include <sys/poll.h>
25 #include <pthread.h>
26 #include <semaphore.h>
27 #include <errno.h>
28 #include <stdio.h>
29
30 #if defined(__FreeBSD__)
31 #include <sys/socket.h>
32 #include <net/if.h>
33 #endif
34
35 #include "cpu_emulation.h"
36 #include "main.h"
37 #include "macos_util.h"
38 #include "prefs.h"
39 #include "user_strings.h"
40 #include "ether.h"
41 #include "ether_defs.h"
42
43 #define DEBUG 0
44 #include "debug.h"
45
46 #define MONITOR 0
47
48
49 // List of attached protocols
50 struct NetProtocol {
51 NetProtocol *next;
52 uint16 type;
53 uint32 handler;
54 };
55
56 static NetProtocol *prot_list = NULL;
57
58
59 // Global variables
60 static int fd = -1; // fd of sheep_net device
61 static pthread_t ether_thread; // Packet reception thread
62 static pthread_attr_t ether_thread_attr; // Packet reception thread attributes
63 static bool thread_active = false; // Flag: Packet reception thread installed
64 static sem_t int_ack; // Interrupt acknowledge semaphore
65 static bool is_ethertap; // Flag: Ethernet device is ethertap
66
67 // Prototypes
68 static void *receive_func(void *arg);
69
70
71 /*
72 * Find protocol in list
73 */
74
75 static NetProtocol *find_protocol(uint16 type)
76 {
77 // All 802.2 types are the same
78 if (type <= 1500)
79 type = 0;
80
81 // Search list (we could use hashing here but there are usually only three
82 // handlers installed: 0x0000 for AppleTalk and 0x0800/0x0806 for TCP/IP)
83 NetProtocol *p = prot_list;
84 while (p) {
85 if (p->type == type)
86 return p;
87 p = p->next;
88 }
89 return NULL;
90 }
91
92
93 /*
94 * Remove all protocols
95 */
96
97 static void remove_all_protocols(void)
98 {
99 NetProtocol *p = prot_list;
100 while (p) {
101 NetProtocol *next = p->next;
102 delete p;
103 p = next;
104 }
105 prot_list = NULL;
106 }
107
108
109 /*
110 * Initialization
111 */
112
113 void EtherInit(void)
114 {
115 int nonblock = 1;
116 char str[256];
117
118 // Do nothing if no Ethernet device specified
119 const char *name = PrefsFindString("ether");
120 if (name == NULL)
121 return;
122
123 // Is it Ethertap?
124 is_ethertap = (strncmp(name, "tap", 3) == 0);
125
126 // Open sheep_net or ethertap device
127 char dev_name[16];
128 if (is_ethertap)
129 sprintf(dev_name, "/dev/%s", name);
130 else
131 strcpy(dev_name, "/dev/sheep_net");
132 fd = open(dev_name, O_RDWR);
133 if (fd < 0) {
134 sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
135 WarningAlert(str);
136 goto open_error;
137 }
138
139 #if defined(__linux__)
140 // Attach sheep_net to selected Ethernet card
141 if (!is_ethertap && ioctl(fd, SIOCSIFLINK, name) < 0) {
142 sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
143 WarningAlert(str);
144 goto open_error;
145 }
146 #endif
147
148 // Set nonblocking I/O
149 ioctl(fd, FIONBIO, &nonblock);
150
151 // Get Ethernet address
152 if (is_ethertap) {
153 pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID
154 ether_addr[0] = 0xfe;
155 ether_addr[1] = 0xfd;
156 ether_addr[2] = p >> 24;
157 ether_addr[3] = p >> 16;
158 ether_addr[4] = p >> 8;
159 ether_addr[5] = p;
160 } else
161 ioctl(fd, SIOCGIFADDR, ether_addr);
162 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]));
163
164 // Start packet reception thread
165 if (sem_init(&int_ack, 0, 0) < 0) {
166 printf("WARNING: Cannot init semaphore");
167 goto open_error;
168 }
169 pthread_attr_init(&ether_thread_attr);
170 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
171 if (geteuid() == 0) {
172 pthread_attr_setinheritsched(&ether_thread_attr, PTHREAD_EXPLICIT_SCHED);
173 pthread_attr_setschedpolicy(&ether_thread_attr, SCHED_FIFO);
174 struct sched_param fifo_param;
175 fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2 + 1;
176 pthread_attr_setschedparam(&ether_thread_attr, &fifo_param);
177 }
178 #endif
179 thread_active = (pthread_create(&ether_thread, &ether_thread_attr, receive_func, NULL) == 0);
180 if (!thread_active) {
181 printf("WARNING: Cannot start Ethernet thread");
182 goto open_error;
183 }
184
185 // Everything OK
186 net_open = true;
187 return;
188
189 open_error:
190 if (thread_active) {
191 pthread_cancel(ether_thread);
192 pthread_join(ether_thread, NULL);
193 sem_destroy(&int_ack);
194 thread_active = false;
195 }
196 if (fd > 0) {
197 close(fd);
198 fd = -1;
199 }
200 }
201
202
203 /*
204 * Deinitialization
205 */
206
207 void EtherExit(void)
208 {
209 // Stop reception thread
210 if (thread_active) {
211 pthread_cancel(ether_thread);
212 pthread_join(ether_thread, NULL);
213 sem_destroy(&int_ack);
214 thread_active = false;
215 }
216
217 // Close sheep_net device
218 if (fd > 0)
219 close(fd);
220
221 // Remove all protocols
222 remove_all_protocols();
223 }
224
225
226 /*
227 * Reset
228 */
229
230 void EtherReset(void)
231 {
232 remove_all_protocols();
233 }
234
235
236 /*
237 * Add multicast address
238 */
239
240 int16 ether_add_multicast(uint32 pb)
241 {
242 if (ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
243 D(bug("WARNING: Couldn't enable multicast address\n"));
244 if (is_ethertap)
245 return noErr;
246 else
247 return eMultiErr;
248 } else
249 return noErr;
250 }
251
252
253 /*
254 * Delete multicast address
255 */
256
257 int16 ether_del_multicast(uint32 pb)
258 {
259 if (ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
260 D(bug("WARNING: Couldn't disable multicast address\n"));
261 return eMultiErr;
262 } else
263 return noErr;
264 }
265
266
267 /*
268 * Attach protocol handler
269 */
270
271 int16 ether_attach_ph(uint16 type, uint32 handler)
272 {
273 // Already attached?
274 NetProtocol *p = find_protocol(type);
275 if (p != NULL)
276 return lapProtErr;
277 else {
278 // No, create and attach
279 p = new NetProtocol;
280 p->next = prot_list;
281 p->type = type;
282 p->handler = handler;
283 prot_list = p;
284 return noErr;
285 }
286 }
287
288
289 /*
290 * Detach protocol handler
291 */
292
293 int16 ether_detach_ph(uint16 type)
294 {
295 NetProtocol *p = find_protocol(type);
296 if (p != NULL) {
297 NetProtocol *q = prot_list;
298 if (p == q) {
299 prot_list = p->next;
300 delete p;
301 return noErr;
302 }
303 while (q) {
304 if (q->next == p) {
305 q->next = p->next;
306 delete p;
307 return noErr;
308 }
309 q = q->next;
310 }
311 return lapProtErr;
312 } else
313 return lapProtErr;
314 }
315
316
317 /*
318 * Transmit raw ethernet packet
319 */
320
321 int16 ether_write(uint32 wds)
322 {
323 // Set source address
324 uint32 hdr = ReadMacInt32(wds + 2);
325 Host2Mac_memcpy(hdr + 6, ether_addr, 6);
326
327 // Copy packet to buffer
328 uint8 packet[1516], *p = packet;
329 int len = 0;
330 #if defined(__linux__)
331 if (is_ethertap) {
332 *p++ = 0; // Linux ethertap discards the first 2 bytes
333 *p++ = 0;
334 len += 2;
335 }
336 #endif
337 for (;;) {
338 int w = ReadMacInt16(wds);
339 if (w == 0)
340 break;
341 Mac2Host_memcpy(p, ReadMacInt32(wds + 2), w);
342 len += w;
343 p += w;
344 wds += 6;
345 }
346
347 #if MONITOR
348 bug("Sending Ethernet packet:\n");
349 for (int i=0; i<len; i++) {
350 bug("%02x ", packet[i]);
351 }
352 bug("\n");
353 #endif
354
355 // Transmit packet
356 if (write(fd, packet, len) < 0) {
357 D(bug("WARNING: Couldn't transmit packet\n"));
358 return excessCollsns;
359 } else
360 return noErr;
361 }
362
363
364 /*
365 * Packet reception thread
366 */
367
368 static void *receive_func(void *arg)
369 {
370 for (;;) {
371
372 // Wait for packets to arrive
373 struct pollfd pf = {fd, POLLIN, 0};
374 int res = poll(&pf, 1, -1);
375 if (res <= 0)
376 break;
377
378 // Trigger Ethernet interrupt
379 D(bug(" packet received, triggering Ethernet interrupt\n"));
380 SetInterruptFlag(INTFLAG_ETHER);
381 TriggerInterrupt();
382
383 // Wait for interrupt acknowledge by EtherInterrupt()
384 sem_wait(&int_ack);
385 }
386 return NULL;
387 }
388
389
390 /*
391 * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers
392 */
393
394 void EtherInterrupt(void)
395 {
396 D(bug("EtherIRQ\n"));
397
398 // Call protocol handler for received packets
399 uint8 packet[1516];
400 for (;;) {
401
402 // Read packet from sheep_net device
403 #if defined(__linux__)
404 ssize_t length = read(fd, packet, is_ethertap ? 1516 : 1514);
405 #else
406 ssize_t length = read(fd, packet, 1514);
407 #endif
408 if (length < 14)
409 break;
410
411 #if MONITOR
412 bug("Receiving Ethernet packet:\n");
413 for (int i=0; i<length; i++) {
414 bug("%02x ", packet[i]);
415 }
416 bug("\n");
417 #endif
418
419 // Pointer to packet data (Ethernet header)
420 uint8 *p = packet;
421 #if defined(__linux__)
422 if (is_ethertap) {
423 p += 2; // Linux ethertap has two random bytes before the packet
424 length -= 2;
425 }
426 #endif
427
428 // Get packet type
429 uint16 type = ntohs(*(uint16 *)(p + 12));
430
431 // Look for protocol
432 NetProtocol *prot = find_protocol(type);
433 if (prot == NULL)
434 continue;
435
436 // No default handler
437 if (prot->handler == 0)
438 continue;
439
440 // Copy header to RHA
441 Host2Mac_memcpy(ether_data + ed_RHA, p, 14);
442 D(bug(" header %08lx%04lx %08lx%04lx %04lx\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)));
443
444 // Call protocol handler
445 M68kRegisters r;
446 r.d[0] = type; // Packet type
447 r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket)
448 r.a[0] = (uint32)p + 14; // Pointer to packet (host address, for ReadPacket)
449 r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA
450 r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines
451 D(bug(" calling protocol handler %08lx, type %08lx, length %08lx, data %08lx, rha %08lx, read_packet %08lx\n", prot->handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4]));
452 Execute68k(prot->handler, &r);
453 }
454
455 // Acknowledge interrupt to reception thread
456 D(bug(" EtherIRQ done\n"));
457 sem_post(&int_ack);
458 }