ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.7
Committed: 2002-02-07T16:10:55Z (22 years, 5 months ago) by cebix
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.6: +1 -11 lines
Log Message:
cleaned up pthread attributes [Brian Johnson]

File Contents

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