ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Linux/ether_linux.cpp
Revision: 1.5
Committed: 2000-07-13T16:12:33Z (24 years, 4 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-13072000
Changes since 1.4: +1 -0 lines
Log Message:
- DGA and SHM are only tried on local X11 displays
- re-integrated old window update method (better performance over a networked
  display connection), frameskip=0 selects new method, other values select
  old method
- fixed compilation errors

File Contents

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