Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
ethernet65.c
Go to the documentation of this file.
1 /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator
2  Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
3  Copyright (C)2018 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 #include "xemu/emutools.h"
20 #include "xemu/ethertap.h"
21 #include "ethernet65.h"
22 
23 
24 /* It seems, at least on Nexys4DDR board, the used ethernet controller chip is: LAN8720A
25 
26  http://ww1.microchip.com/downloads/en/DeviceDoc/8720a.pdf
27 
28  This information is not so useful, as M65 programs in general does not "meet" it, however
29  in case of MII registers, it can be an interesting topic, maybe ... */
30 
31 
32 /* Possible future work:
33  * FIXME: there is no IRQ generated currently for the CPU! So only polling method is used.
34  the problem is not as trivial to solve as it seems, since a *thread* can affect IRQs
35  also updated by the main CPU emulator thread ... With using locking for all of these,
36  would result in quite slow emulation :(
37  * FIXME: it uses thread, and it's horrible code!! there are tons of race conditions, etc ...
38  It's not so nice work to use threads, but it would be hard to do otherwise, sadly.
39  * FIXME FIXME FIXME: No support for snapshotting ...
40  * Maybe allow TUN devices to be supported. It does not handle ethernet level stuffs, no source and
41  target MAC for example :-O But TUN devices are supported by wider range of OSes (eg: OSX) and we
42  probably can emulate the missing ethernet level stuffs somehow in this source on RX (and chop
43  them off, in case of TX ...).
44  * Implement a many DHCP client here, so user does not need to install a DHCP server on the PC, just
45  for testing M65 software with Xemu wants to use a DHCP. It would catch / answer for DHCP-specifc
46  requests, instead of using really the TAP device to send, and waiting for answer.
47 */
48 
49 
50 // ETH_FRAME_MAX_SIZE: now I'm a bit unsure about this with/without CRC, etc issues. However hopefully it's OK.
51 #define ETH_FRAME_MAX_SIZE 1536
52 // ETH_FRAME_MIN_SIZE: actually 64 "on the wire", but it included the CRC (4 octets) which we don't handle here at all
53 // this is only used to pad the frame on TX if shorter. No idea, that it's really needed for the EtherTAP device as well
54 // (not a real ethernet device), but still, hopefully it wouldn't hurt (?)
55 #define ETH_FRAME_MIN_SIZE 60
56 // SELECT_WAIT_USEC_MAX: max time to wait for RX/TX before trying it. This is because we don't want to burn the CPU time
57 // in the TAP handler thread but also we want to max the wait, if an ETH op quicks in after a certain time of inactivity
58 #define SELECT_WAIT_USEC_MAX 10000
59 // just by guessing (ehmm), on 100mbit (what M65 has) network, about 200'000 frame/sec is far the max
60 // that is, let's say about 5usec minimal wait
61 #define SELECT_WAIT_USEC_MIN 5
62 #define SELECT_WAIT_INC_VAL 5
63 
64 
65 #define ETH_II_FRAME_ARP 0x0806
66 #define ETH_II_FRAME_IPV4 0x0800
67 
68 static int eth_debug;
69 
70 static volatile struct {
71  int enabled; // the whole M65 eth emulation status
72  int no_reset; //
73  int exited;
74  int rx_enabled; // can the TAP handler thread receive a new packet?
75  int _rx_irq; // status of RX IRQ, note that it is also used for polling without enabled IRQs! (must be 0x20 or zero)
76  int _tx_irq; // status of TX IRQ, note that it is also used for polling without enabled IRQs! (must be 0x10 or zero)
78  int tx_size; // frame size to transmit
79  int tx_trigger; // trigger to TX
80  int rx_irq_enabled; // RX IRQ enabled, so rx_irq actually generates an IRQ
81  int tx_irq_enabled; // TX IRQ enabled, so tx_irq actually generates an IRQ
82  int rx_buffer_using; // RX buffer used by the receiver thread (must be 0 or 0x800, it also means offset in rx_buffer!)
83  int rx_buffer_mapped; // RX buffer which is mapped by the user (must be 0 or 0x800, it also means offset in rx_buffer!)
84  int sense_activity; // handler thread senses activity, helps keeping select_wait_usec low when there is no activity to avoid burning the CPU power without reason
85  int select_wait_usec; // uSeconds to wait at max by select(), the reason for this is the same which is expressed in the previous line
95  Uint8 rx_buffer[0x1000]; // actually, two 2048 bytes long buffers in one array
96  Uint8 tx_buffer[0x0800]; // a 2048 bytes long buffer to TX
97 } eth65;
98 
99 
100 static Uint8 mac_address[6] = {0x02,0x47,0x53,0x65,0x65,0x65}; // 00:80:10:... would be nicer, though a bit of cheating, it belongs to Commodore International(TM).
101 #ifdef HAVE_ETHERTAP
102 static const Uint8 mac_bcast[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; // also used as IPv4 bcast detection with filters, if only the first 4 bytes are checked
103 #endif
104 
105 static Uint16 miimlo8[0x20], miimhi8[0x20];
106 
107 #ifndef ETH65_NO_DEBUG
108 #define ETHDEBUG(...) do { \
109  if (XEMU_UNLIKELY(eth_debug)) { \
110  DEBUGPRINT(__VA_ARGS__); \
111  } \
112  } while (0)
113 #else
114 #define ETHDEBUG(...)
115 #endif
116 
117 // IRQs are not yet supported, especially because it's an async event from a thread, and the main thread should aware it
118 // at every opcode emulation, which would made that slow (ie thread-safe update of the CPU IRQ request ...)
119 static void __eth65_manage_irq_for_cpu ( void )
120 {
121  int status = ((eth65._rx_irq && eth65.rx_irq_enabled) || (eth65._tx_irq && eth65.tx_irq_enabled));
122  if (status != eth65._irq_to_cpu_routed) {
123  ETHDEBUG("ETH: IRQ change should be propogated: %d -> %d" NL, eth65._irq_to_cpu_routed, status);
124  eth65._irq_to_cpu_routed = status;
125  }
126 }
127 
128 #define RX_IRQ_ON() do { eth65._rx_irq = 0x20; __eth65_manage_irq_for_cpu(); } while (0)
129 #define RX_IRQ_OFF() do { eth65._rx_irq = 0; __eth65_manage_irq_for_cpu(); } while (0)
130 #define TX_IRQ_ON() do { eth65._tx_irq = 0x10; __eth65_manage_irq_for_cpu(); } while (0)
131 #define TX_IRQ_OFF() do { eth65._tx_irq = 0; __eth65_manage_irq_for_cpu(); } while (0)
132 
133 
134 
135 
136 /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
137  !!!!! WARNING: these things run as *THREAD* !!!!!
138  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
139  It must be very careful not use anything too much.
140  xemu_tuntap_read and write functions are used only here, so it's maybe OK
141  but exchange data with the main thread (the emulator) must be done very carefully!
142  FIXME: "surely", there are many race conditions still :(
143  however, proper locking etc would be just too painful at this stage of the emulator
144  also, using "ETHDEBUG"s (printfs after all, etc) basically not the best idea in a thread, I guess */
145 
146 #ifdef HAVE_ETHERTAP
147 
148 #include <errno.h>
149 
150 static const char init_error_prefix[] = "ETH: disabled: ";
151 
152 static SDL_Thread *thread_id = NULL;
153 
154 static XEMU_INLINE int ethernet_rx_processor ( void )
155 {
156  Uint8 rx_temp_buffer[0x800];
157  int ethertype, ret;
158  ret = xemu_tuntap_read(rx_temp_buffer, 6 + 6 + 2, sizeof rx_temp_buffer);
159  if (ret == -2) {
160  eth65.select_wait_usec = 1000000; // serious problem, do 1sec wait ...
161  ETHDEBUG("ETH-THREAD: read with EAGAIN, continue ..." NL);
162  return 0;
163  }
164  if (ret < 0) {
165  ETHDEBUG("ETH-THREAD: read error: %s" NL, strerror(errno));
166  return 1; // vote for exit thread ...
167  }
168  if (ret == 0) {
169  eth65.select_wait_usec = 1000000; // serious problem, do 1sec wait ...
170  ETHDEBUG("ETH-THREAD: read() returned with zero! continue ..." NL);
171  return 0;
172  }
173  if (ret > 2046) { // should not happen, but who knows ... (2046=size of buffer - 2, 2 for the length parameter to be passed)
174  // FIXME: maybe we should check the max frame size instead, but I guess TAP device does not allow crazy sizes anyway
175  eth65.select_wait_usec = 1000000; // serious problem, do 1sec wait ...
176  ETHDEBUG("ETH-THREAD: read() had %d bytes, too long! continue ..." NL, ret);
177  return 0;
178  }
179  // something is recevied at least.
180  // Still we can filter things ("m65 magic filters", etc)
181  ETHDEBUG("ETH-THREAD: read() returned with %d bytes read, OK!" NL, ret);
182  eth65.sense_activity = 1;
183  if (ret < 14 || ret > ETH_FRAME_MAX_SIZE) {
184  ETHDEBUG("ETH-THREAD: ... however, insane sized frame, skipping" NL);
185  return 0;
186  }
187  // XEMU stuff, just to test TAP
188  // it only recoginizes Ethernet-II frames, for frame types IPv4 or ARP
189  ethertype = (rx_temp_buffer[12] << 8) | rx_temp_buffer[13];
190  if (ethertype < 1536) {
191  ETHDEBUG("ETH-THREAD: ... however, skipped by XEMU: not an Ethernet-II frame ($%04X)" NL, ethertype);
192  return 0;
193  }
194  if (ethertype != ETH_II_FRAME_ARP && ethertype != ETH_II_FRAME_IPV4) {
195  ETHDEBUG("ETH-THREAD: ... however, skipped by XEMU: not ARP or IPv4 Ethernet-II ethertype ($%04X)" NL, ethertype);
196  return 0;
197  }
198  // MAC filter, when ON, only packets targeting our MAC, or bcast MAC address is received
199  if (
200  (eth65.mac_filter || eth65.xemu_always_filters) &&
201  memcmp(rx_temp_buffer, mac_bcast, 6) &&
202  memcmp(rx_temp_buffer, mac_address, 6)
203  ) {
204  ETHDEBUG("ETH-THREAD: ... however, MAC filter ruled it out %02X:%02X:%02X:%02X:%02X:%02X" NL,
205  rx_temp_buffer[0], rx_temp_buffer[1], rx_temp_buffer[2], rx_temp_buffer[3], rx_temp_buffer[4],rx_temp_buffer[5]
206  );
207  return 0;
208  }
209  ETHDEBUG("ETH-THREAD: ... cool, target MAC seems to survive MAC filter %02X:%02X:%02X:%02X:%02X:%02X" NL,
210  rx_temp_buffer[0], rx_temp_buffer[1], rx_temp_buffer[2], rx_temp_buffer[3], rx_temp_buffer[4],rx_temp_buffer[5]
211  );
212  // check, if frame contains an IPv4 packet, so we can check for IPv4-specific filters as well
213  if (
214  ret >= 20 + 8 + 14 && // we need at least ??? bytes for valid IPv4 packet
215  ethertype == ETH_II_FRAME_IPV4 &&
216  (rx_temp_buffer[15] & 0xF0) == 0x40 // IPv4? [4=version field of IP packet]
217  ) {
218  if ((!eth65.accept_broadcast || eth65.xemu_always_filters) && !memcmp(rx_temp_buffer + 30, mac_bcast, 4)) {
219  ETHDEBUG("ETH-THREAD: ... however, IP filter ruled it out: broadcast (%d.%d.%d.%d)" NL, rx_temp_buffer[30], rx_temp_buffer[31], rx_temp_buffer[32], rx_temp_buffer[33]);
220  return 0;
221  }
222  // check if multicast (224.0.0.0/4)
223  if ((!eth65.accept_multicast || eth65.xemu_always_filters) && (rx_temp_buffer[30] & 0xF0) == 0xE0) {
224  ETHDEBUG("ETH-THREAD: ... however, IP filter ruled it out: multicast (%d.%d.%d.%d)" NL, rx_temp_buffer[30], rx_temp_buffer[31], rx_temp_buffer[32], rx_temp_buffer[33]);
225  return 0;
226  }
227  ETHDEBUG("ETH-THREAD: ... cool, target IP seems to survive IP filter %d.%d.%d.%d" NL, rx_temp_buffer[30], rx_temp_buffer[31], rx_temp_buffer[32], rx_temp_buffer[33]);
228  }
229  ETHDEBUG("ETH-THREAD: ... MEGA[65]-COOL: we are ready to propogate packet" NL);
230  // M65 stores the received frame size in "6502 byte order" as the first two bytes in the RX
231  // (this makes things somewhat non-symmetric, as for TX, the size are in a pair of registers)
232  eth65.rx_buffer[eth65.rx_buffer_using] = ret & 0xFF;
233  eth65.rx_buffer[eth65.rx_buffer_using + 1] = ret >> 8;
234  for (int i = 0; i < ret; i++)
235  eth65.rx_buffer[eth65.rx_buffer_using + 2 + i] = rx_temp_buffer[i];
236  //memcpy(eth65.rx_buffer + eth65.rx_buffer_using + 2, rx_temp_buffer, r);
237  eth65.rx_enabled = 0; // disable RX till user not ACK'ed by swapping RX buffers
238  RX_IRQ_ON(); // signal, that we have something! we don't support IRQs yet, but it's also used for polling!
239  return 0;
240 }
241 
242 
243 static XEMU_INLINE int ethernet_tx_processor ( void )
244 {
245  int ret, size;
246  if (eth65.tx_size < 14 || eth65.tx_size > ETH_FRAME_MAX_SIZE) {
247  ETHDEBUG("ETH-THREAD: skipping TX, because invalid frame size: %d" NL, eth65.tx_size);
248  // still fake an OK answer FIXME ?
249  TX_IRQ_ON();
250  eth65.tx_trigger = 0;
251  return 0;
252  }
253 #if 0
254  // maybe this is not needed, and TAP device can handle autmatically, but anyway
255  if (eth65.tx_size < ETH_FRAME_MIN_SIZE) {
256  memset((void*)eth65.tx_buffer + eth65.tx_size, 0, ETH_FRAME_MIN_SIZE - eth65.tx_size);
258  } else
259 #endif
260  size = eth65.tx_size;
261  ret = xemu_tuntap_write((void*)eth65.tx_buffer, size);
262  if (ret == -2) { // FIXME: with read OK, but for write????
263  eth65.select_wait_usec = 1000000; // serious problem, do 1sec wait ...
264  ETHDEBUG("ETH-THREAD: write with EAGAIN, continue ..." NL);
265  return 0;
266  }
267  if (ret < 0) {
268  ETHDEBUG("ETH-THREAD: write error: %s" NL, strerror(errno));
269  return 1;
270  }
271  if (ret == 0) {
272  eth65.select_wait_usec = 1000000; // serious problem, do 1sec wait ...
273  ETHDEBUG("ETH-THREAD: write() returned with zero! continue ..." NL);
274  return 0;
275  }
276  if (ret != size) {
277  ETHDEBUG("ETH-THREAD: partial write only?! wanted = %d, written = %d" NL, size, ret);
278  return 1;
279  }
280  ETHDEBUG("ETH-THREAD: write() returned with %d bytes read, OK!" NL, ret);
281  eth65.sense_activity = 1;
282  eth65.tx_trigger = 0;
283  TX_IRQ_ON();
284  return 0;
285 }
286 
287 
288 
289 static int ethernet_thread ( void *unused )
290 {
291  ETHDEBUG("ETH-THREAD: hello from the thread." NL);
292  while (eth65.enabled) {
293  int ret;
294  /* ------------------- check for RX condition ------------------- */
295  if (
296  (!eth65.rx_enabled) && eth65.no_reset &&
297  (eth65.rx_buffer_mapped == eth65.rx_buffer_using)
298  ) {
299  eth65.rx_enabled = 1;
300  eth65.rx_buffer_using = eth65.rx_buffer_mapped ^ 0x800;
301  eth65.sense_activity = 1;
302  ETHDEBUG("ETH-THREAD: rx enabled flipped to OK!" NL);
303  }
304  /* ------------------- Throttling controll of thread's CPU usage ------------------- */
305  if (eth65.sense_activity) {
306  eth65.select_wait_usec = SELECT_WAIT_USEC_MIN; // there was some real activity, lower the select wait time in the hope, there is some on-going future stuff soon
307  eth65.sense_activity = 0;
308  } else {
309  if (eth65.select_wait_usec < SELECT_WAIT_USEC_MAX)
310  eth65.select_wait_usec += SELECT_WAIT_INC_VAL;
311  }
312  /* ------------------- The select() stuff ------------------- */
313  ret = xemu_tuntap_select(((eth65.rx_enabled && eth65.no_reset) ? XEMU_TUNTAP_SELECT_R : 0) | ((eth65.tx_trigger && eth65.no_reset) ? XEMU_TUNTAP_SELECT_W : 0), eth65.select_wait_usec);
314  //ETHDEBUG("ETH-THREAD: after select with %d usecs, retval = %d, rx_enabled = %d, tx_trigger = %d" NL,
315  // eth65.select_wait_usec, ret, eth65.rx_enabled, eth65.tx_trigger
316  //);
317  if (ret < 0) {
318  ETHDEBUG("ETH-THREAD: EMERGENCY STOP: select error: %s" NL, strerror(errno));
319  break;
320  }
321  /* ------------------- TO RECEIVE (RX) ------------------- */
322  if (eth65.rx_enabled && eth65.no_reset && (ret & XEMU_TUNTAP_SELECT_R)) {
323  if (ethernet_rx_processor()) {
324  ETHDEBUG("ETH-THREAD: EMERGENCY STOP: requested by RX processor." NL);
325  break;
326  }
327 
328  }
329  /* ------------------- TO TRANSMIT (TX) ------------------- */
330  if (eth65.tx_trigger && eth65.no_reset && (ret & XEMU_TUNTAP_SELECT_W)) {
331  if (ethernet_tx_processor()) {
332  ETHDEBUG("ETH-THREAD: EMERGENCY STOP: requested by TX processor." NL);
333  break;
334  }
335  }
336  }
337  eth65.exited = 1;
338  ETHDEBUG("ETH-THREAD: leaving ..." NL);
339  xemu_tuntap_close();
340  ETHDEBUG("ETH-THREAD: OK now ..." NL);
341  return 0;
342 }
343 
344 
345 /* End of threaded stuffs */
346 
347 #endif
348 
349 /* Start of non-threaded stuffs, NOTE: they work even without ETH emulation for real, but surely it won't RX/TX too much,
350  however we want to keep them, so a network-aware software won't go crazy on totally missing register-level emulation */
351 
352 
354 {
355  DEBUG("ETH: reading register $%02X" NL, addr & 0xF);
356  switch (addr & 0xF) {
357  /* **** $D6E0 register **** */
358  case 0x00:
359  return eth65.no_reset; // FIXME: not other bits emulated yet here
360  /* **** $D6E1 register **** */
361  case 0x01:
362  return
363  // Bit0: reset - should be hold '1' to use, '0' means resetting the ethernet controller chip
364  eth65.no_reset |
365  // Bit1: which RX buffer is mapped, we set the offset according to that
366  (eth65.rx_buffer_mapped ? 0x02 : 0 ) |
367  // Bit2: indicates which RX buffer was used ...
368  (eth65.rx_buffer_using ? 0x04 : 0 ) |
369  // Bit3: Enable real-time video streaming via ethernet
370  eth65.video_streaming |
371  // Bit4: ethernet TX IRQ status, it's also used with POLLING, without IRQ enabled!!
372  eth65._tx_irq |
373  // Bit5: ethernet RX IRQ status, it's also used with POLLING, without IRQ enabled!!
374  eth65._rx_irq |
375  // Bit6: Enable ethernet TX IRQ
376  eth65.tx_irq_enabled |
377  // Bit7: Enable ethernet RX IRQ
378  eth65.rx_irq_enabled
379  ;
380  /* **** $D6E2 register: TX size register low **** */
381  case 0x02: return eth65.tx_size & 0xFF;
382  /* **** $D6E3 register: TX size register high (4 bits only) **** */
383  case 0x03: return (eth65.tx_size >> 8) & 0xFF; // 4 bits, but tx_size var cannot contain more anyway
384  /* **** $D6E5 register **** */
385  case 0x05:
386  return
387  eth65.mac_filter |
388  eth65.disable_crc_chk |
389  ( eth65.adjust_txd_phase << 2) |
390  eth65.accept_broadcast |
391  eth65.accept_multicast
392  ;
393  break;
394  /* **** $D6E6 register **** */
395  case 0x06:
396  return
397  eth65.miim_register |
398  (eth65.phy_number << 5)
399  ;
400  /* **** $D6E7 register **** */
401  case 0x07: return miimlo8[eth65.miim_register];
402  /* **** $D6E8 register **** */
403  case 0x08: return miimhi8[eth65.miim_register];
404  /* **** $D6E9 - $D6EE registers: MAC address **** */
405  case 0x09: return mac_address[0];
406  case 0x0A: return mac_address[1];
407  case 0x0B: return mac_address[2];
408  case 0x0C: return mac_address[3];
409  case 0x0D: return mac_address[4];
410  case 0x0E: return mac_address[5];
411  default:
412  return 0xFF;
413  }
414 }
415 
416 
417 
418 
420 {
421  DEBUG("ETH: writing register $%02X with data $%02X" NL, addr & 0xF, data);
422  switch (addr & 0xF) {
423  /* **** $D6E0 register **** */
424  case 0x00:
425  eth65.no_reset = (data & 0x01); // FIXME: it seems to be the same as $D6E1 bit0???
426  break;
427  /* **** $D6E1 register **** */
428  case 0x01:
429  // Bit0: It seems, it's an output signal of the FPGA for the ethernet controller chip, and does not do too much other ...
430  // ... however it also means, that if this bit is zero, the ethernet controller chip does not do too much, I guess.
431  // that is, for usage, you want to keep this bit '1'.
432  eth65.no_reset = (data & 0x01);
433  // Bit1: which RX buffer is mapped, we set the offset according to that
434  // it seems, RX only works, if ctrl not in reset (bit0) and the ctrl actually waits for user to swap buffer (it can only receive into the not mapped one!)
435  eth65.rx_buffer_mapped = (data & 0x02) ? 0x800 : 0;
436  // Bit2: indicates which RX buffer was used ... maybe it's a read-only bit, we don't care
437  // Bit3: Enable real-time video streaming via ethernet (well, it's not supported by Xemu, but it would be fancy stuff btw ...) -- we don't care on this one
438  eth65.video_streaming = (data & 0x08);
439  // Bit4: ethernet TX IRQ status, this will be cleared on writing $D6E1, regardless of value written!
440  // Bit5: ethernet RX IRQ status, this will be cleared on writing $D6E1, regardless of value written!
441  // Bit6: Enable ethernet TX IRQ, not yet supported in Xemu! FIXME
442  eth65.tx_irq_enabled = (data & 0x40); // not yet used, FIXME
443  // Bit7: Enable ethernet RX IRQ, not yet supported in Xemu! FIXME
444  eth65.rx_irq_enabled = (data & 0x80); // not yet used, FIXME
445  // see comments above with bits 4 and 5
446  TX_IRQ_OFF();
447  RX_IRQ_OFF();
448  ETHDEBUG("ETH: $D6E1 has been written with data $%02X" NL, data);
449  break;
450  /* **** $D6E2 register: TX size low **** */
451  case 0x02:
452  eth65.tx_size = (eth65.tx_size & 0xFF00) | data;
453  break;
454  /* **** $D6E3 register: TX size high (4 bits only) **** */
455  case 0x03:
456  eth65.tx_size = ((data & 0x0F) << 8) | (eth65.tx_size & 0xFF);
457  break;
458  /* **** $D6E4 register **** */
459  case 0x04:
460  // trigger TX if $01 is written?
461  // clear (pending?) TX is $00 [marked as 'DEBUG' in iomap.txt']: FIXME? not implemented
462  if (data == 0) { // but anyway, here you are the debug stuff as well ...
463  eth65.tx_trigger = 0;
464  } else if (data == 1) { // the only "official" command here: TX trigger!
465  if (eth65.tx_size >= 14 || eth65.tx_size <= ETH_FRAME_MAX_SIZE) {
466  eth65.sense_activity = 1;
467  // FIXME: I guess, TX IRQ must be cleared by the user ... so we don't do here
468  eth65.tx_trigger = 1; // now ask the triggy stuff!
469  } else {
470  // leave this for the handler thread to check, but warning here
471  DEBUGPRINT("ETH: warning, invalid sized (%d) frame tried to be TX'ed" NL, eth65.tx_size);
472  }
473  }
474  break;
475  /* **** $D6E5 register **** */
476  case 0x05:
477  eth65.mac_filter = (data & 0x01);
478  eth65.disable_crc_chk = (data & 0x02);
479  eth65.adjust_txd_phase = (data >> 2) & 3;
480  eth65.accept_broadcast = (data & 0x10);
481  eth65.accept_multicast = (data & 0x20);
482  break;
483  /* **** $D6E6 register **** */
484  case 0x06:
485  eth65.miim_register = (data & 0x1F);
486  eth65.phy_number = (data & 0xE0) >> 5;
487  break;
488  /* **** $D6E7 register **** */
489  case 0x07:
490  miimlo8[eth65.miim_register] = data;
491  break;
492  /* **** $D6E8 register **** */
493  case 0x08:
494  miimhi8[eth65.miim_register] = data;
495  break;
496  /* **** $D6E9 - $D6EE registers: MAC address **** */
497  case 0x09: mac_address[0] = data; break;
498  case 0x0A: mac_address[1] = data; break;
499  case 0x0B: mac_address[2] = data; break;
500  case 0x0C: mac_address[3] = data; break;
501  case 0x0D: mac_address[4] = data; break;
502  case 0x0E: mac_address[5] = data; break;
503  }
504 }
505 
506 
507 
509 {
510  // FIXME what happens if M65 receives frame but user switches buffer meanwhile. Not so nice :-O
511  return eth65.rx_buffer[eth65.rx_buffer_mapped + (offset & 0x7FF)];
512 }
513 
514 void eth65_write_tx_buffer ( int offset, Uint8 data )
515 {
516  eth65.tx_buffer[offset & 0x7FF] = data;
517 }
518 
519 
520 
521 void eth65_shutdown ( void )
522 {
523  if (eth65.exited)
524  DEBUGPRINT("ETH: shutting down: handler thread has already exited" NL);
525  else if (eth65.enabled) {
526  eth65.enabled = 0;
527  eth65.no_reset = 0;
528  eth65.rx_enabled = 0;
529  eth65.tx_trigger = 0;
530  eth65.sense_activity = 1;
531  eth65.select_wait_usec = 0;
532  DEBUGPRINT("ETH: shutting down: handler thread seems to be running" NL);
533  } else
534  DEBUGPRINT("ETH: shutting down: handler thread seems hasn't been even running (not enabled?)" NL);
535 #ifdef HAVE_ETHERTAP
536  xemu_tuntap_close();
537 #endif
538 }
539 
540 
541 // This is not the reset what eth65.no_reset stuff does, but reset of M65 (also called by eth65_init)
542 void eth65_reset ( void )
543 {
544  memset(&miimlo8, 0, sizeof miimlo8);
545  memset(&miimhi8, 0, sizeof miimhi8);
546  memset((void*)&eth65, 0, sizeof eth65);
547  RX_IRQ_OFF();
548  TX_IRQ_OFF();
549  memset((void*)eth65.rx_buffer, 0xFF, sizeof eth65.rx_buffer);
550  memset((void*)eth65.tx_buffer, 0xFF, sizeof eth65.tx_buffer);
551  eth65.select_wait_usec = SELECT_WAIT_USEC_MAX;
552  eth65.rx_buffer_using = 0x800; // RX buffer is used to RX @ ofs 0
553  eth65.rx_buffer_mapped = 0x000; // RX buffer mapped @ ofs 0
554  eth65.accept_broadcast = 16;
555  eth65.accept_multicast = 32;
556  eth65.mac_filter = 1;
557 }
558 
559 
560 int eth65_init ( const char *options )
561 {
562  eth65.exited = 0;
563  eth65.enabled = 0;
564  eth65._irq_to_cpu_routed = 1;
565  eth65_reset();
566  eth_debug = 0;
567  if (options && *options) {
568 #ifdef HAVE_ETHERTAP
569  char device_name[64];
570  *device_name = 0;
571  for (;;) {
572  char *r = strchr(options, ',');
573  size_t len = r ? r - options : strlen(options);
574  if (!len || len >= sizeof device_name) {
575  ERROR_WINDOW("%sinvalid CLI switch/config: empty or too long parameter", init_error_prefix);
576  return 1;
577  }
578  if (!*device_name) {
579  strncpy(device_name, options, len);
580  device_name[len] = 0;
581  } else if (len == 5 && !strncmp(options, "debug", len)) {
582  eth_debug = 1;
583  } else if (len == 13 && !strncmp(options, "alwaysfilters", len)) {
584  eth65.xemu_always_filters = 1;
585  } else if (!strncmp(options, "mac=", 4)) {
586  if (len != 16 || sscanf(
587  options + 4,
588  "%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
589  mac_address + 0, mac_address + 1, mac_address + 2,
590  mac_address + 3, mac_address + 4, mac_address + 5
591  ) != 6) {
592  ERROR_WINDOW("%sbad mac= option, invalid MAC", init_error_prefix);
593  return 1;
594  }
595  } else {
596  strncpy(device_name, options, len); // use device_name as a temp storage for now, we won't need it anymore, as final error.
597  device_name[len] = 0;
598  ERROR_WINDOW("%sunknown/bad sub-option given in CLI/config: %s", init_error_prefix, device_name);
599  return 1;
600  }
601  if (r)
602  options = r + 1;
603  else
604  break;
605  }
606  if (xemu_tuntap_alloc(device_name, NULL, 0, XEMU_TUNTAP_IS_TAP | XEMU_TUNTAP_NO_PI | XEMU_TUNTAP_NONBLOCKING_IO) < 0) {
607  ERROR_WINDOW("%sTAP device \"%s\" opening error: %s", init_error_prefix, device_name, strerror(errno));
608  return 1;
609  }
610  eth65.enabled = 1;
611  // Initialize our thread for device read/write ...
612  thread_id = SDL_CreateThread(ethernet_thread, "Xemu-EtherTAP", NULL);
613  if (thread_id) {
614  DEBUGPRINT("ETH: enabled - thread %p started, device attached: \"%s\", initial MAC: %02X:%02X:%02X:%02X:%02X:%02X" NL,
615  thread_id, device_name,
616  mac_address[0], mac_address[1], mac_address[2],
617  mac_address[3], mac_address[4], mac_address[5]
618  );
619  } else {
620  ERROR_WINDOW("%serror creating thread for Ethernet emulation: %s", init_error_prefix, SDL_GetError());
621  eth65.enabled = 0;
622  xemu_tuntap_close();
623  return 1;
624  }
625  return 0;
626 #else
627  ERROR_WINDOW("Ethernet emulation is not supported/was compiled into this Xemu");
628  return 1;
629 #endif
630  } else {
631 #ifdef HAVE_ETHERTAP
632  DEBUGPRINT("ETH: not enabled by config/command line" NL);
633 #else
634  DEBUG("ETH: Ethernet emulation is not supported/was compiled into this Xemu");
635 #endif
636  return 0;
637  }
638 }
tx_irq_enabled
int tx_irq_enabled
Definition: ethernet65.c:81
eth65_reset
void eth65_reset(void)
Definition: ethernet65.c:542
video_streaming
int video_streaming
Definition: ethernet65.c:86
tx_size
int tx_size
Definition: ethernet65.c:78
SELECT_WAIT_INC_VAL
#define SELECT_WAIT_INC_VAL
Definition: ethernet65.c:62
emutools.h
_irq_to_cpu_routed
int _irq_to_cpu_routed
Definition: ethernet65.c:77
eth65_write_reg
void eth65_write_reg(int addr, Uint8 data)
Definition: ethernet65.c:419
adjust_txd_phase
int adjust_txd_phase
Definition: ethernet65.c:92
enabled
int enabled
Definition: ethernet65.c:71
ethernet65.h
SELECT_WAIT_USEC_MIN
#define SELECT_WAIT_USEC_MIN
Definition: ethernet65.c:61
no_reset
int no_reset
Definition: ethernet65.c:72
select_wait_usec
int select_wait_usec
Definition: ethernet65.c:85
rx_irq_enabled
int rx_irq_enabled
Definition: ethernet65.c:80
rx_buffer_using
int rx_buffer_using
Definition: ethernet65.c:82
ethertap.h
phy_number
int phy_number
Definition: ethernet65.c:94
ETH_FRAME_MIN_SIZE
#define ETH_FRAME_MIN_SIZE
Definition: ethernet65.c:55
addr
int addr
Definition: dma65.c:81
XEMU_INLINE
#define XEMU_INLINE
Definition: emutools_basicdefs.h:126
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
eth65_write_tx_buffer
void eth65_write_tx_buffer(int offset, Uint8 data)
Definition: ethernet65.c:514
ETH_FRAME_MAX_SIZE
#define ETH_FRAME_MAX_SIZE
Definition: ethernet65.c:51
TX_IRQ_ON
#define TX_IRQ_ON()
Definition: ethernet65.c:130
accept_multicast
int accept_multicast
Definition: ethernet65.c:88
Uint8
uint8_t Uint8
Definition: fat32.c:51
tx_buffer
Uint8 tx_buffer[0x0800]
Definition: ethernet65.c:96
tx_trigger
int tx_trigger
Definition: ethernet65.c:79
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
options
int options
Definition: cpmfs.c:41
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
NL
#define NL
Definition: fat32.c:37
eth65_init
int eth65_init(const char *options)
Definition: ethernet65.c:560
compress_sd_image.r
def r
Definition: compress_sd_image.py:76
RX_IRQ_ON
#define RX_IRQ_ON()
Definition: ethernet65.c:128
mac_filter
int mac_filter
Definition: ethernet65.c:89
exited
int exited
Definition: ethernet65.c:73
TX_IRQ_OFF
#define TX_IRQ_OFF()
Definition: ethernet65.c:131
_rx_irq
int _rx_irq
Definition: ethernet65.c:75
SELECT_WAIT_USEC_MAX
#define SELECT_WAIT_USEC_MAX
Definition: ethernet65.c:58
size
int size
Definition: inject.c:37
ETH_II_FRAME_IPV4
#define ETH_II_FRAME_IPV4
Definition: ethernet65.c:66
sense_activity
int sense_activity
Definition: ethernet65.c:84
eth65_read_rx_buffer
Uint8 eth65_read_rx_buffer(int offset)
Definition: ethernet65.c:508
ETH_II_FRAME_ARP
#define ETH_II_FRAME_ARP
Definition: ethernet65.c:65
status
enum @26::@29 status
xemu_always_filters
int xemu_always_filters
Definition: ethernet65.c:90
RX_IRQ_OFF
#define RX_IRQ_OFF()
Definition: ethernet65.c:129
Uint16
uint16_t Uint16
Definition: fat32.c:50
rx_buffer_mapped
int rx_buffer_mapped
Definition: ethernet65.c:83
miim_register
int miim_register
Definition: ethernet65.c:93
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
eth65_shutdown
void eth65_shutdown(void)
Definition: ethernet65.c:521
rx_buffer
Uint8 rx_buffer[0x1000]
Definition: ethernet65.c:95
ETHDEBUG
#define ETHDEBUG(...)
Definition: ethernet65.c:108
rx_enabled
int rx_enabled
Definition: ethernet65.c:74
disable_crc_chk
int disable_crc_chk
Definition: ethernet65.c:91
eth65_read_reg
Uint8 eth65_read_reg(int addr)
Definition: ethernet65.c:353
accept_broadcast
int accept_broadcast
Definition: ethernet65.c:87
_tx_irq
int _tx_irq
Definition: ethernet65.c:76