ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.5
Committed: 2001-09-02T13:50:05Z (22 years, 10 months ago) by cebix
Branch: MAIN
Changes since 1.4: +1 -1 lines
Log Message:
- added IRIX patches from Brian Johnson
- the existence of socklen_t is now checked by the configure script

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